@paths.design/caws-cli 9.3.1 → 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 +104 -26
  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 +136 -150
  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 +36 -23
  61. package/dist/templates/.claude/hooks/worktree-write-guard.sh +6 -5
  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 +496 -95
  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 +36 -23
  113. package/templates/.claude/hooks/worktree-write-guard.sh +6 -5
  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
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @fileoverview Unified quality gates CLI command
3
+ * Delegates to the v2 gate pipeline (src/gates/pipeline.js) for evaluation
4
+ * and formatting (src/gates/format.js) for output.
5
+ * @author @darianrosebrook
6
+ */
7
+
8
+ const { evaluateGates } = require('../gates/pipeline');
9
+ const { formatText, formatJson, formatEnrichedText } = require('../gates/format');
10
+ const { enrichGateResults } = require('../gates/feedback');
11
+ const { resolveSpec } = require('../utils/spec-resolver');
12
+ const { commandWrapper } = require('../utils/command-wrapper');
13
+ const { recordGates, loadState } = require('../utils/working-state');
14
+
15
+ /**
16
+ * Run quality gates via the v2 pipeline
17
+ * @param {Object} options - Command options
18
+ * @param {string} [options.context] - Execution context (cli, commit, edit)
19
+ * @param {string} [options.specId] - Target spec ID
20
+ * @param {string} [options.specFile] - Explicit spec file path
21
+ * @param {string} [options.file] - Single file to check (for edit context)
22
+ * @param {boolean} [options.json] - Output as JSON
23
+ * @param {string} [options.format] - Output format (text, json)
24
+ * @param {boolean} [options.quiet] - Minimal output
25
+ */
26
+ async function gatesCommand(options = {}) {
27
+ return commandWrapper(
28
+ async () => {
29
+ const projectRoot = process.cwd();
30
+ const context = options.context || 'cli';
31
+
32
+ // Resolve spec (working-spec or feature spec)
33
+ let spec = null;
34
+ try {
35
+ const resolved = await resolveSpec({
36
+ specId: options.specId,
37
+ specFile: options.specFile,
38
+ warnLegacy: false,
39
+ interactive: false,
40
+ });
41
+ spec = resolved.spec;
42
+ } catch {
43
+ // No spec available — gates that need it will handle gracefully
44
+ }
45
+
46
+ // Get file list based on context
47
+ let stagedFiles = [];
48
+ const { execSync } = require('child_process');
49
+ if (context === 'commit') {
50
+ try {
51
+ stagedFiles = execSync('git diff --cached --name-only', {
52
+ cwd: projectRoot,
53
+ encoding: 'utf8',
54
+ stdio: ['ignore', 'pipe', 'pipe'],
55
+ })
56
+ .trim()
57
+ .split('\n')
58
+ .filter(Boolean);
59
+ } catch {
60
+ /* no staged files */
61
+ }
62
+ } else if (options.file) {
63
+ stagedFiles = [options.file];
64
+ } else if (context === 'cli') {
65
+ // For CLI context, use all tracked files so gates can provide meaningful analysis
66
+ try {
67
+ stagedFiles = execSync('git ls-files', {
68
+ cwd: projectRoot,
69
+ encoding: 'utf8',
70
+ maxBuffer: 10 * 1024 * 1024,
71
+ stdio: ['ignore', 'pipe', 'pipe'],
72
+ })
73
+ .trim()
74
+ .split('\n')
75
+ .filter(Boolean);
76
+ } catch {
77
+ /* not a git repo or git unavailable */
78
+ }
79
+ }
80
+
81
+ const report = await evaluateGates({ projectRoot, stagedFiles, spec, context });
82
+
83
+ // Record to working state
84
+ if (spec && spec.id) {
85
+ try { recordGates(spec.id, report, context, projectRoot); } catch { /* non-fatal */ }
86
+ }
87
+
88
+ if (options.json || options.format === 'json') {
89
+ console.log(formatJson(report));
90
+ } else if (!options.quiet) {
91
+ // Enrich feedback on failure or --verbose
92
+ if (!report.passed || options.verbose) {
93
+ try {
94
+ const state = spec?.id ? loadState(spec.id, projectRoot) : null;
95
+ const enrichments = enrichGateResults(report, { spec, state, projectRoot });
96
+ if (enrichments.size > 0) {
97
+ console.log(formatEnrichedText(report, enrichments));
98
+ } else {
99
+ console.log(formatText(report));
100
+ }
101
+ } catch {
102
+ console.log(formatText(report));
103
+ }
104
+ } else {
105
+ console.log(formatText(report));
106
+ }
107
+ }
108
+
109
+ // Exit with appropriate code
110
+ if (!report.passed) {
111
+ process.exit(1);
112
+ }
113
+ },
114
+ {
115
+ commandName: 'gates',
116
+ context: { options },
117
+ exitOnError: true,
118
+ }
119
+ );
120
+ }
121
+
122
+ module.exports = { gatesCommand };
@@ -8,6 +8,7 @@ const fs = require('fs-extra');
8
8
  const path = require('path');
