import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; import { loadCommitConfig } from "../config/load-config.ts"; import { collectGitChanges, resolveGitRepositoryRoot } from "../git/collect-changes.ts"; import { collectRecentCommitSubjects } from "../git/commit-history.ts"; import { createGitCommit } from "../git/commit.ts"; import { GitClient } from "../git/git-client.ts"; import { GenerationCancelledError } from "../llm/generate-message.ts"; import { buildCommitPrompt } from "../prompt/build-prompt.ts"; import { assertTemplateWithinBudget, loadTemplate } from "../templates/load-template.ts"; import { generateWithWorkingState } from "../ui/generation-loader.ts"; import { showCommitReviewDialog } from "../ui/review-dialog.ts"; const STATUS_KEY = "pi-ai-git-commit"; const CANCELLED_MESSAGE = "Cancelled. The Git staging area was not modified and no commit was created."; /** 设置扩展状态栏文本。 */ function setStatus(ctx: ExtensionCommandContext, message: string | undefined): void { ctx.ui.setStatus(STATUS_KEY, message); } /** 执行生成、审阅、重新生成和最终提交的完整工作流。 */ export async function runCommitFlow( pi: ExtensionAPI, ctx: ExtensionCommandContext, instruction: string, ): Promise { const git = new GitClient(pi); const repositoryRoot = await resolveGitRepositoryRoot(git, ctx.cwd); const config = await loadCommitConfig(repositoryRoot); // 模板内容整个工作流只解析一次,重复生成复用同一系统提示词。 const template = await loadTemplate(config.template); if (template.fallbackReason !== undefined) { ctx.ui.notify( `Template "${template.requestedName}" could not be loaded. Using built-in "${template.resolvedName}" instead.\n${template.fallbackReason}`, "warning", ); } assertTemplateWithinBudget(template, config.maxDiffChars); const systemPrompt = template.content; let previousMessage: string | undefined; for (;;) { setStatus(ctx, previousMessage ? "Regenerating commit…" : "Generating commit…"); let context; let generated; try { context = await collectGitChanges( git, repositoryRoot, config.changeSource, config.maxDiffChars, ); const recentCommitSubjects = await collectRecentCommitSubjects( git, context.root, config.commitHistoryCount, ); const prompt = buildCommitPrompt(context, { language: config.language, instructions: config.instructions ?? undefined, instruction, previousMessage, recentCommitSubjects, }); generated = await generateWithWorkingState( config, systemPrompt, prompt, ctx, previousMessage !== undefined, ); } catch (error) { // 用户主动取消生成不是失败,降级为与审阅取消一致的普通提示。 if (error instanceof GenerationCancelledError) { ctx.ui.notify(CANCELLED_MESSAGE, "info"); return; } throw error; } finally { setStatus(ctx, undefined); } const review = await showCommitReviewDialog(generated, context, ctx, { signoff: config.signoff, }); if (review.action === "cancel") { ctx.ui.notify(CANCELLED_MESSAGE, "info"); return; } if (review.action === "regenerate") { // 使用审阅后的消息(可能已被手动编辑),让重新生成能感知用户的修改意图。 previousMessage = review.message; continue; } setStatus(ctx, "Creating Git commit…"); try { const result = await createGitCommit(git, context, review.message, { signoff: config.signoff, }); ctx.ui.notify(`Commit created ${result.hash}\n${review.message}`, "info"); return; } finally { setStatus(ctx, undefined); } } }