@zhuan-ai/zhuanspec 2.2.4 → 2.4.8

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 (103) hide show
  1. package/bin/zhuanspec-hook.js +3 -0
  2. package/dist/cli/hooks.d.ts +14 -0
  3. package/dist/cli/hooks.js +465 -0
  4. package/dist/cli/index.js +100 -0
  5. package/dist/commands/artifact-workflow.js +15 -34
  6. package/dist/commands/design.d.ts +42 -0
  7. package/dist/commands/design.js +337 -0
  8. package/dist/commands/progress.d.ts +32 -0
  9. package/dist/commands/progress.js +278 -0
  10. package/dist/commands/review.d.ts +32 -0
  11. package/dist/commands/review.js +472 -0
  12. package/dist/commands/validate.d.ts +14 -0
  13. package/dist/commands/validate.js +161 -10
  14. package/dist/core/archive.d.ts +1 -0
  15. package/dist/core/archive.js +45 -3
  16. package/dist/core/completions/command-registry.js +67 -0
  17. package/dist/core/configurators/slash/amazon-q.js +32 -2
  18. package/dist/core/configurators/slash/antigravity.js +8 -2
  19. package/dist/core/configurators/slash/auggie.js +16 -1
  20. package/dist/core/configurators/slash/base.js +1 -1
  21. package/dist/core/configurators/slash/claude.d.ts +4 -0
  22. package/dist/core/configurators/slash/claude.js +56 -1
  23. package/dist/core/configurators/slash/cline.js +8 -2
  24. package/dist/core/configurators/slash/codebuddy.js +22 -1
  25. package/dist/core/configurators/slash/codex.js +21 -0
  26. package/dist/core/configurators/slash/costrict.js +15 -0
  27. package/dist/core/configurators/slash/crush.js +22 -1
  28. package/dist/core/configurators/slash/cursor.js +22 -1
  29. package/dist/core/configurators/slash/factory.js +16 -1
  30. package/dist/core/configurators/slash/gemini.js +8 -2
  31. package/dist/core/configurators/slash/github-copilot.js +19 -1
  32. package/dist/core/configurators/slash/iflow.js +22 -1
  33. package/dist/core/configurators/slash/kilocode.js +4 -1
  34. package/dist/core/configurators/slash/opencode.js +27 -0
  35. package/dist/core/configurators/slash/qoder.d.ts +4 -0
  36. package/dist/core/configurators/slash/qoder.js +59 -1
  37. package/dist/core/configurators/slash/qwen.js +8 -2
  38. package/dist/core/configurators/slash/roocode.js +8 -2
  39. package/dist/core/configurators/slash/windsurf.js +8 -2
  40. package/dist/core/dashboard/metrics.d.ts +33 -0
  41. package/dist/core/dashboard/metrics.js +114 -0
  42. package/dist/core/hooks/collect-knowledge.d.ts +16 -0
  43. package/dist/core/hooks/collect-knowledge.js +203 -0
  44. package/dist/core/hooks/context-load-hook.d.ts +25 -0
  45. package/dist/core/hooks/context-load-hook.js +159 -0
  46. package/dist/core/hooks/deviation-check.d.ts +27 -0
  47. package/dist/core/hooks/deviation-check.js +403 -0
  48. package/dist/core/hooks/deviation-handler.d.ts +43 -0
  49. package/dist/core/hooks/deviation-handler.js +98 -0
  50. package/dist/core/hooks/init.d.ts +14 -0
  51. package/dist/core/hooks/init.js +244 -0
  52. package/dist/core/hooks/notify-milestone.d.ts +14 -0
  53. package/dist/core/hooks/notify-milestone.js +170 -0
  54. package/dist/core/hooks/post-apply.d.ts +29 -0
  55. package/dist/core/hooks/post-apply.js +173 -0
  56. package/dist/core/hooks/post-archive.d.ts +7 -0
  57. package/dist/core/hooks/post-archive.js +208 -0
  58. package/dist/core/hooks/pre-apply.d.ts +34 -0
  59. package/dist/core/hooks/pre-apply.js +139 -0
  60. package/dist/core/hooks/pre-archive.d.ts +7 -0
  61. package/dist/core/hooks/pre-archive.js +50 -0
  62. package/dist/core/hooks/record-progress.d.ts +49 -0
  63. package/dist/core/hooks/record-progress.js +494 -0
  64. package/dist/core/hooks/review-hooks.d.ts +89 -0
  65. package/dist/core/hooks/review-hooks.js +345 -0
  66. package/dist/core/hooks/review-orchestrator.d.ts +40 -0
  67. package/dist/core/hooks/review-orchestrator.js +146 -0
  68. package/dist/core/hooks/summarize.d.ts +15 -0
  69. package/dist/core/hooks/summarize.js +282 -0
  70. package/dist/core/hooks/user-input-hook.d.ts +25 -0
  71. package/dist/core/hooks/user-input-hook.js +179 -0
  72. package/dist/core/init.d.ts +8 -0
  73. package/dist/core/init.js +251 -23
  74. package/dist/core/parsers/requirement-blocks.js +13 -10
  75. package/dist/core/templates/agents-template.d.ts +1 -1
  76. package/dist/core/templates/agents-template.js +510 -243
  77. package/dist/core/templates/index.d.ts +1 -0
  78. package/dist/core/templates/index.js +1 -0
  79. package/dist/core/templates/skill-templates.js +46 -152
  80. package/dist/core/templates/slash-command-templates.d.ts +1 -1
  81. package/dist/core/templates/slash-command-templates.js +352 -20
  82. package/dist/core/templates/tasks-template.d.ts +7 -0
  83. package/dist/core/templates/tasks-template.js +130 -24
  84. package/dist/core/templates/tdd-tasks-template.d.ts +3 -0
  85. package/dist/core/templates/tdd-tasks-template.js +91 -38
  86. package/dist/core/templates/test-cases-template.d.ts +41 -0
  87. package/dist/core/templates/test-cases-template.js +128 -0
  88. package/dist/core/validation/strict-rules.d.ts +44 -5
  89. package/dist/core/validation/strict-rules.js +302 -8
  90. package/dist/core/validation/validator.js +52 -2
  91. package/dist/core/view.d.ts +1 -0
  92. package/dist/core/view.js +60 -2
  93. package/dist/mcp/index.d.ts +28 -0
  94. package/dist/mcp/index.js +31 -0
  95. package/dist/utils/file-system.d.ts +1 -0
  96. package/dist/utils/file-system.js +11 -0
  97. package/dist/utils/item-discovery.js +24 -2
  98. package/dist/utils/phase-utils.d.ts +36 -0
  99. package/dist/utils/phase-utils.js +117 -0
  100. package/package.json +22 -23
  101. package/schemas/spec-driven/schema.yaml +45 -31
  102. package/schemas/spec-driven/templates/spec.md +142 -5
  103. package/schemas/spec-driven/templates/tasks.md +73 -9
