@paths.design/caws-cli 9.3.2 → 10.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 (273) hide show
  1. package/README.md +58 -27
  2. package/dist/commands/archive.js +67 -28
  3. package/dist/commands/burnup.js +20 -11
  4. package/dist/commands/diagnose.js +34 -22
  5. package/dist/commands/evaluate.js +27 -15
  6. package/dist/commands/gates.js +122 -0
  7. package/dist/commands/init.js +143 -15
  8. package/dist/commands/iterate.js +77 -4
  9. package/dist/commands/parallel.js +4 -0
  10. package/dist/commands/plan.js +9 -19
  11. package/dist/commands/provenance.js +53 -17
  12. package/dist/commands/quality-monitor.js +64 -45
  13. package/dist/commands/sidecar.js +71 -0
  14. package/dist/commands/specs.js +233 -44
  15. package/dist/commands/status.js +113 -9
  16. package/dist/commands/tutorial.js +10 -9
  17. package/dist/commands/validate.js +49 -6
  18. package/dist/commands/verify-acs.js +35 -78
  19. package/dist/commands/waivers.js +69 -12
  20. package/dist/commands/worktree.js +50 -25
  21. package/dist/error-handler.js +2 -13
  22. package/dist/gates/budget-limit.js +116 -0
  23. package/dist/gates/feedback.js +260 -0
  24. package/dist/gates/format.js +179 -0
  25. package/dist/gates/god-object.js +117 -0
  26. package/dist/gates/pipeline.js +167 -0
  27. package/dist/gates/scope-boundary.js +93 -0
  28. package/dist/gates/spec-completeness.js +102 -0
  29. package/dist/gates/todo-detection.js +205 -0
  30. package/dist/index.js +130 -151
  31. package/dist/parallel/parallel-manager.js +3 -3
  32. package/dist/policy/PolicyManager.js +42 -10
  33. package/dist/scaffold/claude-hooks.js +24 -1
  34. package/dist/scaffold/git-hooks.js +45 -102
  35. package/dist/scaffold/index.js +4 -3
  36. package/dist/session/session-manager.js +71 -14
  37. package/dist/sidecars/index.js +33 -0
  38. package/dist/sidecars/listeners.js +40 -0
  39. package/dist/sidecars/provenance-summary.js +238 -0
  40. package/dist/sidecars/quality-gaps.js +258 -0
  41. package/dist/sidecars/schema.js +149 -0
  42. package/dist/sidecars/spec-drift.js +151 -0
  43. package/dist/sidecars/waiver-draft.js +176 -0
  44. package/dist/templates/.caws/schemas/policy.schema.json +50 -0
  45. package/dist/templates/.caws/schemas/waivers.schema.json +30 -24
  46. package/dist/templates/.caws/schemas/working-spec.schema.json +51 -8
  47. package/dist/templates/.caws/schemas/worktrees.schema.json +3 -1
  48. package/dist/templates/.caws/templates/working-spec.template.yml +7 -3
  49. package/dist/templates/.claude/hooks/audit.sh +0 -0
  50. package/dist/templates/.claude/hooks/block-dangerous.sh +52 -11
  51. package/dist/templates/.claude/hooks/classify_command.py +592 -0
  52. package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
  53. package/dist/templates/.claude/hooks/quality-check.sh +23 -10
  54. package/dist/templates/.claude/hooks/scope-guard.sh +34 -32
  55. package/dist/templates/.claude/hooks/session-caws-status.sh +2 -2
  56. package/dist/templates/.claude/hooks/session-log.sh +76 -3
  57. package/dist/templates/.claude/hooks/stop-worktree-check.sh +1 -1
  58. package/dist/templates/.claude/hooks/test_classify_command.py +370 -0
  59. package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
  60. package/dist/templates/.claude/hooks/worktree-guard.sh +2 -2
  61. package/dist/templates/.claude/hooks/worktree-write-guard.sh +1 -1
  62. package/dist/templates/.claude/settings.json +26 -0
  63. package/dist/templates/.cursor/hooks/caws-quality-check.sh +4 -4
  64. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
  65. package/dist/templates/.cursor/hooks/session-log.sh +924 -0
  66. package/dist/templates/.cursor/hooks.json +25 -0
  67. package/dist/templates/.cursor/rules/02-quality-gates.mdc +3 -5
  68. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
  69. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
  70. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
  71. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
  72. package/dist/templates/.github/copilot-instructions.md +5 -5
  73. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
  74. package/dist/templates/.junie/guidelines.md +2 -2
  75. package/dist/templates/.vscode/settings.json +3 -1
  76. package/dist/templates/.windsurf/rules/caws-quality-standards.md +2 -2
  77. package/dist/templates/.windsurf/workflows/caws-guided-development.md +3 -3
  78. package/dist/templates/CLAUDE.md +43 -8
  79. package/dist/templates/agents.md +29 -9
  80. package/dist/templates/docs/README.md +8 -7
  81. package/dist/templates/scripts/new_feature.sh +80 -0
  82. package/dist/test-analysis.js +43 -30
  83. package/dist/tool-loader.js +1 -1
  84. package/dist/utils/agent-session.js +202 -0
  85. package/dist/utils/detection.js +8 -2
  86. package/dist/utils/finalization.js +7 -6
  87. package/dist/utils/gitignore-updater.js +3 -0
  88. package/dist/utils/lifecycle-events.js +94 -0
  89. package/dist/utils/quality-gates-utils.js +29 -44
  90. package/dist/utils/schema-validator.js +42 -0
  91. package/dist/utils/spec-resolver.js +93 -21
  92. package/dist/utils/working-state.js +505 -0
  93. package/dist/validation/spec-validation.js +92 -22
  94. package/dist/waivers-manager.js +60 -6
  95. package/dist/worktree/worktree-manager.js +390 -93
  96. package/package.json +6 -6
  97. package/templates/.caws/schemas/policy.schema.json +50 -0
  98. package/templates/.caws/schemas/waivers.schema.json +30 -24
  99. package/templates/.caws/schemas/working-spec.schema.json +51 -8
  100. package/templates/.caws/schemas/worktrees.schema.json +3 -1
  101. package/templates/.caws/templates/working-spec.template.yml +7 -3
  102. package/templates/.claude/hooks/block-dangerous.sh +52 -11
  103. package/templates/.claude/hooks/classify_command.py +592 -0
  104. package/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
  105. package/templates/.claude/hooks/quality-check.sh +23 -10
  106. package/templates/.claude/hooks/scope-guard.sh +34 -32
  107. package/templates/.claude/hooks/session-caws-status.sh +2 -2
  108. package/templates/.claude/hooks/session-log.sh +76 -3
  109. package/templates/.claude/hooks/stop-worktree-check.sh +1 -1
  110. package/templates/.claude/hooks/test_classify_command.py +370 -0
  111. package/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
  112. package/templates/.claude/hooks/worktree-guard.sh +2 -2
  113. package/templates/.claude/hooks/worktree-write-guard.sh +1 -1
  114. package/templates/.claude/settings.json +26 -0
  115. package/templates/.cursor/hooks/caws-quality-check.sh +4 -4
  116. package/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
  117. package/templates/.cursor/hooks/session-log.sh +924 -0
  118. package/templates/.cursor/hooks.json +25 -0
  119. package/templates/.cursor/rules/02-quality-gates.mdc +3 -5
  120. package/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
  121. package/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
  122. package/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
  123. package/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
  124. package/templates/.github/copilot-instructions.md +5 -5
  125. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
  126. package/templates/.junie/guidelines.md +2 -2
  127. package/templates/.vscode/settings.json +3 -1
  128. package/templates/.windsurf/rules/caws-quality-standards.md +2 -2
  129. package/templates/.windsurf/workflows/caws-guided-development.md +3 -3
  130. package/templates/CLAUDE.md +43 -8
  131. package/templates/{AGENTS.md → agents.md} +29 -9
  132. package/templates/docs/README.md +8 -7
  133. package/templates/scripts/new_feature.sh +80 -0
  134. package/dist/budget-derivation.d.ts +0 -74
  135. package/dist/budget-derivation.d.ts.map +0 -1
  136. package/dist/cicd-optimizer.d.ts +0 -142
  137. package/dist/cicd-optimizer.d.ts.map +0 -1
  138. package/dist/commands/archive.d.ts +0 -51
  139. package/dist/commands/archive.d.ts.map +0 -1
  140. package/dist/commands/burnup.d.ts +0 -6
  141. package/dist/commands/burnup.d.ts.map +0 -1
  142. package/dist/commands/diagnose.d.ts +0 -52
  143. package/dist/commands/diagnose.d.ts.map +0 -1
  144. package/dist/commands/evaluate.d.ts +0 -8
  145. package/dist/commands/evaluate.d.ts.map +0 -1
  146. package/dist/commands/init.d.ts +0 -5
  147. package/dist/commands/init.d.ts.map +0 -1
  148. package/dist/commands/iterate.d.ts +0 -8
  149. package/dist/commands/iterate.d.ts.map +0 -1
  150. package/dist/commands/mode.d.ts +0 -25
  151. package/dist/commands/mode.d.ts.map +0 -1
  152. package/dist/commands/parallel.d.ts +0 -7
  153. package/dist/commands/parallel.d.ts.map +0 -1
  154. package/dist/commands/plan.d.ts +0 -49
  155. package/dist/commands/plan.d.ts.map +0 -1
  156. package/dist/commands/provenance.d.ts +0 -32
  157. package/dist/commands/provenance.d.ts.map +0 -1
  158. package/dist/commands/quality-gates.d.ts +0 -6
  159. package/dist/commands/quality-gates.d.ts.map +0 -1
  160. package/dist/commands/quality-gates.js +0 -444
  161. package/dist/commands/quality-monitor.d.ts +0 -17
  162. package/dist/commands/quality-monitor.d.ts.map +0 -1
  163. package/dist/commands/session.d.ts +0 -7
  164. package/dist/commands/session.d.ts.map +0 -1
  165. package/dist/commands/specs.d.ts +0 -77
  166. package/dist/commands/specs.d.ts.map +0 -1
  167. package/dist/commands/status.d.ts +0 -44
  168. package/dist/commands/status.d.ts.map +0 -1
  169. package/dist/commands/templates.d.ts +0 -74
  170. package/dist/commands/templates.d.ts.map +0 -1
  171. package/dist/commands/tool.d.ts +0 -13
  172. package/dist/commands/tool.d.ts.map +0 -1
  173. package/dist/commands/troubleshoot.d.ts +0 -8
  174. package/dist/commands/troubleshoot.d.ts.map +0 -1
  175. package/dist/commands/troubleshoot.js +0 -104
  176. package/dist/commands/tutorial.d.ts +0 -55
  177. package/dist/commands/tutorial.d.ts.map +0 -1
  178. package/dist/commands/validate.d.ts +0 -15
  179. package/dist/commands/validate.d.ts.map +0 -1
  180. package/dist/commands/waivers.d.ts +0 -8
  181. package/dist/commands/waivers.d.ts.map +0 -1
  182. package/dist/commands/workflow.d.ts +0 -85
  183. package/dist/commands/workflow.d.ts.map +0 -1
  184. package/dist/commands/worktree.d.ts +0 -7
  185. package/dist/commands/worktree.d.ts.map +0 -1
  186. package/dist/config/index.d.ts +0 -29
  187. package/dist/config/index.d.ts.map +0 -1
  188. package/dist/config/lite-scope.d.ts +0 -33
  189. package/dist/config/lite-scope.d.ts.map +0 -1
  190. package/dist/config/modes.d.ts +0 -264
  191. package/dist/config/modes.d.ts.map +0 -1
  192. package/dist/constants/spec-types.d.ts +0 -93
  193. package/dist/constants/spec-types.d.ts.map +0 -1
  194. package/dist/error-handler.d.ts +0 -151
  195. package/dist/error-handler.d.ts.map +0 -1
  196. package/dist/generators/jest-config-generator.d.ts +0 -32
  197. package/dist/generators/jest-config-generator.d.ts.map +0 -1
  198. package/dist/generators/jest-config.d.ts +0 -32
  199. package/dist/generators/jest-config.d.ts.map +0 -1
  200. package/dist/generators/jest-config.js +0 -242
  201. package/dist/generators/working-spec.d.ts +0 -13
  202. package/dist/generators/working-spec.d.ts.map +0 -1
  203. package/dist/index-new.d.ts +0 -5
  204. package/dist/index-new.d.ts.map +0 -1
  205. package/dist/index-new.js +0 -317
  206. package/dist/index.d.ts +0 -5
  207. package/dist/index.d.ts.map +0 -1
  208. package/dist/index.js.backup +0 -4711
  209. package/dist/minimal-cli.d.ts +0 -3
  210. package/dist/minimal-cli.d.ts.map +0 -1
  211. package/dist/parallel/parallel-manager.d.ts +0 -67
  212. package/dist/parallel/parallel-manager.d.ts.map +0 -1
  213. package/dist/policy/PolicyManager.d.ts +0 -104
  214. package/dist/policy/PolicyManager.d.ts.map +0 -1
  215. package/dist/scaffold/claude-hooks.d.ts +0 -28
  216. package/dist/scaffold/claude-hooks.d.ts.map +0 -1
  217. package/dist/scaffold/cursor-hooks.d.ts +0 -7
  218. package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
  219. package/dist/scaffold/git-hooks.d.ts +0 -38
  220. package/dist/scaffold/git-hooks.d.ts.map +0 -1
  221. package/dist/scaffold/index.d.ts +0 -17
  222. package/dist/scaffold/index.d.ts.map +0 -1
  223. package/dist/session/session-manager.d.ts +0 -94
  224. package/dist/session/session-manager.d.ts.map +0 -1
  225. package/dist/spec/SpecFileManager.d.ts +0 -146
  226. package/dist/spec/SpecFileManager.d.ts.map +0 -1
  227. package/dist/templates/.cursor/hooks/caws-tool-validation.sh +0 -121
  228. package/dist/templates/.github/copilot/instructions.md +0 -311
  229. package/dist/test-analysis.d.ts +0 -231
  230. package/dist/test-analysis.d.ts.map +0 -1
  231. package/dist/tool-interface.d.ts +0 -236
  232. package/dist/tool-interface.d.ts.map +0 -1
  233. package/dist/tool-loader.d.ts +0 -77
  234. package/dist/tool-loader.d.ts.map +0 -1
  235. package/dist/tool-validator.d.ts +0 -72
  236. package/dist/tool-validator.d.ts.map +0 -1
  237. package/dist/utils/async-utils.d.ts +0 -73
  238. package/dist/utils/async-utils.d.ts.map +0 -1
  239. package/dist/utils/command-wrapper.d.ts +0 -66
  240. package/dist/utils/command-wrapper.d.ts.map +0 -1
  241. package/dist/utils/detection.d.ts +0 -14
  242. package/dist/utils/detection.d.ts.map +0 -1
  243. package/dist/utils/error-categories.d.ts +0 -52
  244. package/dist/utils/error-categories.d.ts.map +0 -1
  245. package/dist/utils/finalization.d.ts +0 -17
  246. package/dist/utils/finalization.d.ts.map +0 -1
  247. package/dist/utils/git-lock.d.ts +0 -13
  248. package/dist/utils/git-lock.d.ts.map +0 -1
  249. package/dist/utils/gitignore-updater.d.ts +0 -39
  250. package/dist/utils/gitignore-updater.d.ts.map +0 -1
  251. package/dist/utils/ide-detection.d.ts +0 -89
  252. package/dist/utils/ide-detection.d.ts.map +0 -1
  253. package/dist/utils/project-analysis.d.ts +0 -34
  254. package/dist/utils/project-analysis.d.ts.map +0 -1
  255. package/dist/utils/promise-utils.d.ts +0 -30
  256. package/dist/utils/promise-utils.d.ts.map +0 -1
  257. package/dist/utils/quality-gates-utils.d.ts +0 -49
  258. package/dist/utils/quality-gates-utils.d.ts.map +0 -1
  259. package/dist/utils/quality-gates.d.ts +0 -49
  260. package/dist/utils/quality-gates.d.ts.map +0 -1
  261. package/dist/utils/quality-gates.js +0 -402
  262. package/dist/utils/spec-resolver.d.ts +0 -80
  263. package/dist/utils/spec-resolver.d.ts.map +0 -1
  264. package/dist/utils/typescript-detector.d.ts +0 -66
  265. package/dist/utils/typescript-detector.d.ts.map +0 -1
  266. package/dist/utils/yaml-validation.d.ts +0 -32
  267. package/dist/utils/yaml-validation.d.ts.map +0 -1
  268. package/dist/validation/spec-validation.d.ts +0 -43
  269. package/dist/validation/spec-validation.d.ts.map +0 -1
  270. package/dist/waivers-manager.d.ts +0 -167
  271. package/dist/waivers-manager.d.ts.map +0 -1
  272. package/dist/worktree/worktree-manager.d.ts +0 -54
  273. package/dist/worktree/worktree-manager.d.ts.map +0 -1
