/** * 共享参数解析 * * Pi 的 registerCommand handler 收到的是原始字符串 args(如 "--force -o agent.md"), * CLI 收到的是 process.argv 数组。两者都解析到同一个 InitCliArgs。 */ import { DEFAULT_FILES } from "./core.ts"; export interface InitCliArgs { force: boolean; files: string[]; dryRun: boolean; depth: number; help: boolean; dir?: string; check?: boolean; print?: boolean; /** 用模板填充生成 AGENTS.md,随后让模型完善 */ template?: boolean; /** 自定义模板路径(配合 --template) */ templatePath?: string; } export function defaultArgs(): InitCliArgs { return { force: false, files: [...DEFAULT_FILES], dryRun: false, depth: 3, help: false, }; } /** 解析 "key value" / "--key=value" / 裸 flag */ function parseToken(args: InitCliArgs, token: string, next: () => string | undefined): void { let key = token; let inlineValue: string | undefined; const eq = token.indexOf("="); if (token.startsWith("--") && eq > 0) { key = token.slice(0, eq); inlineValue = token.slice(eq + 1); } switch (key) { case "-f": case "--force": args.force = true; break; case "-d": case "--dry-run": case "--prompt": args.dryRun = true; break; case "-h": case "--help": args.help = true; break; case "--check": args.check = true; break; case "--print": args.print = true; break; case "--template": args.template = true; // 允许 --template=./path.md;空格形式 --template ./path.md 由调用方 peek 处理 if (inlineValue) args.templatePath = inlineValue; break; case "-o": case "--output": { const v = inlineValue ?? next(); if (v) args.files = [v]; break; } case "-F": case "--files": { const v = inlineValue ?? next(); if (v) { args.files = v .split(",") .map((f) => f.trim()) .filter(Boolean); } break; } case "-D": case "--depth": { const v = inlineValue ?? next(); const n = parseInt(v ?? "", 10); if (!Number.isNaN(n) && n >= 1) args.depth = n; break; } case "--dir": { const v = inlineValue ?? next(); if (v) args.dir = v; break; } default: if (key.startsWith("-") && key !== "-") { throw new Error(`未知参数: ${key}`); } } } /** 从空格分隔的字符串解析(Pi 命令 args) */ export function parseStringArgs(raw: string): InitCliArgs { const args = defaultArgs(); const tokens = raw.split(/\s+/).filter(Boolean); let i = 0; while (i < tokens.length) { const token = tokens[i]; i++; parseToken(args, token, () => tokens[i++]); // --template 后面若跟一个非 flag 的路径,作为自定义模板路径 if (token === "--template" && args.template && !args.templatePath) { const nextTok = tokens[i]; if (nextTok && !nextTok.startsWith("-")) { args.templatePath = nextTok; i++; } } } return args; } /** 从 argv 数组解析(CLI) */ export function parseArgvArgs(argv: string[]): InitCliArgs { const args = defaultArgs(); let i = 0; while (i < argv.length) { const token = argv[i]; i++; parseToken(args, token, () => argv[i++]); if (token === "--template" && args.template && !args.templatePath) { const nextTok = argv[i]; if (nextTok && !nextTok.startsWith("-")) { args.templatePath = nextTok; i++; } } } return args; } export const HELP_TEXT = `codet-pi-init - 在任意项目下生成 AI 编程助手规范文件(AGENTS.md) 用法: /init [选项] # Pi 中 codet-pi-init [选项] # CLI 中 选项: -f, --force 强制覆盖已存在的文件 -o, --output 只输出到单个文件(默认: AGENTS.md) -F, --files 逗号分隔的输出文件列表,如 "AGENTS.md,CLAUDE.md" -d, --dry-run 只输出 LLM 提示词,不写文件 -D, --depth 目录扫描深度(默认: 3) --template [

] 用模板生成 AGENTS.md 并让模型完善;可跟自定义模板路径 --dir 指定目标项目目录(默认: 当前目录) -h, --help 显示帮助 `;