@chongyan/autospec 1.0.1

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 (243) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +472 -0
  3. package/README.md +476 -0
  4. package/bin/autospec.js +3 -0
  5. package/knowledge/README.md +144 -0
  6. package/knowledge/checklists/code.md +182 -0
  7. package/knowledge/checklists/design.md +196 -0
  8. package/knowledge/checklists/release.md +70 -0
  9. package/knowledge/checklists/requirement.md +169 -0
  10. package/knowledge/checklists/test.md +46 -0
  11. package/knowledge/config/README.en.md +44 -0
  12. package/knowledge/config/README.md +44 -0
  13. package/knowledge/config/role-composition.yaml +98 -0
  14. package/knowledge/config/role-extensions.yaml +140 -0
  15. package/knowledge/config/skill-compositions.yaml +142 -0
  16. package/knowledge/config/team-stage.yaml +95 -0
  17. package/knowledge/config/team-tasks.yaml +139 -0
  18. package/knowledge/config/team-triggers.yaml +198 -0
  19. package/knowledge/config/validation-patterns.yaml +137 -0
  20. package/knowledge/domain/README.md +115 -0
  21. package/knowledge/domain/flows/README.md +194 -0
  22. package/knowledge/domain/glossary.md +143 -0
  23. package/knowledge/domain/rules.md +138 -0
  24. package/knowledge/environment/README.en.md +36 -0
  25. package/knowledge/environment/README.md +87 -0
  26. package/knowledge/environment/component-knowledge.md +316 -0
  27. package/knowledge/environment/detection-patterns.yaml +502 -0
  28. package/knowledge/environment/middleware-knowledge.md +237 -0
  29. package/knowledge/environment/template-registry.md +321 -0
  30. package/knowledge/guides/domain-driven-design.md +345 -0
  31. package/knowledge/guides/knowledge-management.md +369 -0
  32. package/knowledge/guides/requirement-engineering.md +329 -0
  33. package/knowledge/guides/stages/ai-effect-evaluator.md +93 -0
  34. package/knowledge/guides/stages/code-implementer.md +205 -0
  35. package/knowledge/guides/stages/code-reviewer.md +111 -0
  36. package/knowledge/guides/stages/consistency-checker.md +177 -0
  37. package/knowledge/guides/stages/design-planner.md +401 -0
  38. package/knowledge/guides/stages/design-reviewer.md +83 -0
  39. package/knowledge/guides/stages/integration-test-runner.md +105 -0
  40. package/knowledge/guides/stages/release-checker.md +205 -0
  41. package/knowledge/guides/stages/requirement-analyzer.md +195 -0
  42. package/knowledge/guides/stages/requirement-reviewer.md +83 -0
  43. package/knowledge/guides/stages/security-reviewer.md +89 -0
  44. package/knowledge/guides/stages/test-context-analyzer.md +250 -0
  45. package/knowledge/guides/stages/test-generator.md +241 -0
  46. package/knowledge/guides/stages/test-planner.md +183 -0
  47. package/knowledge/guides/stages/test-reviewer.md +76 -0
  48. package/knowledge/guides/stages/unit-test-runner.md +83 -0
  49. package/knowledge/guides/support/ai-agent-analyzer.md +362 -0
  50. package/knowledge/guides/support/ai-anomaly-analyzer.md +213 -0
  51. package/knowledge/guides/support/ai-artifact-evaluator.md +192 -0
  52. package/knowledge/guides/support/ai-capability-analyzer.md +193 -0
  53. package/knowledge/guides/support/ai-component-analyzer.md +169 -0
  54. package/knowledge/guides/support/ai-data-validator.md +276 -0
  55. package/knowledge/guides/support/ai-evaluation-planner.md +374 -0
  56. package/knowledge/guides/support/ai-path-evaluator.md +274 -0
  57. package/knowledge/guides/support/ai-pipeline-evaluator.md +219 -0
  58. package/knowledge/guides/support/ai-rag-analyzer.md +339 -0
  59. package/knowledge/guides/support/ai-task-assessor.md +418 -0
  60. package/knowledge/guides/support/ai-test-diagnostics.md +133 -0
  61. package/knowledge/guides/support/complexity-assessor.md +268 -0
  62. package/knowledge/guides/support/component-discovery.md +183 -0
  63. package/knowledge/guides/support/environment-scanner.md +207 -0
  64. package/knowledge/guides/support/environment-validator.md +207 -0
  65. package/knowledge/guides/support/knowledge-generator.md +234 -0
  66. package/knowledge/guides/support/methodology-extractor.md +55 -0
  67. package/knowledge/guides/support/pipeline-protocol.md +438 -0
  68. package/knowledge/guides/support/practice-logger.md +359 -0
  69. package/knowledge/guides/support/scope-inference.md +174 -0
  70. package/knowledge/guides/support/skill-distiller.md +91 -0
  71. package/knowledge/guides/support/skill-updater.md +45 -0
  72. package/knowledge/guides/support/skill-validator.md +72 -0
  73. package/knowledge/guides/support/team-orchestrator.md +323 -0
  74. package/knowledge/guides/support/tech-stack-analyzer.md +139 -0
  75. package/knowledge/guides/support/test-runner.md +254 -0
  76. package/knowledge/guides/system-design.md +352 -0
  77. package/knowledge/organization/ai-native-team.md +318 -0
  78. package/knowledge/organization/team-metrics.md +228 -0
  79. package/knowledge/principles/constitution.md +134 -0
  80. package/knowledge/principles/core-principles.md +368 -0
  81. package/knowledge/principles/design-philosophy.md +877 -0
  82. package/knowledge/principles/evolution.md +553 -0
  83. package/knowledge/process/01-requirement.md +113 -0
  84. package/knowledge/process/02-design.md +123 -0
  85. package/knowledge/process/03-implementation.md +90 -0
  86. package/knowledge/process/04-review.md +80 -0
  87. package/knowledge/process/05-testing.md +90 -0
  88. package/knowledge/process/06-delivery.md +88 -0
  89. package/knowledge/process/README.en.md +38 -0
  90. package/knowledge/process/README.md +48 -0
  91. package/knowledge/process/ai-sdlc.md +475 -0
  92. package/knowledge/process/overview.md +319 -0
  93. package/knowledge/standards/code-review.md +876 -0
  94. package/knowledge/standards/coding-style.md +940 -0
  95. package/knowledge/standards/data-consistency.md +1085 -0
  96. package/knowledge/standards/document-versioning.md +210 -0
  97. package/knowledge/standards/risk-detection.md +186 -0
  98. package/knowledge/templates/ai-evaluation.md +150 -0
  99. package/knowledge/templates/api-design.md +117 -0
  100. package/knowledge/templates/database-design.md +132 -0
  101. package/knowledge/templates/domain-driven-design.md +321 -0
  102. package/knowledge/templates/product-proposal.md +201 -0
  103. package/knowledge/templates/system-design.md +227 -0
  104. package/knowledge/templates/task-breakdown.md +107 -0
  105. package/knowledge/templates/test-case.md +170 -0
  106. package/package.json +53 -0
  107. package/plugins/.claude-plugin/plugin.json +134 -0
  108. package/plugins/agents/roles/ai-engineer.md +129 -0
  109. package/plugins/agents/roles/backend-engineer.md +165 -0
  110. package/plugins/agents/roles/ceo.md +94 -0
  111. package/plugins/agents/roles/data-engineer.md +135 -0
  112. package/plugins/agents/roles/devops-engineer.md +181 -0
  113. package/plugins/agents/roles/frontend-engineer.md +129 -0
  114. package/plugins/agents/roles/product-owner.md +98 -0
  115. package/plugins/agents/roles/quality-engineer.md +129 -0
  116. package/plugins/agents/roles/security-engineer.md +180 -0
  117. package/plugins/agents/roles/tech-lead.md +97 -0
  118. package/plugins/agents/support/blind-comparator.md +88 -0
  119. package/plugins/agents/support/consistency-checker.md +103 -0
  120. package/plugins/agents/support/failure-diagnostician.md +141 -0
  121. package/plugins/agents/support/independent-reviewer.md +80 -0
  122. package/plugins/agents/support/safety-auditor.md +121 -0
  123. package/plugins/agents/support/skill-benchmarker.md +86 -0
  124. package/plugins/agents/support/skill-forger.md +105 -0
  125. package/plugins/agents/support/stage-gate-evaluator.md +121 -0
  126. package/plugins/agents/support/test-coverage-reviewer.md +73 -0
  127. package/plugins/benchmarks/templates/README.md +44 -0
  128. package/plugins/benchmarks/templates/commands/explore-template.yaml +48 -0
  129. package/plugins/benchmarks/templates/pipeline/agile-template.yaml +84 -0
  130. package/plugins/benchmarks/templates/pipeline/waterfall-template.yaml +106 -0
  131. package/plugins/benchmarks/templates/skills/requirement-analyzer-template.yaml +48 -0
  132. package/plugins/commands/README.en.md +96 -0
  133. package/plugins/commands/README.md +96 -0
  134. package/plugins/commands/apply.md +191 -0
  135. package/plugins/commands/archive.md +76 -0
  136. package/plugins/commands/env-export.md +79 -0
  137. package/plugins/commands/env-sync.md +640 -0
  138. package/plugins/commands/env-template.md +223 -0
  139. package/plugins/commands/env-update.md +264 -0
  140. package/plugins/commands/env-validate.md +176 -0
  141. package/plugins/commands/env.md +79 -0
  142. package/plugins/commands/explore.md +76 -0
  143. package/plugins/commands/field-evolve.md +536 -0
  144. package/plugins/commands/memory.md +249 -0
  145. package/plugins/commands/project-evolve.md +821 -0
  146. package/plugins/commands/propose.md +93 -0
  147. package/plugins/commands/review.md +140 -0
  148. package/plugins/commands/run.md +224 -0
  149. package/plugins/commands/status.md +62 -0
  150. package/plugins/commands/validate.md +108 -0
  151. package/plugins/hooks/README.en.md +56 -0
  152. package/plugins/hooks/README.md +56 -0
  153. package/plugins/hooks/ai-project-guard.js +329 -0
  154. package/plugins/hooks/artifact-evaluation-hook.js +237 -0
  155. package/plugins/hooks/constitution-guard.js +211 -0
  156. package/plugins/hooks/environment-autocommit.js +264 -0
  157. package/plugins/hooks/environment-manager.js +778 -0
  158. package/plugins/hooks/execution-tracker.js +354 -0
  159. package/plugins/hooks/frozen-zone-guard.js +140 -0
  160. package/plugins/hooks/layer1-validator.js +423 -0
  161. package/plugins/hooks/lib/artifact-evaluator.js +414 -0
  162. package/plugins/hooks/lib/benchmarks/change-detector.js +390 -0
  163. package/plugins/hooks/lib/benchmarks/evaluator.js +605 -0
  164. package/plugins/hooks/lib/benchmarks/integration-example.js +169 -0
  165. package/plugins/hooks/lib/data-and-ai-detector.js +275 -0
  166. package/plugins/hooks/lib/detection-pattern-loader.js +865 -0
  167. package/plugins/hooks/lib/directory-discovery.js +395 -0
  168. package/plugins/hooks/lib/environment-config-loader.js +341 -0
  169. package/plugins/hooks/lib/environment-detector.js +553 -0
  170. package/plugins/hooks/lib/environment-evolver.js +564 -0
  171. package/plugins/hooks/lib/environment-registry.js +813 -0
  172. package/plugins/hooks/lib/execution-path.js +427 -0
  173. package/plugins/hooks/lib/hook-error-recorder.js +245 -0
  174. package/plugins/hooks/lib/hook-logger.js +538 -0
  175. package/plugins/hooks/lib/hook-runner.js +97 -0
  176. package/plugins/hooks/lib/hook-runner.sh +44 -0
  177. package/plugins/hooks/lib/hook-state-manager.js +480 -0
  178. package/plugins/hooks/lib/memory-extractor.js +377 -0
  179. package/plugins/hooks/lib/memory-manager.js +673 -0
  180. package/plugins/hooks/lib/metrics-analyzer.js +489 -0
  181. package/plugins/hooks/lib/project-evolution/auto-fixer.js +511 -0
  182. package/plugins/hooks/lib/project-evolution/memory-manager.js +346 -0
  183. package/plugins/hooks/lib/project-evolution/pattern-detector.js +476 -0
  184. package/plugins/hooks/lib/project-evolution/semantic-indexer.js +480 -0
  185. package/plugins/hooks/lib/project-structure-detector.js +326 -0
  186. package/plugins/hooks/lib/rollback-tracker.js +346 -0
  187. package/plugins/hooks/lib/source-code-scanner.js +596 -0
  188. package/plugins/hooks/lib/technology-stack-detector.js +374 -0
  189. package/plugins/hooks/lib/test-failure-analyzer.js +375 -0
  190. package/plugins/hooks/lib/test-failure-fixer.js +268 -0
  191. package/plugins/hooks/lib/trace-context.js +277 -0
  192. package/plugins/hooks/lib/validation-patterns.js +415 -0
  193. package/plugins/hooks/memory-sync.js +171 -0
  194. package/plugins/hooks/pipeline-observer.js +413 -0
  195. package/plugins/hooks/scope-sentinel.js +204 -0
  196. package/plugins/hooks/trace-initialization.js +169 -0
  197. package/plugins/memory/templates/code-quality.yaml +149 -0
  198. package/plugins/memory/templates/multi-system.yaml +155 -0
  199. package/plugins/memory/templates/team-habits.yaml +119 -0
  200. package/plugins/memory/templates/testing.yaml +121 -0
  201. package/plugins/skills/README.en.md +47 -0
  202. package/plugins/skills/README.md +104 -0
  203. package/plugins/skills/benchmark-executor/README.md +93 -0
  204. package/plugins/skills/benchmark-executor/SKILL.md +647 -0
  205. package/plugins/skills/benchmark-generator/SKILL.md +349 -0
  206. package/plugins/skills/delivery-stage/SKILL.md +203 -0
  207. package/plugins/skills/design-stage/SKILL.md +216 -0
  208. package/plugins/skills/evolution-process/SKILL.md +291 -0
  209. package/plugins/skills/exploration-phase/SKILL.md +133 -0
  210. package/plugins/skills/implementation-stage/SKILL.md +179 -0
  211. package/plugins/skills/layer1-validation/SKILL.md +79 -0
  212. package/plugins/skills/pending-dashboard/SKILL.md +109 -0
  213. package/plugins/skills/project-evolution/SKILL.md +847 -0
  214. package/plugins/skills/requirement-stage/SKILL.md +183 -0
  215. package/plugins/skills/skill-forge/SKILL.md +223 -0
  216. package/plugins/skills/skill-forge/references/description-guide.md +92 -0
  217. package/plugins/skills/skill-forge/references/quality-rubric.md +104 -0
  218. package/plugins/skills/skill-forge/references/skill-template.md +106 -0
  219. package/plugins/skills/startup-guard/SKILL.md +38 -0
  220. package/plugins/skills/testing-stage/SKILL.md +195 -0
  221. package/scripts/cli/global-init.js +288 -0
  222. package/scripts/cli/global.js +324 -0
  223. package/scripts/cli/index.js +55 -0
  224. package/scripts/cli/init.js +382 -0
  225. package/scripts/cli/list.js +69 -0
  226. package/scripts/cli/org.js +340 -0
  227. package/scripts/cli/update.js +44 -0
  228. package/scripts/config/commands.config.js +145 -0
  229. package/scripts/config/hooks.config.js +197 -0
  230. package/scripts/evolution/evolution-router.js +273 -0
  231. package/scripts/evolution/evolution-signal-collector.js +307 -0
  232. package/scripts/evolution/knowledge-loader.js +346 -0
  233. package/scripts/evolution/marketplace.js +317 -0
  234. package/scripts/evolution/version-manager.js +371 -0
  235. package/scripts/install/agents.js +106 -0
  236. package/scripts/install/commands.js +133 -0
  237. package/scripts/install/constants.js +424 -0
  238. package/scripts/install/hook-logger.js +536 -0
  239. package/scripts/install/hooks.js +110 -0
  240. package/scripts/install/index.js +39 -0
  241. package/scripts/install/skills.js +95 -0
  242. package/scripts/postinstall.js +25 -0
  243. package/scripts/state.js +376 -0