@@ -8,6 +8,7 @@
8
8
  const fs = require('fs-extra');
9
9
  const path = require('path');
10
10
  const yaml = require('js-yaml');
11
+ const { createValidator, getSchemaPath } = require('../utils/schema-validator');
11
12
 
12
13
  /**
13
14
  * Policy Manager - Handles policy loading with intelligent caching
@@ -86,6 +87,34 @@ class PolicyManager {
86
87
  }
87
88
 
88
89
  if (policyPath && policyContent) {
90
+ // Schema validation (warn and fall back to defaults on failure)
91
+ try {
92
+ const schemaFilePath = getSchemaPath('policy.schema.json', projectRoot);
93
+ const validate = createValidator(schemaFilePath);
94
+ const schemaResult = validate(policyContent);
95
+ if (!schemaResult.valid) {
96
+ console.warn('Policy has schema violations:', schemaResult.errors);
97
+ console.warn('Falling back to default policy');
98
+ const defaultPolicy = this.getDefaultPolicy();
99
+ if (this.enableCaching) {
100
+ this.policyCache.set(projectRoot, {
101
+ policy: defaultPolicy,
102
+ cachedAt: Date.now(),
103
+ ttl: cacheTTL,
104
+ });
105
+ }
106
+ return {
107
+ ...defaultPolicy,
108
+ _isDefault: true,
109
+ _schemaErrors: schemaResult.errors,
110
+ _cacheHit: false,
111
+ _loadDuration: Date.now() - startTime,
112
+ };
113
+ }
114
+ } catch (schemaErr) {
115
+ console.warn('Could not validate policy schema:', schemaErr.message);
116
+ }
117
+
89
118
  // Validate policy structure
90
119
  this.validatePolicy(policyContent);
91
120
 
@@ -299,27 +328,30 @@ class PolicyManager {
299
328
  gates: {
300
329
  budget_limit: {
301
330
  enabled: true,
331
+ mode: 'block',
302
332
  description: 'Enforce change budget limits',
303
333
  },
304
334
  spec_completeness: {
305
335
  enabled: true,
336
+ mode: 'block',
306
337
  description: 'Require complete working specifications',
307
338
  },
308
- contract_compliance: {
309
- enabled: true,
310
- description: 'Validate API contracts',
311
- },
312
- coverage_threshold: {
339
+ scope_boundary: {
313
340
  enabled: true,
314
- description: 'Maintain test coverage requirements',
341
+ mode: 'block',
342
+ description: 'Enforce spec scope boundaries',
315
343
  },
316
- mutation_threshold: {
344
+ god_object: {
317
345
  enabled: true,
318
- description: 'Require mutation testing for T1/T2 changes',
346
+ mode: 'warn',
347
+ thresholds: { warning: 1750, critical: 2000 },
348
+ description: 'Detect oversized source files',
319
349
  },
320
- security_scan: {
350
+ todo_detection: {
321
351
  enabled: true,
322
- description: 'Run security vulnerability scans',
352
+ mode: 'warn',
353
+ thresholds: { min_confidence: 0.8 },
354
+ description: 'Scan for TODO/FIXME/HACK/XXX markers',
323
355
  },
324
356
  },
325
357
  };
@@ -54,7 +54,7 @@ async function scaffoldClaudeHooks(projectDir, levels = ['safety', 'quality', 's
54
54
  // Map levels to hook scripts
55
55
  const hookMapping = {
56
56
  safety: ['block-dangerous.sh', 'scan-secrets.sh', 'worktree-guard.sh', 'worktree-write-guard.sh', 'stop-worktree-check.sh', 'session-caws-status.sh'],
57
- quality: ['quality-check.sh', 'validate-spec.sh'],
57
+ quality: ['quality-check.sh', 'validate-spec.sh', 'doc-frontmatter-check.sh'],
58
58
  scope: ['scope-guard.sh', 'naming-check.sh'],
59
59
  audit: ['audit.sh', 'session-log.sh'],
60
60
  lite: ['block-dangerous.sh', 'scope-guard.sh', 'lite-sprawl-check.sh', 'simplification-guard.sh'],
@@ -81,6 +81,7 @@ async function scaffoldClaudeHooks(projectDir, levels = ['safety', 'quality', 's
81
81
  'block-dangerous.sh',
82
82
  'scope-guard.sh',
83
83
  'naming-check.sh',
84
+ 'doc-frontmatter-check.sh',
84
85
  'lite-sprawl-check.sh',
85
86
  'simplification-guard.sh',
86
87
  'worktree-guard.sh',
@@ -90,6 +91,23 @@ async function scaffoldClaudeHooks(projectDir, levels = ['safety', 'quality', 's
90
91
  'session-log.sh',
91
92
  ];
92
93
 
94
+ // Copy supporting scripts (not hooks themselves, but used by hooks)
95
+ const supportingScripts = [
96
+ 'classify_command.py',
97
+ 'test_classify_command.py',
98
+ 'test_wrapper_smoke.sh',
99
+ ];
100
+
101
+ for (const script of supportingScripts) {
102
+ const sourcePath = path.join(claudeHooksTemplateDir, script);
103
+ const destPath = path.join(claudeHooksDir, script);
104
+
105
+ if (fs.existsSync(sourcePath)) {
106
+ await fs.copy(sourcePath, destPath);
107
+ await fs.chmod(destPath, 0o755);
108
+ }
109
+ }
110
+
93
111
  for (const script of allHookScripts) {
94
112
  if (enabledHooks.has(script)) {
95
113
  const sourcePath = path.join(claudeHooksTemplateDir, script);
@@ -254,6 +272,11 @@ function generateClaudeSettings(levels, _enabledHooks) {
254
272
  command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/validate-spec.sh',
255
273
  timeout: 15,
256
274
  },
275
+ {
276
+ type: 'command',
277
+ command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/doc-frontmatter-check.sh',
278
+ timeout: 10,
279
+ },
257
280
  ],
258
281
  });
259
282
  }
@@ -279,7 +279,7 @@ if [ -d ".caws" ]; then
279
279
  var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
280
280
  var wts = Object.values(reg.worktrees || {});
281
281
  var active = wts.filter(function(w) {
282
- return w.status === 'active' && w.baseBranch === '$CURRENT_BRANCH';
282
+ return (w.status === 'active' || w.status === 'fresh' || w.status === 'merged') && w.baseBranch === '$CURRENT_BRANCH';
283
283
  });
284
284
  console.log(active.length + ':' + active.map(function(w) { return w.name; }).join(','));
285
285
  } catch(e) { console.log('0:'); }
@@ -338,7 +338,7 @@ if [ -d ".caws" ]; then
338
338
  HAS_ACTIVE_WT=$(node -e "
339
339
  try {
340
340
  var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
341
- var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active'; });
341
+ var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active' || w.status === 'fresh' || w.status === 'merged'; });
342
342
  console.log(active.length > 0 ? 'yes' : 'no');
343
343
  } catch(e) { console.log('no'); }
344
344
  " 2>/dev/null)
@@ -358,110 +358,42 @@ if [ -d ".caws" ]; then
358
358
  fi
359
359
  # ===== End Multi-Agent Safety Guard =====
360
360
 
361
- # Fallback chain for quality gates:
362
- # 1. Try Node.js script (if exists)
363
- # 2. Try CAWS CLI
364
- # 3. Try Makefile target
365
- # 4. Try Python scripts
366
- # 5. Skip gracefully (warn only)
367
-
361
+ # Run CAWS quality gates
368
362
  QUALITY_GATES_RAN=false
363
+ QUALITY_GATES_WARNED=false
369
364
 
370
- # Option 1: Quality gates package (installed via npm)
371
- if [ -f "node_modules/@paths.design/quality-gates/run-quality-gates.mjs" ]; then
372
- if command -v node >/dev/null 2>&1; then
373
- echo "Running quality gates package..."
374
- if CI= node node_modules/@paths.design/quality-gates/run-quality-gates.mjs --context=commit; then
375
- echo "Quality gates passed"
376
- QUALITY_GATES_RAN=true
377
- else
378
- echo "Quality gates failed - commit blocked"
379
- echo "Fix the violations above before committing"
380
- exit 1
381
- fi
382
- fi
383
- # Option 1b: Quality gates package (monorepo/local copy)
384
- elif [ -f "node_modules/@caws/quality-gates/run-quality-gates.mjs" ]; then
385
- if command -v node >/dev/null 2>&1; then
386
- echo "Running quality gates package (local)..."
387
- if CI= node node_modules/@caws/quality-gates/run-quality-gates.mjs --context=commit; then
388
- echo "Quality gates passed"
389
- QUALITY_GATES_RAN=true
390
- else
391
- echo "Quality gates failed - commit blocked"
392
- echo "Fix the violations above before committing"
393
- exit 1
394
- fi
395
- fi
396
- # Option 2: Legacy Node.js quality gates script (deprecated)
397
- elif [ -f "scripts/quality-gates/run-quality-gates.js" ]; then
398
- if command -v node >/dev/null 2>&1; then
399
- echo "Running legacy Node.js quality gates script..."
400
- if node scripts/quality-gates/run-quality-gates.js; then
401
- echo "Quality gates passed"
402
- QUALITY_GATES_RAN=true
403
- else
404
- echo "Quality gates failed - commit blocked"
405
- echo "Fix the violations above before committing"
406
- exit 1
407
- fi
408
- fi
409
- # Option 3: CAWS CLI validation
410
- elif command -v caws >/dev/null 2>&1; then
411
- # In a worktree, validate only the associated spec to avoid false positives
412
- CAWS_VALIDATE_ARGS="--quiet"
413
- WORKTREE_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
414
- if [ -f ".caws/worktrees.json" ] && command -v node >/dev/null 2>&1; then
415
- SPEC_ID=$(node -e "
416
- try {
417
- var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
418
- var wt = Object.values(reg.worktrees || {}).find(function(w) {
419
- return w.branch === '$WORKTREE_BRANCH';
420
- });
421
- if (wt && wt.specId) console.log(wt.specId);
422
- } catch(e) {}
423
- " 2>/dev/null || echo "")
424
- if [ -n "$SPEC_ID" ]; then
425
- CAWS_VALIDATE_ARGS="--quiet --spec-id $SPEC_ID"
426
- fi
427
- fi
428
- echo "Running CAWS CLI validation..."
429
- if caws validate $CAWS_VALIDATE_ARGS 2>/dev/null; then
430
- echo "CAWS validation passed"
431
- QUALITY_GATES_RAN=true
432
- else
433
- echo "CAWS validation failed, but allowing commit (non-blocking)"
434
- echo "Run 'caws validate' for details"
435
- QUALITY_GATES_RAN=true
436
- fi
437
- # Option 3: Makefile target
438
- elif [ -f "Makefile" ] && grep -q "caws-validate\\|caws-gates" Makefile; then
439
- echo "Running Makefile quality gates..."
440
- if make caws-validate >/dev/null 2>&1 || make caws-gates >/dev/null 2>&1; then
441
- echo "Makefile quality gates passed"
442
- QUALITY_GATES_RAN=true
443
- else
444
- echo "Makefile quality gates failed, but allowing commit (non-blocking)"
445
- QUALITY_GATES_RAN=true
446
- fi
447
- # Option 4: Python scripts
448
- elif [ -f "scripts/simple_gates.py" ] && command -v python3 >/dev/null 2>&1; then
449
- echo "Running Python quality gates script..."
450
- if python3 scripts/simple_gates.py all --tier 2 --profile backend-api >/dev/null 2>&1; then
451
- echo "Python quality gates passed"
365
+ # Resolve spec ID from worktree context if available
366
+ SPEC_ID=""
367
+ WORKTREE_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
368
+ if [ -f ".caws/worktrees.json" ] && command -v node >/dev/null 2>&1; then
369
+ SPEC_ID=$(node -e "
370
+ try {
371
+ var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
372
+ var wt = Object.values(reg.worktrees || {}).find(function(w) {
373
+ return w.branch === '$WORKTREE_BRANCH';
374
+ });
375
+ if (wt && wt.specId) console.log(wt.specId);
376
+ } catch(e) {}
377
+ " 2>/dev/null || echo "")
378
+ fi
379
+
380
+ GATES_ARGS="--context=commit --quiet"
381
+ if [ -n "$SPEC_ID" ]; then
382
+ GATES_ARGS="$GATES_ARGS --spec-id $SPEC_ID"
383
+ fi
384
+
385
+ if command -v caws >/dev/null 2>&1; then
386
+ if caws gates run $GATES_ARGS; then
387
+ echo "Quality gates passed"
452
388
  QUALITY_GATES_RAN=true
453
389
  else
454
- echo "Python quality gates failed, but allowing commit (non-blocking)"
455
- QUALITY_GATES_RAN=true
390
+ GATE_EXIT=$?
391
+ echo "Quality gates BLOCKED the commit (exit code $GATE_EXIT)"
392
+ exit 1
456
393
  fi
457
- # Option 5: Skip gracefully
458
394
  else
459
- echo "Quality gates not available - skipping"
460
- echo "Available options:"
461
- echo " - Install quality gates: npm install --save-dev @paths.design/quality-gates"
462
- echo " - Install CAWS CLI: npm install -g @paths.design/caws-cli"
463
- echo " - Use Python: python3 scripts/simple_gates.py"
464
- echo " - Use Makefile: make caws-gates"
395
+ echo "CAWS CLI not available skipping quality gates"
396
+ echo "Install with: npm install -g @paths.design/caws-cli"
465
397
  QUALITY_GATES_RAN=true
466
398
  fi
467
399
 
@@ -534,7 +466,11 @@ ${todoSuggestion
534
466
  fi
535
467
  fi
536
468
 
537
- echo "All quality checks passed - proceeding with commit"
469
+ if [ "$QUALITY_GATES_WARNED" = true ]; then
470
+ echo "Proceeding with commit (some quality checks had warnings)"
471
+ else
472
+ echo "All quality checks passed - proceeding with commit"
473
+ fi
538
474
  exit 0
539
475
  `;
540
476
  }
@@ -554,6 +490,13 @@ function generatePostCommitHook() {
554
490
  exit 0
555
491
  fi
556
492
 
493
+ # Skip during merge operations — caws worktree merge triggers a merge
494
+ # commit, and writing to .caws/provenance/chain.json here would dirty
495
+ # the tree mid-merge, blocking subsequent operations.
496
+ if [ -f ".git/MERGE_HEAD" ] || [ -f "$(git rev-parse --git-dir 2>/dev/null)/MERGE_HEAD" ]; then
497
+ exit 0
498
+ fi
499
+
557
500
  # Get the current commit hash
558
501
  COMMIT_HASH=$(git rev-parse HEAD)
559
502
 
@@ -828,7 +771,7 @@ if [ -f "$CAWS_ROOT/.caws/worktrees.json" ] && command -v node >/dev/null 2>&1;
828
771
  try {
829
772
  var reg = JSON.parse(require('fs').readFileSync('$CAWS_ROOT/.caws/worktrees.json', 'utf8'));
830
773
  var active = Object.values(reg.worktrees || {}).filter(function(w) {
831
- return w.status === 'active' && w.baseBranch === '$CURRENT_BRANCH';
774
+ return (w.status === 'active' || w.status === 'fresh' || w.status === 'merged') && w.baseBranch === '$CURRENT_BRANCH';
832
775
  });
833
776
  console.log(active.length);
834
777
  } catch(e) { console.log('0'); }
@@ -728,11 +728,12 @@ async function scaffoldProject(options) {
728
728
  } else {
729
729
  console.log('2. Run: caws validate (verify setup)');
730
730
  console.log('3. Run: caws diagnose (check project health)');
731
- console.log('4. Customize .caws/working-spec.yaml for your project');
732
- console.log('5. Optional: Create .caws/policy.yaml for tier-specific budgets');
731
+ console.log('4. Customize .caws/specs/<spec-id>.yaml for your project');
732
+ console.log('5. Treat .caws/working-spec.yaml as a compatibility mirror');
733
+ console.log('6. Optional: Create .caws/policy.yaml for tier-specific budgets');
733
734
  if (!qualityGatesAdded && !options.minimal) {
734
735
  console.log(
735
- chalk.gray('6. Note: Quality gates scripts skipped (git hooks use CAWS CLI by default)')
736
+ chalk.gray('7. Note: Quality gates scripts skipped (git hooks use CAWS CLI by default)')
736
737
  );
737
738
  console.log(
738
739
  chalk.gray(
@@ -11,6 +11,8 @@ const fs = require('fs-extra');
11
11
  const path = require('path');
12
12
  const crypto = require('crypto');
13
13
 
14
+ const { mergeFilesTouched } = require('../utils/working-state');
15
+
14
16
  const SESSIONS_DIR = '.caws/sessions';
15
17
  const REGISTRY_FILE = '.caws/sessions.json';
16
18
  const CAPSULE_SCHEMA_VERSION = 'caws.capsule.v1';
@@ -79,16 +81,62 @@ function getWorkspaceFingerprint(cwd) {
79
81
  }
80
82
 
81
83
  /**
82
- * Get project name from working spec or directory
84
+ * Load the best available spec synchronously (feature specs first, then legacy).
85
+ * @param {string} root - Repository root
86
+ * @param {string} [specId] - Optional specific spec ID
87
+ * @returns {object|null} Parsed spec object or null
88
+ */
89
+ function loadBestSpecSync(root, specId) {
90
+ const yaml = require('js-yaml');
91
+
92
+ // If a specific spec ID is requested, load it directly
93
+ if (specId) {
94
+ for (const ext of ['.yaml', '.yml']) {
95
+ const p = path.join(root, '.caws/specs', `${specId}${ext}`);
96
+ if (fs.existsSync(p)) {
97
+ try { return yaml.load(fs.readFileSync(p, 'utf8')); } catch { /* skip */ }
98
+ }
99
+ }
100
+ }
101
+
102
+ // Check registry for active feature specs
103
+ const registryPath = path.join(root, '.caws/specs/registry.json');
104
+ if (fs.existsSync(registryPath)) {
105
+ try {
106
+ const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
107
+ const specs = registry.specs || {};
108
+ const activeIds = Object.keys(specs).filter(
109
+ id => specs[id].status !== 'closed' && specs[id].status !== 'archived'
110
+ );
111
+ for (const id of activeIds) {
112
+ const specEntry = specs[id];
113
+ const p = path.join(root, '.caws/specs', specEntry.path || `${id}.yaml`);
114
+ if (fs.existsSync(p)) {
115
+ try { return yaml.load(fs.readFileSync(p, 'utf8')); } catch { /* skip */ }
116
+ }
117
+ }
118
+ } catch { /* fall through */ }
119
+ }
120
+
121
+ // Legacy fallback
122
+ const legacyPath = path.join(root, '.caws/working-spec.yaml');
123
+ if (fs.existsSync(legacyPath)) {
124
+ try { return yaml.load(fs.readFileSync(legacyPath, 'utf8')); } catch { /* skip */ }
125
+ }
126
+
127
+ return null;
128
+ }
129
+
130
+ /**
131
+ * Get project name from best available spec or directory
83
132
  * @param {string} root - Repository root
133
+ * @param {string} [specId] - Optional specific spec ID
84
134
  * @returns {string}
85
135
  */
