🐛 fix(vscode): 修复看板缓存并增加问答默认模型
This commit is contained in:
1 parent
c38f1dc468
commit
012daf95b5
25 files changed
+1753
-164
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import type { Terminal } from 'vscode'
|
||||
import type { CodexSessionLaunch } from '../../cc/codexSessionWatcher'
|
||||
import type { IssueRef } from '../../issues/stateRouter'
|
||||
import type { KanbanWebviewPanel } from '../KanbanPanel'
|
||||
import { execFile } from 'node:child_process'
|
||||
import * as path from 'node:path'
|
||||
@@ -11,6 +12,7 @@ import { logger } from '../../logging/logger'
|
||||
import { defaultCodexSessionsDir } from '../../sessions/sessionFiles'
|
||||
|
||||
interface ReviewCapture {
|
||||
source: NonNullable<IssueRef['source']>
|
||||
abort: AbortController
|
||||
launch: CodexSessionLaunch
|
||||
terminal?: Terminal
|
||||
@@ -18,7 +20,7 @@ interface ReviewCapture {
|
||||
}
|
||||
|
||||
const captures = new WeakMap<KanbanWebviewPanel, Map<number, ReviewCapture>>()
|
||||
const recoveries = new WeakMap<KanbanWebviewPanel, Map<number, { abort: AbortController, result: Promise<string | null> }>>()
|
||||
const recoveries = new WeakMap<KanbanWebviewPanel, Map<number, { source: ReviewCapture['source'], abort: AbortController, result: Promise<string | null> }>>()
|
||||
|
||||
function captureMap(panel: KanbanWebviewPanel): Map<number, ReviewCapture> {
|
||||
let map = captures.get(panel)
|
||||
@@ -29,17 +31,20 @@ function captureMap(panel: KanbanWebviewPanel): Map<number, ReviewCapture> {
|
||||
return map
|
||||
}
|
||||
|
||||
function launchKey(panel: KanbanWebviewPanel, issueNumber: number): string {
|
||||
return `superpowers.reviewSessionLaunch.${panel.refFor(issueNumber).source ?? 'gitea'}:${issueNumber}`
|
||||
function launchKey(source: ReviewCapture['source'], issueNumber: number): string {
|
||||
return `superpowers.reviewSessionLaunch.${source}:${issueNumber}`
|
||||
}
|
||||
|
||||
/** 用户清除关联时先中止捕获,防止稍后落盘的文件把关联补回来。 */
|
||||
export async function cancelReviewSessionCapture(panel: KanbanWebviewPanel, issueNumber: number): Promise<void> {
|
||||
const map = captureMap(panel)
|
||||
recoveries.get(panel)?.get(issueNumber)?.abort.abort()
|
||||
map.get(issueNumber)?.abort.abort()
|
||||
const capture = map.get(issueNumber)
|
||||
const recovery = recoveries.get(panel)?.get(issueNumber)
|
||||
const source = capture?.source ?? recovery?.source ?? panel.refFor(issueNumber).source ?? 'gitea'
|
||||
recovery?.abort.abort()
|
||||
capture?.abort.abort()
|
||||
map.delete(issueNumber)
|
||||
await panel.context.workspaceState.update(launchKey(panel, issueNumber), undefined)
|
||||
await panel.context.workspaceState.update(launchKey(source, issueNumber), undefined)
|
||||
}
|
||||
|
||||
export function stopReviewSessionCaptures(panel: KanbanWebviewPanel): void {
|
||||
@@ -64,27 +69,32 @@ function reportCaptureFailure(issueNumber: number, err: unknown): void {
|
||||
async function persistCapture(panel: KanbanWebviewPanel, issueNumber: number, capture: ReviewCapture, id: string): Promise<string | null> {
|
||||
if (capture.abort.signal.aborted || captureMap(panel).get(issueNumber) !== capture || !capture.terminal)
|
||||
return null
|
||||
await mergeLocalIssueState(panel.context, panel.refFor(issueNumber).source, issueNumber, { reviewSessionId: id })
|
||||
await mergeLocalIssueState(panel.context, capture.source, issueNumber, { reviewSessionId: id })
|
||||
if (capture.abort.signal.aborted)
|
||||
return null
|
||||
panel.reviewTerminals.set(id, capture.terminal)
|
||||
panel.trackSessionTerminal(capture.terminal, issueNumber, 'review')
|
||||
panel.postMessage({ type: 'issue/patch', issueNumber, patch: { reviewSessionId: id } })
|
||||
await panel.context.workspaceState.update(launchKey(panel, issueNumber), undefined)
|
||||
await panel.context.workspaceState.update(launchKey(capture.source, issueNumber), undefined)
|
||||
logger.add({ level: 'info', source: 'review', message: `已捕获审查会话 ${id} (#${issueNumber})` })
|
||||
return id
|
||||
}
|
||||
|
||||
/** 返回前完成目录确认和首次扫描,调用方随后才创建终端。 */
|
||||
export async function prepareReviewSessionCapture(panel: KanbanWebviewPanel, issueNumber: number, launch: CodexSessionLaunch): Promise<{
|
||||
export async function prepareReviewSessionCapture(
|
||||
panel: KanbanWebviewPanel,
|
||||
issueNumber: number,
|
||||
launch: CodexSessionLaunch,
|
||||
source: ReviewCapture['source'] = panel.refFor(issueNumber).source ?? 'gitea',
|
||||
): Promise<{
|
||||
attach: (terminal: Terminal) => Promise<string | null>
|
||||
initialSessionId: string | null
|
||||
}> {
|
||||
captureMap(panel).get(issueNumber)?.abort.abort()
|
||||
const capture: ReviewCapture = { abort: new AbortController(), launch }
|
||||
const capture: ReviewCapture = { source, abort: new AbortController(), launch }
|
||||
captureMap(panel).set(issueNumber, capture)
|
||||
try {
|
||||
await panel.context.workspaceState.update(launchKey(panel, issueNumber), launch)
|
||||
await panel.context.workspaceState.update(launchKey(source, issueNumber), launch)
|
||||
const watch = await watchForCodexSession({
|
||||
...launch,
|
||||
baseDir: defaultCodexSessionsDir(),
|
||||
@@ -214,8 +224,8 @@ function terminalProcessId(terminal: Terminal, signal: AbortSignal): Promise<num
|
||||
}
|
||||
|
||||
/** 已有文件立即补关联;尚未落盘则后台等待,刷新不会等完整审查运行。 */
|
||||
async function recoverReviewSessionCaptureInner(panel: KanbanWebviewPanel, issueNumber: number, terminal: Terminal, signal: AbortSignal): Promise<string | null> {
|
||||
const local = getLocalIssueState(panel.context, panel.refFor(issueNumber).source, issueNumber)
|
||||
async function recoverReviewSessionCaptureInner(panel: KanbanWebviewPanel, issueNumber: number, terminal: Terminal, signal: AbortSignal, source: ReviewCapture['source']): Promise<string | null> {
|
||||
const local = getLocalIssueState(panel.context, source, issueNumber)
|
||||
if (local.reviewSessionId === '' || captureMap(panel).has(issueNumber))
|
||||
return null
|
||||
if (local.reviewSessionId)
|
||||
@@ -230,11 +240,11 @@ async function recoverReviewSessionCaptureInner(panel: KanbanWebviewPanel, issue
|
||||
const processStartedAt = await codexProcessStartedAt(pid, signal)
|
||||
if (signal.aborted || terminal.exitStatus !== undefined)
|
||||
return null
|
||||
const stored = panel.context.workspaceState.get<CodexSessionLaunch>(launchKey(panel, issueNumber))
|
||||
const stored = panel.context.workspaceState.get<CodexSessionLaunch>(launchKey(source, issueNumber))
|
||||
const startedAt = stored?.prompt === launch.prompt && stored.cwd === launch.cwd && Number.isFinite(stored.startedAt)
|
||||
? Math.max(stored.startedAt, processStartedAt)
|
||||
: processStartedAt
|
||||
const prepared = await prepareReviewSessionCapture(panel, issueNumber, { ...launch, startedAt })
|
||||
const prepared = await prepareReviewSessionCapture(panel, issueNumber, { ...launch, startedAt }, source)
|
||||
if (signal.aborted) {
|
||||
await cancelReviewSessionCapture(panel, issueNumber)
|
||||
return null
|
||||
@@ -259,12 +269,13 @@ export function recoverReviewSessionCapture(panel: KanbanWebviewPanel, issueNumb
|
||||
if (existing)
|
||||
return existing.result
|
||||
const abort = new AbortController()
|
||||
const result = recoverReviewSessionCaptureInner(panel, issueNumber, terminal, abort.signal)
|
||||
const source = panel.refFor(issueNumber).source ?? 'gitea'
|
||||
const result = recoverReviewSessionCaptureInner(panel, issueNumber, terminal, abort.signal, source)
|
||||
.finally(() => {
|
||||
if (map.get(issueNumber)?.result === result)
|
||||
map.delete(issueNumber)
|
||||
})
|
||||
map.set(issueNumber, { abort, result })
|
||||
map.set(issueNumber, { source, abort, result })
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
defaultImplementProfilePath: string
|
||||
defaultTestProfilePath: string
|
||||
defaultPrototypeProfilePath: string
|
||||
defaultAskProfilePath: string
|
||||
conflictResolutionProfilePath: string
|
||||
commitProfilePath: string
|
||||
prDiffSummaryProfilePath: string
|
||||
@@ -85,6 +86,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
const trimmedDefaultImplementProfile = payload.defaultImplementProfilePath.trim()
|
||||
const trimmedDefaultTestProfile = payload.defaultTestProfilePath.trim()
|
||||
const trimmedDefaultPrototypeProfile = payload.defaultPrototypeProfilePath.trim()
|
||||
const trimmedDefaultAskProfile = payload.defaultAskProfilePath.trim()
|
||||
// conflict-resolution profile: '' is meaningful (= DEFAULT_PROFILE_PATH).
|
||||
const trimmedConflictProfile = payload.conflictResolutionProfilePath.trim()
|
||||
// 提交代码 profile:'' 有意义(= 未配置 → 提交严格失败),只 trim。
|
||||
@@ -134,6 +136,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
defaultImplementProfilePath: trimmedDefaultImplementProfile,
|
||||
defaultTestProfilePath: trimmedDefaultTestProfile,
|
||||
defaultPrototypeProfilePath: trimmedDefaultPrototypeProfile,
|
||||
defaultAskProfilePath: trimmedDefaultAskProfile,
|
||||
conflictResolutionProfilePath: trimmedConflictProfile,
|
||||
commitProfilePath: trimmedCommitProfile,
|
||||
prDiffSummaryProfilePath: trimmedPrDiffSummaryProfile,
|
||||
@@ -168,6 +171,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
defaultImplementProfilePath: trimmedDefaultImplementProfile,
|
||||
defaultTestProfilePath: trimmedDefaultTestProfile,
|
||||
defaultPrototypeProfilePath: trimmedDefaultPrototypeProfile,
|
||||
defaultAskProfilePath: trimmedDefaultAskProfile,
|
||||
conflictResolutionProfilePath: trimmedConflictProfile,
|
||||
commitProfilePath: trimmedCommitProfile,
|
||||
prDiffSummaryProfilePath: trimmedPrDiffSummaryProfile,
|
||||
@@ -178,8 +182,6 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
youtrackProjectShortName: trimmedYtProject,
|
||||
youtrackCloseCommand: trimmedYtClose,
|
||||
})
|
||||
// 保存后立刻让 listClaudeProfiles / getDefaultProfilePath 吃到新目录。
|
||||
setProfilesDirOverride(trimmedProfilesDirectory || undefined)
|
||||
void handleProfilesList(panel)
|
||||
if (!keepExisting) {
|
||||
await setToken(panel.context, trimmedHost, trimmedToken)
|
||||
@@ -279,6 +281,7 @@ export async function handleEditSettingsRequest(panel: KanbanWebviewPanel): Prom
|
||||
defaultImplementProfilePath: s.defaultImplementProfilePath,
|
||||
defaultTestProfilePath: s.defaultTestProfilePath,
|
||||
defaultPrototypeProfilePath: s.defaultPrototypeProfilePath,
|
||||
defaultAskProfilePath: s.defaultAskProfilePath,
|
||||
conflictResolutionProfilePath: s.conflictResolutionProfilePath,
|
||||
commitProfilePath,
|
||||
prDiffSummaryProfilePath,
|
||||
|
||||
Reference in new issue
Block a user