17 lines
8.8 KiB
JSON
17 lines
8.8 KiB
JSON
[
|
|
{
|
|
"name": "run_npm",
|
|
"arguments": "{\"command\": \"install\"}",
|
|
"parsed_arguments": null
|
|
},
|
|
{
|
|
"name": "modify_project_files",
|
|
"arguments": "{\"files\": [{\"path\": \"src/lib/markdown/index.ts\", \"content\": \"import { fromMarkdown } from 'mdast-util-from-markdown';\\nimport { toString } from 'mdast-util-to-string';\\nimport { Root, RootContent, Heading } from 'mdast';\\nimport { logger } from '../config/logger.js';\\nimport { HeadingNode, HeadingNodeSchema, MarkdownNode } from '../types/schema.js';\\n\\nexport function parseMarkdownToTree(markdown: string): HeadingNode {\\n logger.info('Starting markdown parsing');\\n \\n // Parse markdown to AST\\n const ast = fromMarkdown(markdown) as Root;\\n \\n // Root node for our tree\\n const root: HeadingNode = {\\n title: 'root',\\n depth: 0,\\n children: []\\n };\\n \\n let currentSection: HeadingNode[] = [root];\\n let lastNode = root;\\n \\n try {\\n // Process each node in the AST\\n ast.children.forEach((node: RootContent) => {\\n if (node.type === 'heading') {\\n const headingNode = node as Heading;\\n const depth = headingNode.depth;\\n const title = toString(headingNode); \\n logger.debug(`Processing heading: ${title} at depth ${depth}`); \\n // Create new heading node\\n const newHeadingNode: HeadingNode = {\\n title,\\n depth,\\n children: [],\\n content: ''\\n };\\n \\n // Validate the new node\\n HeadingNodeSchema.parse(newHeadingNode);\\n \\n // Find the appropriate parent for this heading\\n while (currentSection.length > 1 && currentSection[currentSection.length - 1].depth >= depth) {\\n currentSection.pop();\\n }\\n \\n // Add to parent's children\\n currentSection[currentSection.length - 1].children.push(newHeadingNode);\\n \\n // Update current section\\n currentSection.push(newHeadingNode);\\n lastNode = newHeadingNode;\\n } else {\\n // Add content to the current section\\n if (lastNode) {\\n lastNode.content = (lastNode.content || '') + toString(node) + '\\\\n';\\n }\\n }\\n });\\n \\n logger.info('Markdown parsing completed successfully');\\n \\n // Validate the entire tree before returning\\n HeadingNodeSchema.parse(root);\\n return root;\\n \\n } catch (error) {\\n logger.error('Error parsing markdown:', error);\\n throw error;\\n }\\n}\"}, {\"path\": \"src/cli.ts\", \"content\": \"#!/usr/bin/env node\\nimport yargs from 'yargs';\\nimport { hideBin } from 'yargs/helpers';\\nimport { parseMarkdownToTree } from './lib/markdown/index.js';\\nimport { readFileSync } from 'fs';\\n\\n// Export the main function for testing\\nexport async function main(args: string[]) {\\n return yargs(hideBin(args))\\n .command('parse <file>', 'Parse a markdown file into a tree structure', (yargs) => {\\n return yargs.positional('file', {\\n describe: 'The markdown file to parse',\\n type: 'string',\\n demandOption: true\\n })\\n .option('json', {\\n alias: 'j',\\n type: 'boolean',\\n description: 'Output as JSON',\\n default: true\\n });\\n }, async (argv) => {\\n try {\\n const markdown = readFileSync(argv.file, 'utf-8');\\n const tree = parseMarkdownToTree(markdown);\\n console.log(JSON.stringify(tree, null, 2));\\n } catch (error) {\\n console.error('Error:', error instanceof Error ? error.message : error);\\n process.exit(1);\\n }\\n })\\n .demandCommand(1)\\n .strict()\\n .help()\\n .parse();\\n}\\n\\n// Only run if this is the main module\\nif (require.main === module) {\\n main(process.argv).catch((error) => {\\n console.error('Error:', error);\\n process.exit(1);\\n });\\n}\"}, {\"path\": \"src/cli.test.ts\", \"content\": \"import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';\\nimport { join } from 'path';\\nimport fs from 'fs';\\nimport * as markdown from './lib/markdown/index.js';\\nimport { main } from './cli';\\n\\n// Mock the modules\\nvi.mock('fs');\\nvi.mock('./lib/markdown/index.js');\\n\\ndescribe('CLI', () => {\\n const mockMarkdown = '# Test Heading\\\\nTest content';\\n const mockTree = {\\n title: 'root',\\n depth: 0,\\n children: [{\\n title: 'Test Heading',\\n depth: 1,\\n content: 'Test content\\\\n',\\n children: []\\n }]\\n };\\n\\n beforeEach(() => {\\n // Reset all mocks before each test\\n vi.resetAllMocks();\\n \\n // Setup default mock implementations\\n vi.mocked(fs.readFileSync).mockReturnValue(mockMarkdown);\\n vi.mocked(markdown.parseMarkdownToTree).mockReturnValue(mockTree);\\n \\n // Mock console.log and console.error\\n vi.spyOn(console, 'log').mockImplementation(() => {});\\n vi.spyOn(console, 'error').mockImplementation(() => {});\\n vi.spyOn(process, 'exit').mockImplementation(() => undefined as never);\\n });\\n\\n afterEach(() => {\\n vi.clearAllMocks();\\n });\\n\\n it('should parse markdown file and output JSON', async () => {\\n const testFile = 'test.md';\\n await main(['node', 'cli.js', 'parse', testFile]);\\n\\n expect(fs.readFileSync).toHaveBeenCalledWith(testFile, 'utf-8');\\n expect(markdown.parseMarkdownToTree).toHaveBeenCalledWith(mockMarkdown);\\n expect(console.log).toHaveBeenCalledWith(JSON.stringify(mockTree, null, 2));\\n });\\n\\n it('should handle file read errors', async () => {\\n const testFile = 'nonexistent.md';\\n const error = new Error('File not found');\\n vi.mocked(fs.readFileSync).mockImplementation(() => {\\n throw error;\\n });\\n\\n await main(['node', 'cli.js', 'parse', testFile]);\\n \\n expect(console.error).toHaveBeenCalledWith('Error:', 'File not found');\\n expect(process.exit).toHaveBeenCalledWith(1);\\n });\\n});\"}, {\"path\": \"README.md\", \"content\": \"# Markdown Tree Parser\\n\\nA TypeScript utility for parsing markdown files into a hierarchical tree structure based on headings.\\n\\n## Features\\n\\n- Parses markdown content into a tree structure\\n- Maintains heading hierarchy\\n- Preserves content between headings\\n- Type-safe implementation using mdast types\\n- Schema validation using Zod\\n- Structured logging with tslog\\n- Provides comprehensive typing support\\n- Command-line interface (CLI)\\n- Comprehensive test suite using Vitest\\n\\n## Installation\\n\\n```bash\\nnpm install\\n```\\n\\n## Build\\n\\nTo build the project:\\n\\n```bash\\nnpm run build\\n```\\n\\nThis will create the compiled files in the `dist` directory.\\n\\n## Testing\\n\\nThe project uses Vitest for testing. The following commands are available:\\n\\n```bash\\n# Run tests once\\nnpm test\\n\\n# Run tests in watch mode\\nnpm run test:watch\\n\\n# Run tests with coverage\\nnpm run test:coverage\\n```\\n\\n## Usage\\n\\n### CLI Usage\\n\\nAfter installing the package, you can use the CLI tool:\\n\\n```bash\\n# Parse a markdown file and output as JSON\\nmdtree parse example.md\\n\\n# Get help\\nmdtree --help\\n```\\n\\nCLI Options:\\n- `--json, -j`: Output as JSON (default: true)\\n\\n### Programmatic Usage\\n\\n```typescript\\nimport { parseMarkdownToTree } from 'markdown-tree-parser';\\n\\nconst markdown = `\\n# Heading 1\\nSome content\\n\\n## Heading 1.1\\nMore content\\n\\n# Heading 2\\nFinal content\\n`;\\n\\nconst tree = parseMarkdownToTree(markdown);\\nconsole.log(JSON.stringify(tree, null, 2));\\n```\\n\\n## Key Components\\n\\n### Zod Schema Validation\\n\\nWe use Zod for runtime type validation of our data structures. The main schemas are:\\n\\n```typescript\\nconst HeadingNodeSchema = z.lazy(() => z.object({\\n title: z.string(),\\n depth: z.number().min(0).max(6),\\n content: z.string().optional(),\\n children: z.array(HeadingNodeSchema)\\n}));\\n\\ntype HeadingNode = z.infer<typeof HeadingNodeSchema>;\\n```\\n\\n## API\\n\\n### parseMarkdownToTree(markdown: string): HeadingNode\\n\\nConverts a markdown string into a tree structure based on headings. Uses mdast under the hood for reliable markdown parsing. All nodes are validated using Zod schemas.\\n\\n## Dependencies\\n\\n- mdast-util-from-markdown: For parsing markdown into AST\\n- mdast-util-to-string: For converting AST nodes to strings\\n- @types/mdast: For TypeScript type definitions\\n- tslog: For structured logging\\n- zod: For runtime type validation\\n- yargs: For CLI functionality\\n- vitest: For testing\\n\\n## Development\\n\\nTo contribute to the project:\\n\\n1. Install dependencies: `npm install`\\n2. Make your changes\\n3. Run tests: `npm test`\\n4. Build the project: `npm run build`\\n\\n## License\\n\\nMIT\"}]}",
|
|
"parsed_arguments": null
|
|
},
|
|
{
|
|
"name": "build_project",
|
|
"arguments": "",
|
|
"parsed_arguments": null
|
|
}
|
|
] |