Skip to content

Commit

Permalink
Merge pull request #87 from yamadashy/feature/markdown-style
Browse files Browse the repository at this point in the history
feat(style): Add Markdown output style
  • Loading branch information
yamadashy authored Sep 28, 2024
2 parents bc4c744 + cd16787 commit cc2ffc0
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 20 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ coverage/
# Repopack output
repopack-output.txt
repopack-output.xml
repopack-output.md

# ESLint cache
.eslintcache
Expand Down
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,14 +223,53 @@ https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/use-xml-
This means that the XML output from Repopack is not just a different format, but potentially a more effective way to feed your codebase into AI systems for analysis, code review, or other tasks.

#### Markdown Format

To generate output in Markdown format, use the `--style markdown` option:
```bash
repopack --style markdown
```

The Markdown format structures the content in a hierarchical manner:

````markdown
This file is a merged representation of the entire codebase, combining all repository files into a single document.

# File Summary
(Metadata and usage AI instructions)

# Repository Structure
```
src/
cli/
cliOutput.ts
index.ts
```
(...remaining directories)

# Repository Files

## File: src/index.js
```
// File contents here
```

(...remaining files)

# Instruction
(Custom instructions from `output.instructionFilePath`)
````

This format provides a clean, readable structure that is both human-friendly and easily parseable by AI systems.

### Command Line Options

