diff --git a/vscode/prompts/test.md b/vscode/prompts/test.md new file mode 100644 index 0000000..f5770d2 --- /dev/null +++ b/vscode/prompts/test.md @@ -0,0 +1,3 @@ +/pr-acceptance-testing 验收 {issueNumber} 号工单({prRef})。 + +验收目标以工单正文为准,不要再问验收哪个。测出问题就停下报告,不要顺手改代码。 diff --git a/vscode/src/cc/prompts.ts b/vscode/src/cc/prompts.ts index 4479d95..345fbc8 100644 --- a/vscode/src/cc/prompts.ts +++ b/vscode/src/cc/prompts.ts @@ -46,6 +46,19 @@ export function getImplementPlanPrompt(ctx: ExtensionContext, vars: { planFile: .join(vars.issueRef ?? String(vars.issueNumber)) } +/** + * 测试会话首个提示词。`prRef` 是给验收技能的定位线索:主 worktree 上是 + * 已合并的 PR,worktree 里就是那条分支。 + */ +export function getTestPrompt(ctx: ExtensionContext, vars: { issueNumber: number, issueRef?: string, prRef: string }): string { + const tpl = getSettings(ctx).testPrompt + return tpl + .split('{issueNumber}') + .join(vars.issueRef ?? String(vars.issueNumber)) + .split('{prRef}') + .join(vars.prRef) +} + export function getReviewPrompt(ctx: ExtensionContext, vars: { prNumber: string }): string { const tpl = getSettings(ctx).reviewPrompt return tpl.split('{prNumber}').join(vars.prNumber) diff --git a/vscode/src/panel/KanbanPanel.ts b/vscode/src/panel/KanbanPanel.ts index b2524a9..6d41ab7 100644 --- a/vscode/src/panel/KanbanPanel.ts +++ b/vscode/src/panel/KanbanPanel.ts @@ -988,6 +988,7 @@ export class KanbanWebviewPanel { implementPlanPrompt: s.implementPlanPrompt, autoReview: s.autoReview, reviewPrompt: s.reviewPrompt, + testPrompt: s.testPrompt, devBranch: s.devBranch, releaseBranchPrefix: s.releaseBranchPrefix, jenkinsJob: s.jenkinsJob, @@ -1076,6 +1077,7 @@ export class KanbanWebviewPanel { implementPlanPrompt: s.implementPlanPrompt, autoReview: s.autoReview, reviewPrompt: s.reviewPrompt, + testPrompt: s.testPrompt, devBranch: s.devBranch, releaseBranchPrefix: s.releaseBranchPrefix, jenkinsJob: s.jenkinsJob, diff --git a/vscode/src/panel/handlers/sessions.ts b/vscode/src/panel/handlers/sessions.ts index ccf3574..8e1e51e 100644 --- a/vscode/src/panel/handlers/sessions.ts +++ b/vscode/src/panel/handlers/sessions.ts @@ -13,7 +13,7 @@ import { buildCcCommand } from '../../cc/ccCommand' import { buildCodexCommandString } from '../../cc/codexCommand' import { watchForNewCodexSession } from '../../cc/codexSessionWatcher' import { resolveProfilePath } from '../../cc/profiles' -import { getBrainstormContinuePrompt, getImplementPlanPrompt, getReviewPrompt } from '../../cc/prompts' +import { getBrainstormContinuePrompt, getImplementPlanPrompt, getReviewPrompt, getTestPrompt } from '../../cc/prompts' import { projectsDirFor, watchForNewSession } from '../../cc/sessionWatcher' import { detectRepo } from '../../git/remote' import { ensureWorktree, resolveWorktreePath } from '../../git/worktree' @@ -1176,8 +1176,14 @@ export async function handleStartTestSession(panel: KanbanWebviewPanel, issueNum } // worktree 里:代码就在当前工作目录,不用 tea 拉取,直接测; - // 主 worktree:PR 已合并,先用 tea 熟悉改动再测。 - const prompt = '/pr-acceptance-testing' + // 主 worktree:PR 已合并,先用 tea 熟悉改动再测。验收目标直接写进提示词, + // 省得验收技能反问要测哪个。 + const prompt = getTestPrompt(panel.context, { + issueNumber, + prRef: inWorktree + ? `worktree 分支 ${branch || effectiveCwd}` + : `PR #${pr}`, + }) if (prompt.includes('\'')) { void window.showErrorMessage('启动测试失败:prompt 含单引号,拒绝执行') return diff --git a/vscode/src/panel/handlers/settings.ts b/vscode/src/panel/handlers/settings.ts index 7b5045a..c94c520 100644 --- a/vscode/src/panel/handlers/settings.ts +++ b/vscode/src/panel/handlers/settings.ts @@ -27,6 +27,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: { implementPlanPrompt: string autoReview: boolean reviewPrompt: string + testPrompt: string devBranch: string releaseBranchPrefix: string jenkinsJob: string @@ -113,6 +114,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: { implementPlanPrompt: payload.implementPlanPrompt || prev.implementPlanPrompt, autoReview: payload.autoReview, reviewPrompt: payload.reviewPrompt || prev.reviewPrompt, + testPrompt: payload.testPrompt || prev.testPrompt, devBranch: trimmedDevBranch || prev.devBranch, releaseBranchPrefix: trimmedReleasePrefix || prev.releaseBranchPrefix, jenkinsJob: trimmedJenkinsJob, @@ -145,6 +147,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: { implementPlanPrompt: payload.implementPlanPrompt, autoReview: payload.autoReview, reviewPrompt: payload.reviewPrompt, + testPrompt: payload.testPrompt, devBranch: trimmedDevBranch, releaseBranchPrefix: trimmedReleasePrefix, jenkinsJob: trimmedJenkinsJob, @@ -254,6 +257,7 @@ export async function handleEditSettingsRequest(panel: KanbanWebviewPanel): Prom implementPlanPrompt: s.implementPlanPrompt, autoReview: s.autoReview, reviewPrompt: s.reviewPrompt, + testPrompt: s.testPrompt, devBranch: s.devBranch, releaseBranchPrefix: s.releaseBranchPrefix, jenkinsJob: s.jenkinsJob, diff --git a/vscode/src/panel/messages.ts b/vscode/src/panel/messages.ts index 9baf1b8..2f188f3 100644 --- a/vscode/src/panel/messages.ts +++ b/vscode/src/panel/messages.ts @@ -76,6 +76,7 @@ export type ExtensionToWebview implementPlanPrompt: string autoReview: boolean reviewPrompt: string + testPrompt: string devBranch: string releaseBranchPrefix: string jenkinsJob: string @@ -158,6 +159,7 @@ export type WebviewToExtension implementPlanPrompt: string autoReview: boolean reviewPrompt: string + testPrompt: string devBranch: string releaseBranchPrefix: string jenkinsJob: string diff --git a/vscode/src/settings/store.ts b/vscode/src/settings/store.ts index b87acfe..90bf858 100644 --- a/vscode/src/settings/store.ts +++ b/vscode/src/settings/store.ts @@ -30,9 +30,11 @@ const FALLBACK_BRAINSTORM_CONTINUE_PROMPT = `/ecc:grilling 讨论下 {issueNumbe const FALLBACK_IMPLEMENT_PLAN_PROMPT = `/ecc:tdd @{planFile} 按计划实施,编码派子代理,发起 PR 时在 body 中包含 "Closes #{issueNumber}"。严禁合并 PR。` +const FALLBACK_TEST_PROMPT = `/pr-acceptance-testing 验收 {issueNumber} 号工单({prRef})。验收目标以工单正文为准,不要再问验收哪个。` + const FALLBACK_REVIEW_PROMPT = `用 tea 拿到 #{prNumber} PR,diff 交给 ecc:code-reviewer 子代理审查。审查意见用 spx pr review-comment 发评论,body 第一行写 。` -type PromptName = 'brainstorm' | 'brainstorm-continue' | 'implement-plan' | 'review' +type PromptName = 'brainstorm' | 'brainstorm-continue' | 'implement-plan' | 'review' | 'test' /** * Read a default prompt template from `${extensionPath}/prompts/${name}.md`. @@ -54,6 +56,7 @@ export function readDefaultPrompt(extensionPath: string, name: PromptName): stri case 'brainstorm-continue': return FALLBACK_BRAINSTORM_CONTINUE_PROMPT case 'implement-plan': return FALLBACK_IMPLEMENT_PLAN_PROMPT case 'review': return FALLBACK_REVIEW_PROMPT + case 'test': return FALLBACK_TEST_PROMPT } } } @@ -87,6 +90,8 @@ export interface Settings { autoReview: boolean /** Prompt template for the auto-review flow. `{prNumber}` placeholder. */ reviewPrompt: string + /** 测试会话首个提示词模板;占位符 {issueNumber} / {prRef}。 */ + testPrompt: string /** Day-to-day development branch (e.g. `main`). Jenkins doesn't watch it. */ devBranch: string /** @@ -266,6 +271,7 @@ function defaults(ctx: ExtensionContext): Settings { implementPlanPrompt: readDefaultPrompt(ctx.extensionPath, 'implement-plan'), autoReview: true, reviewPrompt: readDefaultPrompt(ctx.extensionPath, 'review'), + testPrompt: readDefaultPrompt(ctx.extensionPath, 'test'), devBranch: 'main', releaseBranchPrefix: 'release/', jenkinsJob: '', @@ -319,6 +325,9 @@ export function getSettings(ctx: ExtensionContext): Settings { const reviewPrompt = typeof stored.reviewPrompt === 'string' && stored.reviewPrompt.length > 0 ? stored.reviewPrompt : base.reviewPrompt + const testPrompt = typeof stored.testPrompt === 'string' && stored.testPrompt.length > 0 + ? stored.testPrompt + : base.testPrompt // devBranch always has a usable value — fall back to 'main' if missing/blank // so we never end up trying to fetch the empty string. const devBranch = typeof stored.devBranch === 'string' && stored.devBranch.length > 0 @@ -424,6 +433,7 @@ export function getSettings(ctx: ExtensionContext): Settings { implementPlanPrompt, autoReview, reviewPrompt, + testPrompt, devBranch, releaseBranchPrefix, jenkinsJob, diff --git a/vscode/webview-ui/src/App.tsx b/vscode/webview-ui/src/App.tsx index 512f9c7..1cdc745 100644 --- a/vscode/webview-ui/src/App.tsx +++ b/vscode/webview-ui/src/App.tsx @@ -538,6 +538,7 @@ export function App() { initialImplementPlanPrompt={settings?.implementPlanPrompt ?? ''} initialAutoReview={settings?.autoReview ?? true} initialReviewPrompt={settings?.reviewPrompt ?? ''} + initialTestPrompt={settings?.testPrompt ?? ''} initialDevBranch={settings?.devBranch ?? 'main'} initialReleaseBranchPrefix={settings?.releaseBranchPrefix ?? 'release/'} initialJenkinsJob={settings?.jenkinsJob ?? ''} diff --git a/vscode/webview-ui/src/components/SettingsModal.tsx b/vscode/webview-ui/src/components/SettingsModal.tsx index e790a7c..24d5c78 100644 --- a/vscode/webview-ui/src/components/SettingsModal.tsx +++ b/vscode/webview-ui/src/components/SettingsModal.tsx @@ -25,6 +25,7 @@ interface SubmitValues { implementPlanPrompt: string autoReview: boolean reviewPrompt: string + testPrompt: string devBranch: string releaseBranchPrefix: string jenkinsJob: string @@ -78,6 +79,7 @@ export interface SettingsModalProps { initialImplementPlanPrompt: string initialAutoReview: boolean initialReviewPrompt: string + initialTestPrompt: string initialDevBranch: string initialReleaseBranchPrefix: string initialJenkinsJob: string @@ -167,6 +169,7 @@ export function SettingsModal({ initialImplementPlanPrompt, initialAutoReview, initialReviewPrompt, + initialTestPrompt, initialDevBranch, initialReleaseBranchPrefix, initialJenkinsJob, @@ -208,6 +211,7 @@ export function SettingsModal({ const [implementPlanPrompt, setImplementPlanPrompt] = useState(initialImplementPlanPrompt) const [autoReview, setAutoReview] = useState(initialAutoReview) const [reviewPrompt, setReviewPrompt] = useState(initialReviewPrompt) + const [testPrompt, setTestPrompt] = useState(initialTestPrompt) const [devBranch, setDevBranch] = useState(initialDevBranch) const [releaseBranchPrefix, setReleaseBranchPrefix] = useState(initialReleaseBranchPrefix) const [jenkinsJob, setJenkinsJob] = useState(initialJenkinsJob) @@ -251,6 +255,7 @@ export function SettingsModal({ setImplementPlanPrompt(initialImplementPlanPrompt) setAutoReview(initialAutoReview) setReviewPrompt(initialReviewPrompt) + setTestPrompt(initialTestPrompt) setDevBranch(initialDevBranch) setReleaseBranchPrefix(initialReleaseBranchPrefix) setJenkinsJob(initialJenkinsJob) @@ -342,6 +347,7 @@ export function SettingsModal({ implementPlanPrompt, autoReview, reviewPrompt, + testPrompt, devBranch: devBranch.trim(), releaseBranchPrefix: releaseBranchPrefix.trim(), jenkinsJob: jenkinsJob.trim(), @@ -1002,6 +1008,26 @@ export function SettingsModal({ className={`${inputClass} resize-y font-mono`} /> + + + 可用占位符: + {'{issueNumber}'} + 、 + {'{prRef}'} + (worktree 里是分支,主仓库里是已合并的 PR) + + )} + > +