@paths.design/caws-cli 9.3.2 → 10.1.0

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 (286) hide show
  1. package/README.md +71 -32
  2. package/dist/budget-derivation.js +221 -74
  3. package/dist/commands/archive.js +67 -28
  4. package/dist/commands/burnup.js +20 -11
  5. package/dist/commands/diagnose.js +34 -22
  6. package/dist/commands/evaluate.js +41 -15
  7. package/dist/commands/gates.js +149 -0
  8. package/dist/commands/init.js +150 -19
  9. package/dist/commands/iterate.js +81 -4
  10. package/dist/commands/parallel.js +4 -0
  11. package/dist/commands/plan.js +9 -19
  12. package/dist/commands/provenance.js +53 -17
  13. package/dist/commands/quality-monitor.js +64 -45
  14. package/dist/commands/scope.js +264 -0
  15. package/dist/commands/sidecar.js +74 -0
  16. package/dist/commands/specs.js +381 -45
  17. package/dist/commands/status.js +117 -9
  18. package/dist/commands/templates.js +0 -8
  19. package/dist/commands/tutorial.js +10 -9
  20. package/dist/commands/validate.js +70 -6
  21. package/dist/commands/verify-acs.js +48 -76
  22. package/dist/commands/waivers.js +212 -13
  23. package/dist/commands/worktree.js +131 -26
  24. package/dist/error-handler.js +2 -13
  25. package/dist/gates/budget-limit.js +121 -0
  26. package/dist/gates/feedback.js +260 -0
  27. package/dist/gates/format.js +179 -0
  28. package/dist/gates/god-object.js +117 -0
  29. package/dist/gates/pipeline.js +167 -0
  30. package/dist/gates/scope-boundary.js +93 -0
  31. package/dist/gates/spec-completeness.js +109 -0
  32. package/dist/gates/todo-detection.js +205 -0
  33. package/dist/index.js +157 -151
  34. package/dist/parallel/parallel-manager.js +3 -3
  35. package/dist/policy/PolicyManager.js +51 -17
  36. package/dist/scaffold/claude-hooks.js +24 -1
  37. package/dist/scaffold/git-hooks.js +45 -102
  38. package/dist/scaffold/index.js +4 -3
  39. package/dist/session/session-manager.js +105 -14
  40. package/dist/sidecars/index.js +33 -0
  41. package/dist/sidecars/listeners.js +40 -0
  42. package/dist/sidecars/provenance-summary.js +238 -0
  43. package/dist/sidecars/quality-gaps.js +258 -0
  44. package/dist/sidecars/schema.js +149 -0
  45. package/dist/sidecars/spec-drift.js +151 -0
  46. package/dist/sidecars/waiver-draft.js +176 -0
  47. package/dist/templates/.caws/schemas/policy.schema.json +112 -0
  48. package/dist/templates/.caws/schemas/scope.schema.json +3 -3
  49. package/dist/templates/.caws/schemas/waivers.schema.json +96 -20
  50. package/dist/templates/.caws/schemas/working-spec.schema.json +264 -57
  51. package/dist/templates/.caws/schemas/worktrees.schema.json +3 -1
  52. package/dist/templates/.caws/templates/working-spec.template.yml +10 -4
  53. package/dist/templates/.caws/tools/scope-guard.js +66 -15
  54. package/dist/templates/.claude/README.md +1 -1
  55. package/dist/templates/.claude/hooks/audit.sh +0 -0
  56. package/dist/templates/.claude/hooks/block-dangerous.sh +52 -11
  57. package/dist/templates/.claude/hooks/classify_command.py +592 -0
  58. package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
  59. package/dist/templates/.claude/hooks/protected-paths.sh +39 -0
  60. package/dist/templates/.claude/hooks/quality-check.sh +23 -10
  61. package/dist/templates/.claude/hooks/scope-guard.sh +136 -55
  62. package/dist/templates/.claude/hooks/session-caws-status.sh +2 -2
  63. package/dist/templates/.claude/hooks/session-log.sh +76 -3
  64. package/dist/templates/.claude/hooks/stop-worktree-check.sh +1 -1
  65. package/dist/templates/.claude/hooks/test_classify_command.py +370 -0
  66. package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
  67. package/dist/templates/.claude/hooks/worktree-guard.sh +2 -2
  68. package/dist/templates/.claude/hooks/worktree-write-guard.sh +97 -4
  69. package/dist/templates/.claude/settings.json +31 -0
  70. package/dist/templates/.cursor/hooks/caws-quality-check.sh +4 -4
  71. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
  72. package/dist/templates/.cursor/hooks/session-log.sh +924 -0
  73. package/dist/templates/.cursor/hooks.json +25 -0
  74. package/dist/templates/.cursor/rules/02-quality-gates.mdc +3 -5
  75. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
  76. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
  77. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
  78. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
  79. package/dist/templates/.github/copilot-instructions.md +5 -5
  80. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
  81. package/dist/templates/.junie/guidelines.md +2 -2
  82. package/dist/templates/.vscode/settings.json +3 -1
  83. package/dist/templates/.windsurf/rules/caws-quality-standards.md +2 -2
  84. package/dist/templates/.windsurf/workflows/caws-guided-development.md +3 -3
  85. package/dist/templates/CLAUDE.md +77 -8
  86. package/dist/templates/agents.md +50 -9
  87. package/dist/templates/docs/README.md +8 -7
  88. package/dist/templates/scripts/new_feature.sh +80 -0
  89. package/dist/test-analysis.js +43 -30
  90. package/dist/tool-loader.js +1 -1
  91. package/dist/utils/agent-session.js +202 -0
  92. package/dist/utils/detection.js +8 -2
  93. package/dist/utils/event-log.js +584 -0
  94. package/dist/utils/event-renderer.js +521 -0
  95. package/dist/utils/finalization.js +7 -6
  96. package/dist/utils/gitignore-updater.js +3 -0
  97. package/dist/utils/lifecycle-events.js +94 -0
  98. package/dist/utils/quality-gates-utils.js +29 -44
  99. package/dist/utils/schema-validator.js +50 -0
  100. package/dist/utils/spec-resolver.js +93 -21
  101. package/dist/utils/working-state.js +530 -0
  102. package/dist/validation/spec-validation.js +191 -31
  103. package/dist/waivers-manager.js +144 -6
  104. package/dist/worktree/worktree-manager.js +598 -95
  105. package/package.json +9 -8
  106. package/templates/.caws/schemas/policy.schema.json +112 -0
  107. package/templates/.caws/schemas/scope.schema.json +3 -3
  108. package/templates/.caws/schemas/waivers.schema.json +96 -20
  109. package/templates/.caws/schemas/working-spec.schema.json +264 -57
  110. package/templates/.caws/schemas/worktrees.schema.json +3 -1
  111. package/templates/.caws/templates/working-spec.template.yml +10 -4
  112. package/templates/.caws/tools/scope-guard.js +66 -15
  113. package/templates/.claude/README.md +1 -1
  114. package/templates/.claude/hooks/block-dangerous.sh +52 -11
  115. package/templates/.claude/hooks/classify_command.py +592 -0
  116. package/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
  117. package/templates/.claude/hooks/protected-paths.sh +39 -0
  118. package/templates/.claude/hooks/quality-check.sh +23 -10
  119. package/templates/.claude/hooks/scope-guard.sh +136 -55
  120. package/templates/.claude/hooks/session-caws-status.sh +2 -2
  121. package/templates/.claude/hooks/session-log.sh +76 -3
  122. package/templates/.claude/hooks/stop-worktree-check.sh +1 -1
  123. package/templates/.claude/hooks/test_classify_command.py +370 -0
  124. package/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
  125. package/templates/.claude/hooks/worktree-guard.sh +2 -2
  126. package/templates/.claude/hooks/worktree-write-guard.sh +97 -4
  127. package/templates/.claude/settings.json +31 -0
  128. package/templates/.cursor/hooks/caws-quality-check.sh +4 -4
  129. package/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
  130. package/templates/.cursor/hooks/session-log.sh +924 -0
  131. package/templates/.cursor/hooks.json +25 -0
  132. package/templates/.cursor/rules/02-quality-gates.mdc +3 -5
  133. package/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
  134. package/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
  135. package/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
  136. package/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
  137. package/templates/.github/copilot-instructions.md +5 -5
  138. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
  139. package/templates/.junie/guidelines.md +2 -2
  140. package/templates/.vscode/settings.json +3 -1
  141. package/templates/.windsurf/rules/caws-quality-standards.md +2 -2
  142. package/templates/.windsurf/workflows/caws-guided-development.md +3 -3
  143. package/templates/CLAUDE.md +77 -8
  144. package/templates/{AGENTS.md → agents.md} +50 -9
  145. package/templates/docs/README.md +8 -7
  146. package/templates/scripts/new_feature.sh +80 -0
  147. package/dist/budget-derivation.d.ts +0 -74
  148. package/dist/budget-derivation.d.ts.map +0 -1
  149. package/dist/cicd-optimizer.d.ts +0 -142
  150. package/dist/cicd-optimizer.d.ts.map +0 -1
  151. package/dist/commands/archive.d.ts +0 -51
  152. package/dist/commands/archive.d.ts.map +0 -1
  153. package/dist/commands/burnup.d.ts +0 -6
  154. package/dist/commands/burnup.d.ts.map +0 -1
  155. package/dist/commands/diagnose.d.ts +0 -52
  156. package/dist/commands/diagnose.d.ts.map +0 -1
  157. package/dist/commands/evaluate.d.ts +0 -8
  158. package/dist/commands/evaluate.d.ts.map +0 -1
  159. package/dist/commands/init.d.ts +0 -5
  160. package/dist/commands/init.d.ts.map +0 -1
  161. package/dist/commands/iterate.d.ts +0 -8
  162. package/dist/commands/iterate.d.ts.map +0 -1
  163. package/dist/commands/mode.d.ts +0 -25
  164. package/dist/commands/mode.d.ts.map +0 -1
  165. package/dist/commands/parallel.d.ts +0 -7
  166. package/dist/commands/parallel.d.ts.map +0 -1
  167. package/dist/commands/plan.d.ts +0 -49
  168. package/dist/commands/plan.d.ts.map +0 -1
  169. package/dist/commands/provenance.d.ts +0 -32
  170. package/dist/commands/provenance.d.ts.map +0 -1
  171. package/dist/commands/quality-gates.d.ts +0 -6
  172. package/dist/commands/quality-gates.d.ts.map +0 -1
  173. package/dist/commands/quality-gates.js +0 -444
  174. package/dist/commands/quality-monitor.d.ts +0 -17
  175. package/dist/commands/quality-monitor.d.ts.map +0 -1
  176. package/dist/commands/session.d.ts +0 -7
  177. package/dist/commands/session.d.ts.map +0 -1
  178. package/dist/commands/specs.d.ts +0 -77
  179. package/dist/commands/specs.d.ts.map +0 -1
  180. package/dist/commands/status.d.ts +0 -44
  181. package/dist/commands/status.d.ts.map +0 -1
  182. package/dist/commands/templates.d.ts +0 -74
  183. package/dist/commands/templates.d.ts.map +0 -1
  184. package/dist/commands/tool.d.ts +0 -13
  185. package/dist/commands/tool.d.ts.map +0 -1
  186. package/dist/commands/troubleshoot.d.ts +0 -8
  187. package/dist/commands/troubleshoot.d.ts.map +0 -1
  188. package/dist/commands/troubleshoot.js +0 -104
  189. package/dist/commands/tutorial.d.ts +0 -55
  190. package/dist/commands/tutorial.d.ts.map +0 -1
  191. package/dist/commands/validate.d.ts +0 -15
  192. package/dist/commands/validate.d.ts.map +0 -1
  193. package/dist/commands/waivers.d.ts +0 -8
  194. package/dist/commands/waivers.d.ts.map +0 -1
  195. package/dist/commands/workflow.d.ts +0 -85
  196. package/dist/commands/workflow.d.ts.map +0 -1
  197. package/dist/commands/worktree.d.ts +0 -7
  198. package/dist/commands/worktree.d.ts.map +0 -1
  199. package/dist/config/index.d.ts +0 -29
  200. package/dist/config/index.d.ts.map +0 -1
  201. package/dist/config/lite-scope.d.ts +0 -33
  202. package/dist/config/lite-scope.d.ts.map +0 -1
  203. package/dist/config/modes.d.ts +0 -264
  204. package/dist/config/modes.d.ts.map +0 -1
  205. package/dist/constants/spec-types.d.ts +0 -93
  206. package/dist/constants/spec-types.d.ts.map +0 -1
  207. package/dist/error-handler.d.ts +0 -151
  208. package/dist/error-handler.d.ts.map +0 -1
  209. package/dist/generators/jest-config-generator.d.ts +0 -32
  210. package/dist/generators/jest-config-generator.d.ts.map +0 -1
  211. package/dist/generators/jest-config.d.ts +0 -32
  212. package/dist/generators/jest-config.d.ts.map +0 -1
  213. package/dist/generators/jest-config.js +0 -242
  214. package/dist/generators/working-spec.d.ts +0 -13
  215. package/dist/generators/working-spec.d.ts.map +0 -1
  216. package/dist/index-new.d.ts +0 -5
  217. package/dist/index-new.d.ts.map +0 -1
  218. package/dist/index-new.js +0 -317
  219. package/dist/index.d.ts +0 -5
  220. package/dist/index.d.ts.map +0 -1
  221. package/dist/index.js.backup +0 -4711
  222. package/dist/minimal-cli.d.ts +0 -3
  223. package/dist/minimal-cli.d.ts.map +0 -1
  224. package/dist/parallel/parallel-manager.d.ts +0 -67
  225. package/dist/parallel/parallel-manager.d.ts.map +0 -1
  226. package/dist/policy/PolicyManager.d.ts +0 -104
  227. package/dist/policy/PolicyManager.d.ts.map +0 -1
  228. package/dist/scaffold/claude-hooks.d.ts +0 -28
  229. package/dist/scaffold/claude-hooks.d.ts.map +0 -1
  230. package/dist/scaffold/cursor-hooks.d.ts +0 -7
  231. package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
  232. package/dist/scaffold/git-hooks.d.ts +0 -38
  233. package/dist/scaffold/git-hooks.d.ts.map +0 -1
  234. package/dist/scaffold/index.d.ts +0 -17
  235. package/dist/scaffold/index.d.ts.map +0 -1
  236. package/dist/session/session-manager.d.ts +0 -94
  237. package/dist/session/session-manager.d.ts.map +0 -1
  238. package/dist/spec/SpecFileManager.d.ts +0 -146
  239. package/dist/spec/SpecFileManager.d.ts.map +0 -1
  240. package/dist/templates/.cursor/hooks/caws-tool-validation.sh +0 -121
  241. package/dist/templates/.github/copilot/instructions.md +0 -311
  242. package/dist/test-analysis.d.ts +0 -231
  243. package/dist/test-analysis.d.ts.map +0 -1
  244. package/dist/tool-interface.d.ts +0 -236
  245. package/dist/tool-interface.d.ts.map +0 -1
  246. package/dist/tool-loader.d.ts +0 -77
  247. package/dist/tool-loader.d.ts.map +0 -1
  248. package/dist/tool-validator.d.ts +0 -72
  249. package/dist/tool-validator.d.ts.map +0 -1
  250. package/dist/utils/async-utils.d.ts +0 -73
  251. package/dist/utils/async-utils.d.ts.map +0 -1
  252. package/dist/utils/command-wrapper.d.ts +0 -66
  253. package/dist/utils/command-wrapper.d.ts.map +0 -1
  254. package/dist/utils/detection.d.ts +0 -14
  255. package/dist/utils/detection.d.ts.map +0 -1
  256. package/dist/utils/error-categories.d.ts +0 -52
  257. package/dist/utils/error-categories.d.ts.map +0 -1
  258. package/dist/utils/finalization.d.ts +0 -17
  259. package/dist/utils/finalization.d.ts.map +0 -1
  260. package/dist/utils/git-lock.d.ts +0 -13
  261. package/dist/utils/git-lock.d.ts.map +0 -1
  262. package/dist/utils/gitignore-updater.d.ts +0 -39
  263. package/dist/utils/gitignore-updater.d.ts.map +0 -1
  264. package/dist/utils/ide-detection.d.ts +0 -89
  265. package/dist/utils/ide-detection.d.ts.map +0 -1
  266. package/dist/utils/project-analysis.d.ts +0 -34
  267. package/dist/utils/project-analysis.d.ts.map +0 -1
  268. package/dist/utils/promise-utils.d.ts +0 -30
  269. package/dist/utils/promise-utils.d.ts.map +0 -1
  270. package/dist/utils/quality-gates-utils.d.ts +0 -49
  271. package/dist/utils/quality-gates-utils.d.ts.map +0 -1
  272. package/dist/utils/quality-gates.d.ts +0 -49
  273. package/dist/utils/quality-gates.d.ts.map +0 -1
  274. package/dist/utils/quality-gates.js +0 -402
  275. package/dist/utils/spec-resolver.d.ts +0 -80
  276. package/dist/utils/spec-resolver.d.ts.map +0 -1
  277. package/dist/utils/typescript-detector.d.ts +0 -66
  278. package/dist/utils/typescript-detector.d.ts.map +0 -1
  279. package/dist/utils/yaml-validation.d.ts +0 -32
  280. package/dist/utils/yaml-validation.d.ts.map +0 -1
  281. package/dist/validation/spec-validation.d.ts +0 -43
  282. package/dist/validation/spec-validation.d.ts.map +0 -1
  283. package/dist/waivers-manager.d.ts +0 -167
  284. package/dist/waivers-manager.d.ts.map +0 -1
  285. package/dist/worktree/worktree-manager.d.ts +0 -54
  286. package/dist/worktree/worktree-manager.d.ts.map +0 -1