9
9
  const inquirer = require('inquirer');
10
10
  const chalk = require('chalk');
11
+ const yaml = require('js-yaml');
11
12
 
12
13
  // Import shared utilities
13
14
  const { detectCAWSSetup } = require('../utils/detection');
@@ -28,6 +29,117 @@ const {
28
29
  parseIDESelection,
29
30
  } = require('../utils/ide-detection');
30
31
 
32
+ function buildInitialFeatureSpec(specContent, fallbackId) {
33
+ const parsed = yaml.load(specContent);
34
+ const riskTier =
35
+ typeof parsed.risk_tier === 'string'
36
+ ? parseInt(parsed.risk_tier.replace(/^T/i, ''), 10) || 3
37
+ : parsed.risk_tier || 3;
38
+ const aiConfidenceRaw = parsed.ai_assessment?.confidence_level;
39
+ const aiConfidence =
40
+ typeof aiConfidenceRaw === 'number' && aiConfidenceRaw <= 1
41
+ ? Math.max(1, Math.min(10, Math.round(aiConfidenceRaw * 10)))
42
+ : typeof aiConfidenceRaw === 'number'
43
+ ? Math.max(1, Math.min(10, Math.round(aiConfidenceRaw)))
44
+ : 8;
45
+ const normalizedContracts =
46
+ Array.isArray(parsed.contracts) && parsed.contracts.length > 0
47
+ ? parsed.contracts.map((contract, index) => ({
48
+ type: ['openapi', 'graphql', 'proto', 'pact'].includes(contract?.type)
49
+ ? contract.type
50
+ : 'openapi',
51
+ path:
52
+ contract?.path ||
53
+ (index === 0 ? 'docs/api/initial-feature.yaml' : `docs/api/contract-${index + 1}.yaml`),
54
+ }))
55
+ : riskTier <= 2
56
+ ? [{ type: 'openapi', path: 'docs/api/initial-feature.yaml' }]
57
+ : [];
58
+
59
+ return {
60
+ id: parsed.id || fallbackId,
61
+ title: parsed.title || 'New CAWS Project',
62
+ risk_tier: riskTier,
63
+ mode: parsed.mode || 'feature',
64
+ blast_radius: parsed.blast_radius || { modules: ['src', 'tests'], data_migration: false },
65
+ operational_rollback_slo: parsed.operational_rollback_slo || '5m',
66
+ scope: parsed.scope || {
67
+ in: ['src/', 'tests/'],
68
+ out: ['node_modules/', 'dist/', 'build/'],
69
+ },
70
+ invariants: Array.isArray(parsed.invariants) && parsed.invariants.length > 0
71
+ ? parsed.invariants
72
+ : ['System maintains data consistency'],
73
+ acceptance: Array.isArray(parsed.acceptance) && parsed.acceptance.length > 0
74
+ ? parsed.acceptance
75
+ : [
76
+ {
77
+ id: 'A1',
78
+ given: 'Current system state',
79
+ when: 'the initial project is bootstrapped',
80
+ then: 'the CAWS project should validate successfully',
81
+ },
82
+ ],
83
+ non_functional: parsed.non_functional || {
84
+ a11y: ['keyboard'],
85
+ perf: { api_p95_ms: 250 },
86
+ security: [],
87
+ },
88
+ contracts: normalizedContracts,
89
+ observability: parsed.observability || { logs: [], metrics: [], traces: [] },
90
+ migrations: Array.isArray(parsed.migrations) ? parsed.migrations : [],
91
+ rollback: Array.isArray(parsed.rollback) ? parsed.rollback : [],
92
+ ai_assessment: {
93
+ confidence_level: aiConfidence,
94
+ uncertainty_areas: Array.isArray(parsed.ai_assessment?.uncertainty_areas)
95
+ ? parsed.ai_assessment.uncertainty_areas
96
+ : [],
97
+ recommended_pairing:
98
+ parsed.ai_assessment?.recommended_pairing !== undefined
99
+ ? Boolean(parsed.ai_assessment.recommended_pairing)
100
+ : aiConfidence <= 6,
101
+ },
102
+ };
103
+ }
104
+
105
+ async function writeInitialSpecArtifacts(specContent, fallbackId) {
106
+ const canonicalSpec = buildInitialFeatureSpec(specContent, fallbackId);
107
+ const now = new Date().toISOString();
108
+ const canonicalContent = yaml.dump(canonicalSpec, { indent: 2 });
109
+ const specsDir = path.join('.caws', 'specs');
110
+ const featureSpecPath = path.join(specsDir, `${canonicalSpec.id}.yaml`);
111
+ const workingSpecPath = path.join('.caws', 'working-spec.yaml');
112
+ const registryPath = path.join(specsDir, 'registry.json');
113
+
114
+ await fs.ensureDir(specsDir);
115
+ await fs.writeFile(featureSpecPath, canonicalContent);
116
+ await fs.writeFile(workingSpecPath, canonicalContent);
117
+ await fs.writeJson(
118
+ registryPath,
119
+ {
120
+ version: '1.0.0',
121
+ specs: {
122
+ [canonicalSpec.id]: {
123
+ path: `${canonicalSpec.id}.yaml`,
124
+ type: 'feature',
125
+ status: 'active',
126
+ created_at: now,
127
+ updated_at: now,
128
+ owner: null,
129
+ },
130
+ },
131
+ lastUpdated: now,
132
+ },
133
+ { spaces: 2 }
134
+ );
135
+
136
+ return {
137
+ canonicalSpec,
138
+ featureSpecPath,
139
+ workingSpecPath,
140
+ };
141
+ }
142
+
31
143
  /**
32
144
  * Initialize a new project with CAWS
33
145
  */