86
- function getProjectName(root) {
136
+ function getProjectName(root, specId) {
87
137
  try {
88
- const yaml = require('js-yaml');
89
- const specPath = path.join(root, '.caws/working-spec.yaml');
90
- if (fs.existsSync(specPath)) {
91
- const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
138
+ const spec = loadBestSpecSync(root, specId);
139
+ if (spec) {
92
140
  return spec.title || spec.id || path.basename(root);
93
141
  }
94
142
  } catch {
@@ -98,16 +146,15 @@ function getProjectName(root) {
98
146
  }
99
147
 
100
148
  /**
101
- * Get skein ID from working spec
149
+ * Get skein ID from best available spec
102
150
  * @param {string} root - Repository root
151
+ * @param {string} [specId] - Optional specific spec ID
103
152
  * @returns {string}
104
153
  */
105
- function getSkeinId(root) {
154
+ function getSkeinId(root, specId) {
106
155
  try {
107
- const yaml = require('js-yaml');
108
- const specPath = path.join(root, '.caws/working-spec.yaml');
109
- if (fs.existsSync(specPath)) {
110
- const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
156
+ const spec = loadBestSpecSync(root, specId);
157
+ if (spec) {
111
158
  return spec.id || 'unknown';
112
159
  }
113
160
  } catch {
@@ -201,8 +248,8 @@ function startSession(options = {}) {
201
248
 
202
249
  const capsule = {
203
250
  schema: CAPSULE_SCHEMA_VERSION,
204
- project: getProjectName(root),
205
- skein_id: getSkeinId(root),
251
+ project: getProjectName(root, specId),
252
+ skein_id: getSkeinId(root, specId),
206
253
  session_id: sessionId,
207
254
  role,
208
255
  spec_id: specId || null,
@@ -318,6 +365,11 @@ function checkpointSession(data = {}) {
318
365
  // Write updated capsule
319
366
  fs.writeFileSync(capsulePath, JSON.stringify(capsule, null, 2));
320
367
 
368
+ // Bridge to working state (per-spec)
369
+ if (capsule.spec_id && capsule.work_summary.paths_touched.length > 0) {
370
+ try { mergeFilesTouched(capsule.spec_id, capsule.work_summary.paths_touched, root); } catch { /* non-fatal */ }
371
+ }
372
+
321
373
  return capsule;
322
374
  }
323
375
 
@@ -385,6 +437,11 @@ function endSession(data = {}) {
385
437
  // Write finalized capsule
386
438
  fs.writeFileSync(capsulePath, JSON.stringify(capsule, null, 2));
387
439
 
440
+ // Bridge to working state (per-spec)
441
+ if (capsule.spec_id && capsule.work_summary.paths_touched.length > 0) {
442
+ try { mergeFilesTouched(capsule.spec_id, capsule.work_summary.paths_touched, root); } catch { /* non-fatal */ }
443
+ }
444
+
388
445
  // Update registry
389
446
  registry.sessions[sessionId].status = 'completed';
390
447
  registry.sessions[sessionId].ended_at = capsule.ended_at;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @fileoverview Sidecar Registry
3
+ * Central registry of all bounded governance sidecars.
4
+ * @author @darianrosebrook
5
+ */
6
+
7
+ const { analyzeSpecDrift } = require('./spec-drift');
8
+ const { diagnoseQualityGaps } = require('./quality-gaps');
9
+ const { draftWaiver } = require('./waiver-draft');
10
+ const { summarizeProvenance } = require('./provenance-summary');
11
+ const { createSidecarOutput, createNoStateOutput, formatSidecarText } = require('./schema');
12
+
13
+ /**
14
+ * Registry of available sidecars.
15
+ * Each entry has a function and a short description for help text.
16
+ */
17
+ const SIDECARS = {
18
+ drift: { fn: analyzeSpecDrift, description: 'Analyze spec drift vs implementation evidence' },
19
+ gaps: { fn: diagnoseQualityGaps, description: 'Diagnose quality gaps preventing phase advancement' },
20
+ 'waiver-draft': { fn: draftWaiver, description: 'Generate pre-filled waiver templates from gate failures' },
21
+ provenance: { fn: summarizeProvenance, description: 'Summarize work provenance for merge readiness' },
22
+ };
23
+
24
+ module.exports = {
25
+ SIDECARS,
26
+ analyzeSpecDrift,
27
+ diagnoseQualityGaps,
28
+ draftWaiver,
29
+ summarizeProvenance,
30
+ createSidecarOutput,
31
+ createNoStateOutput,
32
+ formatSidecarText,
33
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @fileoverview Sidecar Lifecycle Listeners
3
+ * Registers lightweight event handlers that print hints to run sidecars
4
+ * at governance-significant moments. Non-blocking, non-fatal.
5
+ * @author @darianrosebrook
6
+ */
7
+
8
+ const { lifecycle, EVENTS } = require('../utils/lifecycle-events');
9
+
10
+ /**
11
+ * Register sidecar hint listeners on the lifecycle emitter.
12
+ * Call once during CLI startup.
13
+ */
14
+ function registerSidecarListeners() {
15
+ // On gate blocked → suggest waiver draft
16
+ lifecycle.on(EVENTS.GATES_BLOCKED, (payload) => {
17
+ try {
18
+ if (process.env.CAWS_QUIET === '1') return;
19
+ const specFlag = payload.specId ? ` --spec-id ${payload.specId}` : '';
20
+ const gateFlag = payload.gateName ? ` --gate ${payload.gateName}` : '';
21
+ console.log(
22
+ `\n [Sidecar] Waiver draft available: caws sidecar waiver-draft${specFlag}${gateFlag}`
23
+ );
24
+ } catch { /* non-fatal */ }
25
+ });
26
+
27
+ // On phase transition (non-complete) → suggest quality gap analysis
28
+ lifecycle.on(EVENTS.PHASE_TRANSITION, (payload) => {
29
+ try {
30
+ if (process.env.CAWS_QUIET === '1') return;
31
+ if (payload.newPhase === 'complete') return;
32
+ const specFlag = payload.specId ? ` --spec-id ${payload.specId}` : '';
33
+ console.log(
34
+ `\n [Sidecar] Phase changed to ${payload.newPhase}. Run: caws sidecar gaps${specFlag}`
35
+ );
36
+ } catch { /* non-fatal */ }
37
+ });
38
+ }
39
+
40
+ module.exports = { registerSidecarListeners };