- `-v, --version`: Show tool version
- `-o, --output <file>`: Specify the output file name
- `--include <patterns>`: List of include patterns (comma-separated)
- `-i, --ignore <patterns>`: Additional ignore patterns (comma-separated)
- `-c, --config <path>`: Path to a custom config file
- `--style <style>`: Specify the output style (`plain` or `xml`)
- `--style <style>`: Specify the output style (`plain`, `xml`, `markdown`)
- `--top-files-len <number>`: Number of top files to display in the summary
- `--output-show-line-numbers`: Show line numbers in the output
- `--remote <url>`: Process a remote Git repository
Expand Down Expand Up @@ -290,7 +329,7 @@ Here's an explanation of the configuration options:
| Option | Description | Default |
|--------|-------------|---------|
|`output.filePath`| The name of the output file | `"repopack-output.txt"` |
|`output.style`| The style of the output (`plain`, `xml`) |`"plain"`|
|`output.style`| The style of the output (`plain`, `xml`, `markdown`) |`"plain"`|
|`output.headerText`| Custom text to include in the file header |`null`|
|`output.instructionFilePath`| Path to a file containing detailed custom instructions |`null`|
|`output.removeComments`| Whether to remove comments from supported file types | `false` |
Expand Down
2 changes: 1 addition & 1 deletion src/cli/cliRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export async function run() {
.option('-c, --config <path>', 'path to a custom config file')
.option('--top-files-len <number>', 'specify the number of top files to display', Number.parseInt)
.option('--output-show-line-numbers', 'add line numbers to each line in the output')
.option('--style <type>', 'specify the output style (plain or xml)')
.option('--style <type>', 'specify the output style (plain, xml, markdown)')
.option('--verbose', 'enable verbose logging for detailed output')
.option('--init', 'initialize a new repopack.config.json file')
.option('--global', 'use global configuration (only applicable with --init)')
Expand Down
2 changes: 1 addition & 1 deletion src/config/configTypes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type RepopackOutputStyle = 'plain' | 'xml';
export type RepopackOutputStyle = 'plain' | 'xml' | 'markdown';

interface RepopackConfigBase {
output?: {
Expand Down
81 changes: 81 additions & 0 deletions src/core/output/markdownStyleGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import Handlebars from 'handlebars';
import type { OutputGeneratorContext } from './outputGeneratorTypes.js';
import {
generateHeader,
generateSummaryAdditionalInfo,
generateSummaryFileFormat,
generateSummaryNotes,
generateSummaryPurpose,
generateSummaryUsageGuidelines,
} from './outputStyleDecorator.js';

export const generateMarkdownStyle = (outputGeneratorContext: OutputGeneratorContext) => {
const template = Handlebars.compile(markdownTemplate);

const renderContext = {
generationHeader: generateHeader(outputGeneratorContext.generationDate),
summaryPurpose: generateSummaryPurpose(),
summaryFileFormat: generateSummaryFileFormat(),
summaryUsageGuidelines: generateSummaryUsageGuidelines(
outputGeneratorContext.config,
outputGeneratorContext.instruction,
),
summaryNotes: generateSummaryNotes(outputGeneratorContext.config),
summaryAdditionalInfo: generateSummaryAdditionalInfo(),
headerText: outputGeneratorContext.config.output.headerText,
instruction: outputGeneratorContext.instruction,
treeString: outputGeneratorContext.treeString,
processedFiles: outputGeneratorContext.processedFiles,
};

return `${template(renderContext).trim()}\n`;
};

const markdownTemplate = /* md */ `
{{{generationHeader}}}
# File Summary
## Purpose
{{{summaryPurpose}}}
## File Format
{{{summaryFileFormat}}}
4. Multiple file entries, each consisting of:
a. A header with the file path (## File: path/to/file)
b. The full contents of the file in a code block
## Usage Guidelines
{{{summaryUsageGuidelines}}}
## Notes
{{{summaryNotes}}}
## Additional Info
{{#if headerText}}
### User Provided Header
{{{headerText}}}
{{/if}}
{{{summaryAdditionalInfo}}}
# Repository Structure
\`\`\`
{{{treeString}}}
\`\`\`
# Repository Files
{{#each processedFiles}}
## File: {{{this.path}}}
\`\`\`
{{{this.content}}}
\`\`\`
{{/each}}
{{#if instruction}}
# Instruction
{{{instruction}}}
{{/if}}
`;
4 changes: 4 additions & 0 deletions src/core/output/outputGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { RepopackConfigMerged } from '../../config/configTypes.js';
import { RepopackError } from '../../shared/errorHandler.js';
import { generateTreeString } from '../file/fileTreeGenerator.js';
import type { ProcessedFile } from '../file/fileTypes.js';
import { generateMarkdownStyle } from './markdownStyleGenerator.js';
import type { OutputGeneratorContext } from './outputGeneratorTypes.js';
import { generatePlainStyle } from './plainStyleGenerator.js';
import { generateXmlStyle } from './xmlStyleGenerator.js';
Expand All @@ -21,6 +22,9 @@ export const generateOutput = async (
case 'xml':
output = generateXmlStyle(outputGeneratorContext);
break;
case 'markdown':
output = generateMarkdownStyle(outputGeneratorContext);
break;
default:
output = generatePlainStyle(outputGeneratorContext);
}
Expand Down
28 changes: 13 additions & 15 deletions src/core/output/plainStyleGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,11 @@ import {
generateSummaryUsageGuidelines,
} from './outputStyleDecorator.js';

const PLAIN_SEPARATOR = '='.repeat(16);
const PLAIN_LONG_SEPARATOR = '='.repeat(64);

export const generatePlainStyle = (outputGeneratorContext: OutputGeneratorContext) => {
const template = Handlebars.compile(plainTemplate);

const renderContext = {
generationHeader: generateHeader(outputGeneratorContext.generationDate),
plainSeparator: PLAIN_SEPARATOR,
plainLongSeparator: PLAIN_LONG_SEPARATOR,
summaryPurpose: generateSummaryPurpose(),
summaryFileFormat: generateSummaryFileFormat(),
summaryUsageGuidelines: generateSummaryUsageGuidelines(
Expand All @@ -36,12 +31,15 @@ export const generatePlainStyle = (outputGeneratorContext: OutputGeneratorContex
return `${template(renderContext).trim()}\n`;
};

const PLAIN_SEPARATOR = '='.repeat(16);
const PLAIN_LONG_SEPARATOR = '='.repeat(64);

const plainTemplate = `
{{{generationHeader}}}
{{{plainLongSeparator}}}
${PLAIN_LONG_SEPARATOR}
File Summary
{{{plainLongSeparator}}}
${PLAIN_LONG_SEPARATOR}
Purpose:
--------
Expand Down Expand Up @@ -75,27 +73,27 @@ User Provided Header:
{{{summaryAdditionalInfo}}}
{{{plainLongSeparator}}}
${PLAIN_LONG_SEPARATOR}
Repository Structure
{{{plainLongSeparator}}}
${PLAIN_LONG_SEPARATOR}
{{{treeString}}}
{{{plainLongSeparator}}}
${PLAIN_LONG_SEPARATOR}
Repository Files
{{{plainLongSeparator}}}
${PLAIN_LONG_SEPARATOR}
{{#each processedFiles}}
{{{../plainSeparator}}}
${PLAIN_SEPARATOR}
File: {{{this.path}}}
{{{../plainSeparator}}}
${PLAIN_SEPARATOR}
{{{this.content}}}
{{/each}}
{{#if instruction}}
{{{plainLongSeparator}}}
${PLAIN_LONG_SEPARATOR}
Instruction
{{{plainLongSeparator}}}
${PLAIN_LONG_SEPARATOR}
{{{instruction}}}
{{/if}}
Expand Down
2 changes: 1 addition & 1 deletion src/core/output/xmlStyleGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const generateXmlStyle = (outputGeneratorContext: OutputGeneratorContext)
return `${template(renderContext).trim()}\n`;
};

const xmlTemplate = `
const xmlTemplate = /* xml */ `
{{{generationHeader}}}
<file_summary>
Expand Down
34 changes: 34 additions & 0 deletions tests/core/output/markdownStyleGenerator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import process from 'node:process';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { generateMarkdownStyle } from '../../../src/core/output/markdownStyleGenerator.js';
import { buildOutputGeneratorContext } from '../../../src/core/output/outputGenerator.js';
import { createMockConfig } from '../../testing/testUtils.js';

vi.mock('fs/promises');

describe('outputGenerator', () => {
beforeEach(() => {
vi.resetAllMocks();
});

test('generateMarkdownOutput should include user-provided header text', async () => {
const mockConfig = createMockConfig({
output: {
filePath: 'output.md',
style: 'markdown',
headerText: 'Custom header text',
topFilesLength: 2,
showLineNumbers: false,
removeComments: false,
removeEmptyLines: false,
},
});

const context = await buildOutputGeneratorContext(process.cwd(), mockConfig, [], []);
const output = await generateMarkdownStyle(context);

expect(output).toContain('# File Summary');
expect(output).toContain('# Repository Structure');
expect(output).toContain('# Repository Files');
});
});

0 comments on commit cc2ffc0

Please sign in to comment.