@@ -492,10 +604,12 @@ async function initProject(projectName, options) {
492
604
  console.log(chalk.blue('\nGenerating CAWS working spec...'));
493
605
  const specContent = generateWorkingSpec(answers);
494
606
 
495
- // Write working spec
607
+ // Write canonical feature spec plus legacy compatibility mirror
496
608
  await fs.ensureDir('.caws');
497
- await fs.writeFile(path.join('.caws', 'working-spec.yaml'), specContent);
609
+ const initialSpec = await writeInitialSpecArtifacts(specContent, answers.projectId);
610
+ console.log(chalk.green(`Created ${initialSpec.featureSpecPath}`));
498
611
  console.log(chalk.green('Created .caws/working-spec.yaml'));
612
+ console.log(chalk.green('Created .caws/specs/registry.json'));
499
613
 
500
614
  // Optionally create policy.yaml (optional - defaults work fine)
501
615
  const policyPath = path.join('.caws', 'policy.yaml');
@@ -503,7 +617,6 @@ async function initProject(projectName, options) {
503
617
  const { PolicyManager } = require('../policy/PolicyManager');
504
618
  const policyManager = new PolicyManager();
505
619
  const defaultPolicy = policyManager.getDefaultPolicy();
506
- const yaml = require('js-yaml');
507
620
  const policyContent = yaml.dump(defaultPolicy, { indent: 2 });
508
621
  await fs.writeFile(policyPath, policyContent);
509
622
  console.log(chalk.green('Created .caws/policy.yaml (optional - defaults work fine)'));
@@ -522,10 +635,20 @@ ${answers.projectDescription}
522
635
  ## Risk Tier: ${answers.riskTier === 1 ? 'High (T1)' : answers.riskTier === 2 ? 'Medium (T2)' : 'Low (T3)'}
523
636
 
524
637
  ## Next Steps
525
- 1. Review and customize \`.caws/working-spec.yaml\`
638
+ 1. Review and customize \`.caws/specs/${answers.projectId}.yaml\`
526
639
  2. Set up your development environment
527
640
  3. Implement features according to the spec
528
- 4. Run \`caws validate\` to check your progress
641
+ 4. Run \`caws validate --spec-id ${answers.projectId}\` to check your progress
642
+
643
+ ## Multi-Agent Recommendation
644
+ The initial project spec is also available in \`.caws/specs/${answers.projectId}.yaml\`.
645
+ For multi-agent work, treat feature specs in \`.caws/specs/\` as canonical and use
646
+ \`.caws/working-spec.yaml\` only as a compatibility mirror:
647
+
648
+ \`\`\`bash
649
+ caws specs create my-feature --type feature --title "My Feature"
650
+ caws validate --spec-id my-feature
651
+ \`\`\`
529
652
 
530
653
  ## Quality Gates
531
654
  - **Coverage**: ${answers.riskTier === 1 ? '90%+' : answers.riskTier === 2 ? '80%+' : '70%+'}
@@ -584,7 +707,7 @@ Happy coding! `;
584
707
  projectTitle: path.basename(process.cwd()),
585
708
  projectDescription: `A ${detectedType} project managed with CAWS`,
586
709
  riskTier: 2,
587
- projectId: `PROJ-${Math.floor(Math.random() * 1000) + 1}`,
710
+ projectId: `PROJ-${String(Math.floor(Math.random() * 1000) + 1).padStart(3, '0')}`,
588
711
  useCursorHooks: true,
589
712
  generateExamples: false,
590
713
  projectMode: 'feature',
@@ -624,8 +747,10 @@ Happy coding! `;
624
747
 
625
748
  const specContent = generateWorkingSpec(defaultAnswers);
626
749
  await fs.ensureDir('.caws');
627
- await fs.writeFile(path.join('.caws', 'working-spec.yaml'), specContent);
750
+ const initialSpec = await writeInitialSpecArtifacts(specContent, defaultAnswers.projectId);
751
+ console.log(chalk.green(`Created ${initialSpec.featureSpecPath}`));
628
752
  console.log(chalk.green('Created .caws/working-spec.yaml'));
753
+ console.log(chalk.green('Created .caws/specs/registry.json'));
629
754
 
630
755
  // Optionally create policy.yaml (optional - defaults work fine)
631
756
  const policyPath = path.join('.caws', 'policy.yaml');
@@ -633,7 +758,6 @@ Happy coding! `;
633
758
  const { PolicyManager } = require('../policy/PolicyManager');
634
759
  const policyManager = new PolicyManager();
635
760
  const defaultPolicy = policyManager.getDefaultPolicy();
636
- const yaml = require('js-yaml');
637
761
  const policyContent = yaml.dump(defaultPolicy, { indent: 2 });
638
762
  await fs.writeFile(policyPath, policyContent);
639
763
  console.log(chalk.green('Created .caws/policy.yaml (optional - defaults work fine)'));
@@ -670,8 +794,10 @@ Happy coding! `;
670
794
  // Success message
671
795
  console.log(chalk.green('\nCAWS project initialized successfully!'));
672
796
  console.log(chalk.blue('\nNext steps:'));
673
- console.log('1. Review .caws/working-spec.yaml');
674
- console.log('2. Customize the specification for your needs');
797
+ console.log('1. Review .caws/specs/<spec-id>.yaml');
798
+ console.log('2. Treat .caws/working-spec.yaml as the compatibility mirror, not the long-term source of truth');
799
+ console.log('3. If multiple agents will collaborate, create more feature specs with `caws specs create <id>`');
800
+ console.log('4. Use `--spec-id` on validation/status/diagnose commands for feature work');
675
801
 
676
802
  // Show contract requirements if Tier 1 or 2
677
803
  // Use answers if available (interactive mode), otherwise default to 2
@@ -688,16 +814,18 @@ Happy coding! `;
688
814
  }
689
815
 
690
816
  console.log('\nRecommended Setup Workflow:');
691
- console.log(' 1. Review .caws/working-spec.yaml');
817
+ console.log(' 1. Review .caws/specs/<spec-id>.yaml');
692
818
  console.log(' 2. Run: caws scaffold (adds tools and templates)');
693
- console.log(' 3. Run: caws validate (verify setup)');
694
- console.log(' 4. Run: caws diagnose (check health)');
695
- console.log(' 5. Optional: Create .caws/policy.yaml for custom budgets');
819
+ console.log(' 3. For multi-agent work, run: caws specs create <feature-id>');
820
+ console.log(' 4. Run: caws validate --spec-id <spec-id> (verify setup)');
821
+ console.log(' 5. Run: caws diagnose --spec-id <spec-id> (check health)');
822
+ console.log(' 6. Optional: Create .caws/policy.yaml for custom budgets');
696
823
  const finalIDEs = answers?.selectedIDEs || [];
697
824
  if (finalIDEs.includes('cursor') || finalIDEs.includes('claude') || options.interactive === false) {
698
- console.log(' 6. Restart your IDE to activate quality gates');
825
+ console.log(' 7. Restart your IDE to activate quality gates');
699
826
  }
700
827
  console.log('\nQuick start: caws scaffold && caws validate && caws diagnose');
828
+ console.log('Multi-agent quick start: caws specs create my-feature && caws validate --spec-id my-feature');
701
829
  } catch (error) {
702
830
  console.error(chalk.red('Error during initialization:'), error.message);
703
831
  if (error.stack) {
@@ -14,6 +14,7 @@ const { initializeGlobalSetup } = require('../config');
14
14
 
15
15
  // Import spec resolution system
16
16
  const { resolveSpec } = require('../utils/spec-resolver');
17
+ const { loadState } = require('../utils/working-state');
17
18
 
18
19
  /**
19
20
  * Iterate command handler
@@ -21,12 +22,12 @@ const { resolveSpec } = require('../utils/spec-resolver');
21
22
  * @param {string} specFile - Path to working spec file
22
23
  * @param {object} options - Command options
23
24
  */
24
- async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}) {
25
+ async function iterateCommand(specFile, options = {}) {
25
26
  try {
26
27
  // Resolve spec using priority system
27
28
  const resolved = await resolveSpec({
28
29
  specId: options.specId,
29
- specFile,
30
+ specFile: specFile || undefined,
30
31
  warnLegacy: false,
31
32
  });
32
33
 
@@ -36,12 +37,19 @@ async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}
36
37
  const setup = initializeGlobalSetup();
37
38
 
38
39
  if (setup.hasWorkingSpec) {
39
- console.log(`Detected ${setup.setupType} CAWS setup`);
40
+ console.log(`Detected ${setup.type} CAWS setup`);
40
41
  console.log(` Capabilities: ${setup.capabilities.join(', ')}`);
41
42
  }
42
43
 
43
44
  // Parse current state from options
44
- const currentState = options.currentState ? JSON.parse(options.currentState) : {};
45
+ let currentState = {};
46
+ if (options.currentState) {
47
+ try {
48
+ currentState = JSON.parse(options.currentState);
49
+ } catch {
50
+ console.error('Invalid JSON in --current-state. Using defaults.');
51
+ }
52
+ }
45
53
  const stateDescription = currentState.description || 'Starting implementation';
46
54
 
47
55
  console.log(chalk.blue('\nIterative Development Guidance\n'));
@@ -50,9 +58,48 @@ async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}
50
58
  console.log(`ID: ${spec.id} | Tier: ${spec.risk_tier} | Mode: ${spec.mode}`);
51
59
  console.log(`Current State: ${stateDescription}\n`);
52
60
 
61
+ // Load working state for evidence-based guidance
62
+ let workingState = null;
63
+ try { workingState = loadState(spec.id); } catch { /* non-fatal */ }
64
+
53
65
  // Analyze progress based on mode
54
66
  const guidance = generateGuidance(spec, currentState, options);
55
67
 
68
+ // If working state exists, overlay evidence-based data
69
+ if (workingState && workingState.phase !== 'not-started') {
70
+ guidance.phase = formatPhase(workingState.phase);
71
+
72
+ // Build evidence-based completed steps
73
+ const evidence = [];
74
+ if (workingState.validation && workingState.validation.passed) {
75
+ evidence.push(`Validation passed (Grade ${workingState.validation.grade || '?'})`);
76
+ }
77
+ if (workingState.evaluation) {
78
+ evidence.push(`Evaluation: ${workingState.evaluation.percentage}% (Grade ${workingState.evaluation.grade})`);
79
+ }
80
+ if (workingState.gates && workingState.gates.passed) {
81
+ evidence.push(`All gates passing (last run: ${workingState.gates.context} context)`);
82
+ }
83
+ if (workingState.acceptance_criteria) {
84
+ const ac = workingState.acceptance_criteria;
85
+ evidence.push(`AC verification: ${ac.pass}/${ac.total} pass, ${ac.fail} fail, ${ac.unchecked} unchecked`);
86
+ }
87
+ if (workingState.files_touched && workingState.files_touched.length > 0) {
88
+ evidence.push(`${workingState.files_touched.length} file(s) touched`);
89
+ }
90
+ if (evidence.length > 0) {
91
+ guidance.completed = evidence;
92
+ }
93
+
94
+ // Overlay blockers and next actions from state
95
+ if (workingState.blockers && workingState.blockers.length > 0) {
96
+ guidance.blockers = workingState.blockers.map(b => b.message);
97
+ }
98
+ if (workingState.next_actions && workingState.next_actions.length > 0) {
99
+ guidance.nextActions = workingState.next_actions;
100
+ }
101
+ }
102
+
56
103
  // Display guidance
57
104
  console.log(chalk.blue('Current Phase:\n'));
58
105
  console.log(` ${guidance.phase}\n`);
@@ -72,6 +119,18 @@ async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}
72
119
  guidance.blockers.forEach((blocker) => {
73
120
  console.log(chalk.red(` ${blocker}`));
74
121
  });
