From 5f1352df8611a83b647659375a803e3644a65fff Mon Sep 17 00:00:00 2001 From: cruldra Date: Fri, 2 Oct 2026 01:29:25 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(vscode):=20=E9=87=8D?= =?UTF-8?q?=E5=86=99=E6=B3=A8=E9=87=8A=E7=9B=B4=E6=8E=A5=E8=B0=83=E7=94=A8?= =?UTF-8?q?=20/ecc:writing-code-comments=EF=BC=8C=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E8=87=AA=E5=B7=B1=E8=AF=BB=E6=8A=80=E8=83=BD=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vscode/src/codeReader/commentRewrite.test.ts | 21 ++------- vscode/src/codeReader/commentRewrite.ts | 45 +++---------------- vscode/src/codeReader/editor/previewEditor.ts | 16 +------ 3 files changed, 10 insertions(+), 72 deletions(-) diff --git a/vscode/src/codeReader/commentRewrite.test.ts b/vscode/src/codeReader/commentRewrite.test.ts index 0ac8f98..0aac85a 100644 --- a/vscode/src/codeReader/commentRewrite.test.ts +++ b/vscode/src/codeReader/commentRewrite.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { commentProse, commentRewritePrompt, commentSource, isModuleComment, loadCommentSkill, modelCommentBody, refillComment, skillBody } from './commentRewrite' +import { COMMENT_SKILL, commentProse, commentRewritePrompt, commentSource, isModuleComment, modelCommentBody, refillComment } from './commentRewrite' import { commonIndent, replaceLines } from './editText' /** 和预览写回同一条路:正文套回壳,再按原来的缩进替换这几行。 */ @@ -116,15 +116,14 @@ describe('重写注释的提示', () => { it('文件顶上的三引号:上下文是去掉这段之后的文件,太长会截掉并说明', () => { const rest = Array.from({ length: 130 }, (_, index) => `line ${index}`).join('\n') const prompt = commentRewritePrompt({ - skill: '技能正文', comment: '"""旧"""', source: `"""旧"""\n${rest}`, startLine: 0, endLine: 0, earlierKinds: [], }) - expect(prompt).toContain('技能正文') - expect(prompt).toContain('只按上面的技能重写这一段注释') + expect(prompt?.startsWith(`${COMMENT_SKILL}\n`)).toBe(true) + expect(prompt).toContain('只按这个技能重写这一段注释') expect(prompt).toContain('这段注释现在的正文:\n旧') expect(prompt).not.toContain('"""') expect(prompt).toContain('截断了') @@ -134,7 +133,6 @@ describe('重写注释的提示', () => { it('普通注释只带后面紧挨着的那一块,不带整份文件', () => { const prompt = commentRewritePrompt({ - skill: '技能正文', comment: '# 旧', source: '整份文件不该出现', startLine: 4, @@ -149,19 +147,6 @@ describe('重写注释的提示', () => { expect(isModuleComment('"""旧"""', ['function'])).toBe(false) }) - it('技能按给出的顺序找,用到第一份;都没有就是没有', async () => { - const read = async (path: string): Promise => { - if (path.includes('/marketplaces/ecc/')) - throw Object.assign(new Error('no'), { code: 'ENOENT' }) - return '---\nname: writing-code-comments\n---\n规范正文\n' - } - expect(await loadCommentSkill(read)).toBe('规范正文') - expect(await loadCommentSkill(async () => { - throw Object.assign(new Error('no'), { code: 'ENOENT' }) - })).toBeUndefined() - expect(skillBody('没有头的正文')).toBe('没有头的正文') - }) - it('模型套了代码围栏时只留里面的正文', () => { expect(modelCommentBody('```\n新说明\n```')).toBe('新说明') expect(modelCommentBody(' ')).toBeUndefined() diff --git a/vscode/src/codeReader/commentRewrite.ts b/vscode/src/codeReader/commentRewrite.ts index a5b1214..27380e6 100644 --- a/vscode/src/codeReader/commentRewrite.ts +++ b/vscode/src/codeReader/commentRewrite.ts @@ -3,19 +3,13 @@ * 认 Python 的三引号、井号行注释,以及块注释和双斜杠行注释。认不出就不改。 */ -import { readFile } from 'node:fs/promises' -import { homedir } from 'node:os' -import { join } from 'node:path' import { commonIndent, splitLines } from './editText' /** 贴给模型的代码最多这么多行,再长就截断并在提示里说明。 */ export const COMMENT_CONTEXT_LINES = 120 -/** 运行时按这个顺序找技能,用到的第一份。不把技能正文放进仓库。 */ -export const COMMENT_SKILL_PATHS = [ - join(homedir(), '.claude/plugins/marketplaces/ecc/skills/writing-code-comments/SKILL.md'), - join(homedir(), '.claude/writing-code-comments/SKILL.md'), -] as const +/** 跟在对话框里输入这个命令一样,由 Claude Code 自己加载写注释的技能。 */ +export const COMMENT_SKILL = '/ecc:writing-code-comments' const DOUBLE_DOC = '"""' const SINGLE_DOC = '\'' + '\'' + '\'' @@ -28,31 +22,6 @@ export function commentSource(lines: readonly string[]): string { return lines.map(line => line.slice(Math.min(width, line.length))).join('\n') } -/** 去掉技能文件开头的 frontmatter,剩下的才是给模型的规范。 */ -export function skillBody(markdown: string): string { - const text = markdown.charCodeAt(0) === 0xFEFF ? markdown.slice(1) : markdown - const fence = text.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/) - return (fence ? text.slice(fence[0].length) : text).trim() -} - -function missingFile(err: unknown): boolean { - return typeof err === 'object' && err !== null && 'code' in err && err.code === 'ENOENT' -} - -/** 读到的第一份技能正文;两处都没有就是 undefined。读失败(不是没有文件)照样抛。 */ -export async function loadCommentSkill(read: (path: string) => Promise = path => readFile(path, 'utf8')): Promise { - for (const path of COMMENT_SKILL_PATHS) { - try { - return skillBody(await read(path)) - } - catch (err) { - if (!missingFile(err)) - throw err - } - } - return undefined -} - interface DocShell { prefix: string quote: string @@ -307,8 +276,6 @@ function clip(text: string, what: string): { text: string, note: string } { } export interface CommentRewriteInput { - /** 去掉 frontmatter 的技能正文;没有技能就不组提示 */ - skill: string | undefined /** 去掉共同缩进的注释原文 */ comment: string /** 整份文件,用来在是模块说明时拿掉这段注释 */ @@ -320,10 +287,8 @@ export interface CommentRewriteInput { following?: string } -/** 发给模型的整段话。注释认不出,或没有技能,就不组。 */ +/** 发给模型的整段话。开头是技能命令,Claude Code 自己加载技能。注释认不出就不组。 */ export function commentRewritePrompt(input: CommentRewriteInput): string | undefined { - if (input.skill === undefined) - return undefined const body = commentProse(input.comment) if (body === undefined) return undefined @@ -334,8 +299,8 @@ export function commentRewritePrompt(input: CommentRewriteInput): string | undef ? clip(input.following, '这块代码') : { text: '(后面没有紧挨着的代码)', note: '' } return [ - input.skill, - '只按上面的技能重写这一段注释。用中文。不要改代码,不要加技能没要求的中文名记号。只输出新注释的正文,不要定界符,不要解释。', + COMMENT_SKILL, + '只按这个技能重写这一段注释。用中文。不要改代码,不要加技能没要求的中文名记号。只输出新注释的正文,不要定界符,不要解释。', `这段注释现在的正文:\n${body}`, `它说明的代码:\n${context.text}${context.note}`, ].join('\n\n') diff --git a/vscode/src/codeReader/editor/previewEditor.ts b/vscode/src/codeReader/editor/previewEditor.ts index 0251d12..d1df1ef 100644 --- a/vscode/src/codeReader/editor/previewEditor.ts +++ b/vscode/src/codeReader/editor/previewEditor.ts @@ -17,7 +17,7 @@ import { commands, Range, window, workspace, WorkspaceEdit } from 'vscode' import { logger } from '../../logging/logger' import { webviewHtml, webviewUiRoot } from '../../webview/html' import { DocumentCallees } from '../calleeKinds' -import { commentRewritePrompt, commentSource, findPlaced, loadCommentSkill, modelCommentBody, refillComment } from '../commentRewrite' +import { commentRewritePrompt, commentSource, findPlaced, modelCommentBody, refillComment } from '../commentRewrite' import { PreviewDiff } from '../diff/previewDiff' import { blockReplacement } from '../editText' import { locate } from '../folderStore' @@ -588,19 +588,7 @@ class Preview { const following = next && next.startLine >= 0 && next.endLine >= next.startLine && next.endLine < document.lineCount ? Array.from({ length: next.endLine - next.startLine + 1 }, (_, i) => document.lineAt(next.startLine + i).text).join('\n') : undefined - let skill: string | undefined - try { - skill = await loadCommentSkill() - } - catch (err) { - if (!this.rewriteAbort.signal.aborted) - this.failRewrite(`读注释技能失败:${err instanceof Error ? err.message : String(err)}`) - return - } - if (this.rewriteAbort.signal.aborted) - return const prompt = commentRewritePrompt({ - skill, comment, source, startLine: block.startLine, @@ -609,7 +597,7 @@ class Preview { ...(following !== undefined && { following }), }) if (!prompt) { - this.failRewrite(skill === undefined ? '找不到写注释的技能' : '这段注释的定界符认不出来,没有改文件。') + this.failRewrite('这段注释的定界符认不出来,没有改文件。') return } const at = locate(document.uri)