Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ structures it into clean markdown documents:
documents:
- description: User Authentication System
outputPath: auth.md
showFileStats: false # Optional — omit the auto-generated File Statistics section
sources:
- type: file
sourcePaths: [ src/Auth ]
Expand All @@ -115,6 +116,8 @@ documents:
commit: "last-week"
```

Set `showFileStats: false` per document or globally under `settings.showFileStats: false` to suppress the auto-generated File Statistics section. Per-document takes precedence over the global default (`true`).

### 📐 Declarative Config with JSON Schema

Everything is configured through `context.yaml` with full JSON Schema support. Your AI assistant can generate and modify
Expand Down
29 changes: 26 additions & 3 deletions docs/config/config-system-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ final class Document implements \JsonSerializable
public readonly string $description,
public readonly string $outputPath,
public readonly bool $overwrite = true,
public readonly bool $showFileStats = true,
private array $modifiers = [],
private array $tags = [],
SourceInterface ...$sources,
Expand Down Expand Up @@ -232,16 +233,38 @@ import:
documents:
- description: API Documentation
outputPath: docs/api.md
overwrite: true # Whether to overwrite existing file
tags: [ api, docs ] # Optional tags
modifiers: [ ... ] # Optional modifiers
overwrite: true # Optional, default: true — overwrite existing file
showFileStats: true # Optional, default: true — append the File Statistics section
tags: [ api, docs ] # Optional tags
modifiers: [ ... ] # Optional modifiers
sources:
- type: file # Source type
sourcePaths: # Paths to source files
- src/Api
- src/Controllers
```

### Global Default via `settings.showFileStats`

The `showFileStats` flag can also be set globally for all documents using the `settings` block at the root of
`context.yaml`. The per-document value always takes precedence; when neither level is set the default is `true`.

```yaml
settings:
showFileStats: false # Disable File Statistics for all documents by default

documents:
- description: API Documentation
outputPath: docs/api.md
# Inherits showFileStats: false from settings

- description: Reference
outputPath: docs/reference.md
showFileStats: true # Per-document override — stats shown for this document only
```

**Precedence:** per-document `showFileStats` > `settings.showFileStats` > default (`true`).

## 5. Usage Examples

### Basic Configuration Loading
Expand Down
48 changes: 44 additions & 4 deletions docs/document-compilation-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ final class Document implements \JsonSerializable
public readonly string $description,
public readonly string $outputPath,
public readonly bool $overwrite = true,
public readonly bool $showFileStats = true,
private array $modifiers = [],
private array $tags = [],
SourceInterface ...$sources,
Expand Down Expand Up @@ -165,7 +166,8 @@ A document definition in configuration follows this structure:
documents:
- description: "API Documentation"
outputPath: "docs/api.md"
overwrite: true # Optional, default: true
overwrite: true # Optional, default: true
showFileStats: true # Optional, default: true — set to false to omit the auto-generated File Statistics section
tags: # Optional tags for categorization
- api
- reference
Expand All @@ -187,6 +189,42 @@ documents:
This is custom markdown content.
```

### Global Default via `settings.showFileStats`

The `showFileStats` flag can also be set globally in the `settings` block at the root of `context.yaml`, providing a
project-wide default for all documents:

```yaml
settings:
showFileStats: false # Disable File Statistics section for all documents by default

documents:
- description: "API Documentation"
outputPath: "docs/api.md"
sources:
- type: file
sourcePaths: ["src/Api"]
# Inherits showFileStats: false from settings