122
+
123
+ // Show quality gap summary if sidecars available
124
+ try {
125
+ const { diagnoseQualityGaps } = require('../sidecars');
126
+ if (workingState) {
127
+ const gapResult = diagnoseQualityGaps(workingState, spec);
128
+ if (gapResult.data && gapResult.data.gaps && gapResult.data.gaps.length > 0) {
129
+ console.log(chalk.yellow(`\n Quality gaps: ${gapResult.data.summary}`));
130
+ console.log(chalk.yellow(' Run: caws sidecar gaps --spec-id ' + spec.id));
131
+ }
132
+ }
133
+ } catch { /* sidecars not available — non-fatal */ }
75
134
  }
76
135
 
77
136
  console.log(chalk.blue('\nRecommendations:\n'));
@@ -337,4 +396,18 @@ function getQualityGates(riskTier) {
337
396
  return gates[riskTier] || gates[2];
338
397
  }
339
398
 
399
+ /**
400
+ * Format phase slug into human-readable label
401
+ */
402
+ function formatPhase(phase) {
403
+ const labels = {
404
+ 'not-started': 'Not Started',
405
+ 'spec-authoring': 'Spec Authoring',
406
+ 'implementation': 'Implementation',
407
+ 'verification': 'Verification',
408
+ 'complete': 'Complete',
409
+ };
410
+ return labels[phase] || phase;
411
+ }
412
+
340
413
  module.exports = { iterateCommand };
