Skip to main content
Glama
MIT License
27,120
19,746
  • Linux
  • Apple
cliRun.ts10.6 kB
import process from 'node:process'; import { Option, program } from 'commander'; import pc from 'picocolors'; import { getVersion } from '../core/file/packageJsonParse.js'; import { handleError, RepomixError } from '../shared/errorHandle.js'; import { logger, repomixLogLevels } from '../shared/logger.js'; import { runDefaultAction } from './actions/defaultAction.js'; import { runInitAction } from './actions/initAction.js'; import { runMcpAction } from './actions/mcpAction.js'; import { runRemoteAction } from './actions/remoteAction.js'; import { runVersionAction } from './actions/versionAction.js'; import type { CliOptions } from './types.js'; // Semantic mapping for CLI suggestions // This maps conceptually related terms (not typos) to valid options const semanticSuggestionMap: Record<string, string[]> = { exclude: ['--ignore'], reject: ['--ignore'], omit: ['--ignore'], skip: ['--ignore'], blacklist: ['--ignore'], save: ['--output'], export: ['--output'], out: ['--output'], file: ['--output'], format: ['--style'], type: ['--style'], syntax: ['--style'], debug: ['--verbose'], detailed: ['--verbose'], silent: ['--quiet'], mute: ['--quiet'], add: ['--include'], with: ['--include'], whitelist: ['--include'], clone: ['--remote'], git: ['--remote'], minimize: ['--compress'], reduce: ['--compress'], 'strip-comments': ['--remove-comments'], 'no-comments': ['--remove-comments'], print: ['--stdout'], console: ['--stdout'], terminal: ['--stdout'], pipe: ['--stdin'], }; export const run = async () => { try { program .description('Repomix - Pack your repository into a single AI-friendly file') .argument('[directories...]', 'list of directories to process', ['.']) // Basic Options .optionsGroup('Basic Options') .option('-v, --version', 'Show version information and exit') // CLI Input/Output Options .optionsGroup('CLI Input/Output Options') .addOption( new Option( '--verbose', 'Enable detailed debug logging (shows file processing, token counts, and configuration details)', ).conflicts('quiet'), ) .addOption( new Option('--quiet', 'Suppress all console output except errors (useful for scripting)').conflicts('verbose'), ) .addOption( new Option( '--stdout', 'Write packed output directly to stdout instead of a file (suppresses all logging)', ).conflicts('output'), ) .option('--stdin', 'Read file paths from stdin, one per line (specified files are processed directly)') .option('--copy', 'Copy the generated output to system clipboard after processing') .option( '--token-count-tree [threshold]', 'Show file tree with token counts; optional threshold to show only files with ≥N tokens (e.g., --token-count-tree 100)', (value: string | boolean) => { if (typeof value === 'string') { if (!/^\d+$/.test(value)) { throw new RepomixError(`Invalid token count threshold: '${value}'. Must be a non-negative integer.`); } return Number(value); } return value; }, ) .option( '--top-files-len <number>', 'Number of largest files to show in summary (default: 5, e.g., --top-files-len 20)', (v: string) => { if (!/^\d+$/.test(v)) { throw new RepomixError(`Invalid number for --top-files-len: '${v}'. Must be a non-negative integer.`); } return Number(v); }, ) // Repomix Output Options .optionsGroup('Repomix Output Options') .option('-o, --output <file>', 'Output file path (default: repomix-output.xml, use "-" for stdout)') .option('--style <type>', 'Output format: xml, markdown, json, or plain (default: xml)') .option( '--parsable-style', 'Escape special characters to ensure valid XML/Markdown (needed when output contains code that breaks formatting)', ) .option( '--compress', 'Extract essential code structure (classes, functions, interfaces) using Tree-sitter parsing', ) .option('--output-show-line-numbers', 'Prefix each line with its line number in the output') .option('--no-file-summary', 'Omit the file summary section from output') .option('--no-directory-structure', 'Omit the directory tree visualization from output') .option('--no-files', 'Generate metadata only without file contents (useful for repository analysis)') .option('--remove-comments', 'Strip all code comments before packing') .option('--remove-empty-lines', 'Remove blank lines from all files') .option('--truncate-base64', 'Truncate long base64 data strings to reduce output size') .option('--header-text <text>', 'Custom text to include at the beginning of the output') .option('--instruction-file-path <path>', 'Path to file containing custom instructions to include in output') .option('--include-empty-directories', 'Include folders with no files in directory structure') .option( '--include-full-directory-structure', 'Show entire repository tree in the Directory Structure section, even when using --include patterns', ) .option( '--no-git-sort-by-changes', "Don't sort files by git change frequency (default: most changed files first)", ) .option('--include-diffs', 'Add git diff section showing working tree and staged changes') .option('--include-logs', 'Add git commit history with messages and changed files') .option( '--include-logs-count <count>', 'Number of recent commits to include with --include-logs (default: 50)', (v: string) => { if (!/^\d+$/.test(v)) { throw new RepomixError(`Invalid number for --include-logs-count: '${v}'. Must be a non-negative integer.`); } return Number(v); }, ) // File Selection Options .optionsGroup('File Selection Options') .option( '--include <patterns>', 'Include only files matching these glob patterns (comma-separated, e.g., "src/**/*.js,*.md")', ) .option('-i, --ignore <patterns>', 'Additional patterns to exclude (comma-separated, e.g., "*.test.js,docs/**")') .option('--no-gitignore', "Don't use .gitignore rules for filtering files") .option('--no-default-patterns', "Don't apply built-in ignore patterns (node_modules, .git, build dirs, etc.)") // Remote Repository Options .optionsGroup('Remote Repository Options') .option('--remote <url>', 'Clone and pack a remote repository (GitHub URL or user/repo format)') .option('--remote-branch <name>', "Specific branch, tag, or commit to use (default: repository's default branch)") // Configuration Options .optionsGroup('Configuration Options') .option('-c, --config <path>', 'Use custom config file instead of repomix.config.json') .option('--init', 'Create a new repomix.config.json file with defaults') .option('--global', 'With --init, create config in home directory instead of current directory') // Security Options .optionsGroup('Security Options') .option('--no-security-check', 'Skip scanning for sensitive data like API keys and passwords') // Token Count Options .optionsGroup('Token Count Options') .option( '--token-count-encoding <encoding>', 'Tokenizer model for counting: o200k_base (GPT-4o), cl100k_base (GPT-3.5/4), etc. (default: o200k_base)', ) // MCP .optionsGroup('MCP') .option('--mcp', 'Run as Model Context Protocol server for AI tool integration') .action(commanderActionEndpoint); // Custom error handling function const configOutput = program.configureOutput(); const originalOutputError = configOutput.outputError || ((str, write) => write(str)); program.configureOutput({ outputError: (str, write) => { // Check if this is an unknown option error if (str.includes('unknown option')) { const match = str.match(/unknown option '?(-{1,2}[^ ']+)'?/i); if (match?.[1]) { const unknownOption = match[1]; const cleanOption = unknownOption.replace(/^-+/, ''); // Check if the option has a semantic match const semanticMatches = semanticSuggestionMap[cleanOption]; if (semanticMatches) { // We have a direct semantic match logger.error(`✖ Unknown option: ${unknownOption}`); logger.info(`Did you mean: ${semanticMatches.join(' or ')}?`); return; } } } // Fall back to the original Commander error handler originalOutputError(str, write); }, }); await program.parseAsync(process.argv); } catch (error) { handleError(error); process.exit(1); } }; const commanderActionEndpoint = async (directories: string[], options: CliOptions = {}) => { await runCli(directories, process.cwd(), options); }; export const runCli = async (directories: string[], cwd: string, options: CliOptions) => { // Detect stdout mode // NOTE: For compatibility, currently not detecting pipe mode const isForceStdoutMode = options.output === '-'; if (isForceStdoutMode) { options.stdout = true; } // Set log level based on verbose and quiet flags if (options.quiet) { logger.setLogLevel(repomixLogLevels.SILENT); } else if (options.verbose) { logger.setLogLevel(repomixLogLevels.DEBUG); } else { logger.setLogLevel(repomixLogLevels.INFO); } // In stdout mode, set log level to SILENT if (options.stdout) { logger.setLogLevel(repomixLogLevels.SILENT); } logger.trace('directories:', directories); logger.trace('cwd:', cwd); logger.trace('options:', options); if (options.mcp) { return await runMcpAction(); } if (options.version) { await runVersionAction(); return; } // Skip version header in stdin mode to avoid interfering with piped output from interactive tools like fzf if (!options.stdin) { const version = await getVersion(); logger.log(pc.dim(`\n📦 Repomix v${version}\n`)); } if (options.init) { await runInitAction(cwd, options.global || false); return; } if (options.remote) { return await runRemoteAction(options.remote, options); } return await runDefaultAction(directories, cwd, options); };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yamadashy/repomix'

If you have feedback or need assistance with the MCP directory API, please join our Discord server