@@ -0,0 +1,403 @@
1
+ /**
2
+ * PreToolUse Hook - Deviation Check
3
+ *
4
+ * This hook runs before Write/Edit operations to check if the modification
5
+ * is within the approved proposal scope. It implements the "Reverse Sync" iron law.
6
+ *
7
+ * Logic:
8
+ * 1. If ZHUANSPEC_PHASE is 'idle' or no change bound → allow (normal workflow)
9
+ * 2. If ZHUANSPEC_PHASE is 'propose' → block (no code changes in propose phase)
10
+ * 3. If ZHUANSPEC_PHASE is 'apply' → check if file is in proposal scope:
11
+ * - In scope → allow
12
+ * - Out of scope → block with deviation handling options
13
+ */
14
+ import path from 'path';
15
+ import { execSync } from 'child_process';
16
+ import { FileSystemUtils } from '../../utils/file-system.js';
17
+ import { checkPreApplyConditions } from './pre-apply.js';
18
+ import { recordHookTrigger } from './record-progress.js';
19
+ // Pitfall keywords for knowledge collection
20
+ const PITFALL_KEYWORDS = [
21
+ '不对', '错了', '踩坑', '坑', '失败', '问题', 'bug',
22
+ '错误', '异常', '不工作', '不生效', '没想到',
23
+ '注意', '小心', '避免', '陷阱', '教训',
24
+ 'wrong', 'error', 'fail', 'bug', 'issue', 'pitfall',
25
+ ];
26
+ // Best practice keywords for knowledge collection
27
+ const BEST_PRACTICE_KEYWORDS = [
28
+ '最佳实践', '推荐做法', '最佳做法', '建议', '经验',
29
+ '成功经验', '最佳方案', '最佳选择', '正确做法',
30
+ 'best practice', 'recommended', 'recommendation',
31
+ ];
32
+ // Implicit convention keywords for knowledge collection
33
+ const IMPLICIT_CONVENTION_KEYWORDS = [
34
+ '隐式约定', '默认规则', '约定', '习惯', '惯例',
35
+ '默认行为', '默认配置', '默认值', '隐式规则',
36
+ 'implicit convention', 'default behavior', 'convention',
37
+ ];
38
+ // User correction keywords for deviation detection trigger
39
+ const USER_CORRECTION_KEYWORDS = [
40
+ '不对', '错了', '这里应该是', '应该是', '不是这样',
41
+ '这里不对', '改一下', '需要修改', '修改提案',
42
+ 'wrong', 'should be', 'not right', 'need to fix',
43
+ ];
44
+ export async function deviationCheckHook(options) {
45
+ const startTime = Date.now();
46
+ const toolName = options.tool || 'Write';
47
+ // Read stdin for Claude Code hook input
48
+ let stdinData = {};
49
+ try {
50
+ const stdinContent = await readStdin();
51
+ if (stdinContent) {
52
+ stdinData = JSON.parse(stdinContent);
53
+ }
54
+ }
55
+ catch {
56
+ // Ignore parse errors
57
+ }
58
+ // Get file path from stdin or options
59
+ const filePath = stdinData.tool_input?.file_path
60
+ || options.file
61
+ || '';
62
+ const promptText = stdinData?.prompt
63
+ || options.prompt
64
+ || '';
65
+ const trigger = options.trigger || 'pre-tool';
66
+ const output = await runDeviationCheck(filePath, trigger, promptText, options);
67
+ const durationMs = Date.now() - startTime;
68
+ // Record hook trigger
69
+ await recordHookTrigger('deviation-check', 'PreToolUse', output.continue, {
70
+ toolName,
71
+ filePath,
72
+ message: output.systemMessage,
73
+ durationMs,
74
+ });
75
+ if (options.json) {
76
+ console.log(JSON.stringify(output));
77
+ }
78
+ else {
79
+ if (output.systemMessage) {
80
+ console.log(output.systemMessage);
81
+ }
82
+ if (!output.continue) {
83
+ // Exit with non-zero to block the operation
84
+ process.exit(1);
85
+ }
86
+ }
87
+ }
88
+ async function readStdin() {
89
+ return new Promise((resolve) => {
90
+ let data = '';
91
+ process.stdin.setEncoding('utf8');
92
+ process.stdin.on('data', chunk => data += chunk);
93
+ process.stdin.on('end', () => resolve(data));
94
+ process.stdin.on('error', () => resolve(null));
95
+ });
96
+ }
97
+ async function runDeviationCheck(filePath, trigger, promptText, _options) {
98
+ const phase = process.env.ZHUANSPEC_PHASE || 'idle';
99
+ const changeId = process.env.ZHUANSPEC_CHANGE_ID || '';
100
+ const currentTask = process.env.ZHUANSPEC_CURRENT_TASK || '';
101
+ // No change bound or idle phase → allow normal workflow
102
+ if (!changeId || phase === 'idle') {
103
+ return {
104
+ continue: true,
105
+ systemMessage: '✓ No active ZhuanSpec change, normal workflow allowed',
106
+ };
107
+ }
108
+ const cwd = process.cwd();
109
+ const zhuanspecDir = path.join(cwd, 'zhuanspec');
110
+ const changeDir = path.join(zhuanspecDir, 'changes', changeId);
111
+ const proposalPath = path.join(changeDir, 'proposal.md');
112
+ const tasksPath = path.join(changeDir, 'tasks.md');
113
+ // Resume check: when session is resumed in apply phase, do a one-time deviation scan.
114
+ if (phase === 'apply' && trigger === 'resume') {
115
+ const estimatedFiles = await parseEstimatedChanges(proposalPath);
116
+ const gitDiffFiles = await parseGitChangedFiles(cwd);
117
+ const outOfScope = gitDiffFiles.filter((file) => {
118
+ if (file.includes('zhuanspec/'))
119
+ return false;
120
+ const normalized = normalizePath(file);
121
+ return !estimatedFiles.map(normalizePath).some((scope) => normalized.includes(scope) || scope.includes(normalized));
122
+ });
123
+ if (outOfScope.length > 0) {
124
+ return {
125
+ continue: false,
126
+ stopReason: 'Resume deviation detected',
127
+ systemMessage: `⚠️ RESUME DEVIATION: 检测到 ${outOfScope.length} 个提案范围外改动,请先执行 Reverse Sync(更新 proposal/tasks)再继续 Apply。`,
128
+ hookSpecificOutput: {
129
+ additionalContext: `Out-of-scope files: ${outOfScope.join(', ')}`,
130
+ options: ['更新提案后继续实施', '取消本次恢复并重新评估'],
131
+ },
132
+ };
133
+ }
134
+ return {
135
+ continue: true,
136
+ systemMessage: '✓ Resume deviation check passed',
137
+ };
138
+ }
139
+ // Post-prompt check: catch "additional request after apply/interruption" and force reverse-sync first.
140
+ if (phase === 'apply' && trigger === 'post-prompt') {
141
+ // Check for user correction keywords
142
+ const correctionKeywordFound = USER_CORRECTION_KEYWORDS.some(keyword => promptText.toLowerCase().includes(keyword.toLowerCase()));
143
+ if (correctionKeywordFound) {
144
+ // User correction detected - trigger deviation handling
145
+ return {
146
+ continue: false,
147
+ stopReason: 'User correction detected in Apply phase',
148
+ systemMessage: `⚠️ DEVIATION TRIGGER: 检测到用户纠正关键词,当前阶段为 ${phase}
149
+ 请按偏离修正流程处理:
150
+ 1. 先修改 spec 文件
151
+ 2. 用户确认后再修改代码
152
+ 3. 用户确认无误后触发知识沉淀`,
153
+ hookSpecificOutput: {
154
+ additionalContext: `User input: ${promptText.slice(0, 200)}`,
155
+ options: [
156
+ '修改 Spec 文件',
157
+ '直接修改代码(跳过 Spec)',
158
+ '取消修改',
159
+ ],
160
+ },
161
+ };
162
+ }
163
+ const extraRequestPattern = /(另外|额外|顺便|再加|同时|also|additionally|by the way|one more)/i;
164
+ const editIntentPattern = /(修改|改|新增|删除|重构|optimi[sz]e|add|change|edit|update|refactor|remove)/i;
165
+ if (extraRequestPattern.test(promptText) && editIntentPattern.test(promptText)) {
166
+ return {
167
+ continue: false,
168
+ stopReason: 'Post-prompt additional requirement detected',
169
+ systemMessage: '⚠️ 检测到 Apply 阶段追加需求,请先更新 proposal/tasks(Reverse Sync)后再实施。',
170
+ hookSpecificOutput: {
171
+ additionalContext: `Prompt: ${promptText.slice(0, 300)}`,
172
+ options: ['更新提案后继续实施', '标记为Bug修复豁免', '取消追加需求'],
173
+ },
174
+ };
175
+ }
176
+ return {
177
+ continue: true,
178
+ systemMessage: '✓ Post-prompt deviation check passed',
179
+ };
180
+ }
181
+ // Propose phase → block all code modifications (Iron Law 1: No Spec No Code)
182
+ // But check if we can transition to Apply phase
183
+ if (phase === 'propose') {
184
+ // Allow proposal file modifications
185
+ if (filePath.includes('zhuanspec/changes/') || filePath.includes('zhuanspec/specs/')) {
186
+ return {
187
+ continue: true,
188
+ systemMessage: '✓ Proposal file modification allowed in propose phase',
189
+ };
190
+ }
191
+ // Check if this is a code file that might trigger Apply phase transition
192
+ const isCodeFile = !filePath.includes('zhuanspec/') &&
193
+ /\.(java|ts|tsx|js|jsx|py|go|rs|sql|kt|scala|c|cpp|cs)$/i.test(filePath);
194
+ if (isCodeFile && changeId && trigger === 'pre-tool') {
195
+ // Try to run pre-apply check to see if we can transition to Apply phase
196
+ const changeDir = path.join(cwd, 'zhuanspec', 'changes', changeId);
197
+ const preApplyResult = await checkPreApplyConditions(changeDir);
198
+ if (preApplyResult.continue) {
199
+ // Pre-apply passed - allow code modification and suggest phase transition
200
+ return {
201
+ continue: true,
202
+ systemMessage: `✓ PRE-APPLY CHECK PASSED. Ready to transition to Apply phase.\n\nAll proposal requirements met:\n- proposal.md exists ✓\n- tasks.md exists ✓\n- specs directory non-empty ✓\n- validate --strict passed ✓\n- Plan approved ✓\n\nYou may now proceed with implementation. Consider updating ZHUANSPEC_PHASE to 'apply'.`,
203
+ };
204
+ }
205
+ // Pre-apply failed - block and show what's missing
206
+ const checks = preApplyResult.checks;
207
+ const missingItems = [];
208
+ if (!checks?.proposalExists)
209
+ missingItems.push('proposal.md');
210
+ if (!checks?.tasksExists)
211
+ missingItems.push('tasks.md');
212
+ if (!checks?.specsExists)
213
+ missingItems.push('specs directory (non-empty)');
214
+ if (!checks?.validationPassed)
215
+ missingItems.push('validate --strict passing');
216
+ if (!checks?.approved)
217
+ missingItems.push('.approved file (Plan approval)');
218
+ return {
219
+ continue: false,
220
+ stopReason: 'Pre-apply check failed: Cannot transition to Apply phase',
221
+ systemMessage: `⚠️ BLOCKED: Cannot modify "${filePath}" - Pre-apply check failed\n\nMissing requirements:\n${missingItems.map(m => `- ${m}`).join('\n')}\n\nPlease complete Propose phase requirements before implementing code.`,
222
+ hookSpecificOutput: {
223
+ additionalContext: 'ZhuanSpec Iron Law 1: No Spec No Code - Complete proposal before implementation.',
224
+ },
225
+ };
226
+ }
227
+ // Block other non-code file modifications in propose phase
228
+ return {
229
+ continue: false,
230
+ stopReason: 'Propose phase: No code modifications allowed (No Spec No Code)',
231
+ systemMessage: `⚠️ BLOCKED: Propose phase - Cannot modify "${filePath}"\nOnly proposal files (zhuanspec/changes/, zhuanspec/specs/) can be modified.\nPlease wait for proposal approval before implementing.`,
232
+ hookSpecificOutput: {
233
+ additionalContext: 'ZhuanSpec Iron Law 1: No Spec No Code - Implementation cannot proceed until proposal is approved.',
234
+ },
235
+ };
236
+ }
237
+ // Apply phase → check deviation
238
+ if (phase === 'apply') {
239
+ let estimatedFiles = [];
240
+ let currentTaskFiles = [];
241
+ // Parse proposal for estimated changes
242
+ if (await FileSystemUtils.fileExists(proposalPath)) {
243
+ estimatedFiles = await parseEstimatedChanges(proposalPath);
244
+ }
245
+ // Parse tasks for current task scope
246
+ if (await FileSystemUtils.fileExists(tasksPath)) {
247
+ currentTaskFiles = await parseCurrentTaskFiles(tasksPath, currentTask);
248
+ }
249
+ // Normalize paths for comparison
250
+ const normalizedTarget = normalizePath(filePath);
251
+ const normalizedEstimated = estimatedFiles.map(normalizePath);
252
+ const normalizedCurrentTask = currentTaskFiles.map(normalizePath);
253
+ // Check if file is in scope
254
+ const inEstimatedScope = normalizedEstimated.some(f => normalizedTarget.includes(f) || f.includes(normalizedTarget));
255
+ const inCurrentTaskScope = normalizedCurrentTask.some(f => normalizedTarget.includes(f) || f.includes(normalizedTarget));
256
+ if (inEstimatedScope || inCurrentTaskScope) {
257
+ return {
258
+ continue: true,
259
+ systemMessage: `✓ File "${filePath}" is within proposal scope`,
260
+ };
261
+ }
262
+ // File is out of scope → deviation detected
263
+ const deviationId = `dev-${Date.now()}`;
264
+ return {
265
+ continue: false,
266
+ stopReason: 'Deviation detected: File not in approved proposal scope',
267
+ systemMessage: `⚠️ DEVIATION DETECTED: "${filePath}" is not in the approved proposal scope.\n\nProposal scope: [${estimatedFiles.join(', ') || 'not specified'}]\nCurrent task scope: [${currentTaskFiles.join(', ') || 'not specified'}]\n\nPlease choose how to handle this deviation:`,
268
+ hookSpecificOutput: {
269
+ deviationId,
270
+ additionalContext: `
271
+ Deviation detected for file: ${filePath}
272
+
273
+ Options:
274
+ 1. 更新提案后继续实施 - Update proposal to include this file, then continue
275
+ 2. 此修改是Bug修复 - Mark as bug fix (exempt from proposal flow)
276
+ 3. 取消修改 - Cancel this modification
277
+ 4. 其他 - Other (provide explanation)
278
+
279
+ ZhuanSpec Iron Law 3: Reverse Sync - When deviation is found, update proposal first.
280
+ `,
281
+ options: [
282
+ '更新提案后继续实施',
283
+ '此修改是Bug修复',
284
+ '取消修改',
285
+ '其他',
286
+ ],
287
+ },
288
+ };
289
+ }
290
+ // Default: allow
291
+ return {
292
+ continue: true,
293
+ };
294
+ }
295
+ async function parseGitChangedFiles(repoRoot) {
296
+ try {
297
+ const output = execSync('git diff --name-only', {
298
+ cwd: repoRoot,
299
+ encoding: 'utf-8',
300
+ stdio: ['pipe', 'pipe', 'ignore'],
301
+ });
302
+ return output.split('\n').map((line) => line.trim()).filter(Boolean);
303
+ }
304
+ catch {
305
+ // fall through
306
+ }
307
+ try {
308
+ const status = execSync('git status --porcelain', {
309
+ cwd: repoRoot,
310
+ encoding: 'utf-8',
311
+ stdio: ['pipe', 'pipe', 'ignore'],
312
+ });
313
+ return status
314
+ .split('\n')
315
+ .map((line) => line.replace(/^\s*[A-Z?]+\s+/, '').trim())
316
+ .filter(Boolean);
317
+ }
318
+ catch {
319
+ // ignore
320
+ }
321
+ return [];
322
+ }
323
+ async function parseEstimatedChanges(proposalPath) {
324
+ try {
325
+ const content = await FileSystemUtils.readFile(proposalPath);
326
+ const files = [];
327
+ // Look for "影响范围" or "Affected code" sections
328
+ const lines = content.split('\n');
329
+ let inAffectedSection = false;
330
+ for (const line of lines) {
331
+ if (line.includes('影响范围') || line.includes('Affected code') || line.includes('Affected files')) {
332
+ inAffectedSection = true;
333
+ continue;
334
+ }
335
+ if (inAffectedSection && line.startsWith('##')) {
336
+ // End of section
337
+ inAffectedSection = false;
338
+ }
339
+ if (inAffectedSection && line.trim().startsWith('-')) {
340
+ // Extract file path from bullet point
341
+ const match = line.match(/-\s*(.+)/);
342
+ if (match) {
343
+ files.push(match[1].trim());
344
+ }
345
+ }
346
+ }
347
+ return files;
348
+ }
349
+ catch {
350
+ return [];
351
+ }
352
+ }
353
+ async function parseCurrentTaskFiles(tasksPath, currentTask) {
354
+ if (!currentTask) {
355
+ return [];
356
+ }
357
+ try {
358
+ const content = await FileSystemUtils.readFile(tasksPath);
359
+ const files = [];
360
+ // Find the current task section
361
+ const lines = content.split('\n');
362
+ let inCurrentTask = false;
363
+ for (const line of lines) {
364
+ // Match task line: - [ ] X.Y description or - [x] X.Y description
365
+ const taskMatch = line.match(/-\s*\[[x ]\]\s*(\d+\.\d+)\s*(.+)/);
366
+ if (taskMatch) {
367
+ const taskId = taskMatch[1];
368
+ if (taskId === currentTask) {
369
+ inCurrentTask = true;
370
+ // Extract files mentioned in task description
371
+ const description = taskMatch[2];
372
+ // Look for file patterns in description
373
+ const filePatterns = description.match(/[a-zA-Z0-9_\-/]+\.(java|ts|tsx|js|jsx|py|go|rs|md|yaml|json|sql)/g);
374
+ if (filePatterns) {
375
+ files.push(...filePatterns);
376
+ }
377
+ }
378
+ else {
379
+ inCurrentTask = false;
380
+ }
381
+ }
382
+ // Also check for file mentions within task details
383
+ if (inCurrentTask) {
384
+ const filePatterns = line.match(/[a-zA-Z0-9_\-/]+\.(java|ts|tsx|js|jsx|py|go|rs|md|yaml|json|sql)/g);
385
+ if (filePatterns) {
386
+ files.push(...filePatterns);
387
+ }
388
+ }
389
+ }
390
+ return files;
391
+ }
392
+ catch {
393
+ return [];
394
+ }
395
+ }
396
+ function normalizePath(p) {
397
+ return p
398
+ .replace(/\\/g, '/')
399
+ .replace(/^\.\/|^\/+/, '')
400
+ .toLowerCase();
401
+ }
402
+ export { PITFALL_KEYWORDS, USER_CORRECTION_KEYWORDS, BEST_PRACTICE_KEYWORDS, IMPLICIT_CONVENTION_KEYWORDS };
403
+ //# sourceMappingURL=deviation-check.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Deviation Handler - 偏离修正流程处理
3
+ *
4
+ * 当用户在 Apply 阶段输入纠正关键词时触发偏离修正流程:
5
+ * 1. 先修改 spec 文件,同步用户确认
6
+ * 2. 用户确认后修改代码,完成后验证
7
+ * 3. 用户确认无误后触发知识沉淀
8
+ */
9
+ export interface DeviationContext {
10
+ changeId: string;
11
+ phase: string;
12
+ userInput: string;
13
+ detectedKeywords: string[];
14
+ }
15
+ export interface DeviationAction {
16
+ type: 'modify-spec' | 'modify-code' | 'knowledge-collect' | 'cancel';
17
+ targetFile?: string;
18
+ content?: string;
19
+ }
20
+ /**
21
+ * Handle deviation correction flow
22
+ *
23
+ * @param context - Deviation context with changeId, phase, userInput
24
+ * @returns Action recommendation for the AI
25
+ */
26
+ export declare function handleDeviation(context: DeviationContext): Promise<DeviationAction>;
27
+ /**
28
+ * Complete deviation handling after user confirms
29
+ *
30
+ * @param deviationId - The deviation ID to mark as resolved
31
+ * @param specChanged - List of spec files that were changed
32
+ * @param codeChanged - List of code files that were changed
33
+ */
34
+ export declare function completeDeviation(deviationId: string, changeId: string, specChanged: string[], codeChanged: string[]): Promise<void>;
35
+ /**
36
+ * Trigger knowledge collection after deviation is resolved
37
+ *
38
+ * @param changeId - The change ID
39
+ * @param knowledgeContent - Knowledge content to write
40
+ */
41
+ export declare function triggerKnowledgeCollection(changeId: string, knowledgeContent: string): Promise<void>;
42
+ export { USER_CORRECTION_KEYWORDS } from './deviation-check.js';
43
+ //# sourceMappingURL=deviation-handler.d.ts.map
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Deviation Handler - 偏离修正流程处理
3
+ *
4
+ * 当用户在 Apply 阶段输入纠正关键词时触发偏离修正流程:
5
+ * 1. 先修改 spec 文件,同步用户确认
6
+ * 2. 用户确认后修改代码,完成后验证
7
+ * 3. 用户确认无误后触发知识沉淀
8
+ */
9
+ import path from 'path';
10
+ import { FileSystemUtils } from '../../utils/file-system.js';
11
+ import { getBeijingTime } from './record-progress.js';
12
+ /**
13
+ * Handle deviation correction flow
14
+ *
15
+ * @param context - Deviation context with changeId, phase, userInput
16
+ * @returns Action recommendation for the AI
17
+ */
18
+ export async function handleDeviation(context) {
19
+ const cwd = process.cwd();
20
+ const zhuanspecDir = path.join(cwd, 'zhuanspec');
21
+ const changeDir = path.join(zhuanspecDir, 'changes', context.changeId);
22
+ const progressPath = path.join(changeDir, 'metrics', 'progress.json');
23
+ // Record deviation in progress.json
24
+ const deviationId = `dev-${Date.now()}`;
25
+ const deviationRecord = {
26
+ deviationId,
27
+ detectedAt: getBeijingTime(),
28
+ triggerType: 'user-input',
29
+ userInput: context.userInput.slice(0, 500),
30
+ specChanged: [],
31
+ codeChanged: [],
32
+ resolvedAt: undefined,
33
+ };
34
+ // Update progress.json with deviation record
35
+ if (await FileSystemUtils.fileExists(progressPath)) {
36
+ const progressData = JSON.parse(await FileSystemUtils.readFile(progressPath));
37
+ progressData.deviationRecords = progressData.deviationRecords || [];
38
+ progressData.deviationRecords.push(deviationRecord);
39
+ progressData.deviationCount = (progressData.deviationCount || 0) + 1;
40
+ await FileSystemUtils.writeFile(progressPath, JSON.stringify(progressData, null, 2));
41
+ }
42
+ // Step 1: Modify spec file first
43
+ return {
44
+ type: 'modify-spec',
45
+ targetFile: 'specs/',
46
+ };
47
+ }
48
+ /**
49
+ * Complete deviation handling after user confirms
50
+ *
51
+ * @param deviationId - The deviation ID to mark as resolved
52
+ * @param specChanged - List of spec files that were changed
53
+ * @param codeChanged - List of code files that were changed
54
+ */
55
+ export async function completeDeviation(deviationId, changeId, specChanged, codeChanged) {
56
+ const cwd = process.cwd();
57
+ const zhuanspecDir = path.join(cwd, 'zhuanspec');
58
+ const changeDir = path.join(zhuanspecDir, 'changes', changeId);
59
+ const progressPath = path.join(changeDir, 'metrics', 'progress.json');
60
+ if (await FileSystemUtils.fileExists(progressPath)) {
61
+ const progressData = JSON.parse(await FileSystemUtils.readFile(progressPath));
62
+ const deviation = progressData.deviationRecords?.find((d) => d.deviationId === deviationId);
63
+ if (deviation) {
64
+ deviation.specChanged = specChanged;
65
+ deviation.codeChanged = codeChanged;
66
+ deviation.resolvedAt = getBeijingTime();
67
+ await FileSystemUtils.writeFile(progressPath, JSON.stringify(progressData, null, 2));
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * Trigger knowledge collection after deviation is resolved
73
+ *
74
+ * @param changeId - The change ID
75
+ * @param knowledgeContent - Knowledge content to write
76
+ */
77
+ export async function triggerKnowledgeCollection(changeId, knowledgeContent) {
78
+ const cwd = process.cwd();
79
+ const zhuanspecDir = path.join(cwd, 'zhuanspec');
80
+ const knowledgePath = path.join(zhuanspecDir, 'knowledge', 'best-practices');
81
+ // Write knowledge file
82
+ const knowledgeFile = path.join(knowledgePath, `deviation-${changeId}.md`);
83
+ await FileSystemUtils.createDirectory(knowledgePath);
84
+ await FileSystemUtils.writeFile(knowledgeFile, knowledgeContent);
85
+ // Update knowledge index
86
+ const indexPath = path.join(zhuanspecDir, 'knowledge', 'index.md');
87
+ if (await FileSystemUtils.fileExists(indexPath)) {
88
+ let indexContent = await FileSystemUtils.readFile(indexPath);
89
+ // Add new entry under Best Practices section
90
+ const entry = `- [偏离处理经验](best-practices/deviation-${changeId}.md) — 偏离修正流程沉淀`;
91
+ if (!indexContent.includes(entry)) {
92
+ indexContent = indexContent.replace('## Best Practices\n', `## Best Practices\n${entry}\n`);
93
+ await FileSystemUtils.writeFile(indexPath, indexContent);
94
+ }
95
+ }
96
+ }
97
+ export { USER_CORRECTION_KEYWORDS } from './deviation-check.js';
98
+ //# sourceMappingURL=deviation-handler.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * SessionStart Hook - Initialize ZhuanSpec environment
3
+ *
4
+ * This hook runs when Claude Code starts a new session:
5
+ * 1. Loads project.md and knowledge/ files
6
+ * 2. Sets environment variables for phase tracking
7
+ * 3. Retrieves relevant implicit conventions and change patterns
8
+ */
9
+ interface InitHookOptions {
10
+ json?: boolean;
11
+ }
12
+ export declare function initHook(options: InitHookOptions): Promise<void>;
13
+ export {};
14
+ //# sourceMappingURL=init.d.ts.map