✨ feat(vscode): 实施和审查会话行加启动箭头

不用再把卡片拖到「实施中」「审查」列:

- 实施会话id 行在有计划文件且没实施会话时显示 ▶,等同拖进实施中列
- 审查会话id 行在有 PR 时显示 ▶,按 PR 事件那套流程起 codex 审查

Claude-Session: https://claude.ai/code/session_01Kgmb1LvrPmFX3NoUDB4zZC
This commit is contained in:
cruldra committed 2026-09-13 10:52:14 +08:00
1 parent 620b7c73c8
commit 410a04da77
9 files changed
+101 -8

No files matched your search

+4
View File
@@ -439,6 +439,10 @@ export class KanbanWebviewPanel {
void sessions.handleResumeReviewSession(this, msg.sessionId, msg.issueNumber, msg.cwd)
return
}
if (msg.type === 'session/start-review') {
void sessions.handleStartReviewSession(this, msg.issueNumber)
return
}
if (msg.type === 'session/start-test') {
void sessions.handleStartTestSession(this, msg.issueNumber)
return
+52 -1
View File
@@ -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 } from '../../cc/prompts'
import { getBrainstormContinuePrompt, getImplementPlanPrompt, getReviewPrompt } from '../../cc/prompts'
import { projectsDirFor, watchForNewSession } from '../../cc/sessionWatcher'
import { detectRepo } from '../../git/remote'
import { ensureWorktree, resolveWorktreePath } from '../../git/worktree'
@@ -1270,6 +1270,57 @@ export async function handleStartTestSession(panel: KanbanWebviewPanel, issueNum
}
}
/**
* 手动启动审查会话:等同 PR 事件触发的自动审查,只是入口在详情面板的
* 「审查会话id」行。必须已有 PR——审查提示词就是让 codex 去看这个 PR。
*/
export async function handleStartReviewSession(panel: KanbanWebviewPanel, issueNumber: number): Promise<void> {
const workspaceRoot = workspace.workspaceFolders?.[0]?.uri.fsPath
if (!workspaceRoot) {
void window.showErrorMessage('请先打开一个工作区文件夹')
return
}
const lockCheck = await panel.resolveLockedReason(issueNumber)
if (lockCheck.locked) {
await window.showWarningMessage(`等待前置工单 #${lockCheck.prerequisiteNumber} 完成`)
return
}
let pr: string | undefined
let worktreePath = ''
try {
const stateObj = await panel.readIssueState(issueNumber)
if (typeof stateObj.pr === 'string' && stateObj.pr.length > 0)
pr = stateObj.pr.replace(/\(已合并\)\s*$/, '')
if (typeof stateObj.worktreePath === 'string')
worktreePath = stateObj.worktreePath
}
catch (err) {
logger.add({
level: 'warn',
source: 'review',
message: `读取 #${issueNumber} state JSON 失败,无法启动审查会话`,
details: err instanceof Error ? err.message : String(err),
})
}
if (!pr) {
void window.showWarningMessage('该工单还没有 PR,无法启动审查会话')
return
}
const ok = await triggerAutoReviewTab(panel, {
issueNumber,
prNumber: pr,
prompt: getReviewPrompt(panel.context, { prNumber: pr }),
worktreePath,
workspaceRoot,
})
if (!ok)
void window.showErrorMessage(`启动审查会话失败 #${issueNumber},详见扩展日志`)
}
/**
* 恢复一个已存在的测试 cc 会话:详情面板点测试会话行(testTabOpen=false 时)
* 触发,照搬 handleResumeSession 的 implement 分支,但 kind='test'、终端名
+1
View File
@@ -193,6 +193,7 @@ export type WebviewToExtension
| { type: 'session/focus', issueNumber: number }
| { type: 'session/resume-review', sessionId: string, issueNumber: number, cwd?: string }
| { type: 'session/resume-test', sessionId: string, issueNumber: number, cwd?: string }
| { type: 'session/start-review', issueNumber: number }
| { type: 'session/start-test', issueNumber: number }
| { type: 'editor/open-file', path: string }
| { type: 'profiles/list' }
+1
View File
@@ -14,6 +14,7 @@ export const OWNER_ONLY_MESSAGE_TYPES: ReadonlySet<string> = new Set([
'session/focus',
'session/resume-review',
'session/resume-test',
'session/start-review',
'session/start-test',
'session/close-tab',
'brainstorm/continue',