sillyspec 3.20.6 → 3.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/.claude/CLAUDE.md +23 -0
  2. package/.claude/skills/sillyspec-archive/SKILL.md +86 -21
  3. package/.claude/skills/sillyspec-auto/SKILL.md +98 -83
  4. package/.claude/skills/sillyspec-brainstorm/SKILL.md +78 -44
  5. package/.claude/skills/sillyspec-doctor/SKILL.md +61 -31
  6. package/.claude/skills/sillyspec-execute/SKILL.md +90 -30
  7. package/.claude/skills/sillyspec-explore/SKILL.md +96 -109
  8. package/.claude/skills/sillyspec-export/SKILL.md +4 -0
  9. package/.claude/skills/sillyspec-init/SKILL.md +7 -0
  10. package/.claude/skills/sillyspec-plan/SKILL.md +74 -21
  11. package/.claude/skills/sillyspec-propose/SKILL.md +61 -21
  12. package/.claude/skills/sillyspec-quick/SKILL.md +84 -21
  13. package/.claude/skills/sillyspec-scan/SKILL.md +74 -21
  14. package/.claude/skills/sillyspec-state/SKILL.md +11 -1
  15. package/.claude/skills/sillyspec-status/SKILL.md +55 -21
  16. package/.claude/skills/sillyspec-verify/SKILL.md +82 -21
  17. package/.claude/skills/sillyspec-workspace/SKILL.md +12 -0
  18. package/CLAUDE.md +4 -0
  19. package/package.json +2 -2
  20. package/src/change-list.js +51 -16
  21. package/src/contract-matrix.js +67 -0
  22. package/src/doctor-diagnostics.js +575 -0
  23. package/src/hooks/worktree-guard.js +111 -111
  24. package/src/index.js +154 -82
  25. package/src/progress.js +59 -9
  26. package/src/run.js +139 -38
  27. package/src/stage-contract.js +14 -2
  28. package/src/stages/execute.js +37 -18
  29. package/src/stages/plan-postcheck.js +234 -0
  30. package/src/stages/plan.js +13 -0
  31. package/src/sync.js +13 -3
  32. package/src/worktree.js +69 -28
  33. package/docs/brainstorm-plan-contract.md +0 -64
  34. package/docs/plan-execute-contract.md +0 -123
  35. package/docs/platform-scan-protocol.md +0 -298
  36. package/docs/revision-mode.md +0 -115
  37. package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +0 -99
  38. package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +0 -223
  39. package/docs/sillyspec/file-lifecycle/stage-artifacts.md +0 -167
  40. package/docs/sillyspec/file-lifecycle/storage-and-state.md +0 -148
  41. package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +0 -211
  42. package/docs/sillyspec/file-lifecycle.md +0 -132
  43. package/docs/workflow-contract-regression.md +0 -106
  44. package/docs/worktree-isolation.md +0 -252
  45. package/test/brainstorm-plan-contract.test.mjs +0 -273
  46. package/test/check-syntax.mjs +0 -26
  47. package/test/cli-top-level-aliases.test.mjs +0 -174
  48. package/test/contract-artifacts.test.mjs +0 -323
  49. package/test/decision-ref-version.mjs +0 -85
  50. package/test/decision-supersede.test.mjs +0 -277
  51. package/test/knowledge-match.test.mjs +0 -231
  52. package/test/plan-execute-contract.test.mjs +0 -357
  53. package/test/plan-optimization.test.mjs +0 -572
  54. package/test/platform-artifacts.test.mjs +0 -190
  55. package/test/platform-failure-samples.test.mjs +0 -199
  56. package/test/platform-recovery-chain.test.mjs +0 -179
  57. package/test/platform-recovery.test.mjs +0 -167
  58. package/test/platform-scan-p0.test.mjs +0 -186
  59. package/test/quick-recommend.test.mjs +0 -146
  60. package/test/revision-v1.test.mjs +0 -1145
  61. package/test/run-sanitize-project-name.test.mjs +0 -51
  62. package/test/run-scan-postcheck-fail.test.mjs +0 -64
  63. package/test/run-scan-project-parse.test.mjs +0 -200
  64. package/test/run-tests.mjs +0 -48
  65. package/test/runtime-cleanup-keeps-worktree.test.mjs +0 -107
  66. package/test/scan-docs-yaml-placeholders.test.mjs +0 -84
  67. package/test/scan-knowledge.test.mjs +0 -175
  68. package/test/scan-paths.test.mjs +0 -68
  69. package/test/scan-postcheck-project-priority.test.mjs +0 -85
  70. package/test/scan-postcheck.test.mjs +0 -197
  71. package/test/scan-workflow-anyfailed-block.test.mjs +0 -52
  72. package/test/spec-dir.test.mjs +0 -206
  73. package/test/stage-contract-failed-post-check.test.mjs +0 -102
  74. package/test/stage-contract.test.mjs +0 -301
  75. package/test/stage-definitions.test.mjs +0 -39
  76. package/test/wait-gates.test.mjs +0 -501
  77. package/test/workflow-spec-base.test.mjs +0 -142
  78. package/test/worktree-deps-provision.test.mjs +0 -148
  79. package/test/worktree-guard.test.mjs +0 -136
  80. package/test/worktree-native-overlay.test.mjs +0 -188