- description: "Reference Guide"
outputPath: "docs/reference.md"
showFileStats: true # Per-document override — stats section is shown for this document only
sources:
- type: file
sourcePaths: ["src/Reference"]
```

**Precedence rule:** the per-document `showFileStats` value always wins over the global `settings.showFileStats`.
When neither is set, both default to `true`.

| Setting | Per-document | Global default | Effective value |
|---------|-------------|----------------|-----------------|
| Neither set | — | — | `true` |
| Global only | — | `false` | `false` |
| Per-document only | `true` | — | `true` |
| Both set | `true` | `false` | `true` (per-document wins) |
| Both set | `false` | `true` | `false` (per-document wins) |

## 5. Compilation Process

The document compilation process follows these steps:
Expand All @@ -202,9 +240,10 @@ The document compilation process follows these steps:
- Parse source content
- Apply modifiers (source-specific and document-level)
- Add content to the builder
5. **Error Collection**: Collect any errors that occur during compilation
6. **Directory Creation**: Ensure the output directory exists
7. **File Writing**: Write the compiled content to the output file
5. **File Statistics**: If `showFileStats` is `true` (the default), append the auto-generated File Statistics section summarising each included source file. If `showFileStats` is `false`, the compiled content is written as-is — no statistics block is appended and the prior version of the output file is not read.
6. **Error Collection**: Collect any errors that occur during compilation
7. **Directory Creation**: Ensure the output directory exists
8. **File Writing**: Write the compiled content to the output file

## 6. Usage Examples

Expand All @@ -216,6 +255,7 @@ $document = Document::create(
description: 'API Documentation',
outputPath: 'docs/api.md',
overwrite: true,
showFileStats: true, // Set to false to omit the File Statistics section
tags: ['api', 'reference']
);

Expand Down
1 change: 1 addition & 0 deletions docs/markdown-import/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ The system uses this priority order for determining titles and descriptions:
- `title`/`description`: Document description
- `outputPath`: Where to save the generated document
- `overwrite`: Whether to overwrite existing files
- `showFileStats`: Whether to append the auto-generated File Statistics section (default: `true`). When absent from front-matter, the global `settings.showFileStats` value is used; per-document takes precedence when set.
- `tags`: Array or comma-separated string of tags
- `sources`: Array of source configurations (auto-generated from content if not provided)

Expand Down
10 changes: 10 additions & 0 deletions json-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,11 @@
}
}
}
},
"showFileStats": {
"type": "boolean",
"description": "Global default for whether to append the auto-generated File Statistics section to compiled documents",
"default": true
}
}
},
Expand Down Expand Up @@ -925,6 +930,11 @@
"description": "Whether to overwrite existing files",
"default": true
},
"showFileStats": {
"type": "boolean",
"description": "Whether to append the auto-generated File Statistics section to this document",
"default": true
},
"sources": {
"type": "array",
"description": "List of content sources for this document",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ private function createDocumentResource(string $id, array $metadata, string $con
$document['overwrite'] = (bool) $metadata['overwrite'];
}

// Add showFileStats flag if specified
if (isset($metadata['showFileStats'])) {
$document['showFileStats'] = (bool) $metadata['showFileStats'];
}

// Add tags if present
if (!empty($metadata['tags'])) {
$document['tags'] = $this->normalizeTagsArray($metadata['tags']);
Expand Down
62 changes: 26 additions & 36 deletions src/Document/Compiler/DocumentCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ public function compile(Document $document): CompiledDocument
$this->files->ensureDirectory($directory);

// Add file statistics to the generated content before writing
$finalContent = $this->addFileStatistics($compiledDocument->content, $resultPath, $outputPath);
$finalContent = $document->showFileStats
? $this->addFileStatistics($compiledDocument->content, $outputPath)
: (string) $compiledDocument->content;

$this->logger?->debug('Writing compiled document to file', ['path' => $resultPath]);
$this->files->write($resultPath, $finalContent);
Expand All @@ -83,8 +85,7 @@ public function compile(Document $document): CompiledDocument
} else {
$this->logger?->info('Document compiled successfully', [
'path' => $resultPath,
'contentLength' => \strlen($finalContent),
'fileSize' => $this->files->size($resultPath),
'fileSize' => \strlen($finalContent),
]);
}

Expand Down Expand Up @@ -173,47 +174,36 @@ public function buildContent(ErrorCollection $errors, Document $document): Compi
}

/**
* Add file statistics to the generated content
* Add file statistics to the generated content.
*
* Statistics are computed directly from the in-memory content string — no disk I/O is
* performed. Byte count and line count therefore describe the content as written in the
* current run, including on the very first compilation where the output file does not yet
* exist.
*
* @param string|\Stringable $content The original content
* @param string $resultPath The actual file path where content was written
* @param string $outputPath The configured output path
* @param string $outputPath The configured output path (used as the display path in the stats block)
* @return string The content with file statistics added
*/
private function addFileStatistics(string|\Stringable $content, string $resultPath, string $outputPath): string
private function addFileStatistics(string|\Stringable $content, string $outputPath): string
{
try {
// Check if file exists before attempting to read it
if (!$this->files->exists($resultPath)) {
$this->logger?->debug('File does not exist, skipping statistics', ['path' => $resultPath]);
return (string) $content;
}

$fileSize = $this->files->size($resultPath);
$fileContent = $this->files->read($resultPath);
$lineCount = \substr_count($fileContent, "\n") + 1; // Count lines including the last line
$contentStr = (string) $content;
$fileSize = \strlen($contentStr);
$lineCount = \substr_count($contentStr, "\n") + 1;

// Create a new content builder with the original content
$builder = $this->builderFactory->create();
$builder->addText((string) $content);
// Create a new content builder with the original content
$builder = $this->builderFactory->create();
$builder->addText($contentStr);

// Add file statistics
$this->logger?->debug('Adding file statistics', [
'fileSize' => $fileSize,
'lineCount' => $lineCount,
'filePath' => $outputPath,
]);
// Add file statistics
$this->logger?->debug('Adding file statistics', [
'fileSize' => $fileSize,
'lineCount' => $lineCount,
'filePath' => $outputPath,
]);

$builder->addFileStats($fileSize, $lineCount, $outputPath);
$builder->addFileStats($fileSize, $lineCount, $outputPath);

return $builder->build();
} catch (\Throwable $e) {
$this->logger?->warning('Failed to add file statistics', [
'path' => $resultPath,
'error' => $e->getMessage(),
]);
// Return original content if statistics calculation fails
return (string) $content;
}
return $builder->build();
}
}
7 changes: 7 additions & 0 deletions src/Document/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ final class Document implements \JsonSerializable
* @param string $description Human-readable description
* @param string $outputPath Path where to write the output
* @param bool $overwrite Whether to overwrite the file if it already exists
* @param bool $showFileStats Whether to append the auto-generated File Statistics section
* @param array<Modifier> $modifiers Modifiers to apply to all sources
* @param array<non-empty-string> $tags Tags to apply to the document
*/
public function __construct(
public readonly string $description,
public readonly string $outputPath,
public readonly bool $overwrite = true,
public readonly bool $showFileStats = true,
private array $modifiers = [],
private array $tags = [],
SourceInterface ...$sources,
Expand All @@ -35,19 +37,22 @@ public function __construct(

/**
* @param bool $overwrite Whether to overwrite the file if it already exists
* @param bool $showFileStats Whether to append the auto-generated File Statistics section
* @param array<non-empty-string> $tags Tags to apply to the document
*/
public static function create(
string $description,
string $outputPath,
bool $overwrite = true,
bool $showFileStats = true,
array $modifiers = [],
array $tags = [],
): self {
return new self(
description: $description,
outputPath: $outputPath,
overwrite: $overwrite,
showFileStats: $showFileStats,
modifiers: $modifiers,
tags: $tags,
);
Expand Down Expand Up @@ -152,12 +157,14 @@ public function getSources(): array
return \array_values($this->sources);
}

#[\Override]
public function jsonSerialize(): array
{
return \array_filter([
'description' => $this->description,
'outputPath' => $this->outputPath,
'overwrite' => $this->overwrite,
'showFileStats' => $this->showFileStats,
'sources' => $this->getSources(),
'modifiers' => $this->getModifiers(),
'tags' => $this->getTags(),
Expand Down
15 changes: 15 additions & 0 deletions src/Document/DocumentsParserPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ public function updateConfig(array $config, string $rootPath): array
return $config;
}

/**
* Parse the "documents" section of the configuration and build a DocumentRegistry.
*
* **`showFileStats` resolution order (highest priority first):**
* 1. Per-document `showFileStats` key — when present on a document entry it always takes
* precedence over the global setting.
* 2. Global `settings.showFileStats` — when the per-document key is absent, the value from
* `$config['settings']['showFileStats']` is used.
* 3. Default `true` — when neither the per-document key nor `settings.showFileStats` is
* present, the flag defaults to `true` so the File Statistics section is appended
* (backward-compatible behaviour).
*/
public function parse(array $config, string $rootPath): ?RegistryInterface
{
if (!$this->supports($config)) {
Expand All @@ -48,6 +60,8 @@ public function parse(array $config, string $rootPath): ?RegistryInterface

$registry = new DocumentRegistry();

$globalShowFileStats = (bool) ($config['settings']['showFileStats'] ?? true);

foreach ($config['documents'] as $index => $docData) {
if (!isset($docData['description'], $docData['outputPath'])) {
throw new \RuntimeException(
Expand All @@ -71,6 +85,7 @@ public function parse(array $config, string $rootPath): ?RegistryInterface
description: (string) $docData['description'],
outputPath: (string) $docData['outputPath'],
overwrite: (bool) ($docData['overwrite'] ?? true),
showFileStats: (bool) ($docData['showFileStats'] ?? $globalShowFileStats),
modifiers: $documentModifiers,
tags: $documentTags,
);
Expand Down
Loading
Loading