/** * codet-pi-init — Pi Extension (薄桥接 + Skill) * * 注册命令: * - /init 默认走 Skill:pi.sendUserMessage 调用 /skill:pi-init * 让模型用 bash/read/write 扫描项目并生成 AGENTS.md(Pi-native) * - /init --template [] 离线确定性:读模板填充占位符,写 AGENTS.md,不调模型 * - /init --dry-run 只输出 LLM 提示词,不写文件 * - /init-check 检查已有 AGENTS.md 质量 * * 与 CodeT 实践方案对齐:/init 形式为 Skill(MD),逻辑活在文本资产里; * 本扩展仅做命令入口与离线兜底,不自己扫描生成(--template 除外)。 */ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; import { runInitAsync, collectProjectInfo, buildPrompt, buildRefineMessage, DEFAULT_FILES, type HostContext, type InitOptions, } from "../src/core.ts"; import { checkAgentsMd } from "../src/check.ts"; import { parseStringArgs, HELP_TEXT } from "../src/args.ts"; /** 把 Pi 的 ctx.ui.notify 适配成宿主无关的 HostContext */ function makeHost(ctx: ExtensionCommandContext): HostContext { return { notify: (message, level = "info") => ctx.ui.notify(message, level), }; } /** 调用 Skill 路径:通过 pi.sendUserMessage 触发 /skill:pi-init */ function triggerSkill( pi: ExtensionAPI, ctx: ExtensionCommandContext, args: string ): void { const skillInvocation = `/skill:pi-init ${args}`.trim(); if (ctx.isIdle()) { ctx.ui.notify("🤖 调用 pi-init Skill,由模型扫描项目并生成 AGENTS.md...", "info"); pi.sendUserMessage(skillInvocation); } else { ctx.ui.notify("Agent 正忙,pi-init Skill 已排队(稍后自动执行)。", "info"); pi.sendUserMessage(skillInvocation, { deliverAs: "followUp" }); } } export default function codetPiInitExtension(pi: ExtensionAPI): void { pi.registerCommand("init", { description: "扫描项目并生成 AGENTS.md。默认走 Skill;--template 用模板打底再让模型完善", handler: async (args, ctx) => { let opts; try { opts = parseStringArgs(args ?? ""); } catch (err) { const msg = err instanceof Error ? err.message : String(err); ctx.ui.notify(`❌ ${msg}。运行 /init --help 查看用法。`, "error"); return; } if (opts.help) { ctx.ui.notify(HELP_TEXT, "info"); return; } const options: InitOptions = { force: opts.force, files: opts.files, dryRun: opts.dryRun, depth: opts.depth, template: opts.template, templatePath: opts.templatePath, }; // --dry-run:输出 LLM 提示词,不写文件、不调模型 if (opts.dryRun) { const info = collectProjectInfo(ctx.cwd, opts.depth); ctx.ui.notify("--- DRY RUN: LLM 提示词 ---", "info"); console.log(buildPrompt(info)); return; } // --template:先用模板填充写 AGENTS.md,再触发模型回合让 LLM 完善 if (opts.template) { const result = await runInitAsync(ctx.cwd, options, makeHost(ctx)); if (!result.ok) { ctx.ui.notify("💡 可用 --force 覆盖已存在的文件。", "info"); return; } const info = collectProjectInfo(ctx.cwd, opts.depth); const files = opts.files?.length ? opts.files : DEFAULT_FILES; const message = buildRefineMessage(info, files); if (ctx.isIdle()) { ctx.ui.notify("🤖 正在让模型完善规范文件...", "info"); pi.sendUserMessage(message); } else { ctx.ui.notify("Agent 正忙,完善任务已排队(稍后自动执行)。", "info"); pi.sendUserMessage(message, { deliverAs: "followUp" }); } return; } // 默认:走 Skill 路径,由模型用工具扫描+写 AGENTS.md triggerSkill(pi, ctx, args ?? ""); }, }); pi.registerCommand("init-check", { description: "检查当前项目已有的 AI 规范文件(AGENTS.md)质量", handler: async (_args, ctx) => { checkAgentsMd(ctx.cwd, (message, level = "info") => ctx.ui.notify(message, level)); }, }); pi.on("session_start", async (_event, ctx) => { ctx.ui.notify("codet-pi-init 已加载:/init, /init-check", "info"); }); }