@@ -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');
@@ -27,6 +28,119 @@ const {
27
28
  getRecommendedIDEs,
28
29
  parseIDESelection,
29
30
  } = require('../utils/ide-detection');
31
+ // CAWSFIX-10: share the canonical spec-ID regex with the validator
32
+ const { SPEC_ID_PATTERN } = require('../validation/spec-validation');
33
+
34
+ function buildInitialFeatureSpec(specContent, fallbackId) {
35
+ const parsed = yaml.load(specContent);
36
+ const riskTier =
37
+ typeof parsed.risk_tier === 'string'
38
+ ? parseInt(parsed.risk_tier.replace(/^T/i, ''), 10) || 3
39
+ : parsed.risk_tier || 3;
40
+ const aiConfidenceRaw = parsed.ai_assessment?.confidence_level;
41
+ const aiConfidence =
42
+ typeof aiConfidenceRaw === 'number' && aiConfidenceRaw <= 1
43
+ ? Math.max(1, Math.min(10, Math.round(aiConfidenceRaw * 10)))
44
+ : typeof aiConfidenceRaw === 'number'
45
+ ? Math.max(1, Math.min(10, Math.round(aiConfidenceRaw)))
46
+ : 8;
47
+ const normalizedContracts =
48
+ Array.isArray(parsed.contracts) && parsed.contracts.length > 0
49
+ ? parsed.contracts.map((contract, index) => ({
50
+ type: ['openapi', 'graphql', 'proto', 'pact'].includes(contract?.type)
51
+ ? contract.type
52
+ : 'openapi',
53
+ path:
54
+ contract?.path ||
55
+ (index === 0 ? 'docs/api/initial-feature.yaml' : `docs/api/contract-${index + 1}.yaml`),
56
+ }))
57
+ : riskTier <= 2
58
+ ? [{ type: 'openapi', path: 'docs/api/initial-feature.yaml' }]
59
+ : [];
60
+
61
+ return {
62
+ id: parsed.id || fallbackId,
63
+ title: parsed.title || 'New CAWS Project',
64
+ risk_tier: riskTier,
65
+ mode: parsed.mode || 'feature',
66
+ blast_radius: parsed.blast_radius || { modules: ['src', 'tests'], data_migration: false },
67
+ operational_rollback_slo: parsed.operational_rollback_slo || '5m',
68
+ scope: parsed.scope || {
69
+ in: ['src/', 'tests/'],
70
+ out: ['node_modules/', 'dist/', 'build/'],
71
+ },
72
+ invariants: Array.isArray(parsed.invariants) && parsed.invariants.length > 0
73
+ ? parsed.invariants
74
+ : ['System maintains data consistency'],
75
+ acceptance: Array.isArray(parsed.acceptance) && parsed.acceptance.length > 0
76
+ ? parsed.acceptance
77
+ : [
78
+ {
79
+ id: 'A1',
80
+ given: 'Current system state',
81
+ when: 'the initial project is bootstrapped',
82
+ then: 'the CAWS project should validate successfully',
83
+ },
84
+ ],
85
+ non_functional: parsed.non_functional || {
86
+ a11y: ['keyboard'],
87
+ perf: { api_p95_ms: 250 },
88
+ security: [],
89
+ },
90
+ contracts: normalizedContracts,
91
+ observability: parsed.observability || { logs: [], metrics: [], traces: [] },
92
+ migrations: Array.isArray(parsed.migrations) ? parsed.migrations : [],
93
+ rollback: Array.isArray(parsed.rollback) ? parsed.rollback : [],
94
+ ai_assessment: {
95
+ confidence_level: aiConfidence,
96
+ uncertainty_areas: Array.isArray(parsed.ai_assessment?.uncertainty_areas)
97
+ ? parsed.ai_assessment.uncertainty_areas
98
+ : [],
99
+ recommended_pairing:
100
+ parsed.ai_assessment?.recommended_pairing !== undefined
101
+ ? Boolean(parsed.ai_assessment.recommended_pairing)
102
+ : aiConfidence <= 6,
103
+ },
104
+ };
105
+ }
106
+
107
+ async function writeInitialSpecArtifacts(specContent, fallbackId) {
108
+ const canonicalSpec = buildInitialFeatureSpec(specContent, fallbackId);
109
+ const now = new Date().toISOString();
110
+ const canonicalContent = yaml.dump(canonicalSpec, { indent: 2 });
111
+ const specsDir = path.join('.caws', 'specs');
112
+ const featureSpecPath = path.join(specsDir, `${canonicalSpec.id}.yaml`);
113
+ const workingSpecPath = path.join('.caws', 'working-spec.yaml');
114
+ const registryPath = path.join(specsDir, 'registry.json');
115
+
116
+ await fs.ensureDir(specsDir);
117
+ await fs.writeFile(featureSpecPath, canonicalContent);
118
+ await fs.writeFile(workingSpecPath, canonicalContent);
119
+ await fs.writeJson(
120
+ registryPath,
121
+ {
122
+ version: '1.0.0',
123
+ specs: {
124
+ [canonicalSpec.id]: {
125
+ path: `${canonicalSpec.id}.yaml`,
126
+ type: 'feature',
127
+ status: 'active',
128
+ created_at: now,
129
+ updated_at: now,
130
+ owner: null,
131
+ },
132
+ },
133
+ lastUpdated: now,
134
+ },
135
+ { spaces: 2 }
136
+ );
137
+
138
+ return {
139
+ canonicalSpec,
140
+ featureSpecPath,
141
+ workingSpecPath,
142
+ };
143
+ }
30
144
 
