✨ feat(vscode): 新增第五种工单会话「原型会话」

照测试会话那条链路补一份 prototype:

- 消息与路由:session/start-prototype、session/resume-prototype、
  issue/update-prototype-profile-path,都进只读门禁
- 会话:handleStartPrototypeSession / handleResumePrototypeSession,
  终端名 issue-N-原型;cwd 有 worktree 用 worktree、没有退主仓库根
  (原 resolveTestSessionCwd 改名 resolveWorktreeOrRootCwd 两种会话共用)
- 不要求有 PR,任何阶段都能起;前置工单锁仍然拦
- 提示词:新增 prompts/prototype.md,设置里可改,占位符 {issueNumber}
- profile:工单级 prototypeProfilePath > 设置里的原型默认 profile >
  内置默认,与其它会话互不回退
- 状态字段 prototypeSessionId / prototypeProfilePath / prototypeTabOpen
  贯通 gitea、youtrack、本机 state 与移交包
- 详情面板加「原型会话id」「原型配置文件」两行,设置面板加对应两项

Claude-Session: https://claude.ai/code/session_01Kgmb1LvrPmFX3NoUDB4zZC
This commit is contained in:
cruldra committed 2026-09-14 10:20:30 +08:00
1 parent 3fff796a61
commit b62e58d739
27 files changed
+760 -50

No files matched your search

