UNPKG

markmv

Version:

TypeScript CLI for markdown file operations with intelligent link refactoring

257 lines (243 loc) 14.3 kB
#!/usr/bin/env node import { Command } from 'commander'; import { convertCommand } from './commands/convert.js'; import { indexCommand } from './commands/index.js'; import { joinCommand } from './commands/join.js'; import { mergeCommand } from './commands/merge.js'; import { moveCommand } from './commands/move.js'; import { splitCommand } from './commands/split.js'; import { tocCommand } from './commands/toc.js'; import { validateCommand } from './commands/validate.js'; const program = new Command(); program .name('markmv') .description('CLI for markdown file operations with intelligent link refactoring') .version('0.1.0'); program .command('convert') .description('Convert markdown link formats and path resolution') .argument('<files...>', 'Markdown files to convert (supports globs like *.md, **/*.md)') .option('--path-resolution <type>', 'Convert path resolution: absolute|relative') .option('--base-path <path>', 'Base path for relative path calculations (defaults to current directory)') .option('--link-style <style>', 'Convert link style: markdown|claude|combined|wikilink') .option('-r, --recursive', 'Process directories recursively') .option('-d, --dry-run', 'Show what would be changed without making changes') .option('-v, --verbose', 'Show detailed output with processing information') .option('--json', 'Output results in JSON format') .addHelpText('after', ` Examples: $ markmv convert docs/*.md --link-style wikilink --path-resolution relative $ markmv convert README.md --link-style claude --dry-run $ markmv convert **/*.md --path-resolution absolute --recursive $ markmv convert docs/ --link-style combined --recursive --verbose Link Styles: markdown Standard markdown links: [text](url) claude Claude import syntax: @url combined Combined format: [@url](url) wikilink Obsidian wikilinks: [[url]] Path Resolution: absolute Convert to absolute file paths relative Convert to relative file paths from base-path`) .action(convertCommand); program .command('move') .description('Move markdown files while updating cross-references') .argument('<sources...>', 'Source markdown files and destination (supports globs like *.md, **/*.md)') .option('-d, --dry-run', 'Show what would be changed without making changes') .option('-v, --verbose', 'Show detailed output') .option('--json', 'Output results in JSON format') .action(moveCommand); program .command('split') .description('Split large markdown files maintaining link integrity') .argument('<source>', 'Source markdown file to split') .option('-s, --strategy <strategy>', 'Split strategy: headers|size|manual|lines', 'headers') .option('-o, --output <dir>', 'Output directory for split files') .option('-l, --header-level <level>', 'Header level to split on (1-6)', '2') .option('-m, --max-size <kb>', 'Maximum size per section in KB', '100') .option('--split-lines <lines>', 'Comma-separated line numbers to split on (for lines strategy)') .option('-d, --dry-run', 'Show what would be changed without making changes') .option('-v, --verbose', 'Show detailed output') .option('--json', 'Output results in JSON format') .action(splitCommand); program .command('join') .description('Join multiple markdown files resolving conflicts') .argument('<files...>', 'Markdown files to join') .option('-o, --output <file>', 'Output file name') .option('--order-strategy <strategy>', 'Order strategy: alphabetical|manual|dependency|chronological', 'dependency') .option('-d, --dry-run', 'Show what would be changed without making changes') .option('-v, --verbose', 'Show detailed output') .option('--json', 'Output results in JSON format') .action(joinCommand); program .command('merge') .description('Merge markdown content with link reconciliation') .argument('<source>', 'Source markdown file') .argument('<target>', 'Target markdown file to merge into') .option('-s, --strategy <strategy>', 'Merge strategy: append|prepend|interactive', 'interactive') .option('--create-transclusions', 'Create Obsidian transclusions instead of copying content') .option('-d, --dry-run', 'Show what would be changed without making changes') .option('-v, --verbose', 'Show detailed output') .option('--json', 'Output results in JSON format') .action(mergeCommand); program .command('index') .description('Generate index files for markdown documentation') .argument('[directory]', 'Directory to generate indexes for', '.') .option('-t, --type <type>', 'Index type: links|import|embed|hybrid', 'links') .option('-s, --strategy <strategy>', 'Organization strategy: directory|metadata|manual', 'directory') .option('-l, --location <location>', 'Index placement: all|root|branch|existing', 'root') .option('-n, --name <name>', 'Index filename', 'index.md') .option('--embed-style <style>', 'Embed style for embed type: obsidian|markdown', 'obsidian') .option('--template <file>', 'Custom template file') .option('--max-depth <number>', 'Maximum depth to traverse subdirectories', parseInt) .option('--no-traverse-up', 'Prevent traversing above the specified directory') .option('--boundary <path>', 'Explicit boundary path to limit scanning scope') .option('--generate-toc', 'Generate table of contents for each indexed file') .option('--toc-min-depth <number>', 'Minimum heading level for TOC (1-6)', parseInt, 1) .option('--toc-max-depth <number>', 'Maximum heading level for TOC (1-6)', parseInt, 6) .option('--toc-include-line-numbers', 'Include line numbers in table of contents') .option('-d, --dry-run', 'Show what would be generated without creating files') .option('-v, --verbose', 'Show detailed output') .option('--json', 'Output results in JSON format') .addHelpText('after', ` Examples: $ markmv index --type links --strategy directory --generate-toc $ markmv index docs/ --type hybrid --generate-toc --toc-min-depth 2 --toc-max-depth 4 $ markmv index --generate-toc --toc-include-line-numbers --dry-run $ markmv index --type links --strategy metadata --location all --generate-toc Table of Contents Options: --generate-toc Enable TOC generation for indexed files --toc-min-depth <number> Minimum heading level to include (1-6, default: 1) --toc-max-depth <number> Maximum heading level to include (1-6, default: 6) --toc-include-line-numbers Include line numbers in TOC entries Index Types: links Generate simple link lists with optional TOC import Generate Claude import syntax (@path) embed Generate embedded content (Obsidian style) hybrid Generate linked headers with descriptions and optional TOC Organization Strategies: directory Group by directory structure metadata Group by frontmatter categories manual Custom organization (extensible) Index Placement: all Create index in every directory root Create index only in root directory branch Create index in directories with subdirectories existing Update only existing index files`) .action(indexCommand); program .command('barrel') .description('Generate barrel files for themed content aggregation (alias for index)') .argument('[directory]', 'Directory to generate barrel files for', '.') .option('-t, --type <type>', 'Barrel type: links|import|embed|hybrid', 'links') .option('-s, --strategy <strategy>', 'Organization strategy: directory|metadata|manual', 'directory') .option('-l, --location <location>', 'Barrel placement: all|root|branch|existing', 'root') .option('-n, --name <name>', 'Barrel filename', 'index.md') .option('--embed-style <style>', 'Embed style for embed type: obsidian|markdown', 'obsidian') .option('--template <file>', 'Custom template file') .option('--max-depth <number>', 'Maximum depth to traverse subdirectories', parseInt) .option('--no-traverse-up', 'Prevent traversing above the specified directory') .option('--boundary <path>', 'Explicit boundary path to limit scanning scope') .option('--generate-toc', 'Generate table of contents for each indexed file') .option('--toc-min-depth <number>', 'Minimum heading level for TOC (1-6)', parseInt, 1) .option('--toc-max-depth <number>', 'Maximum heading level for TOC (1-6)', parseInt, 6) .option('--toc-include-line-numbers', 'Include line numbers in table of contents') .option('-d, --dry-run', 'Show what would be generated without creating files') .option('-v, --verbose', 'Show detailed output') .option('--json', 'Output results in JSON format') .addHelpText('after', ` Examples: $ markmv barrel --type links --strategy directory --generate-toc $ markmv barrel docs/ --type hybrid --generate-toc --toc-min-depth 2 --toc-max-depth 4 $ markmv barrel --generate-toc --toc-include-line-numbers --dry-run $ markmv barrel --type links --strategy metadata --location all --generate-toc Barrel Types: links Generate simple link lists with optional TOC import Generate Claude import syntax (@path) embed Generate embedded content (Obsidian style) hybrid Generate linked headers with descriptions and optional TOC Organization Strategies: directory Group by directory structure metadata Group by frontmatter categories manual Custom organization (extensible) Barrel Placement: all Create barrel in every directory root Create barrel only in root directory branch Create barrel in directories with subdirectories existing Update only existing barrel files Note: This is an alias for the 'index' command with barrel-focused terminology.`) .action(indexCommand); program .command('toc') .description('Generate and insert table of contents into markdown files') .argument('<files...>', 'Markdown files to process (supports globs like *.md, **/*.md)') .option('--min-depth <number>', 'Minimum heading level to include (1-6)', parseInt, 1) .option('--max-depth <number>', 'Maximum heading level to include (1-6)', parseInt, 6) .option('--include-line-numbers', 'Include line numbers in TOC entries') .option('--position <position>', 'TOC position: top|after-title|before-content|replace', 'after-title') .option('--title <title>', 'TOC title', 'Table of Contents') .option('--heading-level <level>', 'TOC heading level (1-6)', parseInt, 2) .option('--marker <marker>', 'Custom marker for TOC replacement (requires --position replace)') .option('--skip-empty', "Skip files that don't have any headings", true) .option('-d, --dry-run', 'Show what would be changed without making changes') .option('-v, --verbose', 'Show detailed output') .option('--json', 'Output results in JSON format') .addHelpText('after', ` Examples: $ markmv toc README.md $ markmv toc docs/*.md --position after-title --min-depth 2 --max-depth 4 $ markmv toc file.md --position replace --marker "<!-- TOC -->" $ markmv toc **/*.md --title "Contents" --heading-level 3 --include-line-numbers Position Options: top Insert TOC at the very beginning of the file after-title Insert TOC after the first heading (default) before-content Insert TOC before main content (after frontmatter) replace Replace existing TOC using marker or auto-detection TOC Customization: --title <title> Custom TOC title (default: "Table of Contents") --heading-level <level> TOC heading level 1-6 (default: 2, creates ## title) --marker <marker> Custom marker for replacement (e.g., "<!-- TOC -->") --min-depth <number> Minimum heading level to include (1-6, default: 1) --max-depth <number> Maximum heading level to include (1-6, default: 6) --include-line-numbers Include line numbers in TOC entries`) .action(tocCommand); program .command('validate') .description('Find broken links in markdown files') .argument('[files...]', 'Markdown files to validate (supports globs like *.md, **/*.md, defaults to current directory)') .option('--link-types <types>', 'Comma-separated link types to check: internal,external,anchor,image,reference,claude-import') .option('--check-external', 'Enable external HTTP/HTTPS link validation', false) .option('--external-timeout <ms>', 'Timeout for external link validation (ms)', parseInt, 5000) .option('--strict-internal', 'Treat missing internal files as errors', true) .option('--check-claude-imports', 'Validate Claude import paths', true) .option('--check-circular', 'Check for circular references in file dependencies', false) .option('--max-depth <number>', 'Maximum depth to traverse subdirectories', parseInt) .option('--only-broken', 'Show only broken links, not all validation results', true) .option('--group-by <method>', 'Group results by: file|type', 'file') .option('--include-context', 'Include line numbers and context in output', false) .option('-v, --verbose', 'Show detailed output with processing information') .option('--json', 'Output results in JSON format') .addHelpText('after', ` Examples: $ markmv validate # Validate current directory $ markmv validate . # Validate current directory $ markmv validate ./ # Validate current directory $ markmv validate docs/**/*.md --check-external --verbose $ markmv validate README.md --link-types internal,image --include-context $ markmv validate **/*.md --group-by type --only-broken $ markmv validate docs/ --check-circular --strict-internal Link Types: internal Links to other markdown files external HTTP/HTTPS URLs anchor Same-file section links (#heading) image Image references (local and external) reference Reference-style links ([text][ref]) claude-import Claude @import syntax (@path/to/file) Output Options: --group-by file Group broken links by file (default) --group-by type Group broken links by link type`) .action(validateCommand); program.parse(); //# sourceMappingURL=cli.js.map