@@ -0,0 +1,427 @@
1
+ /**
2
+ * Execution Path - 执行路径记录模块
3
+ *
4
+ * 记录和分析执行路径,评估 SDD 引导效果
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { getState, getMetrics, updateMetrics } from './hook-state-manager.js';
10
+ import { getTraceId, generateEventId } from './trace-context.js';
11
+
12
+ /**
13
+ * 路径事件类型
14
+ */
15
+ export const PATH_EVENT_TYPES = {
16
+ COMMAND: 'command', // 命令调用
17
+ STAGE: 'stage', // 阶段转换
18
+ HOOK: 'hook', // Hook 触发
19
+ TOOL: 'tool', // 工具调用
20
+ DECISION: 'decision', // 决策点
21
+ ROLLBACK: 'rollback', // 回退
22
+ GATE: 'gate' // 门禁检查
23
+ };
24
+
25
+ /**
26
+ * 记录路径事件
27
+ * @param {string} projectRoot - 项目根目录
28
+ * @param {string} eventType - 事件类型
29
+ * @param {Object} eventData - 事件数据
30
+ */
31
+ export function recordPathEvent(projectRoot, eventType, eventData) {
32
+ const traceId = getTraceId(projectRoot);
33
+
34
+ const event = {
35
+ eventId: generateEventId(),
36
+ traceId,
37
+ timestamp: new Date().toISOString(),
38
+ type: eventType,
39
+ ...eventData
40
+ };
41
+
42
+ // 更新 metrics.json 中的 executionPath
43
+ const metrics = getMetrics(projectRoot, false);
44
+
45
+ if (!metrics.executionPath) {
46
+ metrics.executionPath = {
47
+ commands: [],
48
+ stages: [],
49
+ toolStats: {},
50
+ decisionStats: { total: 0, human: 0, auto: 0 },
51
+ rollbackStats: { total: 0, success: 0, escalate: 0 },
52
+ gateStats: { passed: 0, blocked: 0 },
53
+ events: []
54
+ };
55
+ }
56
+
57
+ // 根据事件类型更新统计
58
+ switch (eventType) {
59
+ case PATH_EVENT_TYPES.COMMAND:
60
+ if (eventData.command && !metrics.executionPath.commands.includes(eventData.command)) {
61
+ metrics.executionPath.commands.push(eventData.command);
62
+ }
63
+ break;
64
+
65
+ case PATH_EVENT_TYPES.STAGE:
66
+ if (eventData.stage && !metrics.executionPath.stages.includes(eventData.stage)) {
67
+ metrics.executionPath.stages.push(eventData.stage);
68
+ }
69
+ break;
70
+
71
+ case PATH_EVENT_TYPES.TOOL:
72
+ if (eventData.tool) {
73
+ metrics.executionPath.toolStats[eventData.tool] =
74
+ (metrics.executionPath.toolStats[eventData.tool] || 0) + 1;
75
+ }
76
+ break;
77
+
78
+ case PATH_EVENT_TYPES.DECISION:
79
+ metrics.executionPath.decisionStats.total++;
80
+ if (eventData.decisionType === 'human') {
81
+ metrics.executionPath.decisionStats.human++;
82
+ } else {
83
+ metrics.executionPath.decisionStats.auto++;
84
+ }
85
+ break;
86
+
87
+ case PATH_EVENT_TYPES.ROLLBACK:
88
+ metrics.executionPath.rollbackStats.total++;
89
+ if (eventData.outcome === 'success') {
90
+ metrics.executionPath.rollbackStats.success++;
91
+ } else if (eventData.outcome === 'escalate') {
92
+ metrics.executionPath.rollbackStats.escalate++;
93
+ }
94
+ break;
95
+
96
+ case PATH_EVENT_TYPES.GATE:
97
+ if (eventData.passed) {
98
+ metrics.executionPath.gateStats.passed++;
99
+ } else {
100
+ metrics.executionPath.gateStats.blocked++;
101
+ }
102
+ break;
103
+ }
104
+
105
+ // 添加事件到事件列表(限制最近 1000 个)
106
+ metrics.executionPath.events.push(event);
107
+ if (metrics.executionPath.events.length > 1000) {
108
+ metrics.executionPath.events = metrics.executionPath.events.slice(-1000);
109
+ }
110
+
111
+ updateMetrics(projectRoot, metrics);
112
+
113
+ return event;
114
+ }
115
+
116
+ /**
117
+ * 生成执行路径摘要
118
+ * @param {string} projectRoot - 项目根目录
119
+ * @returns {Object} 执行路径摘要
120
+ */
121
+ export function generatePathSummary(projectRoot) {
122
+ const metrics = getMetrics(projectRoot, true);
123
+ const executionPath = metrics?.executionPath || {};
124
+
125
+ return {
126
+ // 命令序列
127
+ commands: executionPath.commands || [],
128
+
129
+ // 阶段路径
130
+ stages: executionPath.stages || [],
131
+
132
+ // 工具调用统计
133
+ toolStats: executionPath.toolStats || {},
134
+
135
+ // 决策点统计
136
+ decisionStats: executionPath.decisionStats || { total: 0, human: 0, auto: 0 },
137
+
138
+ // 回退统计
139
+ rollbackStats: executionPath.rollbackStats || { total: 0, success: 0, escalate: 0 },
140
+
141
+ // 门禁统计
142
+ gateStats: executionPath.gateStats || { passed: 0, blocked: 0 },
143
+
144
+ // 事件总数
145
+ totalEvents: (executionPath.events || []).length
146
+ };
147
+ }
148
+
149
+ /**
150
+ * 提取命令序列
151
+ * @param {Object} traceLog - trace 日志
152
+ * @returns {Array} 命令序列
153
+ */
154
+ export function extractCommandSequence(traceLog) {
155
+ const events = traceLog?.events || [];
156
+ return events
157
+ .filter(e => e.type === PATH_EVENT_TYPES.COMMAND)
158
+ .map(e => ({
159
+ command: e.command,
160
+ timestamp: e.timestamp
161
+ }));
162
+ }
163
+
164
+ /**
165
+ * 提取阶段序列
166
+ * @param {Object} traceLog - trace 日志
167
+ * @returns {Array} 阶段序列
168
+ */
169
+ export function extractStageSequence(traceLog) {
170
+ const events = traceLog?.events || [];
171
+ const stages = [];
172
+ const seen = new Set();
173
+
174
+ for (const event of events) {
175
+ if (event.type === PATH_EVENT_TYPES.STAGE && event.stage && !seen.has(event.stage)) {
176
+ stages.push({
177
+ stage: event.stage,
178
+ timestamp: event.timestamp
179
+ });
180
+ seen.add(event.stage);
181
+ }
182
+ }
183
+
184
+ return stages;
185
+ }
186
+
187
+ /**
188
+ * 计算工具统计
189
+ * @param {Object} traceLog - trace 日志
190
+ * @returns {Object} 工具统计
191
+ */
192
+ export function calculateToolStats(traceLog) {
193
+ const events = traceLog?.events || [];
194
+ const stats = {};
195
+
196
+ for (const event of events) {
197
+ if (event.type === PATH_EVENT_TYPES.TOOL && event.tool) {
198
+ stats[event.tool] = (stats[event.tool] || 0) + 1;
199
+ }
200
+ }
201
+
202
+ return stats;
203
+ }
204
+
205
+ /**
206
+ * 计算决策统计
207
+ * @param {Object} traceLog - trace 日志
208
+ * @returns {Object} 决策统计
209
+ */
210
+ export function calculateDecisionStats(traceLog) {
211
+ const events = traceLog?.events || [];
212
+ const stats = { total: 0, human: 0, auto: 0, byType: {} };
213
+
214
+ for (const event of events) {
215
+ if (event.type === PATH_EVENT_TYPES.DECISION) {
216
+ stats.total++;
217
+ if (event.decisionType === 'human') {
218
+ stats.human++;
219
+ } else {
220
+ stats.auto++;
221
+ }
222
+
223
+ const decisionType = event.decisionType || 'unknown';
224
+ stats.byType[decisionType] = (stats.byType[decisionType] || 0) + 1;
225
+ }
226
+ }
227
+
228
+ return stats;
229
+ }
230
+
231
+ /**
232
+ * 计算回退统计
233
+ * @param {Object} traceLog - trace 日志
234
+ * @returns {Object} 回退统计
235
+ */
236
+ export function calculateRollbackStats(traceLog) {
237
+ const events = traceLog?.events || [];
238
+ const stats = { total: 0, success: 0, escalate: 0, byLevel: {} };
239
+
240
+ for (const event of events) {
241
+ if (event.type === PATH_EVENT_TYPES.ROLLBACK) {
242
+ stats.total++;
243
+ if (event.outcome === 'success') {
244
+ stats.success++;
245
+ } else if (event.outcome === 'escalate') {
246
+ stats.escalate++;
247
+ }
248
+
249
+ const level = event.level || 'unknown';
250
+ stats.byLevel[level] = (stats.byLevel[level] || 0) + 1;
251
+ }
252
+ }
253
+
254
+ return stats;
255
+ }
256
+
257
+ /**
258
+ * 计算门禁统计
259
+ * @param {Object} traceLog - trace 日志
260
+ * @returns {Object} 门禁统计
261
+ */
262
+ export function calculateGateStats(traceLog) {
263
+ const events = traceLog?.events || [];
264
+ const stats = { passed: 0, blocked: 0, byGate: {} };
265
+
266
+ for (const event of events) {
267
+ if (event.type === PATH_EVENT_TYPES.GATE) {
268
+ if (event.passed) {
269
+ stats.passed++;
270
+ } else {
271
+ stats.blocked++;
272
+ }
273
+
274
+ const gate = event.gate || 'unknown';
275
+ if (!stats.byGate[gate]) {
276
+ stats.byGate[gate] = { passed: 0, blocked: 0 };
277
+ }
278
+ if (event.passed) {
279
+ stats.byGate[gate].passed++;
280
+ } else {
281
+ stats.byGate[gate].blocked++;
282
+ }
283
+ }
284
+ }
285
+
286
+ return stats;
287
+ }
288
+
289
+ /**
290
+ * 检测重复操作
291
+ * @param {Object} traceLog - trace 日志
292
+ * @returns {Array} 重复操作列表
293
+ */
294
+ export function detectRepeatedOperations(traceLog) {
295
+ const events = traceLog?.events || [];
296
+ const operations = {};
297
+ const repeated = [];
298
+
299
+ for (const event of events) {
300
+ if (event.type === PATH_EVENT_TYPES.TOOL) {
301
+ const key = `${event.tool}:${event.action || ''}`;
302
+ if (!operations[key]) {
303
+ operations[key] = { count: 0, timestamps: [] };
304
+ }
305
+ operations[key].count++;
306
+ operations[key].timestamps.push(event.timestamp);
307
+ }
308
+ }
309
+
310
+ for (const [key, data] of Object.entries(operations)) {
311
+ if (data.count >= 3) {
312
+ repeated.push({
313
+ operation: key,
314
+ count: data.count,
315
+ timestamps: data.timestamps.slice(-5)
316
+ });
317
+ }
318
+ }
319
+
320
+ return repeated;
321
+ }
322
+
323
+ /**
324
+ * 检测无效操作
325
+ * @param {Object} traceLog - trace 日志
326
+ * @returns {Array} 无效操作列表
327
+ */
328
+ export function detectWastedOperations(traceLog) {
329
+ const events = traceLog?.events || [];
330
+ const wasted = [];
331
+
332
+ // 检测回退后重做的操作
333
+ const rollbackEvents = events.filter(e => e.type === PATH_EVENT_TYPES.ROLLBACK);
334
+
335
+ for (const rollback of rollbackEvents) {
336
+ // 找到回退后 5 分钟内的相同操作
337
+ const rollbackTime = new Date(rollback.timestamp).getTime();
338
+ const relatedEvents = events.filter(e => {
339
+ const eventTime = new Date(e.timestamp).getTime();
340
+ return eventTime > rollbackTime && eventTime < rollbackTime + 5 * 60 * 1000;
341
+ });
342
+
343
+ if (relatedEvents.length > 0) {
344
+ wasted.push({
345
+ rollbackType: rollback.trigger,
346
+ wastedCount: relatedEvents.length,
347
+ message: `回退后 ${relatedEvents.length} 次操作可能为重复工作`
348
+ });
349
+ }
350
+ }
351
+
352
+ return wasted;
353
+ }
354
+
355
+ /**
356
+ * 便捷方法:记录命令调用
357
+ */
358
+ export function recordCommand(projectRoot, command) {
359
+ return recordPathEvent(projectRoot, PATH_EVENT_TYPES.COMMAND, { command });
360
+ }
361
+
362
+ /**
363
+ * 便捷方法:记录阶段转换
364
+ */
365
+ export function recordStage(projectRoot, stage) {
366
+ return recordPathEvent(projectRoot, PATH_EVENT_TYPES.STAGE, { stage });
367
+ }
368
+
369
+ /**
370
+ * 便捷方法:记录工具调用
371
+ */
372
+ export function recordTool(projectRoot, tool, action = null) {
373
+ return recordPathEvent(projectRoot, PATH_EVENT_TYPES.TOOL, { tool, action });
374
+ }
375
+
376
+ /**
377
+ * 便捷方法:记录决策
378
+ */
379
+ export function recordDecision(projectRoot, decisionType, context = {}) {
380
+ return recordPathEvent(projectRoot, PATH_EVENT_TYPES.DECISION, {
381
+ decisionType,
382
+ ...context
383
+ });
384
+ }
385
+
386
+ /**
387
+ * 便捷方法:记录回退
388
+ */
389
+ export function recordRollback(projectRoot, level, trigger, outcome, context = {}) {
390
+ return recordPathEvent(projectRoot, PATH_EVENT_TYPES.ROLLBACK, {
391
+ level,
392
+ trigger,
393
+ outcome,
394
+ ...context
395
+ });
396
+ }
397
+
398
+ /**
399
+ * 便捷方法:记录门禁检查
400
+ */
401
+ export function recordGate(projectRoot, gate, passed, reason = null) {
402
+ return recordPathEvent(projectRoot, PATH_EVENT_TYPES.GATE, {
403
+ gate,
404
+ passed,
405
+ reason
406
+ });
407
+ }
408
+
409
+ export default {
410
+ PATH_EVENT_TYPES,
411
+ recordPathEvent,
412
+ generatePathSummary,
413
+ extractCommandSequence,
414
+ extractStageSequence,
415
+ calculateToolStats,
416
+ calculateDecisionStats,
417
+ calculateRollbackStats,
418
+ calculateGateStats,
419
+ detectRepeatedOperations,
420
+ detectWastedOperations,
421
+ recordCommand,
422
+ recordStage,
423
+ recordTool,
424
+ recordDecision,
425
+ recordRollback,
426
+ recordGate
427
+ };
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Hook 执行日志记录器
3
+ *
4
+ * 记录完整的 hook 执行历史,包括:
5
+ * - 输入(stdin)
6
+ * - 输出(stdout)
7
+ * - 执行时间
8
+ * - 错误信息
9
+ *
10
+ * 日志存储位置:.autospec/logs/hooks/hook-execution-{date}.json
11
+ */
12
+
13
+ import fs from 'fs';
14
+ import path from 'path';
15
+ import { getTraceId, generateEventId, appendTraceEvent } from './trace-context.js';
16
+
17
+ /**
18
+ * 获取 hook 日志目录
19
+ */
20
+ function getHookLogDir(projectRoot) {
21
+ return path.join(projectRoot, '.autospec', 'logs', 'hooks');
22
+ }
23
+
24
+ /**
25
+ * 确保 hook 日志目录存在
26
+ */
27
+ function ensureHookLogDir(projectRoot) {
28
+ const logDir = getHookLogDir(projectRoot);
29
+ if (!fs.existsSync(logDir)) {
30
+ fs.mkdirSync(logDir, { recursive: true });
31
+ }
32
+ return logDir;
33
+ }
34
+
35
+ /**
36
+ * 获取当天的 hook 日志文件路径
37
+ */
38
+ function getHookLogFile(projectRoot) {
39
+ const date = new Date().toISOString().split('T')[0];
40
+ return path.join(getHookLogDir(projectRoot), `hook-execution-${date}.json`);
41
+ }
42
+
43
+ /**
44
+ * 读取 hook 日志
45
+ */
46
+ function readHookLog(projectRoot) {
47
+ const logPath = getHookLogFile(projectRoot);
48
+ if (fs.existsSync(logPath)) {
49
+ try {
50
+ return JSON.parse(fs.readFileSync(logPath, 'utf-8'));
51
+ } catch {
52
+ return { executions: [], errors: [], summary: {} };
53
+ }
54
+ }
55
+ return { executions: [], errors: [], summary: {} };
56
+ }
57
+
58
+ /**
59
+ * 写入 hook 日志
60
+ */
61
+ function writeHookLog(projectRoot, logData) {
62
+ ensureHookLogDir(projectRoot);
63
+ const logPath = getHookLogFile(projectRoot);
64
+ fs.writeFileSync(logPath, JSON.stringify(logData, null, 2), 'utf-8');
65
+ }
66
+
67
+ /**
68
+ * 记录 hook 执行(成功或失败)
69
+ * @param {string} projectRoot - 项目根目录
70
+ * @param {string} hookName - Hook 名称
71
+ * @param {Object} input - Hook 输入
72
+ * @param {Object} output - Hook 输出
73
+ * @param {number} duration - 执行时长(毫秒)
74
+ * @param {boolean} success - 是否成功
75
+ */
76
+ export function recordHookExecution(projectRoot, hookName, input, output, duration, success) {
77
+ if (!projectRoot) return null;
78
+
79
+ const logData = readHookLog(projectRoot);
80
+ const traceId = getTraceId(projectRoot);
81
+
82
+ const execution = {
83
+ eventId: generateEventId(),
84
+ traceId,
85
+ timestamp: new Date().toISOString(),
86
+ hookName,
87
+ duration: `${duration}ms`,
88
+ success,
89
+ // 输入摘要(避免记录过大内容)
90
+ input: {
91
+ toolName: input?.tool_name,
92
+ filePath: input?.tool_input?.file_path,
93
+ event: input?.event_name
94
+ },
95
+ // 输出摘要
96
+ output: output ? {
97
+ hadPermissionDecision: !!output.hookSpecificOutput?.permissionDecision,
98
+ hadAdditionalContext: !!output.hookSpecificOutput?.additionalContext
99
+ } : null
100
+ };
101
+
102
+ logData.executions.push(execution);
103
+
104
+ // 更新摘要
105
+ updateSummary(logData, hookName, success);
106
+
107
+ // 限制日志大小(保留最近 1000 条)
108
+ if (logData.executions.length > 1000) {
109
+ logData.executions = logData.executions.slice(-1000);
110
+ }
111
+
112
+ writeHookLog(projectRoot, logData);
113
+
114
+ return execution;
115
+ }
116
+
117
+ /**
118
+ * 记录 hook 错误
119
+ * @param {string} projectRoot - 项目根目录
120
+ * @param {Object} errorInfo - 错误信息
121
+ */
122
+ export function recordHookError(projectRoot, errorInfo) {
123
+ if (!projectRoot) return null;
124
+
125
+ const logData = readHookLog(projectRoot);
126
+ const traceId = getTraceId(projectRoot);
127
+
128
+ const errorEntry = {
129
+ eventId: generateEventId(),
130
+ traceId,
131
+ ...errorInfo
132
+ };
133
+
134
+ logData.errors.push(errorEntry);
135
+
136
+ // 更新错误摘要
137
+ if (!logData.summary.errorsByHook) {
138
+ logData.summary.errorsByHook = {};
139
+ }
140
+ logData.summary.errorsByHook[errorInfo.hookName] =
141
+ (logData.summary.errorsByHook[errorInfo.hookName] || 0) + 1;
142
+
143
+ // 限制错误日志大小
144
+ if (logData.errors.length > 500) {
145
+ logData.errors = logData.errors.slice(-500);
146
+ }
147
+
148
+ writeHookLog(projectRoot, logData);
149
+
150
+ // 同时写入 trace 日志
151
+ if (traceId) {
152
+ appendTraceEvent(projectRoot, {
153
+ eventId: errorEntry.eventId,
154
+ traceId,
155
+ timestamp: errorEntry.timestamp,
156
+ type: 'hook_error',
157
+ source: errorInfo.hookName,
158
+ data: {
159
+ errorType: errorInfo.errorType,
160
+ errorMessage: errorInfo.errorMessage
161
+ }
162
+ });
163
+ }
164
+
165
+ return errorEntry;
166
+ }
167
+
168
+ /**
169
+ * 更新摘要统计
170
+ */
171
+ function updateSummary(logData, hookName, success) {
172
+ if (!logData.summary.byHook) {
173
+ logData.summary.byHook = {};
174
+ }
175
+
176
+ if (!logData.summary.byHook[hookName]) {
177
+ logData.summary.byHook[hookName] = { total: 0, success: 0, fail: 0 };
178
+ }
179
+
180
+ logData.summary.byHook[hookName].total++;
181
+ if (success) {
182
+ logData.summary.byHook[hookName].success++;
183
+ } else {
184
+ logData.summary.byHook[hookName].fail++;
185
+ }
186
+
187
+ // 计算全局统计
188
+ logData.summary.totalExecutions = logData.executions.length;
189
+ logData.summary.totalErrors = logData.errors.length;
190
+ }
191
+
192
+ /**
193
+ * 获取 hook 错误率
194
+ * @param {string} projectRoot - 项目根目录
195
+ * @param {string} hookName - Hook 名称(可选,不传则返回全局错误率)
196
+ * @returns {number} 错误率(0-1)
197
+ */
198
+ export function getHookErrorRate(projectRoot, hookName = null) {
199
+ const logData = readHookLog(projectRoot);
200
+
201
+ if (hookName) {
202
+ const stats = logData.summary.byHook?.[hookName];
203
+ if (!stats || stats.total === 0) return 0;
204
+ return stats.fail / stats.total;
205
+ }
206
+
207
+ // 全局错误率
208
+ const total = logData.executions.length;
209
+ if (total === 0) return 0;
210
+ return logData.errors.length / total;
211
+ }
212
+
213
+ /**
214
+ * 获取最近的 hook 错误
215
+ * @param {string} projectRoot - 项目根目录
216
+ * @param {number} limit - 限制数量
217
+ * @returns {Array} 错误列表
218
+ */
219
+ export function getRecentHookErrors(projectRoot, limit = 10) {
220
+ const logData = readHookLog(projectRoot);
221
+ return logData.errors.slice(-limit);
222
+ }
223
+
224
+ /**
225
+ * 获取 hook 执行统计
226
+ * @param {string} projectRoot - 项目根目录
227
+ * @returns {Object} 统计信息
228
+ */
229
+ export function getHookStats(projectRoot) {
230
+ const logData = readHookLog(projectRoot);
231
+ return {
232
+ totalExecutions: logData.summary.totalExecutions || 0,
233
+ totalErrors: logData.summary.totalErrors || 0,
234
+ byHook: logData.summary.byHook || {},
235
+ errorsByHook: logData.summary.errorsByHook || {}
236
+ };
237
+ }
238
+
239
+ export default {
240
+ recordHookExecution,
241
+ recordHookError,
242
+ getHookErrorRate,
243
+ getRecentHookErrors,
244
+ getHookStats
245
+ };