From a3e557f9ee18231ae76c57752369f6184c18b707 Mon Sep 17 00:00:00 2001 From: cruldra Date: Thu, 16 Jul 2026 01:55:35 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(vscode):=20?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=20state=20schema=20=E4=B8=8E=20knownStateFie?= =?UTF-8?q?lds=20=E6=BC=82=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vscode/cli/cmd/spx/main.go | 35 +++++++++++++----------- vscode/cli/internal/state/schema.json | 17 +++++++++++- vscode/cli/internal/state/schema_test.go | 12 +++++++- vscode/schemas/state-json.schema.json | 17 +++++++++++- vscode/src/youtrack/stateComment.ts | 2 +- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/vscode/cli/cmd/spx/main.go b/vscode/cli/cmd/spx/main.go index f75bc41..f1adb11 100644 --- a/vscode/cli/cmd/spx/main.go +++ b/vscode/cli/cmd/spx/main.go @@ -381,22 +381,25 @@ func mergeAndPostState(rc *runtimeContext, issueNumber int, patch map[string]any // an object carrying at least one of these — mirrors the TypeScript loader so // the two stay in lockstep. var knownStateFields = map[string]struct{}{ - "column": {}, - "sessionId": {}, - "implementSessionId": {}, - "reviewSessionId": {}, - "testSessionId": {}, - "profilePath": {}, - "specFile": {}, - "planFile": {}, - "prDiffFile": {}, - "pr": {}, - "prMerged": {}, - "branch": {}, - "worktreePath": {}, - "implementStatus": {}, - "color": {}, - "autoReview": {}, + "column": {}, + "sessionId": {}, + "implementSessionId": {}, + "reviewSessionId": {}, + "testSessionId": {}, + "profilePath": {}, + "brainstormProfilePath": {}, + "testProfilePath": {}, + "specFile": {}, + "planFile": {}, + "prDiffFile": {}, + "pr": {}, + "prMerged": {}, + "prMergedAt": {}, + "branch": {}, + "worktreePath": {}, + "implementStatus": {}, + "color": {}, + "autoReview": {}, } // parseStateComment returns the parsed map when body is a JSON object carrying diff --git a/vscode/cli/internal/state/schema.json b/vscode/cli/internal/state/schema.json index d36fc77..13c709c 100644 --- a/vscode/cli/internal/state/schema.json +++ b/vscode/cli/internal/state/schema.json @@ -36,6 +36,16 @@ "description": "创建工单时使用的 Claude settings 配置文件绝对路径,resume 时作为 --settings 传入。", "minLength": 1 }, + "brainstormProfilePath": { + "type": "string", + "description": "头脑风暴/规划会话使用的 Claude settings 配置文件绝对路径;与实施 profilePath 独立,空则用默认。", + "minLength": 1 + }, + "testProfilePath": { + "type": "string", + "description": "测试会话使用的 Claude settings 配置文件绝对路径;与实施 profilePath 独立,空则用默认。", + "minLength": 1 + }, "specFile": { "type": "string", "description": "本工单的 spec 文档路径,相对 workspace,必须形如 `dir/.../name.md`。", @@ -63,6 +73,11 @@ "type": "boolean", "description": "关联 PR 是否已合并;优先以 PR API 实时结果为准,此字段是 fallback。" }, + "prMergedAt": { + "type": "string", + "description": "关联 PR 合并时间(ISO 8601);webhook 在标记 prMerged 时写入。", + "minLength": 1 + }, "branch": { "type": "string", "description": "实施分支名,例如 `feature/`。", @@ -70,7 +85,7 @@ }, "worktreePath": { "type": "string", - "description": "实施 worktree 的 workspace 相对路径。", + "description": "实施 worktree 路径:通常为绝对路径,也可为 `~/...` 或 workspace 相对路径;扩展端经 resolveWorktreePath 解析。", "minLength": 1 }, "implementStatus": { diff --git a/vscode/cli/internal/state/schema_test.go b/vscode/cli/internal/state/schema_test.go index 9014e70..380cf68 100644 --- a/vscode/cli/internal/state/schema_test.go +++ b/vscode/cli/internal/state/schema_test.go @@ -70,14 +70,18 @@ func TestValidateAcceptsFullState(t *testing.T) { "sessionId":"abc", "implementSessionId":"impl-1", "reviewSessionId":"rev-1", + "testSessionId":"test-1", "profilePath":"/home/x/.claude/settings.json", + "brainstormProfilePath":"/home/x/.claude/profiles/brainstorm.json", + "testProfilePath":"/home/x/.claude/profiles/test.json", "specFile":"docs/superpowers/specs/foo.md", "planFile":"docs/superpowers/plans/foo.md", "prDiffFile":"docs/pr-diff/pr-42-issue-7.md", "pr":"42", "prMerged":true, + "prMergedAt":"2026-07-16T01:00:00Z", "branch":"feature/abc", - "worktreePath":".worktrees/foo", + "worktreePath":"/home/x/Sources/worktree/proj/slug", "implementStatus":"done", "color":"terminal.ansiBlue", "autoReview":true @@ -86,3 +90,9 @@ func TestValidateAcceptsFullState(t *testing.T) { t.Fatalf("完整合法 state 应当通过校验,得到: %v", err) } } + +func TestValidateAcceptsBrainstormProfilePathOnly(t *testing.T) { + if err := Validate([]byte(`{"brainstormProfilePath":"/home/x/.claude/profiles/brainstorm.json"}`)); err != nil { + t.Fatalf("仅含 brainstormProfilePath 应当通过校验,得到: %v", err) + } +} diff --git a/vscode/schemas/state-json.schema.json b/vscode/schemas/state-json.schema.json index d36fc77..13c709c 100644 --- a/vscode/schemas/state-json.schema.json +++ b/vscode/schemas/state-json.schema.json @@ -36,6 +36,16 @@ "description": "创建工单时使用的 Claude settings 配置文件绝对路径,resume 时作为 --settings 传入。", "minLength": 1 }, + "brainstormProfilePath": { + "type": "string", + "description": "头脑风暴/规划会话使用的 Claude settings 配置文件绝对路径;与实施 profilePath 独立,空则用默认。", + "minLength": 1 + }, + "testProfilePath": { + "type": "string", + "description": "测试会话使用的 Claude settings 配置文件绝对路径;与实施 profilePath 独立,空则用默认。", + "minLength": 1 + }, "specFile": { "type": "string", "description": "本工单的 spec 文档路径,相对 workspace,必须形如 `dir/.../name.md`。", @@ -63,6 +73,11 @@ "type": "boolean", "description": "关联 PR 是否已合并;优先以 PR API 实时结果为准,此字段是 fallback。" }, + "prMergedAt": { + "type": "string", + "description": "关联 PR 合并时间(ISO 8601);webhook 在标记 prMerged 时写入。", + "minLength": 1 + }, "branch": { "type": "string", "description": "实施分支名,例如 `feature/`。", @@ -70,7 +85,7 @@ }, "worktreePath": { "type": "string", - "description": "实施 worktree 的 workspace 相对路径。", + "description": "实施 worktree 路径:通常为绝对路径,也可为 `~/...` 或 workspace 相对路径;扩展端经 resolveWorktreePath 解析。", "minLength": 1 }, "implementStatus": { diff --git a/vscode/src/youtrack/stateComment.ts b/vscode/src/youtrack/stateComment.ts index dce3eb7..cbee03b 100644 --- a/vscode/src/youtrack/stateComment.ts +++ b/vscode/src/youtrack/stateComment.ts @@ -20,7 +20,7 @@ import { addComment, listComments } from './api' export const STATE_MARKER = '' /** Same field set as the Gitea state blob — kept independent to avoid coupling. */ -const KNOWN_STATE_FIELDS = ['column', 'sessionId', 'implementSessionId', 'reviewSessionId', 'testSessionId', 'profilePath', 'brainstormProfilePath', 'testProfilePath', 'specFile', 'planFile', 'prDiffFile', 'pr', 'prMerged', 'branch', 'worktreePath', 'implementStatus', 'color', 'autoReview'] as const +const KNOWN_STATE_FIELDS = ['column', 'sessionId', 'implementSessionId', 'reviewSessionId', 'testSessionId', 'profilePath', 'brainstormProfilePath', 'testProfilePath', 'specFile', 'planFile', 'prDiffFile', 'pr', 'prMerged', 'prMergedAt', 'branch', 'worktreePath', 'implementStatus', 'color', 'autoReview'] as const /** * Parse a comment body into a state object, or null if it isn't a state