@paths.design/caws-cli 7.0.2 → 8.0.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 (217) hide show
  1. package/dist/budget-derivation.js +5 -4
  2. package/dist/commands/diagnose.js +24 -19
  3. package/dist/commands/init.js +51 -4
  4. package/dist/commands/quality-gates.js +147 -9
  5. package/dist/commands/specs.js +148 -14
  6. package/dist/commands/status.js +2 -2
  7. package/dist/commands/tool.js +2 -4
  8. package/dist/config/index.js +17 -8
  9. package/dist/generators/working-spec.js +19 -6
  10. package/dist/scaffold/git-hooks.js +245 -46
  11. package/dist/scaffold/index.js +53 -7
  12. package/dist/templates/.caws/tools/README.md +21 -0
  13. package/dist/templates/.cursor/README.md +311 -0
  14. package/dist/templates/.cursor/hooks/audit.sh +55 -0
  15. package/dist/templates/.cursor/hooks/block-dangerous.sh +83 -0
  16. package/dist/templates/.cursor/hooks/caws-quality-check.sh +52 -0
  17. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +130 -0
  18. package/dist/templates/.cursor/hooks/caws-tool-validation.sh +121 -0
  19. package/dist/templates/.cursor/hooks/format.sh +38 -0
  20. package/dist/templates/.cursor/hooks/naming-check.sh +64 -0
  21. package/dist/templates/.cursor/hooks/scan-secrets.sh +46 -0
  22. package/dist/templates/.cursor/hooks/scope-guard.sh +52 -0
  23. package/dist/templates/.cursor/hooks/validate-spec.sh +83 -0
  24. package/dist/templates/.cursor/hooks.json +59 -0
  25. package/dist/templates/.cursor/rules/00-claims-verification.mdc +144 -0
  26. package/dist/templates/.cursor/rules/01-working-style.mdc +50 -0
  27. package/dist/templates/.cursor/rules/02-quality-gates.mdc +370 -0
  28. package/dist/templates/.cursor/rules/03-naming-and-refactor.mdc +33 -0
  29. package/dist/templates/.cursor/rules/04-logging-language-style.mdc +23 -0
  30. package/dist/templates/.cursor/rules/05-safe-defaults-guards.mdc +23 -0
  31. package/dist/templates/.cursor/rules/06-typescript-conventions.mdc +36 -0
  32. package/dist/templates/.cursor/rules/07-process-ops.mdc +20 -0
  33. package/dist/templates/.cursor/rules/08-solid-and-architecture.mdc +16 -0
  34. package/dist/templates/.cursor/rules/09-docstrings.mdc +89 -0
  35. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +390 -0
  36. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +385 -0
  37. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +516 -0
  38. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +588 -0
  39. package/dist/templates/.cursor/rules/README.md +148 -0
  40. package/dist/templates/.github/copilot/instructions.md +311 -0
  41. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +5 -0
  42. package/dist/templates/.idea/runConfigurations/CAWS_Validate.xml +5 -0
  43. package/dist/templates/.vscode/launch.json +56 -0
  44. package/dist/templates/.vscode/settings.json +93 -0
  45. package/dist/templates/.windsurf/workflows/caws-guided-development.md +92 -0
  46. package/dist/templates/COMMIT_CONVENTIONS.md +86 -0
  47. package/dist/templates/OIDC_SETUP.md +300 -0
  48. package/dist/templates/agents.md +1047 -0
  49. package/dist/templates/codemod/README.md +1 -0
  50. package/dist/templates/codemod/test.js +93 -0
  51. package/dist/templates/docs/README.md +150 -0
  52. package/dist/templates/scripts/quality-gates/check-god-objects.js +146 -0
  53. package/dist/templates/scripts/quality-gates/run-quality-gates.js +50 -0
  54. package/dist/templates/scripts/v3/analysis/todo_analyzer.py +1997 -0
  55. package/dist/tool-loader.js +6 -1
  56. package/dist/tool-validator.js +8 -2
  57. package/dist/utils/detection.js +4 -3
  58. package/dist/utils/git-lock.js +119 -0
  59. package/dist/utils/gitignore-updater.js +148 -0
  60. package/dist/utils/project-analysis.js +176 -16
  61. package/dist/utils/quality-gates.js +48 -7
  62. package/dist/utils/spec-resolver.js +27 -3
  63. package/dist/utils/yaml-validation.js +156 -0
  64. package/dist/validation/spec-validation.js +81 -2
  65. package/package.json +2 -2
  66. package/templates/.caws/schemas/waivers.schema.json +30 -0
  67. package/templates/.caws/schemas/working-spec.schema.json +133 -0
  68. package/templates/.caws/templates/working-spec.template.yml +74 -0
  69. package/templates/.caws/tools/README.md +21 -0
  70. package/templates/.caws/tools/scope-guard.js +208 -0
  71. package/templates/.caws/tools-allow.json +331 -0
  72. package/templates/.caws/waivers.yml +19 -0
  73. package/templates/.cursor/hooks/scope-guard.sh +2 -2
  74. package/templates/.cursor/hooks/validate-spec.sh +42 -7
  75. package/dist/budget-derivation.d.ts +0 -74
  76. package/dist/budget-derivation.d.ts.map +0 -1
  77. package/dist/cicd-optimizer.d.ts +0 -142
  78. package/dist/cicd-optimizer.d.ts.map +0 -1
  79. package/dist/commands/archive.d.ts +0 -50
  80. package/dist/commands/archive.d.ts.map +0 -1
  81. package/dist/commands/burnup.d.ts +0 -6
  82. package/dist/commands/burnup.d.ts.map +0 -1
  83. package/dist/commands/diagnose.d.ts +0 -52
  84. package/dist/commands/diagnose.d.ts.map +0 -1
  85. package/dist/commands/evaluate.d.ts +0 -8
  86. package/dist/commands/evaluate.d.ts.map +0 -1
  87. package/dist/commands/init.d.ts +0 -5
  88. package/dist/commands/init.d.ts.map +0 -1
  89. package/dist/commands/iterate.d.ts +0 -8
  90. package/dist/commands/iterate.d.ts.map +0 -1
  91. package/dist/commands/mode.d.ts +0 -24
  92. package/dist/commands/mode.d.ts.map +0 -1
  93. package/dist/commands/plan.d.ts +0 -49
  94. package/dist/commands/plan.d.ts.map +0 -1
  95. package/dist/commands/provenance.d.ts +0 -32
  96. package/dist/commands/provenance.d.ts.map +0 -1
  97. package/dist/commands/quality-gates.d.ts +0 -52
  98. package/dist/commands/quality-gates.d.ts.map +0 -1
  99. package/dist/commands/quality-monitor.d.ts +0 -17
  100. package/dist/commands/quality-monitor.d.ts.map +0 -1
  101. package/dist/commands/specs.d.ts +0 -71
  102. package/dist/commands/specs.d.ts.map +0 -1
  103. package/dist/commands/status.d.ts +0 -44
  104. package/dist/commands/status.d.ts.map +0 -1
  105. package/dist/commands/templates.d.ts +0 -74
  106. package/dist/commands/templates.d.ts.map +0 -1
  107. package/dist/commands/tool.d.ts +0 -13
  108. package/dist/commands/tool.d.ts.map +0 -1
  109. package/dist/commands/troubleshoot.d.ts +0 -8
  110. package/dist/commands/troubleshoot.d.ts.map +0 -1
  111. package/dist/commands/tutorial.d.ts +0 -55
  112. package/dist/commands/tutorial.d.ts.map +0 -1
  113. package/dist/commands/validate.d.ts +0 -15
  114. package/dist/commands/validate.d.ts.map +0 -1
  115. package/dist/commands/waivers.d.ts +0 -8
  116. package/dist/commands/waivers.d.ts.map +0 -1
  117. package/dist/commands/workflow.d.ts +0 -85
  118. package/dist/commands/workflow.d.ts.map +0 -1
  119. package/dist/config/index.d.ts +0 -29
  120. package/dist/config/index.d.ts.map +0 -1
  121. package/dist/config/modes.d.ts +0 -225
  122. package/dist/config/modes.d.ts.map +0 -1
  123. package/dist/constants/spec-types.d.ts +0 -41
  124. package/dist/constants/spec-types.d.ts.map +0 -1
  125. package/dist/error-handler.d.ts +0 -164
  126. package/dist/error-handler.d.ts.map +0 -1
  127. package/dist/generators/jest-config.d.ts +0 -32
  128. package/dist/generators/jest-config.d.ts.map +0 -1
  129. package/dist/generators/working-spec.d.ts +0 -13
  130. package/dist/generators/working-spec.d.ts.map +0 -1
  131. package/dist/index-new.d.ts +0 -5
  132. package/dist/index-new.d.ts.map +0 -1
  133. package/dist/index-new.js +0 -317
  134. package/dist/index.d.ts +0 -5
  135. package/dist/index.d.ts.map +0 -1
  136. package/dist/index.js.backup +0 -4711
  137. package/dist/minimal-cli.d.ts +0 -3
  138. package/dist/minimal-cli.d.ts.map +0 -1
  139. package/dist/policy/PolicyManager.d.ts +0 -104
  140. package/dist/policy/PolicyManager.d.ts.map +0 -1
  141. package/dist/scaffold/cursor-hooks.d.ts +0 -7
  142. package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
  143. package/dist/scaffold/git-hooks.d.ts +0 -20
  144. package/dist/scaffold/git-hooks.d.ts.map +0 -1
  145. package/dist/scaffold/index.d.ts +0 -20
  146. package/dist/scaffold/index.d.ts.map +0 -1
  147. package/dist/spec/SpecFileManager.d.ts +0 -146
  148. package/dist/spec/SpecFileManager.d.ts.map +0 -1
  149. package/dist/test-analysis.d.ts +0 -182
  150. package/dist/test-analysis.d.ts.map +0 -1
  151. package/dist/tool-interface.d.ts +0 -236
  152. package/dist/tool-interface.d.ts.map +0 -1
  153. package/dist/tool-loader.d.ts +0 -77
  154. package/dist/tool-loader.d.ts.map +0 -1
  155. package/dist/tool-validator.d.ts +0 -72
  156. package/dist/tool-validator.d.ts.map +0 -1
  157. package/dist/utils/detection.d.ts +0 -7
  158. package/dist/utils/detection.d.ts.map +0 -1
  159. package/dist/utils/finalization.d.ts +0 -17
  160. package/dist/utils/finalization.d.ts.map +0 -1
  161. package/dist/utils/project-analysis.d.ts +0 -14
  162. package/dist/utils/project-analysis.d.ts.map +0 -1
  163. package/dist/utils/quality-gates.d.ts +0 -49
  164. package/dist/utils/quality-gates.d.ts.map +0 -1
  165. package/dist/utils/spec-resolver.d.ts +0 -88
  166. package/dist/utils/spec-resolver.d.ts.map +0 -1
  167. package/dist/utils/typescript-detector.d.ts +0 -63
  168. package/dist/utils/typescript-detector.d.ts.map +0 -1
  169. package/dist/validation/spec-validation.d.ts +0 -43
  170. package/dist/validation/spec-validation.d.ts.map +0 -1
  171. package/dist/waivers-manager.d.ts +0 -167
  172. package/dist/waivers-manager.d.ts.map +0 -1
  173. package/templates/apps/tools/caws/COMPLETION_REPORT.md +0 -331
  174. package/templates/apps/tools/caws/MIGRATION_SUMMARY.md +0 -360
  175. package/templates/apps/tools/caws/README.md +0 -463
  176. package/templates/apps/tools/caws/TEST_STATUS.md +0 -365
  177. package/templates/apps/tools/caws/attest.js +0 -357
  178. package/templates/apps/tools/caws/ci-optimizer.js +0 -642
  179. package/templates/apps/tools/caws/config.ts +0 -245
  180. package/templates/apps/tools/caws/cross-functional.js +0 -876
  181. package/templates/apps/tools/caws/dashboard.js +0 -1112
  182. package/templates/apps/tools/caws/flake-detector.ts +0 -362
  183. package/templates/apps/tools/caws/gates.js +0 -198
  184. package/templates/apps/tools/caws/gates.ts +0 -271
  185. package/templates/apps/tools/caws/language-adapters.ts +0 -381
  186. package/templates/apps/tools/caws/language-support.d.ts +0 -367
  187. package/templates/apps/tools/caws/language-support.d.ts.map +0 -1
  188. package/templates/apps/tools/caws/language-support.js +0 -585
  189. package/templates/apps/tools/caws/legacy-assessment.ts +0 -408
  190. package/templates/apps/tools/caws/legacy-assessor.js +0 -764
  191. package/templates/apps/tools/caws/mutant-analyzer.js +0 -734
  192. package/templates/apps/tools/caws/perf-budgets.ts +0 -349
  193. package/templates/apps/tools/caws/prompt-lint.js.backup +0 -274
  194. package/templates/apps/tools/caws/property-testing.js +0 -707
  195. package/templates/apps/tools/caws/provenance.d.ts +0 -14
  196. package/templates/apps/tools/caws/provenance.d.ts.map +0 -1
  197. package/templates/apps/tools/caws/provenance.js +0 -132
  198. package/templates/apps/tools/caws/provenance.js.backup +0 -73
  199. package/templates/apps/tools/caws/provenance.ts +0 -211
  200. package/templates/apps/tools/caws/security-provenance.ts +0 -483
  201. package/templates/apps/tools/caws/shared/base-tool.ts +0 -281
  202. package/templates/apps/tools/caws/shared/config-manager.ts +0 -366
  203. package/templates/apps/tools/caws/shared/gate-checker.ts +0 -849
  204. package/templates/apps/tools/caws/shared/types.ts +0 -444
  205. package/templates/apps/tools/caws/shared/validator.ts +0 -305
  206. package/templates/apps/tools/caws/shared/waivers-manager.ts +0 -174
  207. package/templates/apps/tools/caws/spec-test-mapper.ts +0 -391
  208. package/templates/apps/tools/caws/test-quality.js +0 -578
  209. package/templates/apps/tools/caws/validate.js +0 -76
  210. package/templates/apps/tools/caws/validate.ts +0 -228
  211. package/templates/apps/tools/caws/waivers.js +0 -344
  212. /package/{templates/apps/tools/caws → dist/templates/.caws}/schemas/waivers.schema.json +0 -0
  213. /package/{templates/apps/tools/caws → dist/templates/.caws}/schemas/working-spec.schema.json +0 -0
  214. /package/{templates/apps/tools/caws → dist/templates/.caws}/templates/working-spec.template.yml +0 -0
  215. /package/{templates/apps/tools/caws → dist/templates/.caws/tools}/scope-guard.js +0 -0
  216. /package/{templates/apps/tools/caws → dist/templates/.caws}/tools-allow.json +0 -0
  217. /package/{templates/apps/tools/caws → dist/templates/.caws}/waivers.yml +0 -0
