UNPKG

@ideascol/cli-maker

Version:
196 lines (195 loc) 8.18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createAiGuideCommand = createAiGuideCommand; const interfaces_1 = require("./interfaces"); const colors_1 = require("./colors"); function createAiGuideCommand(cli) { return { name: 'ai-guide', description: 'Print machine-readable CLI spec for AI agents (JSON or markdown)', params: [ { name: 'format', description: 'Output format: json (default) or markdown', required: false, type: interfaces_1.ParamType.List, options: ['json', 'markdown'], defaultValue: 'json', }, { name: 'command', description: 'Limit output to a single top-level command by name', required: false, type: interfaces_1.ParamType.Text, }, { name: 'pretty', description: 'Pretty-print JSON output (ignored for markdown)', required: false, type: interfaces_1.ParamType.Boolean, }, ], action: (args) => { const format = (args.format || 'json').toLowerCase(); const allCommands = cli.getCommands(); const commands = args.command ? allCommands.filter((c) => c.name === args.command) : allCommands; if (args.command && commands.length === 0) { console.log(`${colors_1.Colors.Error}Unknown command: ${args.command}${colors_1.Colors.Reset}`); process.exit(1); } const spec = buildSpec(cli, commands); if (format === 'markdown' || format === 'md') { console.log(renderMarkdown(spec)); } else { const pretty = args.pretty !== false; console.log(JSON.stringify(spec, null, pretty ? 2 : 0)); } }, }; } function buildSpec(cli, commands) { return { cli: { name: cli.name, description: cli.description, executable: cli.executableName, version: cli.version, poweredBy: '@ideascol/cli-maker', globalFlags: [ { name: '--help', description: 'Show help for the CLI or a specific command' }, { name: '--version', description: 'Print CLI version' }, { name: '--intro-always', description: 'Force intro animation on this run' }, { name: '--no-intro', description: 'Skip intro animation on this run' }, ], }, invocation: { runHelp: `${cli.executableName} --help`, runVersion: `${cli.executableName} --version`, runCommandHelp: `${cli.executableName} <command> --help`, flagFormats: ['--name value', '--name=value'], interactivePromptsNote: 'When interactive mode is on and required params are missing, the CLI prompts the user. Pass all flags to run non-interactively.', booleanFlagNote: 'Boolean params: presence of the flag (or --flag true) means true.', listFlagNote: 'List params: value must match one of `options` exactly (case-sensitive).', passwordFlagNote: 'Password params are stored encrypted via setup; reading them at runtime may prompt for a passphrase unless CLI_PASSPHRASE env var is set.', }, paramTypes: [ { type: 'text', meaning: 'free-form string' }, { type: 'number', meaning: 'parsed as number' }, { type: 'boolean', meaning: 'true/false flag' }, { type: 'list', meaning: 'one of `options`' }, { type: 'email', meaning: 'validated email' }, { type: 'phone', meaning: 'validated phone' }, { type: 'url', meaning: 'validated URL' }, { type: 'password', meaning: 'hidden input; encrypted at rest when setup encryption is enabled' }, { type: 'Package', meaning: 'npm package name, optionally scoped (@scope/name)' }, { type: 'custom', meaning: 'free-form value, e.g. JSON string' }, ], commands: commands.map((c) => specForCommand(c, cli.executableName, [])), }; } function specForCommand(cmd, exec, path) { const fullPath = [...path, cmd.name]; const paramList = cmd.params .map((p) => (p.required ? `<${p.name}>` : `[${p.name}]`)) .join(' '); const usage = `${exec} ${fullPath.join(' ')}${paramList ? ' ' + paramList : ''}`; return { name: cmd.name, description: cmd.description, usage, params: cmd.params.map(specForParam), subcommands: cmd.subcommands && cmd.subcommands.length > 0 ? cmd.subcommands.map((s) => specForCommand(s, exec, fullPath)) : undefined, }; } function specForParam(p) { const spec = { name: p.name, description: p.description, type: p.type || 'text', required: p.required === true, flag: `--${p.name}`, }; if (p.options) spec.options = p.options; if (typeof p.optionsLoader === 'function') spec.hasOptionsLoader = true; if (typeof p.when === 'function') spec.hasWhen = true; if (p.defaultValue !== undefined) { spec.defaultValue = typeof p.defaultValue === 'function' ? '<dynamic>' : p.defaultValue; } if (p.searchable) spec.searchable = true; if (p.pageSize) spec.pageSize = p.pageSize; return spec; } function renderMarkdown(spec) { const lines = []; lines.push(`# ${spec.cli.name}`); lines.push(''); lines.push(spec.cli.description); lines.push(''); lines.push(`- Executable: \`${spec.cli.executable}\``); if (spec.cli.version) lines.push(`- Version: \`${spec.cli.version}\``); lines.push(`- Powered by: \`${spec.cli.poweredBy}\``); lines.push(''); lines.push('## Invocation'); lines.push(`- Help: \`${spec.invocation.runHelp}\``); lines.push(`- Version: \`${spec.invocation.runVersion}\``); lines.push(`- Command help: \`${spec.invocation.runCommandHelp}\``); lines.push(`- Flag formats: ${spec.invocation.flagFormats.map((f) => `\`${f}\``).join(', ')}`); lines.push(`- ${spec.invocation.interactivePromptsNote}`); lines.push(`- ${spec.invocation.booleanFlagNote}`); lines.push(`- ${spec.invocation.listFlagNote}`); lines.push(`- ${spec.invocation.passwordFlagNote}`); lines.push(''); lines.push('## Global flags'); for (const f of spec.cli.globalFlags) lines.push(`- \`${f.name}\` — ${f.description}`); lines.push(''); lines.push('## Param types'); for (const t of spec.paramTypes) lines.push(`- \`${t.type}\` — ${t.meaning}`); lines.push(''); lines.push('## Commands'); for (const c of spec.commands) appendCommandMarkdown(lines, c, 3); return lines.join('\n'); } function appendCommandMarkdown(lines, cmd, depth) { const hashes = '#'.repeat(Math.min(depth, 6)); lines.push(`${hashes} \`${cmd.name}\``); lines.push(cmd.description); lines.push(''); lines.push(`Usage: \`${cmd.usage}\``); lines.push(''); if (cmd.params.length > 0) { lines.push('Params:'); for (const p of cmd.params) { const flags = [p.required ? 'required' : 'optional', p.type]; if (p.options) flags.push(`options: ${JSON.stringify(p.options)}`); if (p.hasOptionsLoader) flags.push('options loaded dynamically'); if (p.hasWhen) flags.push('conditional'); if (p.defaultValue !== undefined) flags.push(`default: ${JSON.stringify(p.defaultValue)}`); if (p.searchable) flags.push('searchable'); lines.push(`- \`${p.flag}\` — ${p.description} (${flags.join(', ')})`); } lines.push(''); } if (cmd.subcommands) { for (const s of cmd.subcommands) appendCommandMarkdown(lines, s, depth + 1); } }