Files
superwork/vscode/src/cc/promptTemplate.test.ts
T
cruldra 7bac5c8b03 ✨ 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
2026-09-15 00:44:28 +08:00

80 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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(/\{\{/)
})
})