@@ -1,277 +0,0 @@
1
- /**
2
- * Stage Contract: decisions.md supersede 关系测试
3
- *
4
- * 验证被 supersede 的旧版本不再被要求引用
5
- */
6
- import { readFileSync } from 'fs'
7
- import { join } from 'path'
8
-
9
- // 直接测试 extractCurrentDecisionIds 的行为
10
- // 由于它不是 exported,我们通过 validateBrainstormOutputs 间接测试
11
-
12
- let failed = 0
13
- const failures = []
14
-
15
- function assert(condition, msg) {
16
- if (!condition) {
17
- failed++
18
- failures.push(msg)
19
- console.log(` ❌ FAIL: ${msg}`)
20
- } else {
21
- console.log(` ✅ PASS: ${msg}`)
22
- }
23
- }
24
-
25
- console.log('=== decisions.md supersede 关系测试 ===\n')
26
-
27
- // 动态导入 stage-contract.js 的内部函数
28
- // 由于 extractCurrentDecisionIds 不是 exported,我们用 validateBrainstormOutputs 来间接验证
29
- const contractPath = join(import.meta.dirname, '..', 'src', 'stage-contract.js')
30
- const contractSource = readFileSync(contractPath, 'utf8')
31
-
32
- // 提取并 eval 需要的函数
33
- // 通过创建临时模块来测试
34
- const { existsSync, mkdirSync, writeFileSync, mkdtempSync, rmSync } = await import('fs')
35
- const { join: pJoin } = await import('path')
36
- const { tmpdir } = await import('os')
37
-
38
- // 我们通过 validateBrainstormOutputs 来端到端测试
39
- const mod = await import(pJoin(import.meta.dirname, '..', 'src', 'stage-contract.js'))
40
-
41
- // ─────────────────────────────────────────
42
- // Test 1: 被 supersede 的旧版本不警告
43
- // ─────────────────────────────────────────
44
- console.log('--- Test 1: 被 supersede 的旧版本不警告 ---')
45
- {
46
- const tmpDir = mkdtempSync(join(tmpdir(), 'sillyspec-supersede-'))
47
- // 模拟 .sillyspec/changes 结构
48
- const changeDir = pJoin(tmpDir, '.sillyspec', 'changes', '2026-06-25-test')
49
- mkdirSync(changeDir, { recursive: true })
50
-
51
- // decisions.md: D-004@v2 supersedes D-004@v1
52
- writeFileSync(pJoin(changeDir, 'decisions.md'), `# Decisions
53
-
54
- ## D-004@v1
55
- - status: accepted
56
- - priority: P1
57
-
58
- ## D-004@v2
59
- - status: accepted
60
- - priority: P1
61
- - supersedes: D-004@v1
62
- `)
63
-
64
- // design.md 只引用 D-004@v2(不引用 v1)
65
- writeFileSync(pJoin(changeDir, 'design.md'), `# Design
66
-
67
- ## 目标
68
- 修复 bug。
69
-
70
- ## 方案
71
- 用方案 B。
72
-
73
- ## 决策
74
- D-004@v2: 选择方案 B
75
- `)
76
-
77
- writeFileSync(pJoin(changeDir, 'requirements.md'), `# Requirements
78
-
79
- ## FR-01
80
- 需求 A。
81
-
82
- ## 决策覆盖
83
- - D-004@v2 → FR-01
84
- `)
85
-
86
- writeFileSync(pJoin(changeDir, 'tasks.md'), `# Tasks
87
- - task-01: 实现
88
- `)
89
-
90
- // 用 validateBrainstormOutputs 验证
91
- const result = mod.runValidators("brainstorm", tmpDir, '2026-06-25-test')
92
-
93
- // 不应有关于 D-004@v1 的未引用警告
94
- const v1Warnings = result.warnings.filter(w => w.includes('D-004@V1'))
95
- assert(v1Warnings.length === 0, `不应有 D-004@V1 未引用警告,实际 warnings: ${JSON.stringify(v1Warnings)}`)
96
-
97
- rmSync(tmpDir, { recursive: true, force: true })
98
- }
99
-
100
- // ─────────────────────────────────────────
101
- // Test 2: 没有 supersede 关系时,旧版本仍被校验
102
- // ─────────────────────────────────────────
103
- console.log('\n--- Test 2: 无 supersede 关系时旧版本仍校验 ---')
104
- {
105
- const tmpDir = mkdtempSync(join(tmpdir(), 'sillyspec-nosupersede-'))
106
- const changeDir = pJoin(tmpDir, '.sillyspec', 'changes', '2026-06-25-test')
107
- mkdirSync(changeDir, { recursive: true })
108
-
109
- // decisions.md: 两个独立决策,无 supersede 关系
110
- writeFileSync(pJoin(changeDir, 'decisions.md'), `# Decisions
111
-
112
- ## D-004@v1
113
- - status: accepted
114
- - priority: P1
115
-
116
- ## D-005@v1
117
- - status: accepted
118
- - priority: P1
119
- `)
120
-
121
- // design.md 只引用 D-004@v1,不引用 D-005@v1
122
- writeFileSync(pJoin(changeDir, 'design.md'), `# Design
123
-
124
- ## 目标
125
- 修复 bug。
126
-
127
- ## 方案
128
- 用方案 B。
129
-
130
- ## 决策
131
- D-004@v1: 选择方案 B
132
- ## 文件变更清单
133
- | 操作 | 文件 | 说明 |
134
- ## 风险登记
135
- - 风险 A
136
- ## 自审
137
- - 已检查
138
- `)
139
-
140
- writeFileSync(pJoin(changeDir, 'requirements.md'), `# Requirements
141
- ## FR-01
142
- 需求 A。
143
- `)
144
-
145
- writeFileSync(pJoin(changeDir, 'proposal.md'), `# Proposal
146
- ## 不在范围内
147
- - 无
148
- `)
149
- writeFileSync(pJoin(changeDir, 'tasks.md'), `# Tasks
150
- - task-01: 实现
151
- `)
152
-
153
- const result = mod.runValidators("brainstorm", tmpDir, '2026-06-25-test')
154
-
155
- // D-005@v1 应该有未引用警告
156
- const v5Warnings = result.warnings.filter(w => w.includes('D-005@V1'))
157
- assert(v5Warnings.length > 0, `应有 D-005@V1 未引用警告`)
158
-
159
- rmSync(tmpDir, { recursive: true, force: true })
160
- }
161
-
162
- // ─────────────────────────────────────────
163
- // Test 3: status=superseded 的旧版本不警告(原有行为,回归测试)
164
- // ─────────────────────────────────────────
165
- console.log('\n--- Test 3: status=superseded 的旧版本不警告(回归) ---')
166
- {
167
- const tmpDir = mkdtempSync(join(tmpdir(), 'sillyspec-status-'))
168
- const changeDir = pJoin(tmpDir, '.sillyspec', 'changes', '2026-06-25-test')
169
- mkdirSync(changeDir, { recursive: true })
170
-
171
- writeFileSync(pJoin(changeDir, 'decisions.md'), `# Decisions
172
-
173
- ## D-004@v1
174
- - status: superseded
175
- - priority: P1
176
-
177
- ## D-004@v2
178
- - status: accepted
179
- - priority: P1
180
- `)
181
-
182
- writeFileSync(pJoin(changeDir, 'design.md'), `# Design
183
-
184
- ## 目标
185
- 修复 bug。
186
-
187
- ## 方案
188
- 用方案 B。
189
-
190
- ## 决策
191
- D-004@v2: 选择方案 B
192
- `)
193
-
194
- writeFileSync(pJoin(changeDir, 'requirements.md'), `# Requirements
195
- ## FR-01
196
- 需求 A。
197
- `)
198
-
199
- writeFileSync(pJoin(changeDir, 'tasks.md'), `# Tasks
200
- - task-01: 实现
201
- `)
202
-
203
- const result = mod.runValidators("brainstorm", tmpDir, '2026-06-25-test')
204
-
205
- const v1Warnings = result.warnings.filter(w => w.includes('D-004@V1'))
206
- assert(v1Warnings.length === 0, `status=superseded 的 D-004@V1 不应警告`)
207
-
208
- rmSync(tmpDir, { recursive: true, force: true })
209
- }
210
-
211
- // ─────────────────────────────────────────
212
- // Test 4: 多级 supersede 链(v1 ← v2 ← v3)
213
- // ─────────────────────────────────────────
214
- console.log('\n--- Test 4: 多级 supersede 链 ---')
215
- {
216
- const tmpDir = mkdtempSync(join(tmpdir(), 'sillyspec-chain-'))
217
- const changeDir = pJoin(tmpDir, '.sillyspec', 'changes', '2026-06-25-test')
218
- mkdirSync(changeDir, { recursive: true })
219
-
220
- writeFileSync(pJoin(changeDir, 'decisions.md'), `# Decisions
221
-
222
- ## D-004@v1
223
- - status: accepted
224
- - priority: P1
225
-
226
- ## D-004@v2
227
- - status: accepted
228
- - priority: P1
229
- - supersedes: D-004@v1
230
-
231
- ## D-004@v3
232
- - status: accepted
233
- - priority: P1
234
- - supersedes: D-004@v2
235
- `)
236
-
237
- // design.md 只引用 D-004@v3
238
- writeFileSync(pJoin(changeDir, 'design.md'), `# Design
239
-
240
- ## 目标
241
- 修复 bug。
242
-
243
- ## 方案
244
- 用方案 C。
245
-
246
- ## 决策
247
- D-004@v3: 选择方案 C
248
- `)
249
-
250
- writeFileSync(pJoin(changeDir, 'requirements.md'), `# Requirements
251
- ## FR-01
252
- 需求 A。
253
- - D-004@v3 → FR-01
254
- `)
255
-
256
- writeFileSync(pJoin(changeDir, 'tasks.md'), `# Tasks
257
- - task-01: 实现
258
- `)
259
-
260
- const result = mod.runValidators("brainstorm", tmpDir, '2026-06-25-test')
261
-
262
- const oldWarnings = result.warnings.filter(w => w.includes('D-004@V1') || w.includes('D-004@V2'))
263
- assert(oldWarnings.length === 0, `多级 supersede 链中 v1/v2 不应警告,实际: ${JSON.stringify(oldWarnings)}`)
264
-
265
- rmSync(tmpDir, { recursive: true, force: true })
266
- }
267
-
268
- // ── 结果 ──
269
- console.log(`\n${'='.repeat(50)}`)
270
- console.log(`✅ 通过: ${4 - failed} ❌ 失败: ${failed}`)
271
- if (failures.length > 0) {
272
- console.log(`失败项:`)
273
- failures.forEach(f => console.log(` - ${f}`))
274
- }
275
- console.log(`${'='.repeat(50)}`)
276
-
277
- if (failed > 0) process.exit(1)
@@ -1,231 +0,0 @@
1
- /**
2
- * knowledge-match.test.mjs — knowledge 关键词匹配引擎测试
3
- */
4
-
5
- import { join } from 'path'
6
- import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs'
7
- import { fileURLToPath, pathToFileURL } from 'url'
8
- import { tmpdir } from 'os'
9
-
10
- const __filename = fileURLToPath(import.meta.url)
11
- const __dirname = join(__filename, '..') // join(__dirname, '..') to get parent
12
- const root = join(__dirname, '..')
13
-
14
- const { parseKnowledgeIndex, matchKnowledge } = await import(
15
- pathToFileURL(join(root, 'src', 'knowledge-match.js')).href
16
- )
17
-
18
- let passed = 0, failed = 0
19
-
20
- function assert(cond, msg) {
21
- if (cond) { console.log(` ✅ PASS: ${msg}`); passed++ }
22
- else { console.log(` ❌ FAIL: ${msg}`); failed++ }
23
- }
24
-
25
- function setup(name) {
26
- const dir = join(tmpdir(), `kh-test-${name}`)
27
- mkdirSync(dir, { recursive: true })
28
- return dir
29
- }
30
- function clean(...dirs) {
31
- for (const d of dirs) try { rmSync(d, { recursive: true, force: true }) } catch {}
32
- }
33
-
34
- // ── 1: 有匹配条目 → matched=true, entries.length > 0 ──
35
- console.log('\n=== Test 1: 有匹配条目 → matched=true ===')
36
- {
37
- const dir = setup('t1')
38
- try {
39
- writeFileSync(join(dir, 'INDEX.md'), [
40
- '# Knowledge Index',
41
- '',
42
- '## Conventions',
43
- '- ESM|module|import → [ESM Only](conventions.md#esm-only)',
44
- '',
45
- '## Patterns',
46
- '- 阶段定义|stage|stages → [Stage Pattern](patterns.md#stage-step-pattern)',
47
- ].join('\n'))
48
-
49
- const result = matchKnowledge(dir, 'setup ESM module imports')
50
- assert(result.matched === true, 'matched is true')
51
- assert(result.entries.length === 1, 'one entry matched')
52
- assert(result.entries[0].file === 'conventions.md', 'file is conventions.md')
53
- assert(result.entries[0].anchor === 'esm-only', 'anchor is esm-only')
54
- assert(result.entries[0].keywords.includes('ESM'), 'keywords include ESM')
55
- } finally { clean(dir) }
56
- }
57
-
58
- // ── 2: 无匹配条目 → matched=false, report 包含 "no matches" ──
59
- console.log('\n=== Test 2: 无匹配条目 → matched=false ===')
60
- {
61
- const dir = setup('t2')
62
- try {
63
- writeFileSync(join(dir, 'INDEX.md'), [
64
- '# Knowledge Index',
65
- '',
66
- '## Patterns',
67
- '- 阶段定义|stage → [Stage Pattern](patterns.md#stage-step-pattern)',
68
- ].join('\n'))
69
-
70
- const result = matchKnowledge(dir, 'implement authentication flow')
71
- assert(result.matched === false, 'matched is false')
72
- assert(result.entries.length === 0, 'no entries matched')
73
- assert(result.report.includes('no matches'), 'report says no matches')
74
- } finally { clean(dir) }
75
- }
76
-
77
- // ── 3: INDEX.md 不存在 → matched=false, report 包含 "not found" ──
78
- console.log('\n=== Test 3: INDEX.md 不存在 → matched=false ===')
79
- {
80
- const dir = setup('t3')
81
- try {
82
- const result = matchKnowledge(dir, 'any context')
83
- assert(result.matched === false, 'matched is false')
84
- assert(result.report.includes('not found'), 'report says not found')
85
- assert(result.json.matched === false, 'json.matched is false')
86
- assert(result.json.entry_count === 0, 'json.entry_count is 0')
87
- } finally { clean(dir) }
88
- }
89
-
90
- // ── 4: INDEX.md 有空锚点引用 → 正常处理(不过度校验)──
91
- console.log('\n=== Test 4: INDEX.md 有空锚点引用 → 正常处理 ===')
92
- {
93
- const dir = setup('t4')
94
- try {
95
- writeFileSync(join(dir, 'INDEX.md'), [
96
- '# Knowledge Index',
97
- '',
98
- '## Conventions',
99
- '- naming|camelCase → [Naming](conventions.md)',
100
- ].join('\n'))
101
-
102
- const result = matchKnowledge(dir, 'naming conventions')
103
- assert(result.matched === true, 'matched is true with empty anchor')
104
- assert(result.entries[0].anchor === '', 'anchor is empty string')
105
- assert(result.entries[0].file === 'conventions.md', 'file is correct')
106
- } finally { clean(dir) }
107
- }
108
-
109
- // ── 5: 大小写不敏感匹配 → "STAGE" 匹配 "stage" ──
110
- console.log('\n=== Test 5: 大小写不敏感匹配 ===')
111
- {
112
- const dir = setup('t5')
113
- try {
114
- writeFileSync(join(dir, 'INDEX.md'), [
115
- '# Knowledge Index',
116
- '',
117
- '## Patterns',
118
- '- stage|stages → [Stage Pattern](patterns.md#stage-step-pattern)',
119
- ].join('\n'))
120
-
121
- const result = matchKnowledge(dir, 'implement STAGE definition')
122
- assert(result.matched === true, 'STAGE matches stage')
123
- assert(result.entries.length === 1, 'one entry matched')
124
- } finally { clean(dir) }
125
- }
126
-
127
- // ── 6: report 格式包含 "Knowledge Context" header ──
128
- console.log('\n=== Test 6: report 格式包含 Knowledge Context header ===')
129
- {
130
- const dir = setup('t6')
131
- try {
132
- writeFileSync(join(dir, 'INDEX.md'), [
133
- '# Knowledge Index',
134
- '',
135
- '## Patterns',
136
- '- 阶段定义|stage → [Stage Pattern](patterns.md#stage-step-pattern)',
137
- ].join('\n'))
138
-
139
- const result = matchKnowledge(dir, 'stage configuration')
140
- assert(result.report.includes('Knowledge Context'), 'report has Knowledge Context header')
141
- assert(result.report.includes('Status: matched'), 'report has Status: matched')
142
- assert(result.report.includes('Entries: 1'), 'report has Entries: 1')
143
- assert(result.report.includes('patterns.md#stage-step-pattern'), 'report has source')
144
- } finally { clean(dir) }
145
- }
146
-
147
- // ── 7: json 结构正确 ──
148
- console.log('\n=== Test 7: json 结构正确 ===')
149
- {
150
- const dir = setup('t7')
151
- try {
152
- writeFileSync(join(dir, 'INDEX.md'), [
153
- '# Knowledge Index',
154
- '',
155
- '## Conventions',
156
- '- ESM|module → [ESM Only](conventions.md#esm-only)',
157
- '',
158
- '## Patterns',
159
- '- stage|stages → [Stage Pattern](patterns.md#stage-step-pattern)',
160
- ].join('\n'))
161
-
162
- const result = matchKnowledge(dir, 'stage module setup')
163
- const j = result.json
164
- assert(j.matched === true, 'json.matched is true')
165
- assert(j.entry_count === 2, 'json.entry_count is 2')
166
- assert(Array.isArray(j.entries), 'json.entries is array')
167
- assert(j.entries.length === 2, 'json.entries length is 2')
168
-
169
- const stageEntry = j.entries.find(e => e.anchor === 'stage-step-pattern')
170
- assert(!!stageEntry, 'stage entry found in json')
171
- assert(stageEntry.keywords.includes('stage'), 'stage keywords correct')
172
- assert(stageEntry.category === 'Patterns', 'stage category correct')
173
- assert(stageEntry.file === 'patterns.md', 'stage file correct')
174
- } finally { clean(dir) }
175
- }
176
-
177
- // ── 8: 空目录(目录存在但无 INDEX.md)→ no matches ──
178
- console.log('\n=== Test 8: 空目录(无 INDEX.md)→ no matches ===')
179
- {
180
- const dir = setup('t8')
181
- try {
182
- const result = matchKnowledge(dir, 'any task')
183
- assert(result.matched === false, 'no INDEX.md → matched false')
184
- assert(result.report.includes('not found'), 'report says not found')
185
- } finally { clean(dir) }
186
- }
187
-
188
- // ── 9: parseKnowledgeIndex 返回正确分类 ──
189
- console.log('\n=== Test 9: parseKnowledgeIndex 分类正确 ===')
190
- {
191
- const dir = setup('t9')
192
- try {
193
- writeFileSync(join(dir, 'INDEX.md'), [
194
- '# Knowledge Index',
195
- '',
196
- '## Known Issues',
197
- '- GLM|proxy → [GLM Proxy](known-issues.md#glm-proxy)',
198
- '',
199
- '## Conventions',
200
- '- 命名|naming → [命名规范](conventions.md#naming)',
201
- ].join('\n'))
202
-
203
- const entries = parseKnowledgeIndex(dir)
204
- assert(entries.length === 2, 'parsed 2 entries')
205
- assert(entries[0].category === 'Known Issues', 'first entry category correct')
206
- assert(entries[1].category === 'Conventions', 'second entry category correct')
207
- assert(entries[0].display === 'GLM Proxy', 'display text correct')
208
- } finally { clean(dir) }
209
- }
210
-
211
- // ── 10: taskContext 为空 → no matches ──
212
- console.log('\n=== Test 10: taskContext 为空 → no matches ===')
213
- {
214
- const dir = setup('t10')
215
- try {
216
- writeFileSync(join(dir, 'INDEX.md'), [
217
- '# Knowledge Index',
218
- '',
219
- '## Patterns',
220
- '- stage → [Stage](patterns.md#stage)',
221
- ].join('\n'))
222
-
223
- const result = matchKnowledge(dir, '')
224
- assert(result.matched === false, 'empty context → matched false')
225
- } finally { clean(dir) }
226
- }
227
-
228
- // ── 汇总 ──
229
- console.log(`\n${'='.repeat(40)}`)
230
- console.log(`knowledge-match tests: ${passed} passed, ${failed} failed, ${passed + failed} total`)
231
- if (failed > 0) process.exit(1)