31
145
  /**
32
146
  * Initialize a new project with CAWS
@@ -407,11 +521,12 @@ async function initProject(projectName, options) {
407
521
  return `PROJ-${randomNum.toString().padStart(3, '0')}`;
408
522
  },
409
523
  validate: (input) => {
410
- if (!input.match(/^[A-Z]+-\d+$/)) {
411
- return 'Project ID must be in format PREFIX-NUMBER (e.g., PROJ-001)';
524
+ // CAWSFIX-10: accept multi-segment IDs like P03-IMPL-01
525
+ if (!SPEC_ID_PATTERN.test(input)) {
526
+ return 'Project ID must be in format PREFIX-NUMBER or PREFIX-SEGMENT-NUMBER (e.g., PROJ-001, P03-IMPL-01)';
412
527
  }
413
- if (input.length > 20) {
414
- return 'Project ID must be less than 20 characters';
528
+ if (input.length > 40) {
529
+ return 'Project ID must be less than 40 characters';
415
530
  }
416
531
  return true;
417
532
  },
@@ -492,10 +607,12 @@ async function initProject(projectName, options) {
492
607
  console.log(chalk.blue('\nGenerating CAWS working spec...'));
493
608
  const specContent = generateWorkingSpec(answers);
494
609
 
495
- // Write working spec
610
+ // Write canonical feature spec plus legacy compatibility mirror
496
611
  await fs.ensureDir('.caws');
497
- await fs.writeFile(path.join('.caws', 'working-spec.yaml'), specContent);
612
+ const initialSpec = await writeInitialSpecArtifacts(specContent, answers.projectId);
613
+ console.log(chalk.green(`Created ${initialSpec.featureSpecPath}`));
498
614
  console.log(chalk.green('Created .caws/working-spec.yaml'));
615
+ console.log(chalk.green('Created .caws/specs/registry.json'));
499
616
 
500
617
  // Optionally create policy.yaml (optional - defaults work fine)
501
618
  const policyPath = path.join('.caws', 'policy.yaml');
@@ -503,7 +620,6 @@ async function initProject(projectName, options) {
503
620
  const { PolicyManager } = require('../policy/PolicyManager');
504
621
  const policyManager = new PolicyManager();
505
622
  const defaultPolicy = policyManager.getDefaultPolicy();
506
- const yaml = require('js-yaml');
507
623
  const policyContent = yaml.dump(defaultPolicy, { indent: 2 });
508
624
  await fs.writeFile(policyPath, policyContent);
509
625
  console.log(chalk.green('Created .caws/policy.yaml (optional - defaults work fine)'));
@@ -522,10 +638,20 @@ ${answers.projectDescription}
522
638
  ## Risk Tier: ${answers.riskTier === 1 ? 'High (T1)' : answers.riskTier === 2 ? 'Medium (T2)' : 'Low (T3)'}
523
639
 
524
640
  ## Next Steps
525
- 1. Review and customize \`.caws/working-spec.yaml\`
641
+ 1. Review and customize \`.caws/specs/${answers.projectId}.yaml\`
526
642
  2. Set up your development environment
527
643
  3. Implement features according to the spec
528
- 4. Run \`caws validate\` to check your progress
644
+ 4. Run \`caws validate --spec-id ${answers.projectId}\` to check your progress
645
+
646
+ ## Multi-Agent Recommendation
647
+ The initial project spec is also available in \`.caws/specs/${answers.projectId}.yaml\`.
648
+ For multi-agent work, treat feature specs in \`.caws/specs/\` as canonical and use
649
+ \`.caws/working-spec.yaml\` only as a compatibility mirror:
650
+
651
+ \`\`\`bash
652
+ caws specs create my-feature --type feature --title "My Feature"
653
+ caws validate --spec-id my-feature
654
+ \`\`\`
529
655
 
530
656
  ## Quality Gates
531
657
  - **Coverage**: ${answers.riskTier === 1 ? '90%+' : answers.riskTier === 2 ? '80%+' : '70%+'}
@@ -584,7 +710,7 @@ Happy coding! `;
584
710
  projectTitle: path.basename(process.cwd()),
585
711
  projectDescription: `A ${detectedType} project managed with CAWS`,
586
712
  riskTier: 2,
587
- projectId: `PROJ-${Math.floor(Math.random() * 1000) + 1}`,
713
+ projectId: `PROJ-${String(Math.floor(Math.random() * 1000) + 1).padStart(3, '0')}`,
588
714
  useCursorHooks: true,
589
715
  generateExamples: false,
590
716
  projectMode: 'feature',
@@ -624,8 +750,10 @@ Happy coding! `;
624
750
 
625
751
  const specContent = generateWorkingSpec(defaultAnswers);
626
752
  await fs.ensureDir('.caws');
627
- await fs.writeFile(path.join('.caws', 'working-spec.yaml'), specContent);
753
+ const initialSpec = await writeInitialSpecArtifacts(specContent, defaultAnswers.projectId);
754
+ console.log(chalk.green(`Created ${initialSpec.featureSpecPath}`));
628
755
  console.log(chalk.green('Created .caws/working-spec.yaml'));
756
+ console.log(chalk.green('Created .caws/specs/registry.json'));
629
757
 
630
758
  // Optionally create policy.yaml (optional - defaults work fine)
631
759
  const policyPath = path.join('.caws', 'policy.yaml');
@@ -633,7 +761,6 @@ Happy coding! `;
633
761
  const { PolicyManager } = require('../policy/PolicyManager');
634
762
  const policyManager = new PolicyManager();
635
763
  const defaultPolicy = policyManager.getDefaultPolicy();
636
- const yaml = require('js-yaml');
637
764
  const policyContent = yaml.dump(defaultPolicy, { indent: 2 });
638
765
  await fs.writeFile(policyPath, policyContent);
639
766
  console.log(chalk.green('Created .caws/policy.yaml (optional - defaults work fine)'));
@@ -670,8 +797,10 @@ Happy coding! `;
670
797
  // Success message
671
798
  console.log(chalk.green('\nCAWS project initialized successfully!'));
672
799
  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');
800
+ console.log('1. Review .caws/specs/<spec-id>.yaml');
801
+ console.log('2. Treat .caws/working-spec.yaml as the compatibility mirror, not the long-term source of truth');
802
+ console.log('3. If multiple agents will collaborate, create more feature specs with `caws specs create <id>`');
803
+ console.log('4. Use `--spec-id` on validation/status/diagnose commands for feature work');
675
804
 
676
805
  // Show contract requirements if Tier 1 or 2
677
806
  // Use answers if available (interactive mode), otherwise default to 2
@@ -688,16 +817,18 @@ Happy coding! `;
688
817
  }
689
818
 
690
819
  console.log('\nRecommended Setup Workflow:');
691
- console.log(' 1. Review .caws/working-spec.yaml');
820
+ console.log(' 1. Review .caws/specs/<spec-id>.yaml');
692
821
  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');
822
+ console.log(' 3. For multi-agent work, run: caws specs create <feature-id>');
823
+ console.log(' 4. Run: caws validate --spec-id <spec-id> (verify setup)');
824
+ console.log(' 5. Run: caws diagnose --spec-id <spec-id> (check health)');
825
+ console.log(' 6. Optional: Create .caws/policy.yaml for custom budgets');
696
826
  const finalIDEs = answers?.selectedIDEs || [];
697
827
  if (finalIDEs.includes('cursor') || finalIDEs.includes('claude') || options.interactive === false) {
698
- console.log(' 6. Restart your IDE to activate quality gates');
828
+ console.log(' 7. Restart your IDE to activate quality gates');
699
829
  }
700
830
  console.log('\nQuick start: caws scaffold && caws validate && caws diagnose');
831
+ console.log('Multi-agent quick start: caws specs create my-feature && caws validate --spec-id my-feature');
701
832
  } catch (error) {
702
833
  console.error(chalk.red('Error during initialization:'), error.message);
703
834
  if (error.stack) {
@@ -14,6 +14,11 @@ const { initializeGlobalSetup } = require('../config');
14
14
 
15
15
  // Import spec resolution system
16
16
  const { resolveSpec } = require('../utils/spec-resolver');
17
+ // EVLOG-002 Phase 2 read flip: iterate reads from the event log instead of
18
+ // the state layer. loadStateFromEvents matches loadState's contract exactly
19
+ // (returns null for specs with no events), so the `workingState &&` guard
20
+ // below stays correct without code changes.
21
+ const { loadStateFromEvents } = require('../utils/event-renderer');
17
22
 
18
23
  /**
19
24
  * Iterate command handler
@@ -21,12 +26,12 @@ const { resolveSpec } = require('../utils/spec-resolver');
21
26
  * @param {string} specFile - Path to working spec file
22
27
  * @param {object} options - Command options
23
28
  */
