✨ feat(vscode): 七份提示词统一走 Handlebars,头脑风暴三档合一
- 模板引擎换成 Handlebars(noEscape,提示词是纯文本不能被转义),单花括号写法彻底退场
- brainstorm-continue / brainstorm-continue-prototype 两份并进 brainstorm.md,
用 {{#if issueNumber}} 分新建 / 续聊,续聊里用 {{#if prototypeFile}} 嵌原型段落,
设置面板对应的两个 textarea 一并删掉
- 模板上下文 = 工单 state JSON 全量字段 + issueNumber / userRequest / nonce /
planFile / prNumber / prRef,以后模板想用新字段不用改扩展代码
- 各会话入口共用一条 state 读取路径,读失败只记 warn 按空状态继续,不拦开会话
- 升级后首次启动把存量自定义提示词迁到双花括号并合并成一份,改写前把
settings.json 备份成 settings.json.bak-<时间戳>,迁移动作全写日志
- 装 handlebars 时给 vsce 传递依赖的老 semver 加 trustPolicyExclude 豁免,
否则 no-downgrade 策略卡住整个装包
Claude-Session: https://claude.ai/code/session_01Kgmb1LvrPmFX3NoUDB4zZC
This commit is contained in:
1 parent
efeac8c978
commit
7bac5c8b03
28 files changed
+853
-424
No files matched your search
@@ -0,0 +1,79 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import * as path from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { buildPromptContext, renderPromptTemplate } from './promptTemplate'
|
||||
|
||||
const brainstormTemplate = readFileSync(
|
||||
path.resolve(__dirname, '../../prompts/brainstorm.md'),
|
||||
'utf-8',
|
||||
)
|
||||
|
||||
describe('buildPromptContext', () => {
|
||||
it('把 state 全量摊平进上下文', () => {
|
||||
const ctx = buildPromptContext({ branch: 'feature/x', pr: '12' }, { issueNumber: '7' })
|
||||
expect(ctx).toEqual({ branch: 'feature/x', pr: '12', issueNumber: '7' })
|
||||
})
|
||||
|
||||
it('额外变量里的 undefined 不覆盖 state 已有的值', () => {
|
||||
const ctx = buildPromptContext({ planFile: '.claude/plans/a.md' }, { planFile: undefined })
|
||||
expect(ctx.planFile).toBe('.claude/plans/a.md')
|
||||
})
|
||||
|
||||
it('state 缺失时退化成只有额外变量', () => {
|
||||
expect(buildPromptContext(undefined, { nonce: 'abc' })).toEqual({ nonce: 'abc' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('renderPromptTemplate', () => {
|
||||
it('不做 HTML 转义,引号和 & 原样进提示词', () => {
|
||||
expect(renderPromptTemplate('说 "{{x}}" & 走', { x: 'a<b' })).toBe('说 "a<b" & 走')
|
||||
})
|
||||
|
||||
it('模板语法错误时抛错', () => {
|
||||
expect(() => renderPromptTemplate('{{#if x}}没闭合', {})).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('默认头脑风暴模板', () => {
|
||||
it('有工单号时走续聊分支,没有时走新建工单分支', () => {
|
||||
const cont = renderPromptTemplate(brainstormTemplate, { issueNumber: '42' })
|
||||
expect(cont).toContain('/ecc:grilling 讨论下 42 号工单')
|
||||
expect(cont).not.toContain('opencli spx issue create')
|
||||
|
||||
const fresh = renderPromptTemplate(brainstormTemplate, { userRequest: '加个按钮', nonce: 'n1' })
|
||||
expect(fresh).toContain('加个按钮')
|
||||
expect(fresh).toContain('<!-- spx:nonce=n1 -->')
|
||||
expect(fresh).not.toContain('/ecc:grilling 讨论下')
|
||||
})
|
||||
|
||||
it('工单挂了原型插件才出现原型段落', () => {
|
||||
const withProto = renderPromptTemplate(brainstormTemplate, {
|
||||
issueNumber: '42',
|
||||
prototypeFile: 'proto/manifest.json',
|
||||
prototypeSessionId: 'sid-1',
|
||||
})
|
||||
expect(withProto).toContain('工单已有原型插件 proto/manifest.json')
|
||||
expect(withProto).toContain('原型会话 id:sid-1')
|
||||
|
||||
const withoutProto = renderPromptTemplate(brainstormTemplate, { issueNumber: '42' })
|
||||
expect(withoutProto).not.toContain('原型插件')
|
||||
})
|
||||
|
||||
it('没有原型会话 id 时不留下半句「原型会话 id:」', () => {
|
||||
const out = renderPromptTemplate(brainstormTemplate, {
|
||||
issueNumber: '42',
|
||||
prototypeFile: 'proto/manifest.json',
|
||||
})
|
||||
expect(out).toContain('原型会话终端名:issue-42-原型')
|
||||
expect(out).not.toContain('原型会话 id')
|
||||
})
|
||||
|
||||
it('渲染结果里不残留未替换的占位符', () => {
|
||||
const out = renderPromptTemplate(brainstormTemplate, {
|
||||
issueNumber: '42',
|
||||
prototypeFile: 'proto/manifest.json',
|
||||
prototypeSessionId: 'sid-1',
|
||||
})
|
||||
expect(out).not.toMatch(/\{\{/)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user