+16
View File
@@ -70,6 +70,7 @@ function parseColumnFromComments(comments: GiteaComment[]): {
profilePath?: string
brainstormProfilePath?: string
testProfilePath?: string
prototypeProfilePath?: string
specFile?: string
planFile?: string
prDiffFile?: string
@@ -82,6 +83,7 @@ function parseColumnFromComments(comments: GiteaComment[]): {
implementSessionId?: string
reviewSessionId?: string
testSessionId?: string
prototypeSessionId?: string
color?: string
autoReview?: boolean
handoffAttachmentId?: string
@@ -116,6 +118,7 @@ function parseColumnFromComments(comments: GiteaComment[]): {
profilePath?: unknown
brainstormProfilePath?: unknown
testProfilePath?: unknown
prototypeProfilePath?: unknown
specFile?: unknown
planFile?: unknown
prDiffFile?: unknown
@@ -128,6 +131,7 @@ function parseColumnFromComments(comments: GiteaComment[]): {
implementSessionId?: unknown
reviewSessionId?: unknown
testSessionId?: unknown
prototypeSessionId?: unknown
color?: unknown
autoReview?: unknown
handoffAttachmentId?: unknown
@@ -146,6 +150,9 @@ function parseColumnFromComments(comments: GiteaComment[]): {
const testProfilePath = typeof obj.testProfilePath === 'string' && obj.testProfilePath.length > 0
? obj.testProfilePath
: undefined
const prototypeProfilePath = typeof obj.prototypeProfilePath === 'string' && obj.prototypeProfilePath.length > 0
? obj.prototypeProfilePath
: undefined
const specFile = isValidSpxFilePath(obj.specFile) ? obj.specFile : undefined
const planFile = isValidSpxFilePath(obj.planFile) ? obj.planFile : undefined
const prDiffFile = isValidPrDiffFilePath(obj.prDiffFile) ? obj.prDiffFile : undefined
@@ -176,6 +183,9 @@ function parseColumnFromComments(comments: GiteaComment[]): {
const testSessionId = typeof obj.testSessionId === 'string' && obj.testSessionId.length > 0
? obj.testSessionId
: undefined
const prototypeSessionId = typeof obj.prototypeSessionId === 'string' && obj.prototypeSessionId.length > 0
? obj.prototypeSessionId
: undefined
const color = typeof obj.color === 'string' && obj.color.length > 0
? obj.color
: undefined
@@ -192,6 +202,7 @@ function parseColumnFromComments(comments: GiteaComment[]): {
profilePath,
brainstormProfilePath,
testProfilePath,
prototypeProfilePath,
specFile,
planFile,
prDiffFile,
@@ -204,6 +215,7 @@ function parseColumnFromComments(comments: GiteaComment[]): {
implementSessionId,
reviewSessionId,
testSessionId,
prototypeSessionId,
color,
autoReview,
handoffAttachmentId,
@@ -333,6 +345,7 @@ async function buildIssue(opts: {
profilePath,
brainstormProfilePath,
testProfilePath,
prototypeProfilePath,
specFile,
planFile,
prDiffFile,
@@ -345,6 +358,7 @@ async function buildIssue(opts: {
implementSessionId,
reviewSessionId,
testSessionId,
prototypeSessionId,
color,
autoReview,
handoffAttachmentId,
@@ -395,6 +409,7 @@ async function buildIssue(opts: {
...(profilePath ? { profilePath } : {}),
...(brainstormProfilePath ? { brainstormProfilePath } : {}),
...(testProfilePath ? { testProfilePath } : {}),
...(prototypeProfilePath ? { prototypeProfilePath } : {}),
...(specFile ? { specFile } : {}),
...(planFile ? { planFile } : {}),
...(prDiffFile ? { prDiffFile } : {}),
@@ -408,6 +423,7 @@ async function buildIssue(opts: {
...(implementSessionId ? { implementSessionId } : {}),
...(reviewSessionId ? { reviewSessionId } : {}),
...(testSessionId ? { testSessionId } : {}),
...(prototypeSessionId ? { prototypeSessionId } : {}),
...(prerequisite !== undefined ? { prerequisite } : {}),
...(color ? { color } : {}),
...(autoReview !== undefined ? { autoReview } : {}),
+1 -1
View File
@@ -19,7 +19,7 @@ import { listIssueComments, postIssueComment } from './api'
* 普通文本评论、审查评论、Gitea 自动关联评论都不含这些字段,因此从尾往前
* 扫描时会被跳过,避免它们插队后下一次 merge 从空状态开始丢历史。
*/
const KNOWN_STATE_FIELDS = ['column', 'sessionId', 'implementSessionId', 'reviewSessionId', 'testSessionId', 'profilePath', 'brainstormProfilePath', 'testProfilePath', 'specFile', 'planFile', 'prDiffFile', 'pr', 'prMerged', 'prMergedAt', 'branch', 'worktreePath', 'implementStatus', 'color', 'autoReview', 'handoffAttachmentId', 'handoffFrom'] as const
const KNOWN_STATE_FIELDS = ['column', 'sessionId', 'implementSessionId', 'reviewSessionId', 'testSessionId', 'prototypeSessionId', 'profilePath', 'brainstormProfilePath', 'testProfilePath', 'prototypeProfilePath', 'specFile', 'planFile', 'prDiffFile', 'pr', 'prMerged', 'prMergedAt', 'branch', 'worktreePath', 'implementStatus', 'color', 'autoReview', 'handoffAttachmentId', 'handoffFrom'] as const
/**
* body 能 parse 成 object 且含至少一个已知 state 字段 → 认为是 state JSON。
+9
View File
@@ -45,6 +45,8 @@ export interface Issue {
brainstormProfilePath?: string
/** 测试会话专用的 Claude 配置文件;空 = DEFAULT_PROFILE_PATH,不回退其它字段。 */
testProfilePath?: string
/** 原型会话专用的 Claude 配置文件;空 = DEFAULT_PROFILE_PATH,不回退其它字段。 */
prototypeProfilePath?: string
/**
* Optional workspace-relative path to the spec file for this issue, as
* surfaced from the Claude session transcript. Lives under
@@ -100,6 +102,11 @@ export interface Issue {
* is merged; reused for resume from the detail panel.
*/
testSessionId?: string
/**
* 原型会话(Claude Code session id)。详情面板点原型会话行的 ▶ 启动后写入,
* 之后该行改为可 resume。与 PR 无关,任何阶段都能起。
*/
prototypeSessionId?: string
/** Browser URL to the Gitea issue page (escape hatch button in the UI). */
htmlUrl: string
/** 前置任务 issue number (Gitea dependencies 取第一个) */
@@ -132,4 +139,6 @@ export interface Issue {
reviewTabOpen?: boolean
/** Whether the test terminal tab is currently alive in this VS Code window. */
testTabOpen?: boolean
/** Whether the prototype terminal tab is currently alive in this VS Code window. */
prototypeTabOpen?: boolean
}