✨ feat(vscode): 五种会话 id 行都能用橡皮擦清除本机记录,别的机器上的旧会话不再挡着新起
This commit is contained in:
1 parent
4d905ff16e
commit
d604b6f426
12 files changed
+169
-64
No files matched your search
@@ -565,8 +565,8 @@ export class KanbanWebviewPanel {
|
||||
void issues.handleUpdateBrainstormProfilePath(this, msg.issueNumber, msg.brainstormProfilePath)
|
||||
return
|
||||
}
|
||||
if (msg.type === 'issue/clear-brainstorm-session') {
|
||||
void issues.handleClearBrainstormSession(this, msg.issueNumber)
|
||||
if (msg.type === 'issue/clear-session') {
|
||||
void issues.handleClearSession(this, msg.issueNumber, msg.kind)
|
||||
return
|
||||
}
|
||||
if (msg.type === 'issue/update-test-profile-path') {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { IssueColumn } from '../../gitea/types'
|
||||
import type { LockedReason } from '../issueLock'
|
||||
import type { KanbanWebviewPanel } from '../KanbanPanel'
|
||||
import type { SessionTabKind } from './terminals'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import * as fs from 'node:fs'
|
||||
@@ -39,6 +40,7 @@ import { pickRandomIssueColor, themeColorIdToIconUri } from '../issueColor'
|
||||
import { evaluateLockedReason } from '../issueLock'
|
||||
import { makeNonce } from '../KanbanPanel'
|
||||
import { cleanupFeatureBranch } from './branchCleanup'
|
||||
import { clearSessionConfirmText, clearSessionDoneText, SESSION_KINDS } from './sessionKinds'
|
||||
import * as sessions from './sessions'
|
||||
import { canResetToTodo, todoResetStateExtra, todoResetUiPatch } from './todoReset'
|
||||
|
||||
@@ -1971,17 +1973,20 @@ export async function handleUpdateProfilePath(panel: KanbanWebviewPanel, issueNu
|
||||
}
|
||||
|
||||
/**
|
||||
* 清掉工单记的头脑风暴会话 id,下次点 ▶ 续聊就新开一个会话。
|
||||
* 清掉工单记的某种会话 id,之后该行可以新起一个会话。
|
||||
*
|
||||
* 用处:旧会话的历史被 `claude --resume` 原样带回来(里面可能还留着几个版本
|
||||
* 之前的提示词和它报的错),想从干净的上下文重新聊需求时用。会话文件本身不动,
|
||||
* 手上有 id 仍可用 `claude --resume <id>` 找回。
|
||||
* 用处:会话 id 是别的机器上开的、本机没有会话文件,resume 不了又挡着新起;
|
||||
* 或者旧会话历史太杂(头脑风暴里还留着几个版本之前的提示词和报错),想从干净
|
||||
* 的上下文重来。只写本机记录('' 墓碑盖住共享值),别的机器不受影响;会话文件
|
||||
* 本身不动,手上有 id 仍可 resume 找回。
|
||||
*/
|
||||
export async function handleClearBrainstormSession(panel: KanbanWebviewPanel, issueNumber: number): Promise<void> {
|
||||
export async function handleClearSession(panel: KanbanWebviewPanel, issueNumber: number, kind: SessionTabKind): Promise<void> {
|
||||
const { field, role } = SESSION_KINDS[kind]
|
||||
let previousValue: string | undefined
|
||||
try {
|
||||
const existingState = await panel.readIssueState(issueNumber)
|
||||
previousValue = typeof existingState.sessionId === 'string' ? existingState.sessionId : undefined
|
||||
const value = existingState[field]
|
||||
previousValue = typeof value === 'string' && value.length > 0 ? value : undefined
|
||||
}
|
||||
catch {
|
||||
previousValue = undefined
|
||||
@@ -1992,14 +1997,14 @@ export async function handleClearBrainstormSession(panel: KanbanWebviewPanel, is
|
||||
type: 'toast/show',
|
||||
id: makeNonce(),
|
||||
level: 'info',
|
||||
message: `工单 #${issueNumber} 本来就没有头脑风暴会话`,
|
||||
message: `工单 #${issueNumber} 本来就没有${role}会话`,
|
||||
dismissOnTimer: 4000,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const choice = await window.showWarningMessage(
|
||||
`确定清除工单 #${issueNumber} 的头脑风暴会话 id(${previousValue})?下次续聊会新开一个会话,旧会话记录还在,可用 claude --resume 找回。`,
|
||||
clearSessionConfirmText(issueNumber, kind, previousValue),
|
||||
{ modal: true },
|
||||
'清除',
|
||||
)
|
||||
@@ -2007,21 +2012,21 @@ export async function handleClearBrainstormSession(panel: KanbanWebviewPanel, is
|
||||
return
|
||||
|
||||
try {
|
||||
await panel.mergeIssueState(issueNumber, { sessionId: '' })
|
||||
await panel.mergeIssueState(issueNumber, { [field]: '' })
|
||||
}
|
||||
catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
logger.add({
|
||||
level: 'error',
|
||||
source: 'panel',
|
||||
message: `清除 sessionId 失败 (issue #${issueNumber})`,
|
||||
message: `清除 ${field} 失败 (issue #${issueNumber})`,
|
||||
details: message,
|
||||
})
|
||||
panel.postMessage({
|
||||
type: 'toast/show',
|
||||
id: makeNonce(),
|
||||
level: 'error',
|
||||
message: `清除工单 #${issueNumber} 头脑风暴会话失败: ${message}`,
|
||||
message: `清除工单 #${issueNumber} ${role}会话失败: ${message}`,
|
||||
dismissOnTimer: 6000,
|
||||
})
|
||||
return
|
||||
@@ -2030,13 +2035,13 @@ export async function handleClearBrainstormSession(panel: KanbanWebviewPanel, is
|
||||
panel.postMessage({
|
||||
type: 'issue/patch',
|
||||
issueNumber,
|
||||
patch: { sessionId: null },
|
||||
patch: { [field]: null },
|
||||
})
|
||||
panel.postMessage({
|
||||
type: 'toast/show',
|
||||
id: makeNonce(),
|
||||
level: 'success',
|
||||
message: `工单 #${issueNumber} 头脑风暴会话已清除,下次续聊将新开会话`,
|
||||
message: clearSessionDoneText(issueNumber, kind),
|
||||
dismissOnTimer: 5000,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { clearSessionConfirmText, clearSessionDoneText, SESSION_KINDS } from './sessionKinds'
|
||||
|
||||
describe('会话种类表', () => {
|
||||
it('五种会话各自对应 state 里的会话 id 字段,互不重复', () => {
|
||||
expect(SESSION_KINDS.brainstorm.field).toBe('sessionId')
|
||||
expect(SESSION_KINDS.implement.field).toBe('implementSessionId')
|
||||
expect(SESSION_KINDS.review.field).toBe('reviewSessionId')
|
||||
expect(SESSION_KINDS.test.field).toBe('testSessionId')
|
||||
expect(SESSION_KINDS.prototype.field).toBe('prototypeSessionId')
|
||||
const fields = Object.values(SESSION_KINDS).map(k => k.field)
|
||||
expect(new Set(fields).size).toBe(fields.length)
|
||||
})
|
||||
})
|
||||
|
||||
describe('清除会话 id 的提示文案', () => {
|
||||
it('确认框:角色 + id + 只清本机,末尾接该种会话的后续提示', () => {
|
||||
expect(clearSessionConfirmText(12, 'brainstorm', 'abc')).toBe(
|
||||
'确定清除工单 #12 的头脑风暴会话 id(abc)?只清本机记录,别的机器不受影响;会话文件不删。下次续聊会新开会话。',
|
||||
)
|
||||
expect(clearSessionConfirmText(12, 'implement', 'abc')).toContain('重新实施会删掉远程 feature 分支')
|
||||
expect(clearSessionConfirmText(12, 'test', 'abc')).toBe(
|
||||
'确定清除工单 #12 的测试会话 id(abc)?只清本机记录,别的机器不受影响;会话文件不删。清除后可点 ▶ 新起一个。',
|
||||
)
|
||||
})
|
||||
|
||||
it('成功 toast:简短复述后续提示', () => {
|
||||
expect(clearSessionDoneText(12, 'brainstorm')).toBe('工单 #12 的头脑风暴会话 id 已清除,下次续聊将新开会话')
|
||||
expect(clearSessionDoneText(12, 'review')).toBe('工单 #12 的审查会话 id 已清除,可点 ▶ 新起一个')
|
||||
expect(clearSessionDoneText(12, 'implement')).toContain('会删掉远程 feature 分支')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import type { SessionTabKind } from './terminals'
|
||||
|
||||
/** 工单 state 里存各种会话 id 的字段。 */
|
||||
export type SessionIdField = 'sessionId' | 'implementSessionId' | 'reviewSessionId' | 'testSessionId' | 'prototypeSessionId'
|
||||
|
||||
/**
|
||||
* 每种会话对应的 state 字段、中文角色名,以及清除该会话 id 后的后续提示:
|
||||
* confirmHint 进确认框,doneHint 进成功 toast。
|
||||
*/
|
||||
export const SESSION_KINDS: Record<SessionTabKind, { field: SessionIdField, role: string, confirmHint: string, doneHint: string }> = {
|
||||
brainstorm: {
|
||||
field: 'sessionId',
|
||||
role: '头脑风暴',
|
||||
confirmHint: '下次续聊会新开会话',
|
||||
doneHint: '下次续聊将新开会话',
|
||||
},
|
||||
implement: {
|
||||
field: 'implementSessionId',
|
||||
role: '实施',
|
||||
confirmHint: '清除后可点 ▶ 重新实施,注意重新实施会删掉远程 feature 分支,别的机器上的实施进度会丢',
|
||||
doneHint: '可点 ▶ 重新实施(会删掉远程 feature 分支)',
|
||||
},
|
||||
review: {
|
||||
field: 'reviewSessionId',
|
||||
role: '审查',
|
||||
confirmHint: '清除后可点 ▶ 新起一个',
|
||||
doneHint: '可点 ▶ 新起一个',
|
||||
},
|
||||
test: {
|
||||
field: 'testSessionId',
|
||||
role: '测试',
|
||||
confirmHint: '清除后可点 ▶ 新起一个',
|
||||
doneHint: '可点 ▶ 新起一个',
|
||||
},
|
||||
prototype: {
|
||||
field: 'prototypeSessionId',
|
||||
role: '原型',
|
||||
confirmHint: '清除后可点 ▶ 新起一个',
|
||||
doneHint: '可点 ▶ 新起一个',
|
||||
},
|
||||
}
|
||||
|
||||
export function clearSessionConfirmText(issueNumber: number, kind: SessionTabKind, sessionId: string): string {
|
||||
const { role, confirmHint } = SESSION_KINDS[kind]
|
||||
return `确定清除工单 #${issueNumber} 的${role}会话 id(${sessionId})?只清本机记录,别的机器不受影响;会话文件不删。${confirmHint}。`
|
||||
}
|
||||
|
||||
export function clearSessionDoneText(issueNumber: number, kind: SessionTabKind): string {
|
||||
const { role, doneHint } = SESSION_KINDS[kind]
|
||||
return `工单 #${issueNumber} 的${role}会话 id 已清除,${doneHint}`
|
||||
}
|
||||
@@ -223,7 +223,7 @@ export type WebviewToExtension
|
||||
| { type: 'issue/update-auto-review', issueNumber: number, value: boolean }
|
||||
| { type: 'issue/update-profile-path', issueNumber: number, profilePath: string }
|
||||
| { type: 'issue/update-brainstorm-profile-path', issueNumber: number, brainstormProfilePath: string }
|
||||
| { type: 'issue/clear-brainstorm-session', issueNumber: number }
|
||||
| { type: 'issue/clear-session', issueNumber: number, kind: 'brainstorm' | 'implement' | 'review' | 'test' | 'prototype' }
|
||||
| { type: 'issue/update-test-profile-path', issueNumber: number, testProfilePath: string }
|
||||
| { type: 'issue/update-prototype-profile-path', issueNumber: number, prototypeProfilePath: string }
|
||||
| { type: 'logs/fetch' }
|
||||
|
||||
@@ -8,6 +8,7 @@ describe('isOwnerOnlyMessage', () => {
|
||||
expect(isOwnerOnlyMessage('issue/close')).toBe(true)
|
||||
expect(isOwnerOnlyMessage('dependency/set')).toBe(true)
|
||||
expect(isOwnerOnlyMessage('issue/update-auto-review')).toBe(true)
|
||||
expect(isOwnerOnlyMessage('issue/clear-session')).toBe(true)
|
||||
})
|
||||
|
||||
it('在本机替别人跑会话的消息要门禁', () => {
|
||||
|
||||
@@ -29,7 +29,7 @@ export const OWNER_ONLY_MESSAGE_TYPES: ReadonlySet<string> = new Set([
|
||||
'issue/update-auto-review',
|
||||
'issue/update-profile-path',
|
||||
'issue/update-brainstorm-profile-path',
|
||||
'issue/clear-brainstorm-session',
|
||||
'issue/clear-session',
|
||||
'issue/update-test-profile-path',
|
||||
'issue/update-prototype-profile-path',
|
||||
'dependency/set',
|
||||
|
||||
Reference in new issue
Block a user