@@ -196,10 +196,11 @@ async function deriveBudget(spec, projectRoot = process.cwd(), options = {}) {
196
196
  ' This may be a path resolution or caching issue\n'
197
197
  );
198
198
  } else {
199
- console.warn(
200
- 'āš ļø Policy file not found: .caws/policy.yaml\n' +
201
- ' Using default policy. Run "caws init" to create policy.yaml'
202
- );
199
+ // Policy.yaml is optional - defaults work fine, so don't warn unnecessarily
200
+ // Only show info message if user explicitly wants to see it
201
+ if (options.showPolicyInfo !== false) {
202
+ // Silent by default - policy.yaml is optional
203
+ }
203
204
  }
204
205
  }
205
206
 
@@ -240,41 +240,44 @@ async function checkTestFiles() {
240
240
  * @returns {Promise<Object>} Check result
241
241
  */
242
242
  async function checkCAWSTools() {
243
- const toolsPath = 'apps/tools/caws';
243
+ // Check new location first, then legacy location for backward compatibility
244
+ const toolsPath = '.caws/tools';
245
+ const legacyToolsPath = 'apps/tools/caws';
244
246
 
245
- if (!(await fs.pathExists(toolsPath))) {
247
+ if (!(await fs.pathExists(toolsPath)) && !(await fs.pathExists(legacyToolsPath))) {
246
248
  return {
247
- passed: false,
248
- severity: 'low',
249
- message: 'CAWS tools directory not found',
250
- fix: 'Scaffold tools: caws scaffold',
249
+ passed: true,
250
+ severity: 'info',
251
+ message: 'CAWS tools directory not found (optional - use CLI commands instead)',
252
+ fix: 'Core functionality available via: caws validate, caws quality-gates, caws provenance',
251
253
  autoFixable: false,
252
254
  };
253
255
  }
254
256
 
255
- // Check for essential tools
256
- const essentialTools = ['validate.js', 'gates.js', 'provenance.js'];
257
- let missingTools = [];
257
+ // Tools directory exists - check for specialized tools (not core CLI duplicates)
258
+ const specializedTools = ['flake-detector.ts', 'spec-test-mapper.ts', 'perf-budgets.ts'];
259
+ const foundTools = [];
258
260
 
259
- for (const tool of essentialTools) {
260
- if (!(await fs.pathExists(path.join(toolsPath, tool)))) {
261
- missingTools.push(tool);
261
+ for (const tool of specializedTools) {
262
+ if (await fs.pathExists(path.join(toolsPath, tool))) {
263
+ foundTools.push(tool);
262
264
  }
263
265
  }
264
266
 
265
- if (missingTools.length > 0) {
267
+ if (foundTools.length === 0) {
266
268
  return {
267
- passed: false,
268
- severity: 'medium',
269
- message: `Missing ${missingTools.length} essential tools`,
270
- fix: 'Re-scaffold tools: caws scaffold --force',
269
+ passed: true,
270
+ severity: 'info',
271
+ message: 'No specialized tools found (optional - use CLI commands for core functionality)',
272
+ fix: 'Core functionality available via: caws validate, caws quality-gates, caws provenance',
271
273
  autoFixable: false,
272
274
  };
273
275
  }
274
276
 
275
277
  return {
276
278
  passed: true,
277
- message: 'All essential CAWS tools present',
279
+ message: `Found ${foundTools.length} specialized tool(s): ${foundTools.join(', ')}`,
280
+ note: 'Core functionality (validate, quality-gates, provenance) available via CLI commands',
278
281
  };
279
282
  }
280
283
 
@@ -484,7 +487,9 @@ async function diagnoseCommand(options = {}) {
484
487
  console.log(chalk.blue(' • Run: caws status --visual to view project health'));
485
488
  console.log(chalk.blue(' • Run: caws validate to check working spec'));
486
489
  console.log(chalk.blue(' • Optional: Create .caws/policy.yaml for custom budgets'));
487
- console.log(chalk.blue(' • Start implementing: caws iterate --current-state "Ready to begin"'));
490
+ console.log(
491
+ chalk.blue(' • Start implementing: caws iterate --current-state "Ready to begin"')
492
+ );
488
493
  }
489
494
  } catch (error) {
490
495
  console.error(chalk.red('\nāŒ Error running diagnosis:'), error.message);
@@ -17,6 +17,7 @@ const { generateWorkingSpec } = require('../generators/working-spec');
17
17
  const { finalizeProject } = require('../utils/finalization');
18
18
  const { scaffoldCursorHooks } = require('../scaffold/cursor-hooks');
19
19
  const { scaffoldIDEIntegrations } = require('../scaffold/index');
20
+ const { updateGitignore } = require('../utils/gitignore-updater');
20
21
 
21
22
  /**
22
23
  * Initialize a new project with CAWS
@@ -361,6 +362,18 @@ async function initProject(projectName, options) {
361
362
  await fs.writeFile(path.join('.caws', 'working-spec.yaml'), specContent);
362
363
  console.log(chalk.green('āœ… Created .caws/working-spec.yaml'));
363
364
 
365
+ // Optionally create policy.yaml (optional - defaults work fine)
366
+ const policyPath = path.join('.caws', 'policy.yaml');
367
+ if (!fs.existsSync(policyPath)) {
368
+ const { PolicyManager } = require('../policy/PolicyManager');
369
+ const policyManager = new PolicyManager();
370
+ const defaultPolicy = policyManager.getDefaultPolicy();
371
+ const yaml = require('js-yaml');
372
+ const policyContent = yaml.dump(defaultPolicy, { indent: 2 });
373
+ await fs.writeFile(policyPath, policyContent);
374
+ console.log(chalk.green('āœ… Created .caws/policy.yaml (optional - defaults work fine)'));
375
+ }
376
+
364
377
  // Generate additional files if requested
365
378
  if (answers.generateExamples) {
366
379
  console.log(chalk.blue('šŸ“ Generating example files...'));
@@ -409,6 +422,17 @@ Happy coding! šŸŽÆ
409
422
  console.log(chalk.blue('šŸŽØ Setting up IDE integrations...'));
410
423
  await scaffoldIDEIntegrations(process.cwd(), { force: false });
411
424
 
425
+ // Update .gitignore to exclude CAWS local runtime files
426
+ console.log(chalk.blue('šŸ“ Updating .gitignore...'));
427
+ const gitignoreUpdated = await updateGitignore(process.cwd());
428
+ if (gitignoreUpdated) {
429
+ console.log(chalk.green('āœ… Updated .gitignore to exclude CAWS local runtime files'));
430
+ console.log(
431
+ chalk.gray(' Tracked: Specs, policy, waivers, provenance, plans (shared with team)')
432
+ );
433
+ console.log(chalk.gray(' Ignored: Agent runtime, temp files, logs (local-only)'));
434
+ }
435
+
412
436
  // Finalize project
413
437
  await finalizeProject(projectName, options, answers);
414
438
  } else {
@@ -456,7 +480,7 @@ Happy coding! šŸŽÆ
456
480
  experimentalSandbox: '',
457
481
  aiConfidence: 0.8,
458
482
  uncertaintyAreas: '',
459
- complexityFactors: ''
483
+ complexityFactors: '',
460
484
  };
461
485
 
462
486
  const specContent = generateWorkingSpec(defaultAnswers);
@@ -464,6 +488,18 @@ Happy coding! šŸŽÆ
464
488
  await fs.writeFile(path.join('.caws', 'working-spec.yaml'), specContent);
465
489
  console.log(chalk.green('āœ… Created .caws/working-spec.yaml'));
466
490
 
491
+ // Optionally create policy.yaml (optional - defaults work fine)
492
+ const policyPath = path.join('.caws', 'policy.yaml');
493
+ if (!fs.existsSync(policyPath)) {
494
+ const { PolicyManager } = require('../policy/PolicyManager');
495
+ const policyManager = new PolicyManager();
496
+ const defaultPolicy = policyManager.getDefaultPolicy();
497
+ const yaml = require('js-yaml');
498
+ const policyContent = yaml.dump(defaultPolicy, { indent: 2 });
499
+ await fs.writeFile(policyPath, policyContent);
500
+ console.log(chalk.green('āœ… Created .caws/policy.yaml (optional - defaults work fine)'));
501
+ }
502
+
467
503
  // Setup Cursor hooks by default in non-interactive mode
468
504
  console.log(chalk.blue('šŸŽÆ Setting up Cursor hooks...'));
469
505
  await scaffoldCursorHooks(process.cwd());
@@ -472,6 +508,17 @@ Happy coding! šŸŽÆ
472
508
  console.log(chalk.blue('šŸŽØ Setting up IDE integrations...'));
473
509
  await scaffoldIDEIntegrations(process.cwd(), { force: false });
474
510
 
511
+ // Update .gitignore to exclude CAWS local runtime files
512
+ console.log(chalk.blue('šŸ“ Updating .gitignore...'));
513
+ const gitignoreUpdated = await updateGitignore(process.cwd());
514
+ if (gitignoreUpdated) {
515
+ console.log(chalk.green('āœ… Updated .gitignore to exclude CAWS local runtime files'));
516
+ console.log(
517
+ chalk.gray(' Tracked: Specs, policy, waivers, provenance, plans (shared with team)')
518
+ );
519
+ console.log(chalk.gray(' Ignored: Agent runtime, temp files, logs (local-only)'));
520
+ }
521
+
475
522
  // Finalize project
476
523
  await finalizeProject(projectName, options, defaultAnswers);
477
524
  }
@@ -481,7 +528,7 @@ Happy coding! šŸŽÆ
481
528
  console.log(chalk.blue('\nNext steps:'));
482
529
  console.log('1. Review .caws/working-spec.yaml');
483
530
  console.log('2. Customize the specification for your needs');
484
-
531
+
485
532
  // Show contract requirements if Tier 1 or 2
486
533
  // Use answers if available (interactive mode), otherwise default to 2
487
534
  const riskTier = answers?.riskTier || 2;
@@ -495,7 +542,7 @@ Happy coding! šŸŽÆ
495
542
  console.log(chalk.gray(' description: "Project-level CAWS configuration"'));
496
543
  console.log(' Or use "chore" mode for maintenance work (mode: chore)');
497
544
  }
498
-
545
+
499
546
  console.log('\nšŸ“‹ Recommended Setup Workflow:');
500
547
  console.log(' 1. Review .caws/working-spec.yaml');
501
548
  console.log(' 2. Run: caws scaffold (adds tools and templates)');
@@ -511,7 +558,7 @@ Happy coding! šŸŽÆ
511
558
  if (error.stack) {
512
559
  console.error(chalk.gray(error.stack));
513
560
  }
514
-
561
+
515
562
  // Cleanup on error (only for new directory creation)
516
563
  if (projectName && projectName !== '.' && fs.existsSync(projectName)) {
517
564
  try {
@@ -44,14 +44,66 @@ async function qualityGatesCommand(options = {}) {
44
44
  const packagesDir = path.dirname(cliPackageDir);
45
45
  const monorepoRunner = path.join(packagesDir, 'quality-gates', 'run-quality-gates.mjs');
46
46
 
47
- // Option 2: Check VS Code extension bundled (if running from extension context)
47
+ // Option 2: Check globally installed CLI for bundled quality gates
48
+ let globalCliPath = null;
49
+ try {
50
+ const { execSync } = require('child_process');
51
+ const whichCaws = execSync('which caws', { encoding: 'utf8', stdio: 'pipe' }).trim();
52
+ if (whichCaws) {
53
+ // Resolve symlink to actual path
54
+ const realPath = fs.realpathSync(whichCaws);
55
+ const globalCliDir = path.dirname(realPath);
56
+ // Check for bundled quality gates in global CLI installation
57
+ const possibleBundledPaths = [
58
+ path.join(
59
+ globalCliDir,
60
+ '..',
61
+ 'lib',
62
+ 'node_modules',
63
+ '@paths.design',
64
+ 'caws-cli',
65
+ 'node_modules',
66
+ '@paths.design',
67
+ 'quality-gates',
68
+ 'run-quality-gates.mjs'
69
+ ),
70
+ path.join(
71
+ globalCliDir,
72
+ '..',
73
+ 'lib',
74
+ 'node_modules',
75
+ '@paths.design',
76
+ 'quality-gates',
77
+ 'run-quality-gates.mjs'
78
+ ),
79
+ path.join(
80
+ globalCliDir,
81
+ '..',
82
+ 'node_modules',
83
+ '@paths.design',
84
+ 'quality-gates',
85
+ 'run-quality-gates.mjs'
86
+ ),
87
+ ];
88
+ for (const bundledPath of possibleBundledPaths) {
89
+ if (fs.existsSync(bundledPath)) {
90
+ globalCliPath = bundledPath;
91
+ break;
92
+ }
93
+ }
94
+ }
95
+ } catch (e) {
96
+ // Ignore errors finding global CLI
97
+ }
98
+
99
+ // Option 3: Check VS Code extension bundled (if running from extension context)
48
100
  const vscodeExtensionPath =
49
101
  process.env.VSCODE_EXTENSION_PATH || process.env.VSCODE_EXTENSION_DIR;
50
102
  const bundledRunner = vscodeExtensionPath
51
103
  ? path.join(vscodeExtensionPath, 'bundled', 'quality-gates', 'run-quality-gates.mjs')
52
104
  : null;
53
105
 
54
- // Option 3: Check node_modules for quality-gates package (prioritize published package)
106
+ // Option 4: Check node_modules for quality-gates package (prioritize published package)
55
107
  const nodeModulesPaths = [
56
108
  // Published npm package (priority)
57
109
  path.join(
@@ -69,6 +121,8 @@ async function qualityGatesCommand(options = {}) {
69
121
  // Try all possible paths in order
70
122
  if (fs.existsSync(monorepoRunner)) {
71
123
  qualityGatesRunner = monorepoRunner;
124
+ } else if (globalCliPath) {
125
+ qualityGatesRunner = globalCliPath;
72
126
  } else if (bundledRunner && fs.existsSync(bundledRunner)) {
73
127
  qualityGatesRunner = bundledRunner;
74
128
  } else {
@@ -132,14 +186,98 @@ async function qualityGatesCommand(options = {}) {
132
186
  }
133
187
  }
134
188
 
135
- // If still no runner found, provide helpful error
189
+ // Option 5: Try npx (no installation required) - works if Node.js is available
136
190
  if (!qualityGatesRunner) {
137
- const suggestions = [
138
- 'Install quality gates package: npm install -g @paths.design/quality-gates',
139
- 'Use Python script (if available): python3 scripts/simple_gates.py all --tier 2 --profile backend-api',
140
- 'Use Makefile target (if available): make caws-gates',
141
- 'Run from CAWS monorepo root',
142
- ];
191
+ try {
192
+ const { execSync } = require('child_process');
193
+ // Check if npx is available
194
+ execSync('command -v npx', { encoding: 'utf8', stdio: 'ignore' });
195
+
196
+ Output.info('Using npx to run quality gates (no installation required)...');
197
+
198
+ // Build npx command - the package exposes 'caws-quality-gates' bin command
199
+ // Use npx to download and run without installing
200
+ const npxArgs = ['npx', '--yes', '@paths.design/quality-gates'];
201
+
202
+ // Map CLI options to runner options
203
+ if (options.ci) {
204
+ npxArgs.push('--ci');
205
+ }
206
+ if (options.json) {
207
+ npxArgs.push('--json');
208
+ }
209
+ if (options.gates && options.gates.trim()) {
210
+ npxArgs.push('--gates', options.gates.trim());
211
+ }
212
+ if (options.fix) {
213
+ npxArgs.push('--fix');
214
+ }
215
+
216
+ Output.progress('Executing quality gates via npx...');
217
+ Output.info(`Command: ${npxArgs.join(' ')}`);
218
+
219
+ // Execute via npx
220
+ const { execSync: execSyncNpx } = require('child_process');
221
+ execSyncNpx(npxArgs.join(' '), {
222
+ stdio: 'inherit',
223
+ cwd: projectRoot,
224
+ env: {
225
+ ...process.env,
226
+ CAWS_CLI_INTEGRATION: 'true',
227
+ CAWS_CLI_VERSION: require(path.join(cliPackageDir, 'package.json')).version,
228
+ },
229
+ });
230
+
231
+ Output.success('Quality gates completed successfully');
232
+ return;
233
+ } catch (npxError) {
234
+ // npx not available or failed - continue to error message
235
+ }
236
+ }
237
+
238
+ // If still no runner found, provide helpful error with language-agnostic suggestions
239
+ if (!qualityGatesRunner) {
240
+ // Check if Node.js/npx is available (language-agnostic check)
241
+ let hasNodeJs = false;
242
+ try {
243
+ const { execSync } = require('child_process');
244
+ execSync('command -v node', { encoding: 'utf8', stdio: 'ignore' });
245
+ execSync('command -v npx', { encoding: 'utf8', stdio: 'ignore' });
246
+ hasNodeJs = true;
247
+ } catch (e) {
248
+ // Node.js/npx not available
249
+ }
250
+
251
+ const suggestions = [];
252
+
253
+ if (hasNodeJs) {
254
+ // Node.js available - suggest npx (works for any language, no installation)
255
+ suggestions.push(
256
+ 'Use npx (no installation required): npx --yes @paths.design/quality-gates'
257
+ );
258
+ suggestions.push('Install globally: npm install -g @paths.design/quality-gates');
259
+ suggestions.push('Install locally: npm install --save-dev @paths.design/quality-gates');
260
+ } else {
261
+ // Node.js not available - suggest installation or alternatives
262
+ suggestions.push('Install Node.js to use quality gates: https://nodejs.org/');
263
+ suggestions.push(
264
+ 'Then use: npx --yes @paths.design/quality-gates (no installation required)'
265
+ );
266
+ suggestions.push('Or install globally: npm install -g @paths.design/quality-gates');
267
+ }
268
+
269
+ // Language-agnostic fallback options (if they exist)
270
+ const pythonScript = path.join(projectRoot, 'scripts', 'simple_gates.py');
271
+ const makefile = path.join(projectRoot, 'Makefile');
272
+
273
+ if (fs.existsSync(pythonScript)) {
274
+ suggestions.push(`Use project script: python3 ${pythonScript} all --tier 2`);
275
+ }
276
+ if (fs.existsSync(makefile)) {
277
+ suggestions.push('Use Makefile target: make caws-gates');
278
+ }
279
+
280
+ suggestions.push('Run from CAWS monorepo root (if developing CAWS itself)');
143
281
 
144
282
  throw new Error(
145
283
  'Quality gates runner not found.\n\n' +
@@ -154,9 +154,13 @@ async function createSpec(id, options = {}) {
154
154
  console.log(chalk.blue('ā„¹ļø Spec creation canceled.'));
155
155
  return null;
156
156
  } else if (answer === 'rename') {
157
- // Generate new name with timestamp suffix
158
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
159
- const newId = `${id}-${timestamp}`;
157
+ // Generate new name with valid PREFIX-NUMBER format
158
+ // Extract prefix from existing ID or use default
159
+ const prefixMatch = id.match(/^([A-Z]+)-\d+$/);
160
+ const prefix = prefixMatch ? prefixMatch[1] : 'FEAT';
161
+ // Generate sequential number based on timestamp
162
+ const number = Date.now().toString().slice(-6); // Last 6 digits of timestamp
163
+ const newId = `${prefix}-${number}`;
160
164
  console.log(chalk.blue(`šŸ“ Creating spec with new name: ${newId}`));
161
165
  return await createSpec(newId, { ...options, interactive: false });
162
166
  } else if (answer === 'merge') {
@@ -183,9 +187,10 @@ async function createSpec(id, options = {}) {
183
187
  // Ensure specs directory exists
184
188
  await fs.ensureDir(SPECS_DIR);
185
189
 
186
- // Generate spec content
187
- const specContent = {
188
- id,
190
+ // Generate spec content with all required fields
191
+ // Merge template carefully to preserve required fields and structure
192
+ const defaultSpec = {
193
+ id, // Always use the provided id parameter
189
194
  type,
190
195
  title,
191
196
  status: 'draft',
@@ -193,8 +198,64 @@ async function createSpec(id, options = {}) {
193
198
  mode,
194
199
  created_at: new Date().toISOString(),
195
200
  updated_at: new Date().toISOString(),
196
- acceptance_criteria: [],
201
+ // Required fields for validation
202
+ blast_radius: {
203
+ modules: [],
204
+ data_migration: false,
205
+ },
206
+ operational_rollback_slo: '5m',
207
+ scope: {
208
+ in: ['src/', 'tests/'],
209
+ out: ['node_modules/', 'dist/', 'build/'],
210
+ },
211
+ invariants: ['System maintains data consistency'],
212
+ acceptance: [], // Note: validation expects 'acceptance', not 'acceptance_criteria'
213
+ acceptance_criteria: [], // Keep for backward compatibility
214
+ non_functional: {
215
+ a11y: [],
216
+ perf: {},
217
+ security: [],
218
+ },
219
+ contracts: [],
220
+ };
221
+
222
+ // Merge template, but preserve required structure
223
+ // Map template.criteria to acceptance if present
224
+ const templateAcceptance = template?.criteria || template?.acceptance;
225
+
226
+ const specContent = {
227
+ ...defaultSpec,
197
228
  ...(template || {}),
229
+ // Always preserve these critical fields
230
+ id, // Never allow template to override id
231
+ // Map criteria to acceptance if template uses criteria
232
+ acceptance: templateAcceptance || defaultSpec.acceptance,
233
+ acceptance_criteria: templateAcceptance || defaultSpec.acceptance_criteria,
234
+ // Deep merge scope if template provides it
235
+ scope: template?.scope
236
+ ? {
237
+ in: template.scope.in || defaultSpec.scope.in,
238
+ out: template.scope.out || defaultSpec.scope.out,
239
+ }
240
+ : defaultSpec.scope,
241
+ // Deep merge blast_radius if template provides it
242
+ blast_radius: template?.blast_radius
243
+ ? {
244
+ modules: template.blast_radius.modules || defaultSpec.blast_radius.modules,
245
+ data_migration:
246
+ template.blast_radius.data_migration !== undefined
247
+ ? template.blast_radius.data_migration
248
+ : defaultSpec.blast_radius.data_migration,
249
+ }
250
+ : defaultSpec.blast_radius,
251
+ // Deep merge non_functional if template provides it
252
+ non_functional: template?.non_functional
253
+ ? {
254
+ a11y: template.non_functional.a11y || defaultSpec.non_functional.a11y,
255
+ perf: template.non_functional.perf || defaultSpec.non_functional.perf,
256
+ security: template.non_functional.security || defaultSpec.non_functional.security,
257
+ }
258
+ : defaultSpec.non_functional,
198
259
  };
199
260
 
200
261
  // Create file path
@@ -202,7 +263,46 @@ async function createSpec(id, options = {}) {
202
263
  const filePath = path.join(SPECS_DIR, fileName);
203
264
 
204
265
  // Write spec file
205
- await fs.writeFile(filePath, yaml.dump(specContent, { indent: 2 }));
266
+ const yamlContent = yaml.dump(specContent, { indent: 2 });
267
+ await fs.writeFile(filePath, yamlContent);
268
+
269
+ // Validate written file (YAML syntax and structure)
270
+ try {
271
+ const writtenContent = await fs.readFile(filePath, 'utf8');
272
+ const parsed = yaml.load(writtenContent);
273
+
274
+ // Validate YAML syntax was preserved
275
+ if (!parsed || typeof parsed !== 'object') {
276
+ await fs.remove(filePath);
277
+ throw new Error('Failed to parse written spec file - invalid YAML structure');
278
+ }
279
+
280
+ // Validate spec structure using CAWS validation
281
+ const { validateWorkingSpec } = require('../validation/spec-validation');
282
+ const validation = validateWorkingSpec(parsed);
283
+
284
+ if (!validation.valid) {
285
+ await fs.remove(filePath);
286
+ const errorMessages = validation.errors
287
+ .map((e) => `${e.instancePath}: ${e.message}`)
288
+ .join('; ');
289
+ throw new Error(`Spec validation failed: ${errorMessages}`);
290
+ }
291
+ } catch (error) {
292
+ // Clean up invalid file if it exists
293
+ if (await fs.pathExists(filePath)) {
294
+ await fs.remove(filePath);
295
+ }
296
+
297
+ // Re-throw with helpful message
298
+ if (error.message.includes('YAMLException') || error.message.includes('yaml')) {
299
+ throw new Error(
300
+ `Failed to create valid spec: YAML syntax error. ${error.message}\n` +
301
+ 'šŸ’” Consider using the interactive mode: caws specs create <id> --interactive'
302
+ );
303
+ }
304
+ throw error;
305
+ }
206
306
 
207
307
  // Update registry
208
308
  const registry = await loadSpecsRegistry();
@@ -403,9 +503,10 @@ function displaySpecDetails(spec) {
403
503
  /**
404
504
  * Migrate from legacy working-spec.yaml to feature-specific specs
405
505
  * @param {Object} options - Migration options
506
+ * @param {Function} [createSpecFn] - Function to create specs (for testing)
406
507
  * @returns {Promise<Object>} Migration result
407
508
  */
408
- async function migrateFromLegacy(options = {}) {
509
+ async function migrateFromLegacy(options = {}, createSpecFn = createSpec) {
409
510
  const fs = require('fs-extra');
410
511
  const path = require('path');
411
512
  const yaml = require('js-yaml');
@@ -422,6 +523,14 @@ async function migrateFromLegacy(options = {}) {
422
523
  const legacyContent = await fs.readFile(legacyPath, 'utf8');
423
524
  const legacySpec = yaml.load(legacyContent);
424
525
 
526
+ if (!legacySpec) {
527
+ throw new Error('Legacy working-spec.yaml is empty or invalid');
528
+ }
529
+
530
+ if (!legacySpec.acceptance || !Array.isArray(legacySpec.acceptance)) {
531
+ throw new Error('Legacy working-spec.yaml must have an acceptance array');
532
+ }
533
+
425
534
  // Suggest feature breakdown based on acceptance criteria
426
535
  const features = suggestFeatureBreakdown(legacySpec);
427
536
 
@@ -441,15 +550,32 @@ async function migrateFromLegacy(options = {}) {
441
550
  }
442
551
 
443
552
  if (options.features && options.features.length > 0) {
553
+ // Filter by original feature IDs (before transformation)
444
554
  selectedFeatures = features.filter((f) => options.features.includes(f.id));
445
- console.log(chalk.blue(`\nšŸ“‹ Migrating selected features: ${options.features.join(', ')}`));
555
+ if (selectedFeatures.length === 0) {
556
+ const errorMsg = `No features found matching: ${options.features.join(', ')}. Available features: ${features.map((f) => f.id).join(', ')}`;
557
+ console.log(chalk.yellow(`āš ļø ${errorMsg}`));
558
+ throw new Error(errorMsg);
559
+ } else {
560
+ console.log(chalk.blue(`\nšŸ“‹ Migrating selected features: ${options.features.join(', ')}`));
561
+ }
446
562
  }
447
563
 
448
564
  // Create each feature spec
449
565
  const createdSpecs = [];
566
+ let featureCounter = 1;
450
567
  for (const feature of selectedFeatures) {
451
568
  try {
452
- await createSpec(feature.id, {
569
+ // Transform feature ID to proper format (PREFIX-NUMBER) if needed
570
+ let specId = feature.id;
571
+ if (!/^[A-Z]+-\d+$/.test(specId)) {
572
+ // Convert 'auth' -> 'FEAT-001', 'payment' -> 'FEAT-002', etc.
573
+ const prefix = specId.toUpperCase().replace(/[^A-Z0-9]/g, '');
574
+ specId = `${prefix || 'FEAT'}-${String(featureCounter).padStart(3, '0')}`;
575
+ featureCounter++;
576
+ }
577
+
578
+ await createSpecFn(specId, {
453
579
  type: 'feature',
454
580
  title: feature.title,
455
581
  risk_tier: 'T3', // Default tier
@@ -457,10 +583,14 @@ async function migrateFromLegacy(options = {}) {
457
583
  template: feature,
458
584
  });
459
585
 
460
- createdSpecs.push(feature.id);
461
- console.log(chalk.green(` āœ… Created spec: ${feature.id}`));
586
+ createdSpecs.push(specId);
587
+ console.log(chalk.green(` āœ… Created spec: ${specId}`));
462
588
  } catch (error) {
589
+ // Log full error details for debugging
463
590
  console.log(chalk.red(` āŒ Failed to create spec ${feature.id}: ${error.message}`));
591
+ if (process.env.DEBUG_MIGRATION) {
592
+ console.log(chalk.gray(` Error details: ${error.stack}`));
593
+ }
464
594
  }
465
595
  }
466
596
 
@@ -589,7 +719,11 @@ async function specsCommand(action, options = {}) {
589
719
  }
590
720
 
591
721
  case 'migrate': {
592
- const result = await migrateFromLegacy(options);
722
+ // Allow tests to inject createSpec function
723
+ const createSpecFn = options._createSpecFn || createSpec;
724
+ const migrationOptions = { ...options };
725
+ delete migrationOptions._createSpecFn; // Remove test-only option
726
+ const result = await migrateFromLegacy(migrationOptions, createSpecFn);
593
727
 
594
728
  return outputResult({
595
729
  command: 'specs migrate',
@@ -182,10 +182,10 @@ async function loadWaiverStatus() {
182
182
  */
183
183
  async function checkQualityGates() {
184
184
  // For now, return a placeholder
185
- // In full implementation, this would run actual gate checks
185
+ // Quality gates are available via CLI or MCP
186
186
  return {
187
187
  checked: false,
188
- message: 'Run: node apps/tools/caws/gates.js for full gate status',
188
+ message: 'Run: caws quality-gates or use MCP tool caws_quality_gates_run for full gate status',
189
189
  };
190
190
  }
191
191
 
@@ -4,7 +4,6 @@
4
4
  * @author @darianrosebrook
5
5
  */
6
6
 
7
- const path = require('path');
8
7
  const { commandWrapper, Output } = require('../utils/command-wrapper');
9
8
 
10
9
  // Import tool system
@@ -23,9 +22,8 @@ async function initializeToolSystem() {
23
22
  if (toolLoader) return toolLoader;
24
23
 
25
24
  try {
26
- toolLoader = new ToolLoader({
27
- toolsDir: path.join(process.cwd(), 'apps/tools/caws'),
28
- });
25
+ // ToolLoader now checks .caws/tools first, then falls back to legacy location
26
+ toolLoader = new ToolLoader();
29
27
 
30
28
  toolValidator = new ToolValidator();
31
29