@@ -128,6 +128,10 @@ function handleStatus() {
128
128
  const statusColor =
129
129
  agent.status === 'active'
130
130
  ? chalk.green
131
+ : agent.status === 'fresh'
132
+ ? chalk.cyan
133
+ : agent.status === 'merged'
134
+ ? chalk.blue
131
135
  : agent.status === 'missing'
132
136
  ? chalk.red
133
137
  : chalk.yellow;
@@ -10,7 +10,7 @@ const chalk = require('chalk');
10
10
  const { safeAsync, outputResult } = require('../error-handler');
11
11
 
12
12
  // Import spec resolution system
13
- const { resolveSpec } = require('../utils/spec-resolver');
13
+ const { resolveSpec, loadSpecsRegistry } = require('../utils/spec-resolver');
14
14
 
15
15
  /**
16
16
  * Plan templates for different spec types
@@ -86,15 +86,11 @@ const PLAN_TEMPLATES = {
86
86
  * @returns {Promise<Object|null>} Spec data or null
87
87
  */
88
88
  async function loadSpecForPlanning(specId) {
89
- try {
90
- const resolved = await resolveSpec({
91
- specId,
92
- warnLegacy: false,
93
- });
94
- return resolved.spec;
95
- } catch (error) {
96
- return null;
97
- }
89
+ const resolved = await resolveSpec({
90
+ specId,
91
+ warnLegacy: false,
92
+ });
93
+ return resolved.spec;
98
94
  }
99
95
 
100
96
  /**
@@ -398,21 +394,18 @@ async function planCommand(action, options = {}) {
398
394
 
399
395
  if (status.specCount === 1) {
400
396
  // Use the single spec automatically
401
- const registry = await require('../utils/spec-resolver').loadSpecsRegistry();
397
+ const registry = await loadSpecsRegistry();
402
398
  const singleSpecId = Object.keys(registry.specs)[0];
403
399
  console.log(chalk.blue(`Auto-detected single spec: ${singleSpecId}`));
404
400
 
405
401
  const spec = await loadSpecForPlanning(singleSpecId);
406
- if (!spec) {
407
- throw new Error(`Auto-detected spec '${singleSpecId}' could not be loaded`);
408
- }
409
-
410
402
  await generateAndDisplayPlan(spec, singleSpecId, options);
411
403
  } else if (status.specCount > 1) {
404
+ const registry = await loadSpecsRegistry();
412
405
  throw new Error(
413
406
  'Multiple specs detected. Please specify which one: caws plan generate --spec-id <id>\n' +
414
407
  'Available specs: ' +
415
- Object.keys(status.registry?.specs || {}).join(', ')
408
+ Object.keys(registry.specs || {}).join(', ')
416
409
  );
417
410
  } else {
418
411
  throw new Error('No specs found. Create a spec first: caws specs create <id>');
@@ -420,9 +413,6 @@ async function planCommand(action, options = {}) {
420
413
  } else {
421
414
  // Load spec for planning
422
415
  const spec = await loadSpecForPlanning(specId);
423
- if (!spec) {
424
- throw new Error(`Spec '${specId}' not found`);
425
- }
426
416
 
427
417
  return await generateAndDisplayPlan(spec, specId, options);
428
418
  }
@@ -12,6 +12,41 @@ const crypto = require('crypto');
12
12
  const yaml = require('js-yaml');
13
13
  const { execSync } = require('child_process');
14
14
  const { commandWrapper } = require('../utils/command-wrapper');
15
+ const { resolveSpec } = require('../utils/spec-resolver');
16
+
17
+ async function resolveProvenanceSpec(options = {}) {
18
+ try {
19
+ return await resolveSpec({
20
+ specId: options.specId,
21
+ specFile: options.specFile,
22
+ warnLegacy: false,
23
+ });
24
+ } catch (error) {
25
+ const shouldFallbackToLegacy =
26
+ !options.specId &&
27
+ !options.specFile &&
28
+ error.message.includes('schema violations');
29
+
30
+ if (!shouldFallbackToLegacy) {
31
+ throw error;
32
+ }
33
+
34
+ const legacyPath = path.join(process.cwd(), '.caws', 'working-spec.yaml');
35
+ if (!(await fs.pathExists(legacyPath))) {
36
+ throw error;
37
+ }
38
+
39
+ const legacyContent = await fs.readFile(legacyPath, 'utf8');
40
+ const legacySpec = yaml.load(legacyContent);
41
+
42
+ return {
43
+ path: legacyPath,
44
+ type: 'legacy',
45
+ spec: legacySpec,
46
+ degradedValidation: true,
47
+ };
48
+ }
49
+ }
15
50
 
16
51
  /**
17
52
  * Get quality gates status from saved report
@@ -90,14 +125,8 @@ async function updateProvenance(options) {
90
125
  // Ensure output directory exists
91
126
  await fs.ensureDir(output);
92
127
 
93
- // Load current working spec
94
- const specPath = '.caws/working-spec.yaml';
95
- if (!(await fs.pathExists(specPath))) {
96
- throw new Error('Working spec not found - not in CAWS project');
97
- }
98
-
99
- const specContent = await fs.readFile(specPath, 'utf8');
100
- const spec = yaml.load(specContent);
128
+ const resolved = await resolveProvenanceSpec(options);
129
+ const spec = resolved.spec;
101
130
 
102
131
  // Load existing provenance chain
103
132
  const provenanceChain = await loadProvenanceChain(output);
@@ -116,6 +145,9 @@ async function updateProvenance(options) {
116
145
  title: spec.title,
117
146
  risk_tier: spec.risk_tier,
118
147
  mode: spec.mode,
148
+ type: resolved.type,
149
+ path: resolved.path,
150
+ status: spec.status || null,
119
151
  waiver_ids: spec.waiver_ids || [],
120
152
  },
121
153
  quality_gates: getQualityGatesStatus(),
@@ -148,6 +180,10 @@ async function updateProvenance(options) {
148
180
 
149
181
  if (!quiet) {
150
182
  console.log(`Provenance updated for commit ${commit.substring(0, 8)}`);
183
+ console.log(` Spec: ${spec.id} (${resolved.type}) -> ${resolved.path}`);
184
+ if (resolved.degradedValidation) {
185
+ console.log(' Note: using legacy spec metadata despite schema validation issues');
186
+ }
151
187
  console.log(` Chain length: ${provenanceChain.length} entries`);
152
188
  console.log(` Hash: ${newEntry.hash.substring(0, 16)}...`);
153
189
  }
@@ -929,17 +965,12 @@ async function initProvenance(options) {
929
965
  await fs.ensureDir(output);
930
966
  console.log(`Created provenance directory: ${output}`);
931
967
 
932
- // Load working spec to validate CAWS project
933
- const specPath = '.caws/working-spec.yaml';
934
- if (!(await fs.pathExists(specPath))) {
935
- console.log('');
936
- console.log('Not in a CAWS project - missing working spec');
937
- console.log('Run "caws init" first to create a CAWS project');
938
- process.exit(1);
968
+ const resolved = await resolveProvenanceSpec(options);
969
+ console.log(`Found CAWS spec: ${resolved.spec.id} (${resolved.type})`);
970
+ if (resolved.degradedValidation) {
971
+ console.log(' Proceeding with legacy spec metadata despite schema validation issues');
939
972
  }
940
973
 
941
- console.log('Found CAWS working spec');
942
-
943
974
  // Initialize empty chain
944
975
  const initialChain = [];
945
976
  await saveProvenanceChain(initialChain, output);
@@ -947,6 +978,11 @@ async function initProvenance(options) {
947
978
 
948
979
  // Create environment configuration hints
949
980
  const envConfig = {
981
+ spec: {
982
+ id: resolved.spec.id,
983
+ path: resolved.path,
984
+ type: resolved.type,
985
+ },
950
986
  cursor_tracking_api: cursorApi || process.env.CURSOR_TRACKING_API || 'not_configured',
951
987
  cursor_checkpoint_api: process.env.CURSOR_CHECKPOINT_API || 'not_configured',
952
988
  cursor_project_id: process.env.CURSOR_PROJECT_ID || 'not_configured',