✨ feat(vscode): 重写注释留会话记录、能接着说,允许画原型图

每块的重写记录存进 .spx/comment-rewrites.json(和问答分开,改名、删文件跟着走),块下面同问答区:灰字历史、当前这一轮、输入框;输入框里的话接着这块最近一次重写的会话说,交回新正文照样写回,写回失败也记下原因。
重写跑在 claude --restricted 下:只放开读搜、OpenPencil 的 op、建 prototypes 目录、改草稿目录,写注释的插件单独加载;超时 15 分钟。
进行中的、出错被叫停留着的那一轮记在扩展端,切走标签页再切回来照样接上(问 AI 同一套)。
This commit is contained in:
cruldra committed 2026-10-12 13:45:37 +11:00
1 parent ae4e2160ef
commit c9f60f5b30
32 files changed
+1654 -860

No files matched your search

+29 -16
View File
@@ -10,7 +10,7 @@ import type { CallTree } from '../callTree/model'
import type { DocumentDiff } from '../diff/previewDiff'
import type { ShownName } from '../nameResolution'
import type { ProfileChoice } from '../qa/askProfiles'
import type { RunningAsk } from '../qa/asks'
import type { AskRound } from '../qa/asks'
import type { Excluded, FilterState } from './blockFilter'
export type { Excluded, ShownName }
@@ -64,7 +64,9 @@ export interface DocumentMessage {
names: ShownName[]
}
/** 问过的一次(给预览看的):问题、回答(Markdown)、什么时候问的(ISO 时间)、用哪个 profile 答的。 */
/**
* 问过、重写过的一次(给预览看的):说的话、回答(Markdown;重写记录是交回的注释正文,没交回时是空的)、什么时候(ISO 时间)、用哪个 profile 答的。
*/
export interface QaEntryView {
id: string
question: string
@@ -72,23 +74,25 @@ export interface QaEntryView {
createdAt: string
/** 用哪个 profile 答的(profile 名);选「默认」时、以前存的没有 */
profile?: string
/** 问题带的图片,预览里能直接用的地址(webview.asWebviewUri);没带图片时没有 */
/** 带的图片,预览里能直接用的地址(webview.asWebviewUri);没带图片时没有 */
images?: string[]
/** 重写记录才有:没写进文件的原因 */
error?: string
}
/** 挂在一块上的问答;记号对不上这份文件里任何一块的不显示。 */
/** 挂在一块上的问答或重写记录;记号对不上这份文件里任何一块的不显示。 */
export interface QaThreadView {
anchor: string
entries: QaEntryView[]
}
/**
* 这份文件的问答:available 为假时文件不在工作区文件夹里、问答没地方存,不给「问 AI」;
* running 是正在答的问题(页面重新加载后靠它接上)。
* 这份文件的问答和重写记录:available 为假时文件不在工作区文件夹里、没地方存,不给「问 AI」和「重写注释」;
* current、rewriteCurrent 是当前这一轮:正在答的问题、正在重写的注释,和出错、被叫停没记下留着的(页面重新加载后靠它们接上)。
*/
export interface QaMessage {
type: 'qa'
/** 不在工作区文件夹里时没地方存,问 AI 不可用 */
/** 不在工作区文件夹里时没地方存,问 AI、重写注释不可用 */
available: boolean
/** 块下面的问答,按块的记号 */
threads: QaThreadView[]
@@ -96,7 +100,10 @@ export interface QaMessage {
path: QaEntryView[]
/** 顶部问的是这个文件还是这个文件夹 */
subject: 'file' | 'folder'
running: RunningAsk[]
current: AskRound[]
/** 块下面的重写记录,按块的记号 */
rewrites: QaThreadView[]
rewriteCurrent: AskRound[]
}
/**
@@ -121,8 +128,9 @@ export interface StickyScrollSettings {
* askProfiles:「问 AI」输入框旁边的 profile 下拉:选项和选中的那项;ready 后发一次,任何一个预览改了都发给所有预览
* answer:askId 那个问题的回答长了(text 是到现在为止的全文)、或换了在做的事
* answerEnd:askId 那个问题答完了:done 带最终的回答和存下的那条的 id,failed 带给人看的原因,stopped 是被叫停
* rewriteProgress:anchor 那块的注释正在按规范重写。question 是发给模型的那一句(技能命令和块在哪),text 是注释正文到现在为止写出来的,activity 是正在读什么
* rewriteEnd:这段重写结束了。done 是正文已经写进文件;failed 带给人看的原因,文件没改;stopped 是被叫停,文件没改
* rewriteProgress:askId 那一轮重写开始了、正文长了(text 是到现在为止写出来的)、或换了在做的事;question 是记在这一轮上的话(点按钮时是那句技能命令)
* rewriteEnd:askId 那一轮重写结束了:done 是模型答完、这一轮记下了,带交回的正文(text)、存下的那条的 id,没写进文件时带原因(error);
* failed 带给人看的原因,stopped 是被叫停,这两种都没记、文件没改
* moveOccurrence:跳到高亮着的名字的下一处(step 1)/ 上一处(step -1)出现,由这两条命令发给活动的预览
* displayNames:version 这份文档里显示成中文名的名字,查到一步发一步(先本文件定义的,再别的文件定义的);不是页面上这个版本的不理
* diff:这份文件相比对比基准改了什么(侧栏开着「未提交的更改」或审查工单时才有,见 DiffBase):每发一份文档之后算好了补发,
@@ -150,8 +158,8 @@ export type ExtensionToWebview
| ({ type: 'askProfiles' } & ProfileChoice)
| { type: 'answer', askId: string, text: string, activity?: string }
| { type: 'answerEnd', askId: string, status: 'done' | 'failed' | 'stopped', text?: string, error?: string, entryId?: string }
| { type: 'rewriteProgress', anchor: string, question: string, text: string, activity?: string }
| { type: 'rewriteEnd', anchor: string, status: 'done' | 'failed' | 'stopped', error?: string }
| { type: 'rewriteProgress', askId: string, question: string, text: string, activity?: string }
| { type: 'rewriteEnd', askId: string, status: 'done' | 'failed' | 'stopped', text?: string, error?: string, entryId?: string }
| { type: 'diff', diff: DocumentDiff | null }
| { type: 'repoLinksChecked', id: number, missing: string[], unresolved: string[] }
| { type: 'repoLinkBroken', path?: string, symbol?: string }
@@ -174,8 +182,11 @@ export type ExtensionToWebview
* setOnlyChanges:打开 / 关掉「只看改动」,扩展端记下后发 filter 给所有预览
* ask:问 anchor 那块代码;askId 由 webview 起,回答的消息带回去;扩展端接着这块最近一次问答的会话问,用眼下选的 profile;
* images 是贴进输入框的图片(没贴是空的),随问题发给 Claude Code,答成了跟这条一起存
* rewriteComment:按注释规范重写 anchor 那块的注释(还没有就新写一段)。过程用 rewriteProgress / rewriteEnd 发回页面,不写入问答
* stopRewrite:叫停 anchor 这段正在进行的重写,不写文件
* rewriteComment:按注释规范重写 anchor 那块的注释(还没有就新写一段);askId 由 webview 起,过程用 rewriteProgress / rewriteEnd 带回去。
* 没有 request 是点了按钮:发技能命令,开新会话;有 request 是在输入框里说的修改意见,接着这块最近一次重写的会话说,images 是贴的图片
* stopRewrite:叫停 askId 那一轮重写,不写文件、不记
* forgetRound:页面上收起了 anchor 那块的问答(ask)或重写(rewrite),扩展端去掉这块留着的那一轮(出错、被叫停的)
* deleteRewrite:删掉 anchor 那块下面的一条重写记录
* stopAsk:叫停 askId 那个问题,这次不存
* deleteQa:删掉 anchor 那块下面的一次问答(已经理解了)
* selectAskProfile:「问 AI」换 profile(id 是 profile 名,「默认」是 ''),扩展端记下后发 askProfiles 给所有预览
@@ -193,8 +204,10 @@ export type WebviewToExtension
| { type: 'setFilter', excluded: Excluded }
| { type: 'setOnlyChanges', on: boolean }
| { type: 'edit', startLine: number, endLine: number, text: string }
| { type: 'rewriteComment', anchor: string }
| { type: 'stopRewrite', anchor: string }
| { type: 'rewriteComment', askId: string, anchor: string, request?: string, images: ClaudeImage[] }
| { type: 'stopRewrite', askId: string }
| { type: 'deleteRewrite', anchor: string, entryId: string }
| { type: 'forgetRound', kind: 'ask' | 'rewrite', anchor: string }
| { type: 'selectTranslator', id: string }
| { type: 'visibleLine', line: number }
| { type: 'probeDefinition', id: number, version: number, offset: number }