specweave 1.0.550 → 1.0.552

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 (166) hide show
  1. package/CLAUDE.md +1 -1
  2. package/bin/specweave.js +23 -1
  3. package/dist/src/cli/commands/hook.d.ts +15 -0
  4. package/dist/src/cli/commands/hook.d.ts.map +1 -0
  5. package/dist/src/cli/commands/hook.js +61 -0
  6. package/dist/src/cli/commands/hook.js.map +1 -0
  7. package/dist/src/cli/commands/init.d.ts.map +1 -1
  8. package/dist/src/cli/commands/init.js +5 -0
  9. package/dist/src/cli/commands/init.js.map +1 -1
  10. package/dist/src/cli/commands/refresh-plugins.d.ts.map +1 -1
  11. package/dist/src/cli/commands/refresh-plugins.js +11 -1
  12. package/dist/src/cli/commands/refresh-plugins.js.map +1 -1
  13. package/dist/src/cli/commands/sync-setup.d.ts.map +1 -1
  14. package/dist/src/cli/commands/sync-setup.js +7 -3
  15. package/dist/src/cli/commands/sync-setup.js.map +1 -1
  16. package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.d.ts +9 -0
  17. package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.d.ts.map +1 -1
  18. package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.js +9 -3
  19. package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.js.map +1 -1
  20. package/dist/src/config/types.d.ts +2 -2
  21. package/dist/src/core/config/types.d.ts +18 -2
  22. package/dist/src/core/config/types.d.ts.map +1 -1
  23. package/dist/src/core/config/types.js.map +1 -1
  24. package/dist/src/core/hooks/handlers/hook-router.d.ts +19 -0
  25. package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -0
  26. package/dist/src/core/hooks/handlers/hook-router.js +75 -0
  27. package/dist/src/core/hooks/handlers/hook-router.js.map +1 -0
  28. package/dist/src/core/hooks/handlers/index.d.ts +10 -0
  29. package/dist/src/core/hooks/handlers/index.d.ts.map +1 -0
  30. package/dist/src/core/hooks/handlers/index.js +9 -0
  31. package/dist/src/core/hooks/handlers/index.js.map +1 -0
  32. package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts +11 -0
  33. package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts.map +1 -0
  34. package/dist/src/core/hooks/handlers/post-tool-use-analytics.js +73 -0
  35. package/dist/src/core/hooks/handlers/post-tool-use-analytics.js.map +1 -0
  36. package/dist/src/core/hooks/handlers/post-tool-use.d.ts +11 -0
  37. package/dist/src/core/hooks/handlers/post-tool-use.d.ts.map +1 -0
  38. package/dist/src/core/hooks/handlers/post-tool-use.js +76 -0
  39. package/dist/src/core/hooks/handlers/post-tool-use.js.map +1 -0
  40. package/dist/src/core/hooks/handlers/pre-compact.d.ts +11 -0
  41. package/dist/src/core/hooks/handlers/pre-compact.d.ts.map +1 -0
  42. package/dist/src/core/hooks/handlers/pre-compact.js +77 -0
  43. package/dist/src/core/hooks/handlers/pre-compact.js.map +1 -0
  44. package/dist/src/core/hooks/handlers/pre-tool-use.d.ts +11 -0
  45. package/dist/src/core/hooks/handlers/pre-tool-use.d.ts.map +1 -0
  46. package/dist/src/core/hooks/handlers/pre-tool-use.js +318 -0
  47. package/dist/src/core/hooks/handlers/pre-tool-use.js.map +1 -0
  48. package/dist/src/core/hooks/handlers/session-start.d.ts +9 -0
  49. package/dist/src/core/hooks/handlers/session-start.d.ts.map +1 -0
  50. package/dist/src/core/hooks/handlers/session-start.js +111 -0
  51. package/dist/src/core/hooks/handlers/session-start.js.map +1 -0
  52. package/dist/src/core/hooks/handlers/stop-auto.d.ts +16 -0
  53. package/dist/src/core/hooks/handlers/stop-auto.d.ts.map +1 -0
  54. package/dist/src/core/hooks/handlers/stop-auto.js +122 -0
  55. package/dist/src/core/hooks/handlers/stop-auto.js.map +1 -0
  56. package/dist/src/core/hooks/handlers/stop-reflect.d.ts +14 -0
  57. package/dist/src/core/hooks/handlers/stop-reflect.d.ts.map +1 -0
  58. package/dist/src/core/hooks/handlers/stop-reflect.js +43 -0
  59. package/dist/src/core/hooks/handlers/stop-reflect.js.map +1 -0
  60. package/dist/src/core/hooks/handlers/stop-sync.d.ts +15 -0
  61. package/dist/src/core/hooks/handlers/stop-sync.d.ts.map +1 -0
  62. package/dist/src/core/hooks/handlers/stop-sync.js +68 -0
  63. package/dist/src/core/hooks/handlers/stop-sync.js.map +1 -0
  64. package/dist/src/core/hooks/handlers/types.d.ts +63 -0
  65. package/dist/src/core/hooks/handlers/types.d.ts.map +1 -0
  66. package/dist/src/core/hooks/handlers/types.js +27 -0
  67. package/dist/src/core/hooks/handlers/types.js.map +1 -0
  68. package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts +14 -0
  69. package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts.map +1 -0
  70. package/dist/src/core/hooks/handlers/user-prompt-submit.js +173 -0
  71. package/dist/src/core/hooks/handlers/user-prompt-submit.js.map +1 -0
  72. package/dist/src/core/hooks/handlers/utils.d.ts +25 -0
  73. package/dist/src/core/hooks/handlers/utils.d.ts.map +1 -0
  74. package/dist/src/core/hooks/handlers/utils.js +64 -0
  75. package/dist/src/core/hooks/handlers/utils.js.map +1 -0
  76. package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
  77. package/dist/src/core/increment/completion-validator.js +32 -0
  78. package/dist/src/core/increment/completion-validator.js.map +1 -1
  79. package/dist/src/init/research/types.d.ts +1 -1
  80. package/dist/src/sync/sync-target-resolver.js.map +1 -1
  81. package/dist/src/utils/lock-manager.d.ts.map +1 -1
  82. package/dist/src/utils/lock-manager.js +5 -0
  83. package/dist/src/utils/lock-manager.js.map +1 -1
  84. package/dist/src/utils/plugin-copier.d.ts +10 -0
  85. package/dist/src/utils/plugin-copier.d.ts.map +1 -1
  86. package/dist/src/utils/plugin-copier.js +63 -35
  87. package/dist/src/utils/plugin-copier.js.map +1 -1
  88. package/package.json +1 -1
  89. package/plugins/specweave/agents/sw-closer.md +3 -2
  90. package/plugins/specweave/hooks/hooks.json +10 -10
  91. package/plugins/specweave/skills/code-reviewer/SKILL.md +180 -16
  92. package/plugins/specweave/skills/code-reviewer/agents/reviewer-comments.md +83 -0
  93. package/plugins/specweave/skills/code-reviewer/agents/reviewer-silent-failures.md +19 -0
  94. package/plugins/specweave/skills/code-reviewer/agents/reviewer-spec-compliance.md +19 -0
  95. package/plugins/specweave/skills/code-reviewer/agents/reviewer-tests.md +101 -0
  96. package/plugins/specweave/skills/code-reviewer/agents/reviewer-types.md +20 -0
  97. package/plugins/specweave/skills/done/SKILL.md +56 -21
  98. package/plugins/specweave/skills/grill/SKILL.md +1 -1
  99. package/plugins/specweave/skills/team-lead/agents/reviewer-logic.md +19 -0
  100. package/plugins/specweave/skills/team-lead/agents/reviewer-performance.md +20 -0
  101. package/plugins/specweave/skills/team-lead/agents/reviewer-security.md +20 -0
  102. package/src/templates/CLAUDE.md.template +7 -4
  103. package/plugins/specweave/hooks/README.md +0 -493
  104. package/plugins/specweave/hooks/_archive/stop-auto-v4-legacy.sh +0 -1319
  105. package/plugins/specweave/hooks/lib/common-setup.sh +0 -144
  106. package/plugins/specweave/hooks/lib/hook-errors.sh +0 -414
  107. package/plugins/specweave/hooks/lib/migrate-increment-work.sh +0 -245
  108. package/plugins/specweave/hooks/lib/resolve-package.sh +0 -146
  109. package/plugins/specweave/hooks/lib/scheduler-startup.sh +0 -135
  110. package/plugins/specweave/hooks/lib/score-increment.sh +0 -87
  111. package/plugins/specweave/hooks/lib/sync-spec-content.sh +0 -193
  112. package/plugins/specweave/hooks/lib/update-active-increment.sh +0 -95
  113. package/plugins/specweave/hooks/lib/update-status-line.sh +0 -233
  114. package/plugins/specweave/hooks/lib/validate-spec-status.sh +0 -171
  115. package/plugins/specweave/hooks/llm-judge-validator.sh +0 -219
  116. package/plugins/specweave/hooks/log-decision.sh +0 -168
  117. package/plugins/specweave/hooks/pre-compact.sh +0 -64
  118. package/plugins/specweave/hooks/startup-health-check.sh +0 -64
  119. package/plugins/specweave/hooks/stop-auto-v5.sh +0 -276
  120. package/plugins/specweave/hooks/stop-reflect.sh +0 -336
  121. package/plugins/specweave/hooks/stop-sync.sh +0 -283
  122. package/plugins/specweave/hooks/tests/test-auto-context-integration.sh +0 -126
  123. package/plugins/specweave/hooks/tests/test-stop-auto-enriched.sh +0 -128
  124. package/plugins/specweave/hooks/universal/dispatcher.mjs +0 -336
  125. package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +0 -325
  126. package/plugins/specweave/hooks/universal/hook-wrapper.cmd +0 -26
  127. package/plugins/specweave/hooks/universal/hook-wrapper.sh +0 -69
  128. package/plugins/specweave/hooks/universal/run-hook.sh +0 -20
  129. package/plugins/specweave/hooks/universal/session-start.cmd +0 -16
  130. package/plugins/specweave/hooks/universal/session-start.ps1 +0 -16
  131. package/plugins/specweave/hooks/user-prompt-submit.sh +0 -2550
  132. package/plugins/specweave/hooks/v2/detectors/lifecycle-detector.sh +0 -87
  133. package/plugins/specweave/hooks/v2/detectors/us-completion-detector.sh +0 -186
  134. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use-analytics.sh +0 -83
  135. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +0 -447
  136. package/plugins/specweave/hooks/v2/dispatchers/pre-tool-use.sh +0 -104
  137. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +0 -270
  138. package/plugins/specweave/hooks/v2/guards/completion-guard.sh +0 -14
  139. package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +0 -14
  140. package/plugins/specweave/hooks/v2/guards/increment-existence-guard.sh +0 -240
  141. package/plugins/specweave/hooks/v2/guards/interview-enforcement-guard.sh +0 -171
  142. package/plugins/specweave/hooks/v2/guards/metadata-json-guard.sh +0 -14
  143. package/plugins/specweave/hooks/v2/guards/skill-chain-enforcement-guard.sh +0 -222
  144. package/plugins/specweave/hooks/v2/guards/spec-template-enforcement-guard.sh +0 -21
  145. package/plugins/specweave/hooks/v2/guards/spec-validation-guard.sh +0 -14
  146. package/plugins/specweave/hooks/v2/guards/status-completion-guard.sh +0 -84
  147. package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +0 -475
  148. package/plugins/specweave/hooks/v2/guards/tdd-enforcement-guard.sh +0 -268
  149. package/plugins/specweave/hooks/v2/handlers/ac-sync-dispatcher.sh +0 -332
  150. package/plugins/specweave/hooks/v2/handlers/ac-validation-handler.sh +0 -50
  151. package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +0 -347
  152. package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +0 -83
  153. package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +0 -268
  154. package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +0 -104
  155. package/plugins/specweave/hooks/v2/handlers/status-line-handler.sh +0 -165
  156. package/plugins/specweave/hooks/v2/handlers/status-update.sh +0 -61
  157. package/plugins/specweave/hooks/v2/handlers/universal-auto-create-dispatcher.sh +0 -270
  158. package/plugins/specweave/hooks/v2/integrations/ado-post-living-docs-update.sh +0 -367
  159. package/plugins/specweave/hooks/v2/integrations/ado-post-task.sh +0 -179
  160. package/plugins/specweave/hooks/v2/integrations/github-auto-create-handler.sh +0 -553
  161. package/plugins/specweave/hooks/v2/integrations/github-post-task.sh +0 -345
  162. package/plugins/specweave/hooks/v2/integrations/jira-post-task.sh +0 -180
  163. package/plugins/specweave/hooks/v2/lib/check-provider-enabled.sh +0 -52
  164. package/plugins/specweave/hooks/v2/queue/enqueue.sh +0 -81
  165. package/plugins/specweave/hooks/v2/session-end.sh +0 -139
  166. package/plugins/specweave/hooks/validate-skill-activations.sh +0 -227
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Stop-reflect hook handler.
3
+ *
4
+ * Fires when a session ends. Checks if reflection is enabled in config.
5
+ * If enabled, logs that reflection was requested (actual reflection logic
6
+ * will call `specweave reflect-stop` CLI in a follow-up).
7
+ *
8
+ * NEVER blocks — always returns approve.
9
+ *
10
+ * @module core/hooks/handlers/stop-reflect
11
+ */
12
+ import * as fs from 'fs';
13
+ import { logHook } from './utils.js';
14
+ /** Check if reflect is enabled in config. Default: true */
15
+ function isReflectEnabled(configPath) {
16
+ try {
17
+ if (!fs.existsSync(configPath))
18
+ return true;
19
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
20
+ // Note: explicit false check — missing key defaults to enabled
21
+ return config.reflect?.enabled !== false;
22
+ }
23
+ catch {
24
+ return true; // Default enabled on error
25
+ }
26
+ }
27
+ export const handle = async (input, context) => {
28
+ try {
29
+ const enabled = isReflectEnabled(context.configPath);
30
+ if (!enabled) {
31
+ logHook(context, 'stop-reflect', 'Reflection disabled in config');
32
+ return { decision: 'approve' };
33
+ }
34
+ // Reflection is enabled — log intent
35
+ logHook(context, 'stop-reflect', 'Reflection requested at session end');
36
+ return { decision: 'approve' };
37
+ }
38
+ catch {
39
+ // Never throw — always approve
40
+ return { decision: 'approve' };
41
+ }
42
+ };
43
+ //# sourceMappingURL=stop-reflect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-reflect.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/stop-reflect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,2DAA2D;AAC3D,SAAS,gBAAgB,CAAC,UAAkB;IAC1C,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;QAC/D,+DAA+D;QAC/D,OAAO,MAAM,CAAC,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,2BAA2B;IAC1C,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAc,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAErD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,+BAA+B,CAAC,CAAC;YAClE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QACjC,CAAC;QAED,qCAAqC;QACrC,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,qCAAqC,CAAC,CAAC;QACxE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;QAC/B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Stop-sync hook handler.
3
+ *
4
+ * Fires at session end (after stop-reflect and stop-auto). Reads pending
5
+ * sync events, deduplicates by increment ID, logs sync intent, and clears
6
+ * the event queue. Actual sync calls (LivingDocsSync, GitHub/JIRA/ADO)
7
+ * will be added in a follow-up.
8
+ *
9
+ * NEVER blocks — always returns approve.
10
+ *
11
+ * @module core/hooks/handlers/stop-sync
12
+ */
13
+ import type { HandlerFn } from './types.js';
14
+ export declare const handle: HandlerFn;
15
+ //# sourceMappingURL=stop-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-sync.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/stop-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAoB5C,eAAO,MAAM,MAAM,EAAE,SA+CpB,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Stop-sync hook handler.
3
+ *
4
+ * Fires at session end (after stop-reflect and stop-auto). Reads pending
5
+ * sync events, deduplicates by increment ID, logs sync intent, and clears
6
+ * the event queue. Actual sync calls (LivingDocsSync, GitHub/JIRA/ADO)
7
+ * will be added in a follow-up.
8
+ *
9
+ * NEVER blocks — always returns approve.
10
+ *
11
+ * @module core/hooks/handlers/stop-sync
12
+ */
13
+ import * as fs from 'fs';
14
+ import * as path from 'path';
15
+ import { logHook } from './utils.js';
16
+ /** Parse a JSONL line safely */
17
+ function parseEventLine(line) {
18
+ try {
19
+ return JSON.parse(line);
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ export const handle = async (input, context) => {
26
+ try {
27
+ const pendingPath = path.join(context.stateDir, 'event-queue', 'pending.jsonl');
28
+ // 1. Check if pending events exist
29
+ if (!fs.existsSync(pendingPath)) {
30
+ logHook(context, 'stop-sync', 'No pending events');
31
+ return { decision: 'approve' };
32
+ }
33
+ const content = fs.readFileSync(pendingPath, 'utf8').trim();
34
+ if (!content) {
35
+ logHook(context, 'stop-sync', 'No pending events');
36
+ return { decision: 'approve' };
37
+ }
38
+ // 2. Parse and deduplicate events by increment ID
39
+ const lines = content.split('\n').filter((l) => l.trim());
40
+ const seenIncrements = new Set();
41
+ for (const line of lines) {
42
+ const event = parseEventLine(line);
43
+ if (!event?.incrementId)
44
+ continue;
45
+ const incId = event.incrementId;
46
+ if (seenIncrements.has(incId))
47
+ continue;
48
+ seenIncrements.add(incId);
49
+ logHook(context, 'stop-sync', `Sync requested for increment: ${incId} (event: ${event.event ?? 'unknown'})`);
50
+ }
51
+ // 3. Clear processed events
52
+ try {
53
+ fs.writeFileSync(pendingPath, '');
54
+ }
55
+ catch {
56
+ // Non-critical — events may be re-processed next time
57
+ }
58
+ if (seenIncrements.size > 0) {
59
+ logHook(context, 'stop-sync', `Processed ${seenIncrements.size} unique increment(s)`);
60
+ }
61
+ return { decision: 'approve' };
62
+ }
63
+ catch {
64
+ // Never throw — always approve
65
+ return { decision: 'approve' };
66
+ }
67
+ };
68
+ //# sourceMappingURL=stop-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-sync.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/stop-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAUrC,gCAAgC;AAChC,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgB,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAc,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,eAAe,CAAC,CAAC;QAEhF,mCAAmC;QACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;YACnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QACjC,CAAC;QAED,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC;YACnD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QACjC,CAAC;QAED,kDAAkD;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1D,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;QAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,KAAK,EAAE,WAAW;gBAAE,SAAS;YAElC,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,CAAC;YAChC,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,SAAS;YAExC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC1B,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,iCAAiC,KAAK,YAAY,KAAK,CAAC,KAAK,IAAI,SAAS,GAAG,CAAC,CAAC;QAC/G,CAAC;QAED,4BAA4B;QAC5B,IAAI,CAAC;YACH,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,sDAAsD;QACxD,CAAC;QAED,IAAI,cAAc,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,aAAa,cAAc,CAAC,IAAI,sBAAsB,CAAC,CAAC;QACxF,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;QAC/B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Hook handler types for CLI delegation model.
3
+ *
4
+ * All hook handlers share this contract:
5
+ * - Read JSON from stdin (HookInput)
6
+ * - Return JSON to stdout (HookResult)
7
+ * - Never throw — errors become safe fallback responses
8
+ *
9
+ * @module core/hooks/handlers/types
10
+ */
11
+ /** Raw JSON input from Claude Code hook protocol */
12
+ export interface HookInput {
13
+ /** Tool name (PreToolUse/PostToolUse) */
14
+ tool_name?: string;
15
+ toolName?: string;
16
+ /** Tool input parameters */
17
+ tool_input?: Record<string, unknown>;
18
+ toolInput?: Record<string, unknown>;
19
+ /** User prompt text (UserPromptSubmit) */
20
+ prompt?: string;
21
+ /** Session transcript (Stop hooks) */
22
+ transcript?: string;
23
+ /** Catch-all for additional fields */
24
+ [key: string]: unknown;
25
+ }
26
+ /** JSON output to Claude Code hook protocol */
27
+ export interface HookResult {
28
+ /** Decision for guard/prompt/stop hooks */
29
+ decision?: 'approve' | 'block' | 'allow';
30
+ /** Continuation signal for session/compact/post-tool hooks */
31
+ continue?: boolean;
32
+ /** Reason for blocking */
33
+ reason?: string;
34
+ /** Hook-specific output for context injection */
35
+ hookSpecificOutput?: {
36
+ hookEventName?: string;
37
+ additionalContext?: string;
38
+ };
39
+ /** Catch-all for additional fields */
40
+ [key: string]: unknown;
41
+ }
42
+ /** Context resolved by the router before handler execution */
43
+ export interface HookContext {
44
+ /** Absolute path to project root (contains .specweave/) */
45
+ projectRoot: string;
46
+ /** Path to .specweave/state/ directory */
47
+ stateDir: string;
48
+ /** Path to .specweave/logs/ directory */
49
+ logsDir: string;
50
+ /** Path to .specweave/config.json */
51
+ configPath: string;
52
+ /** ISO timestamp of hook invocation */
53
+ timestamp: string;
54
+ }
55
+ /** Handler function signature — every handler exports a `handle` matching this */
56
+ export type HandlerFn = (input: HookInput, context: HookContext) => Promise<HookResult>;
57
+ /** Event types supported by the hook router */
58
+ export type HookEventType = 'user-prompt-submit' | 'pre-tool-use' | 'post-tool-use' | 'post-tool-use-analytics' | 'session-start' | 'pre-compact' | 'stop-reflect' | 'stop-auto' | 'stop-sync';
59
+ /** Map of event types to their safe fallback responses */
60
+ export declare const SAFE_DEFAULTS: Record<string, HookResult>;
61
+ /** Get safe default response for an event type */
62
+ export declare function getSafeDefault(eventType: string): HookResult;
63
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,oDAAoD;AACpD,MAAM,WAAW,SAAS;IACxB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,+CAA+C;AAC/C,MAAM,WAAW,UAAU;IACzB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;IACzC,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,kBAAkB,CAAC,EAAE;QACnB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,sCAAsC;IACtC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,CACtB,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,WAAW,KACjB,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzB,+CAA+C;AAC/C,MAAM,MAAM,aAAa,GACrB,oBAAoB,GACpB,cAAc,GACd,eAAe,GACf,yBAAyB,GACzB,eAAe,GACf,aAAa,GACb,cAAc,GACd,WAAW,GACX,WAAW,CAAC;AAEhB,0DAA0D;AAC1D,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAUpD,CAAC;AAEF,kDAAkD;AAClD,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAE5D"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Hook handler types for CLI delegation model.
3
+ *
4
+ * All hook handlers share this contract:
5
+ * - Read JSON from stdin (HookInput)
6
+ * - Return JSON to stdout (HookResult)
7
+ * - Never throw — errors become safe fallback responses
8
+ *
9
+ * @module core/hooks/handlers/types
10
+ */
11
+ /** Map of event types to their safe fallback responses */
12
+ export const SAFE_DEFAULTS = {
13
+ 'user-prompt-submit': { decision: 'approve' },
14
+ 'pre-tool-use': { decision: 'allow' },
15
+ 'post-tool-use': { continue: true },
16
+ 'post-tool-use-analytics': { continue: true },
17
+ 'session-start': { continue: true },
18
+ 'pre-compact': { continue: true },
19
+ 'stop-reflect': { decision: 'approve' },
20
+ 'stop-auto': { decision: 'approve' },
21
+ 'stop-sync': { decision: 'approve' },
22
+ };
23
+ /** Get safe default response for an event type */
24
+ export function getSafeDefault(eventType) {
25
+ return SAFE_DEFAULTS[eventType] ?? { continue: true };
26
+ }
27
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAmEH,0DAA0D;AAC1D,MAAM,CAAC,MAAM,aAAa,GAA+B;IACvD,oBAAoB,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE;IAC7C,cAAc,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE;IACrC,eAAe,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;IACnC,yBAAyB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC7C,eAAe,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;IACnC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;IACjC,cAAc,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE;IACvC,WAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE;IACpC,WAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE;CACrC,CAAC;AAEF,kDAAkD;AAClD,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,OAAO,aAAa,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * UserPromptSubmit hook handler.
3
+ *
4
+ * Fires before the user's prompt is processed. Responsibilities:
5
+ * 1. Fast-path approve for built-in Claude Code commands
6
+ * 2. Fast-path approve for /sw: and /sw- commands
7
+ * 3. Block SW skill invocations in non-initialized projects (scope guard)
8
+ * 4. Inject active increment context + TDD mode info
9
+ *
10
+ * @module core/hooks/handlers/user-prompt-submit
11
+ */
12
+ import type { HandlerFn } from './types.js';
13
+ export declare const handle: HandlerFn;
14
+ //# sourceMappingURL=user-prompt-submit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-prompt-submit.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/user-prompt-submit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AAsHxD,eAAO,MAAM,MAAM,EAAE,SAmDpB,CAAC"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * UserPromptSubmit hook handler.
3
+ *
4
+ * Fires before the user's prompt is processed. Responsibilities:
5
+ * 1. Fast-path approve for built-in Claude Code commands
6
+ * 2. Fast-path approve for /sw: and /sw- commands
7
+ * 3. Block SW skill invocations in non-initialized projects (scope guard)
8
+ * 4. Inject active increment context + TDD mode info
9
+ *
10
+ * @module core/hooks/handlers/user-prompt-submit
11
+ */
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ import { logHook } from './utils.js';
15
+ /** Built-in Claude Code commands that should bypass all SpecWeave processing */
16
+ const BUILTIN_COMMANDS = [
17
+ '/help', '/clear', '/config', '/status', '/context', '/compact', '/cost',
18
+ '/memory', '/permissions', '/doctor', '/login', '/logout',
19
+ ];
20
+ /** Strip IDE metadata tags from prompt (VSCode injects these) */
21
+ function stripIdeMetadata(prompt) {
22
+ return prompt.replace(/.*<\/ide_[a-z_]*>/g, '').trimStart();
23
+ }
24
+ /** Check if prompt starts with a built-in Claude Code command */
25
+ function isBuiltinCommand(prompt) {
26
+ const clean = stripIdeMetadata(prompt).toLowerCase();
27
+ // Match /command at start, followed by end-of-string or whitespace
28
+ const match = clean.match(/^\/([a-z][a-z0-9_-]*)/);
29
+ if (!match)
30
+ return false;
31
+ const cmd = '/' + match[1];
32
+ // If it's a /sw: or /sw- command, it's NOT a built-in
33
+ if (/^\/sw[-:]/.test(cmd))
34
+ return false;
35
+ return BUILTIN_COMMANDS.some((b) => cmd === b) ||
36
+ // Any slash command that isn't /sw: is treated as built-in
37
+ !cmd.startsWith('/sw');
38
+ }
39
+ /** Check if prompt is a SpecWeave command (/sw: or /sw-*:) */
40
+ function isSwCommand(prompt) {
41
+ const clean = stripIdeMetadata(prompt).trimStart().toLowerCase();
42
+ return /^\/sw(-[a-z0-9-]+)?:/.test(clean);
43
+ }
44
+ /** Check if prompt invokes a SpecWeave skill (SW commands + domain plugins) */
45
+ function isSwSkillInvocation(prompt) {
46
+ const clean = stripIdeMetadata(prompt).trimStart().toLowerCase();
47
+ if (/^\/sw(-[a-z0-9-]+)?:/.test(clean))
48
+ return true;
49
+ const DOMAINS = ['frontend', 'backend', 'testing', 'mobile', 'infra', 'k8s', 'ml', 'payments', 'kafka', 'confluent', 'cost', 'docs', 'security', 'skills', 'blockchain'];
50
+ const match = clean.match(/^\/([a-z0-9-]+):/);
51
+ return match !== null && DOMAINS.includes(match[1]);
52
+ }
53
+ /** Read and parse JSON config file, returns null on any error */
54
+ function readJsonSafe(filePath) {
55
+ try {
56
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ /** Build context string from active increments */
63
+ function buildIncrementContext(projectRoot) {
64
+ const incDir = path.join(projectRoot, '.specweave', 'increments');
65
+ if (!fs.existsSync(incDir))
66
+ return '';
67
+ const parts = [];
68
+ let dirs;
69
+ try {
70
+ dirs = fs.readdirSync(incDir);
71
+ }
72
+ catch {
73
+ return '';
74
+ }
75
+ for (const dir of dirs) {
76
+ const metaPath = path.join(incDir, dir, 'metadata.json');
77
+ if (!fs.existsSync(metaPath))
78
+ continue;
79
+ const meta = readJsonSafe(metaPath);
80
+ if (!meta)
81
+ continue;
82
+ if (meta.status !== 'active' && meta.status !== 'in-progress')
83
+ continue;
84
+ const tasksPath = path.join(incDir, dir, 'tasks.md');
85
+ let pending = 0;
86
+ let completed = 0;
87
+ try {
88
+ const tasks = fs.readFileSync(tasksPath, 'utf8');
89
+ pending = (tasks.match(/\[ \]/g) || []).length;
90
+ completed = (tasks.match(/\[x\]/g) || []).length;
91
+ }
92
+ catch {
93
+ // No tasks file
94
+ }
95
+ parts.push(`Active increment: ${dir} (${completed}/${completed + pending} tasks done)`);
96
+ }
97
+ return parts.join('\n');
98
+ }
99
+ /** Build TDD context string from config */
100
+ function buildTddContext(projectRoot) {
101
+ const configPath = path.join(projectRoot, '.specweave', 'config.json');
102
+ const config = readJsonSafe(configPath);
103
+ if (!config)
104
+ return '';
105
+ const testMode = config.testing?.defaultTestMode;
106
+ if (!testMode || (testMode !== 'TDD' && testMode !== 'tdd'))
107
+ return '';
108
+ const enforcement = config.testing?.tddEnforcement ?? 'warn';
109
+ if (enforcement === 'strict') {
110
+ return 'STRICT TDD ACTIVE. RED->GREEN->REFACTOR enforced. No implementation before failing test. Use /sw:tdd-cycle.';
111
+ }
112
+ return 'TDD MODE ACTIVE. RED->GREEN->REFACTOR discipline. Use /sw:tdd-cycle.';
113
+ }
114
+ /** Create approve result with optional context injection */
115
+ function approveWithContext(context) {
116
+ if (!context)
117
+ return { decision: 'approve' };
118
+ return {
119
+ decision: 'approve',
120
+ hookSpecificOutput: {
121
+ hookEventName: 'UserPromptSubmit',
122
+ additionalContext: context,
123
+ },
124
+ };
125
+ }
126
+ export const handle = async (input, context) => {
127
+ try {
128
+ const prompt = input.prompt ?? '';
129
+ // Empty prompt — approve
130
+ if (!prompt.trim()) {
131
+ return { decision: 'approve' };
132
+ }
133
+ // 1. Built-in command fast-path (no SpecWeave processing)
134
+ if (isBuiltinCommand(prompt)) {
135
+ return { decision: 'approve' };
136
+ }
137
+ // 2. Project scope guard — block SW skills in non-initialized projects
138
+ if (isSwSkillInvocation(prompt)) {
139
+ if (process.env.SPECWEAVE_DISABLE_GUARD !== '1') {
140
+ const configExists = fs.existsSync(context.configPath);
141
+ if (!configExists) {
142
+ logHook(context, 'user-prompt-submit', `Blocked: project not initialized`);
143
+ return {
144
+ decision: 'block',
145
+ reason: 'SpecWeave Not Initialized. Run `specweave init` to initialize this project.',
146
+ };
147
+ }
148
+ }
149
+ // SW commands are approved (SpecWeave handles them via skills)
150
+ return { decision: 'approve' };
151
+ }
152
+ // 3. Context injection for regular prompts
153
+ const contextParts = [];
154
+ // Active increment context
155
+ const incCtx = buildIncrementContext(context.projectRoot);
156
+ if (incCtx)
157
+ contextParts.push(incCtx);
158
+ // TDD mode context
159
+ const tddCtx = buildTddContext(context.projectRoot);
160
+ if (tddCtx)
161
+ contextParts.push(tddCtx);
162
+ const combined = contextParts.join('\n');
163
+ if (combined) {
164
+ logHook(context, 'user-prompt-submit', 'Injecting context');
165
+ }
166
+ return approveWithContext(combined || undefined);
167
+ }
168
+ catch (err) {
169
+ // Never throw — return safe default
170
+ return { decision: 'approve' };
171
+ }
172
+ };
173
+ //# sourceMappingURL=user-prompt-submit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-prompt-submit.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/user-prompt-submit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shared utilities for hook handlers.
3
+ *
4
+ * @module core/hooks/handlers/utils
5
+ */
6
+ import type { HookContext, HookInput } from './types.js';
7
+ /**
8
+ * Walk up from `startDir` to find the nearest directory containing `.specweave/config.json`.
9
+ * Returns the project root path, or null if not found.
10
+ */
11
+ export declare function findProjectRoot(startDir?: string): string | null;
12
+ /**
13
+ * Build a HookContext from a project root path.
14
+ */
15
+ export declare function createContext(projectRoot: string): HookContext;
16
+ /**
17
+ * Parse a raw stdin string as JSON. Returns empty object on failure.
18
+ */
19
+ export declare function parseStdinJson(raw: string): HookInput;
20
+ /**
21
+ * Append a log entry to `.specweave/logs/hooks.log`.
22
+ * Never throws — logging failures are silently ignored.
23
+ */
24
+ export declare function logHook(context: HookContext, handler: string, message: string): void;
25
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAahE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAQ9D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAOrD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CACrB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,IAAI,CAQN"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Shared utilities for hook handlers.
3
+ *
4
+ * @module core/hooks/handlers/utils
5
+ */
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ /**
9
+ * Walk up from `startDir` to find the nearest directory containing `.specweave/config.json`.
10
+ * Returns the project root path, or null if not found.
11
+ */
12
+ export function findProjectRoot(startDir) {
13
+ let current = startDir ?? process.cwd();
14
+ const root = path.parse(current).root;
15
+ let limit = 0;
16
+ while (current !== root && limit < 100) {
17
+ if (fs.existsSync(path.join(current, '.specweave', 'config.json'))) {
18
+ return current;
19
+ }
20
+ current = path.dirname(current);
21
+ limit++;
22
+ }
23
+ return null;
24
+ }
25
+ /**
26
+ * Build a HookContext from a project root path.
27
+ */
28
+ export function createContext(projectRoot) {
29
+ return {
30
+ projectRoot,
31
+ stateDir: path.join(projectRoot, '.specweave', 'state'),
32
+ logsDir: path.join(projectRoot, '.specweave', 'logs'),
33
+ configPath: path.join(projectRoot, '.specweave', 'config.json'),
34
+ timestamp: new Date().toISOString(),
35
+ };
36
+ }
37
+ /**
38
+ * Parse a raw stdin string as JSON. Returns empty object on failure.
39
+ */
40
+ export function parseStdinJson(raw) {
41
+ if (!raw || !raw.trim())
42
+ return {};
43
+ try {
44
+ return JSON.parse(raw);
45
+ }
46
+ catch {
47
+ return {};
48
+ }
49
+ }
50
+ /**
51
+ * Append a log entry to `.specweave/logs/hooks.log`.
52
+ * Never throws — logging failures are silently ignored.
53
+ */
54
+ export function logHook(context, handler, message) {
55
+ try {
56
+ fs.mkdirSync(context.logsDir, { recursive: true });
57
+ const entry = `[${context.timestamp}] ${handler}: ${message}\n`;
58
+ fs.appendFileSync(path.join(context.logsDir, 'hooks.log'), entry);
59
+ }
60
+ catch {
61
+ // Never throw from logging
62
+ }
63
+ }
64
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../../src/core/hooks/handlers/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,QAAiB;IAC/C,IAAI,OAAO,GAAG,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;IACtC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,OAAO,OAAO,KAAK,IAAI,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;QACvC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC;YACnE,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,KAAK,EAAE,CAAC;IACV,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,WAAmB;IAC/C,OAAO;QACL,WAAW;QACX,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,EAAE,OAAO,CAAC;QACvD,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,EAAE,MAAM,CAAC;QACrD,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,EAAE,aAAa,CAAC;QAC/D,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CACrB,OAAoB,EACpB,OAAe,EACf,OAAe;IAEf,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,SAAS,KAAK,OAAO,KAAK,OAAO,IAAI,CAAC;QAChE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,KAAK,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"completion-validator.d.ts","sourceRoot":"","sources":["../../../../src/core/increment/completion-validator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAMpD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;GAMG;AACH,qBAAa,4BAA4B;IACvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;WACU,kBAAkB,CAC7B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAAO,GAC5D,OAAO,CAAC,gBAAgB,CAAC;IAuM5B;;;;;;;;;;;;;;;OAeG;mBACkB,0BAA0B;IA4G/C;;;;;;;;;;;OAWG;mBACkB,kBAAkB;IAoEvC;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,uBAAuB;IAiDtC;;;;;;;OAOG;WACU,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAa/D;;;;;;;OAOG;WACU,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAarE"}
1
+ {"version":3,"file":"completion-validator.d.ts","sourceRoot":"","sources":["../../../../src/core/increment/completion-validator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAMpD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;GAMG;AACH,qBAAa,4BAA4B;IACvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;WACU,kBAAkB,CAC7B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAAO,GAC5D,OAAO,CAAC,gBAAgB,CAAC;IAuM5B;;;;;;;;;;;;;;;OAeG;mBACkB,0BAA0B;IAgJ/C;;;;;;;;;;;OAWG;mBACkB,kBAAkB;IAoEvC;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,uBAAuB;IAiDtC;;;;;;;OAOG;WACU,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAa/D;;;;;;;OAOG;WACU,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAarE"}
@@ -240,6 +240,7 @@ export class IncrementCompletionValidator {
240
240
  // Check if quality gates are globally disabled via auto config
241
241
  let skipAll = false;
242
242
  let grillRequired = true;
243
+ let codeReviewRequired = true;
243
244
  try {
244
245
  const configPath = path.join(resolveEffectiveRoot(), '.specweave', 'config.json');
245
246
  if (await fs.pathExists(configPath)) {
@@ -250,6 +251,9 @@ export class IncrementCompletionValidator {
250
251
  if (config.grill?.required === false) {
251
252
  grillRequired = false;
252
253
  }
254
+ if (config.codeReview?.required === false) {
255
+ codeReviewRequired = false;
256
+ }
253
257
  }
254
258
  }
255
259
  catch {
@@ -324,6 +328,34 @@ export class IncrementCompletionValidator {
324
328
  warnings.push('Quality gate: judge-llm-report.json not found.\n' +
325
329
  ' Consider running sw:judge-llm for independent validation.');
326
330
  }
331
+ // --- Code-review report validation (v1.0.646) ---
332
+ if (codeReviewRequired) {
333
+ const codeReviewReportPath = path.join(reportsDir, 'code-review-report.json');
334
+ if (!(await fs.pathExists(codeReviewReportPath))) {
335
+ errors.push('Quality gate: code-review-report.json not found.\n' +
336
+ ` Run sw:code-reviewer --increment ${incrementId} before closing, or set "codeReview": { "required": false } in config.json.\n` +
337
+ ` The report is written to .specweave/increments/${incrementId}/reports/code-review-report.json`);
338
+ }
339
+ else {
340
+ try {
341
+ const report = JSON.parse(await fs.readFile(codeReviewReportPath, 'utf-8'));
342
+ const summary = report.summary ?? {};
343
+ const blockingSeverities = ['critical', 'high', 'medium'];
344
+ const blockingCount = blockingSeverities.reduce((acc, sev) => acc + (summary[sev] ?? 0), 0);
345
+ if (blockingCount > 0) {
346
+ errors.push(`Quality gate: code-review report has ${blockingCount} blocking findings ` +
347
+ `(${summary.critical ?? 0} critical, ${summary.high ?? 0} high, ${summary.medium ?? 0} medium).\n` +
348
+ ` Fix issues and re-run sw:code-reviewer --increment ${incrementId}.`);
349
+ }
350
+ }
351
+ catch {
352
+ warnings.push('Quality gate: code-review-report.json exists but could not be parsed.');
353
+ }
354
+ }
355
+ }
356
+ else {
357
+ logger.info('Quality gate reports: code-review report not required (codeReview.required=false)');
358
+ }
327
359
  return { errors, warnings };
328
360
  }
329
361
  /**