@zhuan-ai/zhuanspec 1.3.0 → 2.0.0-newApply
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.
- package/README.zh.md +1 -1
- package/bin/zhuanspec-hook.js +3 -0
- package/dist/cli/hooks.d.ts +26 -0
- package/dist/cli/hooks.js +558 -0
- package/dist/cli/index.js +296 -2
- package/dist/commands/accuracy.d.ts +15 -0
- package/dist/commands/accuracy.js +159 -0
- package/dist/commands/artifact-workflow.js +35 -36
- package/dist/commands/change.js +9 -4
- package/dist/commands/design.d.ts +53 -0
- package/dist/commands/design.js +418 -0
- package/dist/commands/knowledge.d.ts +31 -0
- package/dist/commands/knowledge.js +354 -0
- package/dist/commands/progress.d.ts +137 -0
- package/dist/commands/progress.js +893 -0
- package/dist/commands/review.d.ts +8 -0
- package/dist/commands/review.js +154 -0
- package/dist/commands/spec.js +12 -4
- package/dist/commands/validate-reports.d.ts +17 -0
- package/dist/commands/validate-reports.js +76 -0
- package/dist/commands/validate.d.ts +24 -0
- package/dist/commands/validate.js +306 -12
- package/dist/core/archive.d.ts +23 -12
- package/dist/core/archive.js +331 -7
- package/dist/core/artifact-graph/instruction-loader.d.ts +3 -1
- package/dist/core/artifact-graph/instruction-loader.js +27 -2
- package/dist/core/business-assets-sync.d.ts +94 -0
- package/dist/core/business-assets-sync.js +562 -0
- package/dist/core/completions/command-registry.js +66 -0
- package/dist/core/configurators/codex.d.ts +191 -0
- package/dist/core/configurators/codex.js +538 -0
- package/dist/core/configurators/registry.js +3 -0
- package/dist/core/configurators/slash/amazon-q.js +32 -2
- package/dist/core/configurators/slash/antigravity.js +8 -2
- package/dist/core/configurators/slash/auggie.js +16 -1
- package/dist/core/configurators/slash/base.js +1 -1
- package/dist/core/configurators/slash/claude.d.ts +4 -0
- package/dist/core/configurators/slash/claude.js +56 -1
- package/dist/core/configurators/slash/cline.js +8 -2
- package/dist/core/configurators/slash/codebuddy.js +22 -1
- package/dist/core/configurators/slash/codex.d.ts +39 -8
- package/dist/core/configurators/slash/codex.js +158 -91
- package/dist/core/configurators/slash/costrict.js +15 -0
- package/dist/core/configurators/slash/crush.js +22 -1
- package/dist/core/configurators/slash/cursor.js +22 -1
- package/dist/core/configurators/slash/factory.js +16 -1
- package/dist/core/configurators/slash/gemini.js +8 -2
- package/dist/core/configurators/slash/github-copilot.js +19 -1
- package/dist/core/configurators/slash/iflow.js +22 -1
- package/dist/core/configurators/slash/kilocode.js +4 -1
- package/dist/core/configurators/slash/opencode.js +27 -0
- package/dist/core/configurators/slash/qoder.d.ts +4 -0
- package/dist/core/configurators/slash/qoder.js +59 -1
- package/dist/core/configurators/slash/qwen.js +8 -2
- package/dist/core/configurators/slash/roocode.js +8 -2
- package/dist/core/configurators/slash/windsurf.js +8 -2
- package/dist/core/corrections/select-candidates.d.ts +58 -0
- package/dist/core/corrections/select-candidates.js +357 -0
- package/dist/core/dashboard/metrics.d.ts +42 -0
- package/dist/core/dashboard/metrics.js +126 -0
- package/dist/core/hooks/collect-knowledge.d.ts +24 -0
- package/dist/core/hooks/collect-knowledge.js +179 -0
- package/dist/core/hooks/context-load-hook.d.ts +25 -0
- package/dist/core/hooks/context-load-hook.js +161 -0
- package/dist/core/hooks/deviation-check.d.ts +62 -0
- package/dist/core/hooks/deviation-check.js +643 -0
- package/dist/core/hooks/deviation-handler.d.ts +44 -0
- package/dist/core/hooks/deviation-handler.js +151 -0
- package/dist/core/hooks/init.d.ts +14 -0
- package/dist/core/hooks/init.js +243 -0
- package/dist/core/hooks/knowledge-index.d.ts +136 -0
- package/dist/core/hooks/knowledge-index.js +506 -0
- package/dist/core/hooks/knowledge-references.d.ts +99 -0
- package/dist/core/hooks/knowledge-references.js +1659 -0
- package/dist/core/hooks/knowledge-usage.d.ts +90 -0
- package/dist/core/hooks/knowledge-usage.js +341 -0
- package/dist/core/hooks/notify-milestone.d.ts +14 -0
- package/dist/core/hooks/notify-milestone.js +196 -0
- package/dist/core/hooks/post-apply.d.ts +33 -0
- package/dist/core/hooks/post-apply.js +309 -0
- package/dist/core/hooks/post-archive.d.ts +7 -0
- package/dist/core/hooks/post-archive.js +312 -0
- package/dist/core/hooks/pre-apply.d.ts +34 -0
- package/dist/core/hooks/pre-apply.js +147 -0
- package/dist/core/hooks/pre-archive.d.ts +7 -0
- package/dist/core/hooks/pre-archive.js +49 -0
- package/dist/core/hooks/pre-review.d.ts +27 -0
- package/dist/core/hooks/pre-review.js +182 -0
- package/dist/core/hooks/record-progress.d.ts +418 -0
- package/dist/core/hooks/record-progress.js +1653 -0
- package/dist/core/hooks/review-hooks.d.ts +133 -0
- package/dist/core/hooks/review-hooks.js +852 -0
- package/dist/core/hooks/summarize.d.ts +15 -0
- package/dist/core/hooks/summarize.js +526 -0
- package/dist/core/hooks/tdd-phase-hook.d.ts +59 -0
- package/dist/core/hooks/tdd-phase-hook.js +313 -0
- package/dist/core/hooks/user-input-hook.d.ts +73 -0
- package/dist/core/hooks/user-input-hook.js +547 -0
- package/dist/core/init.d.ts +55 -0
- package/dist/core/init.js +1065 -43
- package/dist/core/metrics/code-accuracy.d.ts +317 -0
- package/dist/core/metrics/code-accuracy.js +716 -0
- package/dist/core/metrics/knowledge-funnel.d.ts +51 -0
- package/dist/core/metrics/knowledge-funnel.js +144 -0
- package/dist/core/parsers/requirement-blocks.js +13 -10
- package/dist/core/skill-discovery.d.ts +2 -2
- package/dist/core/skill-discovery.js +16 -3
- package/dist/core/specs-apply.js +19 -8
- package/dist/core/styles/palette.d.ts +1 -0
- package/dist/core/styles/palette.js +3 -1
- package/dist/core/task-graph/execution-planner.d.ts +38 -0
- package/dist/core/task-graph/execution-planner.js +237 -0
- package/dist/core/task-graph/index.d.ts +13 -0
- package/dist/core/task-graph/index.js +16 -0
- package/dist/core/task-graph/integration-report-template.d.ts +30 -0
- package/dist/core/task-graph/integration-report-template.js +210 -0
- package/dist/core/task-graph/mermaid-renderer.d.ts +22 -0
- package/dist/core/task-graph/mermaid-renderer.js +128 -0
- package/dist/core/task-graph/task-graph.d.ts +74 -0
- package/dist/core/task-graph/task-graph.js +219 -0
- package/dist/core/task-graph/task-parser.d.ts +27 -0
- package/dist/core/task-graph/task-parser.js +168 -0
- package/dist/core/task-graph/types.d.ts +139 -0
- package/dist/core/task-graph/types.js +7 -0
- package/dist/core/task-graph/xml-renderer.d.ts +21 -0
- package/dist/core/task-graph/xml-renderer.js +139 -0
- package/dist/core/templates/agents-root-stub.d.ts +1 -1
- package/dist/core/templates/agents-root-stub.js +29 -0
- package/dist/core/templates/agents-template.d.ts +1 -1
- package/dist/core/templates/agents-template.js +830 -269
- package/dist/core/templates/codex-agents-template.d.ts +53 -0
- package/dist/core/templates/codex-agents-template.js +177 -0
- package/dist/core/templates/codex-hooks-template.d.ts +116 -0
- package/dist/core/templates/codex-hooks-template.js +233 -0
- package/dist/core/templates/codex-skills-template.d.ts +39 -0
- package/dist/core/templates/codex-skills-template.js +69 -0
- package/dist/core/templates/index.d.ts +1 -0
- package/dist/core/templates/index.js +1 -0
- package/dist/core/templates/skill-templates.js +83 -163
- package/dist/core/templates/slash-command-templates.d.ts +1 -1
- package/dist/core/templates/slash-command-templates.js +1274 -28
- package/dist/core/templates/tasks-template.d.ts +23 -0
- package/dist/core/templates/tasks-template.js +285 -0
- package/dist/core/templates/tdd-tasks-template.d.ts +21 -0
- package/dist/core/templates/tdd-tasks-template.js +259 -0
- package/dist/core/templates/test-cases-template.d.ts +48 -0
- package/dist/core/templates/test-cases-template.js +135 -0
- package/dist/core/update.d.ts +2 -0
- package/dist/core/update.js +182 -2
- package/dist/core/validation/report-schema.d.ts +74 -0
- package/dist/core/validation/report-schema.js +254 -0
- package/dist/core/validation/strict-rules.d.ts +202 -0
- package/dist/core/validation/strict-rules.js +1698 -0
- package/dist/core/validation/types.d.ts +10 -0
- package/dist/core/validation/validator.d.ts +5 -0
- package/dist/core/validation/validator.js +174 -1
- package/dist/core/view.d.ts +1 -0
- package/dist/core/view.js +60 -2
- package/dist/mcp/index.d.ts +28 -0
- package/dist/mcp/index.js +31 -0
- package/dist/utils/file-system.d.ts +1 -0
- package/dist/utils/file-system.js +11 -0
- package/dist/utils/git-repo-detector.d.ts +130 -0
- package/dist/utils/git-repo-detector.js +275 -0
- package/dist/utils/hook-host.d.ts +47 -0
- package/dist/utils/hook-host.js +129 -0
- package/dist/utils/hook-merge.d.ts +35 -0
- package/dist/utils/hook-merge.js +103 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/item-discovery.js +28 -5
- package/dist/utils/line-diff.d.ts +33 -0
- package/dist/utils/line-diff.js +85 -0
- package/dist/utils/phase-utils.d.ts +49 -0
- package/dist/utils/phase-utils.js +286 -0
- package/dist/utils/resolve-root.d.ts +26 -0
- package/dist/utils/resolve-root.js +80 -0
- package/dist/utils/task-progress.js +8 -0
- package/package.json +3 -2
- package/schemas/spec-driven/schema.yaml +66 -42
- package/schemas/spec-driven/templates/design.md +6 -6
- package/schemas/spec-driven/templates/proposal.md +7 -7
- package/schemas/spec-driven/templates/spec.md +142 -5
- package/schemas/spec-driven/templates/tasks.md +198 -9
- package/schemas/tdd/schema.yaml +107 -107
- package/schemas/tdd/templates/implementation.md +5 -5
- package/schemas/tdd/templates/spec.md +6 -6
- package/schemas/tdd/templates/test.md +7 -7
package/README.zh.md
CHANGED
|
@@ -415,7 +415,7 @@ npm install -g ./distributions/fission-ai-zhuanspec-1.0.0.tgz
|
|
|
415
415
|
|
|
416
416
|
1. **升级包**
|
|
417
417
|
```bash
|
|
418
|
-
npm install -g @
|
|
418
|
+
npm install -g @zhuan-ai/zhuanspec@latest
|
|
419
419
|
```
|
|
420
420
|
2. **刷新代理说明**
|
|
421
421
|
- 在每个项目内运行 `zhuanspec update` 以重新生成 AI 指导并确保最新的斜杠命令处于活动状态。
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ZhuanSpec Hooks CLI Entry Point
|
|
4
|
+
*
|
|
5
|
+
* This CLI provides hook commands for Claude Code integration:
|
|
6
|
+
* - init: SessionStart hook - Initialize ZhuanSpec environment
|
|
7
|
+
* - deviation-check: PreToolUse hook - Check for proposal deviations
|
|
8
|
+
* - record-progress: PostToolUse hook - Record task progress
|
|
9
|
+
* - collect-knowledge: PostToolUse hook - Collect knowledge from interactions
|
|
10
|
+
* - summarize: Stop hook - Summarize session progress
|
|
11
|
+
* - review-*: Review phase hooks - Check skill outputs and control fix loops
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Phase Transition Check - Unified function for phase transition validation
|
|
15
|
+
*/
|
|
16
|
+
export declare function runPhaseTransitionCheck(options: {
|
|
17
|
+
from?: string;
|
|
18
|
+
to?: string;
|
|
19
|
+
change?: string;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
continue: boolean;
|
|
22
|
+
systemMessage?: string;
|
|
23
|
+
checks?: Record<string, boolean>;
|
|
24
|
+
requiresUserConfirmation?: boolean;
|
|
25
|
+
}>;
|
|
26
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ZhuanSpec Hooks CLI Entry Point
|
|
4
|
+
*
|
|
5
|
+
* This CLI provides hook commands for Claude Code integration:
|
|
6
|
+
* - init: SessionStart hook - Initialize ZhuanSpec environment
|
|
7
|
+
* - deviation-check: PreToolUse hook - Check for proposal deviations
|
|
8
|
+
* - record-progress: PostToolUse hook - Record task progress
|
|
9
|
+
* - collect-knowledge: PostToolUse hook - Collect knowledge from interactions
|
|
10
|
+
* - summarize: Stop hook - Summarize session progress
|
|
11
|
+
* - review-*: Review phase hooks - Check skill outputs and control fix loops
|
|
12
|
+
*/
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
import { initHook } from '../core/hooks/init.js';
|
|
15
|
+
import { deviationCheckHook } from '../core/hooks/deviation-check.js';
|
|
16
|
+
import { recordProgressHook, initializeProgress } from '../core/hooks/record-progress.js';
|
|
17
|
+
import { collectKnowledgeHook } from '../core/hooks/collect-knowledge.js';
|
|
18
|
+
import { knowledgeUsageHook } from '../core/hooks/knowledge-usage.js';
|
|
19
|
+
import { knowledgeReferencesHook } from '../core/hooks/knowledge-references.js';
|
|
20
|
+
import { summarizeHook } from '../core/hooks/summarize.js';
|
|
21
|
+
import { runUserInputHook } from '../core/hooks/user-input-hook.js';
|
|
22
|
+
import { preArchiveHook } from '../core/hooks/pre-archive.js';
|
|
23
|
+
import { postArchiveHook } from '../core/hooks/post-archive.js';
|
|
24
|
+
import { preApplyHook, checkPreApplyConditions } from '../core/hooks/pre-apply.js';
|
|
25
|
+
import { postApplyHook, executePostApply } from '../core/hooks/post-apply.js';
|
|
26
|
+
import { codeReviewResultCheck, unitTestResultCheck, specConsistencyResultCheck, generateReviewReport, } from '../core/hooks/review-hooks.js';
|
|
27
|
+
import path from 'path';
|
|
28
|
+
import { FileSystemUtils } from '../utils/file-system.js';
|
|
29
|
+
import { resolveZhuanSpecRoot } from '../utils/resolve-root.js';
|
|
30
|
+
const program = new Command();
|
|
31
|
+
program
|
|
32
|
+
.name('zhuanspec-hook')
|
|
33
|
+
.description('ZhuanSpec hooks for Claude Code integration')
|
|
34
|
+
.version('1.0.0');
|
|
35
|
+
// SessionStart hook
|
|
36
|
+
program
|
|
37
|
+
.command('init')
|
|
38
|
+
.description('SessionStart hook - Initialize ZhuanSpec environment')
|
|
39
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
40
|
+
.action(async (options) => {
|
|
41
|
+
await initHook(options);
|
|
42
|
+
});
|
|
43
|
+
// PreToolUse hook
|
|
44
|
+
program
|
|
45
|
+
.command('deviation-check')
|
|
46
|
+
.description('PreToolUse hook - Check for proposal deviations before Write/Edit')
|
|
47
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
48
|
+
.option('--file <path>', 'File being modified')
|
|
49
|
+
.option('--tool <name>', 'Tool being used (Write/Edit)')
|
|
50
|
+
.option('--trigger <type>', 'Trigger type: pre-tool|post-prompt|resume')
|
|
51
|
+
.option('--prompt <text>', 'Prompt content for post-prompt checks')
|
|
52
|
+
.action(async (options) => {
|
|
53
|
+
// v2.15.16 Sprint 3:'post-prompt' 分支已由 user-input-hook 接管,此处保留参数仅为
|
|
54
|
+
// 兼容 v2.15.14 及更早版本的 .claude/settings.json(未执行过 zhuanspec update)。
|
|
55
|
+
// 收到 post-prompt 时直接 no-op 返回,避免破坏 UserPromptSubmit 事件链。
|
|
56
|
+
if (options.trigger === 'post-prompt') {
|
|
57
|
+
if (options.json) {
|
|
58
|
+
console.log(JSON.stringify({ continue: true, hookSpecificOutput: { hookEventName: 'UserPromptSubmit' } }));
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const { trigger, ...rest } = options;
|
|
63
|
+
await deviationCheckHook({ ...rest, trigger: trigger });
|
|
64
|
+
});
|
|
65
|
+
// PostToolUse hook - record progress
|
|
66
|
+
program
|
|
67
|
+
.command('record-progress')
|
|
68
|
+
.description('PostToolUse hook - Record task progress after Write/Edit')
|
|
69
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
70
|
+
.option('--file <path>', 'File that was modified')
|
|
71
|
+
.option('--tool <name>', 'Tool that was used (Write/Edit)')
|
|
72
|
+
.option('--success <boolean>', 'Whether the operation succeeded')
|
|
73
|
+
.action(async (options) => {
|
|
74
|
+
await recordProgressHook(options);
|
|
75
|
+
});
|
|
76
|
+
// Initialize progress - create progress.json at Propose phase start
|
|
77
|
+
program
|
|
78
|
+
.command('init-progress')
|
|
79
|
+
.description('Initialize progress.json for a change at Propose phase start')
|
|
80
|
+
.option('--change <id>', 'Change ID')
|
|
81
|
+
.action(async (options) => {
|
|
82
|
+
if (!options.change) {
|
|
83
|
+
console.error('Error: --change <id> is required');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
await initializeProgress(options.change);
|
|
87
|
+
console.log(`✓ Progress initialized for change: ${options.change}`);
|
|
88
|
+
});
|
|
89
|
+
// PostToolUse hook - collect knowledge
|
|
90
|
+
program
|
|
91
|
+
.command('collect-knowledge')
|
|
92
|
+
.description('PostToolUse hook - Collect knowledge from interactions')
|
|
93
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
94
|
+
.option('--file <path>', 'File that was modified')
|
|
95
|
+
.option('--content <string>', 'Content that was written')
|
|
96
|
+
.action(async (options) => {
|
|
97
|
+
await collectKnowledgeHook(options);
|
|
98
|
+
});
|
|
99
|
+
// PostToolUse hook - knowledge usage(引用上报,旁路指标)
|
|
100
|
+
program
|
|
101
|
+
.command('knowledge-usage')
|
|
102
|
+
.description('PostToolUse hook - 记录 AI 引用 knowledge/ 知识文件的次数(旁路指标)')
|
|
103
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
104
|
+
.option('--file <path>', 'File that was read (override stdin tool_input.file_path)')
|
|
105
|
+
.action(async (options) => {
|
|
106
|
+
await knowledgeUsageHook(options);
|
|
107
|
+
});
|
|
108
|
+
// PostToolUse hook - knowledge references(知识溯源记录)
|
|
109
|
+
program
|
|
110
|
+
.command('knowledge-references')
|
|
111
|
+
.description('PostToolUse hook - Record knowledge references during tech-design/proposal/apply')
|
|
112
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
113
|
+
.action(async (options) => {
|
|
114
|
+
await knowledgeReferencesHook(options);
|
|
115
|
+
});
|
|
116
|
+
// Stop hook
|
|
117
|
+
program
|
|
118
|
+
.command('summarize')
|
|
119
|
+
.description('Stop hook - Summarize session progress')
|
|
120
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
121
|
+
.action(async (options) => {
|
|
122
|
+
await summarizeHook(options);
|
|
123
|
+
});
|
|
124
|
+
// UserPromptSubmit hook - record user input & activate correctionContext from phaseBaselines
|
|
125
|
+
program
|
|
126
|
+
.command('user-input')
|
|
127
|
+
.description('UserPromptSubmit hook - Record user input and activate correction context post-baseline')
|
|
128
|
+
.option('--json', 'Output as JSON for Claude Code consumption (envelope printed regardless)')
|
|
129
|
+
.action(async () => {
|
|
130
|
+
await runUserInputHook();
|
|
131
|
+
});
|
|
132
|
+
program
|
|
133
|
+
.command('pre-archive')
|
|
134
|
+
.description('Pre-archive quality gate')
|
|
135
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
136
|
+
.option('--change <id>', 'Change ID to validate before archive')
|
|
137
|
+
.action(async (options) => {
|
|
138
|
+
await preArchiveHook(options);
|
|
139
|
+
});
|
|
140
|
+
program
|
|
141
|
+
.command('post-archive')
|
|
142
|
+
.description('Post-archive knowledge update')
|
|
143
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
144
|
+
.option('--archived <id>', 'Archived change ID')
|
|
145
|
+
.action(async (options) => {
|
|
146
|
+
await postArchiveHook(options);
|
|
147
|
+
});
|
|
148
|
+
// Pre-Apply hook - quality gate before entering Apply phase
|
|
149
|
+
program
|
|
150
|
+
.command('pre-apply')
|
|
151
|
+
.description('Pre-Apply quality gate - validate change readiness before Apply phase')
|
|
152
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
153
|
+
.option('--change <id>', 'Change ID to validate')
|
|
154
|
+
.action(async (options) => {
|
|
155
|
+
await preApplyHook(options);
|
|
156
|
+
});
|
|
157
|
+
// Post-Apply hook - deviation detection and auto-trigger review
|
|
158
|
+
program
|
|
159
|
+
.command('post-apply')
|
|
160
|
+
.description('Post-Apply hook - detect deviations and trigger automatic review')
|
|
161
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
162
|
+
.option('--change <id>', 'Change ID that was applied')
|
|
163
|
+
.action(async (options) => {
|
|
164
|
+
await postApplyHook(options);
|
|
165
|
+
});
|
|
166
|
+
// Phase Transition Check - Unified phase transition quality gate
|
|
167
|
+
program
|
|
168
|
+
.command('phase-transition-check')
|
|
169
|
+
.description('Check phase transition conditions before moving to next phase')
|
|
170
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
171
|
+
.option('--from <phase>', 'Current phase: propose|apply|review')
|
|
172
|
+
.option('--to <phase>', 'Target phase: apply|review|archive')
|
|
173
|
+
.option('--change <id>', 'Change ID being transitioned')
|
|
174
|
+
.action(async (options) => {
|
|
175
|
+
const result = await runPhaseTransitionCheck(options);
|
|
176
|
+
if (options.json) {
|
|
177
|
+
console.log(JSON.stringify(result));
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
console.log(result.systemMessage || '');
|
|
181
|
+
if (!result.continue) {
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
// ============================================================
|
|
187
|
+
// Review Phase Hooks - Check skill outputs and control fix loops
|
|
188
|
+
// ============================================================
|
|
189
|
+
// Code Review Result Check - Hook after code-review-expert skill
|
|
190
|
+
program
|
|
191
|
+
.command('review-code-check')
|
|
192
|
+
.description('Check code-review-expert skill output and decide fix loop')
|
|
193
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
194
|
+
.option('--change <id>', 'Change ID being reviewed')
|
|
195
|
+
.option('--output <json>', 'Skill output JSON (passed via stdin or option)')
|
|
196
|
+
.action(async (options) => {
|
|
197
|
+
const result = await codeReviewResultCheck(options.change || '', options.output ? JSON.parse(options.output) : undefined);
|
|
198
|
+
if (options.json) {
|
|
199
|
+
console.log(JSON.stringify(result));
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
console.log(`Code Review: ${result.pass ? '✅ PASS' : '❌ FAIL'}`);
|
|
203
|
+
if (result.fixPrompt) {
|
|
204
|
+
console.log(result.fixPrompt);
|
|
205
|
+
}
|
|
206
|
+
if (!result.pass && result.nextAction === 'stop') {
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
// Unit Test Result Check - Hook after generate-mockito-unit-test-skill
|
|
212
|
+
program
|
|
213
|
+
.command('review-unit-test-check')
|
|
214
|
+
.description('Check unit test skill output and decide fix loop')
|
|
215
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
216
|
+
.option('--change <id>', 'Change ID being reviewed')
|
|
217
|
+
.option('--output <json>', 'Skill output JSON')
|
|
218
|
+
.action(async (options) => {
|
|
219
|
+
const result = await unitTestResultCheck(options.change || '', options.output ? JSON.parse(options.output) : undefined);
|
|
220
|
+
if (options.json) {
|
|
221
|
+
console.log(JSON.stringify(result));
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
console.log(`Unit Test: ${result.pass ? '✅ PASS' : '❌ FAIL'}`);
|
|
225
|
+
console.log(`Coverage: ${result.metrics.coverage || 0}%`);
|
|
226
|
+
if (result.fixPrompt) {
|
|
227
|
+
console.log(result.fixPrompt);
|
|
228
|
+
}
|
|
229
|
+
if (!result.pass && result.nextAction === 'stop') {
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
// Spec-Code Consistency Check - Hook after spec-code-consistency check
|
|
235
|
+
program
|
|
236
|
+
.command('review-spec-check')
|
|
237
|
+
.description('Check spec-code consistency output and decide fix loop')
|
|
238
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
239
|
+
.option('--change <id>', 'Change ID being reviewed')
|
|
240
|
+
.option('--output <json>', 'Skill output JSON')
|
|
241
|
+
.action(async (options) => {
|
|
242
|
+
const result = await specConsistencyResultCheck(options.change || '', options.output ? JSON.parse(options.output) : undefined);
|
|
243
|
+
if (options.json) {
|
|
244
|
+
console.log(JSON.stringify(result));
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
console.log(`Spec-Code Consistency: ${result.pass ? '✅ PASS' : '❌ FAIL'}`);
|
|
248
|
+
console.log(`Consistency Rate: ${result.metrics.consistencyRate || 0}%`);
|
|
249
|
+
if (result.fixPrompt) {
|
|
250
|
+
console.log(result.fixPrompt);
|
|
251
|
+
}
|
|
252
|
+
if (!result.pass && result.nextAction === 'stop') {
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
// Generate Review Report - Combine all three check results
|
|
258
|
+
program
|
|
259
|
+
.command('review-report')
|
|
260
|
+
.description('Generate review report from all three check results')
|
|
261
|
+
.option('--json', 'Output as JSON for Claude Code consumption')
|
|
262
|
+
.option('--change <id>', 'Change ID being reviewed')
|
|
263
|
+
.action(async (options) => {
|
|
264
|
+
const changeId = options.change || '';
|
|
265
|
+
// Run all three checks
|
|
266
|
+
const codeReviewResult = await codeReviewResultCheck(changeId);
|
|
267
|
+
const unitTestResult = await unitTestResultCheck(changeId);
|
|
268
|
+
const specConsistencyResult = await specConsistencyResultCheck(changeId);
|
|
269
|
+
// Generate report
|
|
270
|
+
const report = await generateReviewReport(changeId, {
|
|
271
|
+
codeReview: codeReviewResult,
|
|
272
|
+
unitTest: unitTestResult,
|
|
273
|
+
specConsistency: specConsistencyResult,
|
|
274
|
+
});
|
|
275
|
+
if (options.json) {
|
|
276
|
+
console.log(JSON.stringify({
|
|
277
|
+
changeId,
|
|
278
|
+
overallPass: codeReviewResult.pass && unitTestResult.pass && specConsistencyResult.pass,
|
|
279
|
+
codeReview: codeReviewResult,
|
|
280
|
+
unitTest: unitTestResult,
|
|
281
|
+
specConsistency: specConsistencyResult,
|
|
282
|
+
reportPath: `zhuanspec/changes/${changeId}/review/review-report.md`,
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
console.log(report);
|
|
287
|
+
const overallPass = codeReviewResult.pass && unitTestResult.pass && specConsistencyResult.pass;
|
|
288
|
+
if (!overallPass) {
|
|
289
|
+
console.log('\n❌ Review NOT passed. Please fix issues before archiving.');
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
console.log('\n✅ Review passed. Ready for Phase 4 (Archive).');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
// Diagnostic command - inspect hook registration and trigger map
|
|
298
|
+
program
|
|
299
|
+
.command('status')
|
|
300
|
+
.description('Show hook registration status and trigger mapping')
|
|
301
|
+
.option('--json', 'Output as JSON')
|
|
302
|
+
.action(async (options) => {
|
|
303
|
+
const summary = await collectHookStatus();
|
|
304
|
+
if (options.json) {
|
|
305
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
console.log(`settings: ${summary.settingsPath}`);
|
|
309
|
+
console.log(`settings exists: ${summary.settingsExists ? 'yes' : 'no'}`);
|
|
310
|
+
console.log('\n直接注册(Claude 生命周期 hooks):');
|
|
311
|
+
if (summary.registeredLifecycleHooks.length === 0) {
|
|
312
|
+
console.log('- 无');
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
for (const item of summary.registeredLifecycleHooks) {
|
|
316
|
+
console.log(`- ${item.phase}: ${item.command}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
console.log('\n重复注册检测:');
|
|
320
|
+
if (summary.duplicates.length === 0) {
|
|
321
|
+
console.log('- 未发现重复');
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
for (const item of summary.duplicates) {
|
|
325
|
+
console.log(`- ${item.phase}: ${item.command} (x${item.count})`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
console.log('\n间接触发(阶段门禁与 Review 链路):');
|
|
329
|
+
for (const item of summary.phaseCommands) {
|
|
330
|
+
console.log(`- ${item.command}: ${item.trigger}`);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
/**
|
|
334
|
+
* Phase Transition Check - Unified function for phase transition validation
|
|
335
|
+
*/
|
|
336
|
+
export async function runPhaseTransitionCheck(options) {
|
|
337
|
+
const cwd = resolveZhuanSpecRoot();
|
|
338
|
+
const changeId = options.change || process.env.ZHUANSPEC_CHANGE_ID || '';
|
|
339
|
+
const fromPhase = options.from || process.env.ZHUANSPEC_PHASE || 'idle';
|
|
340
|
+
const toPhase = options.to || '';
|
|
341
|
+
if (!changeId) {
|
|
342
|
+
return {
|
|
343
|
+
continue: false,
|
|
344
|
+
systemMessage: 'Phase transition check skipped: no change ID provided',
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
const changeDir = path.join(cwd, 'zhuanspec', 'changes', changeId);
|
|
348
|
+
// Determine target phase if not specified
|
|
349
|
+
let targetPhase = toPhase;
|
|
350
|
+
if (!targetPhase) {
|
|
351
|
+
switch (fromPhase) {
|
|
352
|
+
case 'propose':
|
|
353
|
+
targetPhase = 'apply';
|
|
354
|
+
break;
|
|
355
|
+
case 'apply':
|
|
356
|
+
targetPhase = 'review';
|
|
357
|
+
break;
|
|
358
|
+
case 'review':
|
|
359
|
+
targetPhase = 'archive';
|
|
360
|
+
break;
|
|
361
|
+
default:
|
|
362
|
+
targetPhase = 'apply';
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// === Phase 准确率确认门禅(Task 4)===
|
|
366
|
+
// 规则:上一 phase 存在 phaseAccuracy 桶但未 confirmed 时阻断,
|
|
367
|
+
// systemMessage 指引 AI 调 AskUserQuestion 与 show-phase-accuracy / override-phase / confirm-phase 纠正。
|
|
368
|
+
// 绕过方式:环境变量 ZHUANSPEC_SKIP_ACCURACY_CONFIRM=1(CI/自动化用)。
|
|
369
|
+
const SKIP_CONFIRM = process.env.ZHUANSPEC_SKIP_ACCURACY_CONFIRM === '1';
|
|
370
|
+
const CONFIRMABLE_PHASES = new Set(['techDesign', 'propose', 'apply', 'review']);
|
|
371
|
+
if (!SKIP_CONFIRM && CONFIRMABLE_PHASES.has(fromPhase)) {
|
|
372
|
+
try {
|
|
373
|
+
const progressPath = path.join(changeDir, 'metrics', 'progress.json');
|
|
374
|
+
if (await FileSystemUtils.fileExists(progressPath)) {
|
|
375
|
+
const progressRaw = await FileSystemUtils.readFile(progressPath);
|
|
376
|
+
const progress = JSON.parse(progressRaw);
|
|
377
|
+
const buckets = progress.accuracy?.phaseAccuracy?.filter(b => b.phase === fromPhase) ?? [];
|
|
378
|
+
if (buckets.length > 0) {
|
|
379
|
+
const unconfirmed = buckets.filter(b => !b.confirmed);
|
|
380
|
+
if (unconfirmed.length > 0) {
|
|
381
|
+
// 聚合展示
|
|
382
|
+
const aiTotal = buckets.reduce((s, b) => s + (b.aiTotalLines || 0), 0);
|
|
383
|
+
const correction = buckets.reduce((s, b) => s + (b.userCorrectionLines || 0), 0);
|
|
384
|
+
const avgRate = buckets.length > 0
|
|
385
|
+
? buckets.reduce((s, b) => s + (b.accuracyRate || 0), 0) / buckets.length
|
|
386
|
+
: 1;
|
|
387
|
+
const rateStr = (avgRate * 100).toFixed(2);
|
|
388
|
+
return {
|
|
389
|
+
continue: false,
|
|
390
|
+
requiresUserConfirmation: true,
|
|
391
|
+
systemMessage: [
|
|
392
|
+
`✗ Phase transition blocked: ${fromPhase} → ${targetPhase}`,
|
|
393
|
+
'',
|
|
394
|
+
`阶段 ${fromPhase} 的准确率尚未被用户确认(桶数=${buckets.length},未确认=${unconfirmed.length})。`,
|
|
395
|
+
`当前数据:rate=${rateStr}%, AI产出=${aiTotal} 行, 用户纠偏=${correction} 行`,
|
|
396
|
+
'',
|
|
397
|
+
'请按以下步骤完成确认:',
|
|
398
|
+
`1. 查看详细数据:zhuanspec progress show-phase-accuracy ${changeId} ${fromPhase}`,
|
|
399
|
+
`2. 向用户提问(AskUserQuestion 或宿主等效工具):`,
|
|
400
|
+
` 问题:“${fromPhase} 阶段准确率为 ${rateStr}%(AI 产出 ${aiTotal} 行,纠偏 ${correction} 行),是否正确?”`,
|
|
401
|
+
` 选项A 准确或已内消 → zhuanspec progress confirm-phase ${changeId} ${fromPhase}`,
|
|
402
|
+
` 选项B 需纠正 → 询问正确率 rate 与原因 reason`,
|
|
403
|
+
` → zhuanspec progress override-phase ${changeId} ${fromPhase} --rate <0..1> --reason <text>`,
|
|
404
|
+
` → zhuanspec progress confirm-phase ${changeId} ${fromPhase}`,
|
|
405
|
+
'3. 确认完成后重新运行 phase-transition-check。',
|
|
406
|
+
'',
|
|
407
|
+
'绕过本门禅(CI/自动化):设置 ZHUANSPEC_SKIP_ACCURACY_CONFIRM=1。',
|
|
408
|
+
].join('\n'),
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// 历史数据 / 无桶:跳过此门禅(fall-through 继续其他检查)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
catch {
|
|
416
|
+
// 读取 progress.json 失败不阻断原有检查链路
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// Execute appropriate pre-check based on target phase
|
|
420
|
+
switch (targetPhase) {
|
|
421
|
+
case 'apply':
|
|
422
|
+
// Propose → Apply: Run pre-apply checks
|
|
423
|
+
const preApplyResult = await checkPreApplyConditions(changeDir);
|
|
424
|
+
if (preApplyResult.continue) {
|
|
425
|
+
return {
|
|
426
|
+
continue: true,
|
|
427
|
+
systemMessage: `✓ Phase transition approved: ${fromPhase} → ${targetPhase}\n\nAll pre-apply checks passed. You may now proceed to Apply phase.`,
|
|
428
|
+
checks: preApplyResult.checks,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
continue: false,
|
|
433
|
+
systemMessage: `✗ Phase transition blocked: ${fromPhase} → ${targetPhase}\n\n${preApplyResult.systemMessage}`,
|
|
434
|
+
checks: preApplyResult.checks,
|
|
435
|
+
};
|
|
436
|
+
case 'review':
|
|
437
|
+
// Apply → Review: Run post-apply deviation check
|
|
438
|
+
const postApplyResult = await executePostApply(changeDir, cwd);
|
|
439
|
+
if (postApplyResult.hookSpecificOutput?.deviationDetected) {
|
|
440
|
+
return {
|
|
441
|
+
continue: false,
|
|
442
|
+
systemMessage: `✗ Phase transition blocked: ${fromPhase} → ${targetPhase}\n\nDeviation detected: ${postApplyResult.hookSpecificOutput.deviationFiles?.length || 0} files outside proposal scope.\nPlease update proposal (Reverse Sync) before proceeding to Review.`,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
continue: true,
|
|
447
|
+
systemMessage: `✓ Phase transition approved: ${fromPhase} → ${targetPhase}\n\nNo deviations detected. Review phase can proceed.`,
|
|
448
|
+
};
|
|
449
|
+
case 'archive':
|
|
450
|
+
// Review → Archive: Check for review completion
|
|
451
|
+
const reviewDir = path.join(changeDir, 'review');
|
|
452
|
+
const reviewPath = path.join(reviewDir, 'code-review-result.json');
|
|
453
|
+
const testsPath = path.join(reviewDir, 'unit-test-result.json');
|
|
454
|
+
const reportPath = path.join(reviewDir, 'review-report.md');
|
|
455
|
+
const checks = {
|
|
456
|
+
reviewCompleted: false,
|
|
457
|
+
testsCompleted: false,
|
|
458
|
+
reportExists: false,
|
|
459
|
+
};
|
|
460
|
+
checks.reviewCompleted = await FileSystemUtils.fileExists(reviewPath);
|
|
461
|
+
checks.testsCompleted = await FileSystemUtils.fileExists(testsPath);
|
|
462
|
+
checks.reportExists = await FileSystemUtils.fileExists(reportPath);
|
|
463
|
+
if (!checks.reviewCompleted || !checks.testsCompleted || !checks.reportExists) {
|
|
464
|
+
const missing = [];
|
|
465
|
+
if (!checks.reviewCompleted)
|
|
466
|
+
missing.push('review/code-review-result.json');
|
|
467
|
+
if (!checks.testsCompleted)
|
|
468
|
+
missing.push('review/unit-test-result.json');
|
|
469
|
+
if (!checks.reportExists)
|
|
470
|
+
missing.push('review/review-report.md');
|
|
471
|
+
return {
|
|
472
|
+
continue: false,
|
|
473
|
+
systemMessage: `✗ Phase transition blocked: ${fromPhase} → ${targetPhase}\n\nMissing review artifacts:\n${missing.map(m => `- ${m}`).join('\n')}\n\nPlease complete Review phase before archiving.`,
|
|
474
|
+
checks,
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
return {
|
|
478
|
+
continue: true,
|
|
479
|
+
systemMessage: `✓ Phase transition approved: ${fromPhase} → ${targetPhase}\n\nAll review artifacts present. Archive phase can proceed.`,
|
|
480
|
+
checks,
|
|
481
|
+
};
|
|
482
|
+
default:
|
|
483
|
+
return {
|
|
484
|
+
continue: false,
|
|
485
|
+
systemMessage: `Unknown target phase: ${targetPhase}`,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
async function collectHookStatus() {
|
|
490
|
+
const cwd = resolveZhuanSpecRoot();
|
|
491
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.json');
|
|
492
|
+
const settingsExists = await FileSystemUtils.fileExists(settingsPath);
|
|
493
|
+
const registeredLifecycleHooks = [];
|
|
494
|
+
const duplicates = [];
|
|
495
|
+
if (settingsExists) {
|
|
496
|
+
try {
|
|
497
|
+
const content = await FileSystemUtils.readFile(settingsPath);
|
|
498
|
+
const parsed = JSON.parse(content);
|
|
499
|
+
const hooks = parsed.hooks ?? {};
|
|
500
|
+
for (const [phase, phaseConfigs] of Object.entries(hooks)) {
|
|
501
|
+
if (!Array.isArray(phaseConfigs))
|
|
502
|
+
continue;
|
|
503
|
+
const commandCounts = new Map();
|
|
504
|
+
for (const phaseConfig of phaseConfigs) {
|
|
505
|
+
const hooksArray = phaseConfig.hooks;
|
|
506
|
+
if (!Array.isArray(hooksArray))
|
|
507
|
+
continue;
|
|
508
|
+
for (const hookItem of hooksArray) {
|
|
509
|
+
const command = hookItem.command;
|
|
510
|
+
if (typeof command !== 'string')
|
|
511
|
+
continue;
|
|
512
|
+
registeredLifecycleHooks.push({ phase, command });
|
|
513
|
+
commandCounts.set(command, (commandCounts.get(command) ?? 0) + 1);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
for (const [command, count] of commandCounts.entries()) {
|
|
517
|
+
if (count > 1) {
|
|
518
|
+
duplicates.push({ phase, command, count });
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
catch {
|
|
524
|
+
// Keep defaults; command should still provide guidance.
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
const phaseCommands = [
|
|
528
|
+
{
|
|
529
|
+
command: 'zhuanspec-hook pre-apply',
|
|
530
|
+
trigger: '由 deviation-check 内部调用 checkPreApplyConditions(Propose → Apply 前门禁)',
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
command: 'zhuanspec-hook post-apply',
|
|
534
|
+
trigger: '由 summarize/executePostApply 间接触发(Apply 完成后偏差检测与 review 触发)',
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
command: 'zhuanspec-hook review-code-check',
|
|
538
|
+
trigger: '由 zhuanspec review 结果链路调用(代码审查结果判定)',
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
command: 'zhuanspec-hook review-unit-test-check',
|
|
542
|
+
trigger: '由 zhuanspec review 结果链路调用(单测结果判定)',
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
command: 'zhuanspec-hook review-spec-check',
|
|
546
|
+
trigger: '由 zhuanspec review 结果链路调用(Spec/Code 一致性判定)',
|
|
547
|
+
},
|
|
548
|
+
];
|
|
549
|
+
return {
|
|
550
|
+
settingsPath,
|
|
551
|
+
settingsExists,
|
|
552
|
+
registeredLifecycleHooks,
|
|
553
|
+
duplicates,
|
|
554
|
+
phaseCommands,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
program.parse();
|
|
558
|
+
//# sourceMappingURL=hooks.js.map
|