24
- async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}) {
29
+ async function iterateCommand(specFile, options = {}) {
25
30
  try {
26
31
  // Resolve spec using priority system
27
32
  const resolved = await resolveSpec({
28
33
  specId: options.specId,
29
- specFile,
34
+ specFile: specFile || undefined,
30
35
  warnLegacy: false,
31
36
  });
32
37
 
@@ -36,12 +41,19 @@ async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}
36
41
  const setup = initializeGlobalSetup();
37
42
 
38
43
  if (setup.hasWorkingSpec) {
39
- console.log(`Detected ${setup.setupType} CAWS setup`);
44
+ console.log(`Detected ${setup.type} CAWS setup`);
40
45
  console.log(` Capabilities: ${setup.capabilities.join(', ')}`);
41
46
  }
42
47
 
43
48
  // Parse current state from options
44
- const currentState = options.currentState ? JSON.parse(options.currentState) : {};
49
+ let currentState = {};
50
+ if (options.currentState) {
51
+ try {
52
+ currentState = JSON.parse(options.currentState);
53
+ } catch {
54
+ console.error('Invalid JSON in --current-state. Using defaults.');
55
+ }
56
+ }
45
57
  const stateDescription = currentState.description || 'Starting implementation';
46
58
 
47
59
  console.log(chalk.blue('\nIterative Development Guidance\n'));
@@ -50,9 +62,48 @@ async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}
50
62
  console.log(`ID: ${spec.id} | Tier: ${spec.risk_tier} | Mode: ${spec.mode}`);
51
63
  console.log(`Current State: ${stateDescription}\n`);
52
64
 
65
+ // Load working state for evidence-based guidance (EVLOG-002: from event log)
66
+ let workingState = null;
67
+ try { workingState = loadStateFromEvents(spec.id); } catch { /* non-fatal */ }
68
+
53
69
  // Analyze progress based on mode
54
70
  const guidance = generateGuidance(spec, currentState, options);
55
71
 
72
+ // If working state exists, overlay evidence-based data
73
+ if (workingState && workingState.phase !== 'not-started') {
74
+ guidance.phase = formatPhase(workingState.phase);
75
+
76
+ // Build evidence-based completed steps
77
+ const evidence = [];
78
+ if (workingState.validation && workingState.validation.passed) {
79
+ evidence.push(`Validation passed (Grade ${workingState.validation.grade || '?'})`);
80
+ }
81
+ if (workingState.evaluation) {
82
+ evidence.push(`Evaluation: ${workingState.evaluation.percentage}% (Grade ${workingState.evaluation.grade})`);
83
+ }
84
+ if (workingState.gates && workingState.gates.passed) {
85
+ evidence.push(`All gates passing (last run: ${workingState.gates.context} context)`);
86
+ }
87
+ if (workingState.acceptance_criteria) {
88
+ const ac = workingState.acceptance_criteria;
89
+ evidence.push(`AC verification: ${ac.pass}/${ac.total} pass, ${ac.fail} fail, ${ac.unchecked} unchecked`);
90
+ }
91
+ if (workingState.files_touched && workingState.files_touched.length > 0) {
92
+ evidence.push(`${workingState.files_touched.length} file(s) touched`);
93
+ }
94
+ if (evidence.length > 0) {
95
+ guidance.completed = evidence;
96
+ }
97
+
98
+ // Overlay blockers and next actions from state
99
+ if (workingState.blockers && workingState.blockers.length > 0) {
100
+ guidance.blockers = workingState.blockers.map(b => b.message);
101
+ }
102
+ if (workingState.next_actions && workingState.next_actions.length > 0) {
103
+ guidance.nextActions = workingState.next_actions;
104
+ }
105
+ }
106
+
56
107
  // Display guidance
57
108
  console.log(chalk.blue('Current Phase:\n'));
58
109
  console.log(` ${guidance.phase}\n`);
@@ -72,6 +123,18 @@ async function iterateCommand(specFile = '.caws/working-spec.yaml', options = {}
72
123
  guidance.blockers.forEach((blocker) => {
73
124
  console.log(chalk.red(` ${blocker}`));
74
125
  });
126
+
127
+ // Show quality gap summary if sidecars available
128
+ try {
129
+ const { diagnoseQualityGaps } = require('../sidecars');
130
+ if (workingState) {
131
+ const gapResult = diagnoseQualityGaps(workingState, spec);
132
+ if (gapResult.data && gapResult.data.gaps && gapResult.data.gaps.length > 0) {
133
+ console.log(chalk.yellow(`\n Quality gaps: ${gapResult.data.summary}`));
134
+ console.log(chalk.yellow(' Run: caws sidecar gaps --spec-id ' + spec.id));
135
+ }
136
+ }
137
+ } catch { /* sidecars not available — non-fatal */ }
75
138
  }
76
139
 
77
140
  console.log(chalk.blue('\nRecommendations:\n'));
@@ -337,4 +400,18 @@ function getQualityGates(riskTier) {
337
400
  return gates[riskTier] || gates[2];
338
401
  }
339
402
 
403
+ /**
404
+ * Format phase slug into human-readable label
405
+ */
406
+ function formatPhase(phase) {
407
+ const labels = {
408
+ 'not-started': 'Not Started',
409
+ 'spec-authoring': 'Spec Authoring',
410
+ 'implementation': 'Implementation',
411
+ 'verification': 'Verification',
412
+ 'complete': 'Complete',
413
+ };
414
+ return labels[phase] || phase;
415
+ }
416
+
340
417
  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',