@paths.design/caws-cli 7.0.3 → 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 (111) hide show
  1. package/dist/commands/quality-gates.js +147 -9
  2. package/dist/commands/specs.js +108 -13
  3. package/dist/commands/tool.js +0 -1
  4. package/dist/scaffold/git-hooks.js +159 -58
  5. package/dist/scaffold/index.js +1 -1
  6. package/dist/templates/.caws/tools/README.md +1 -0
  7. package/dist/utils/git-lock.js +1 -0
  8. package/dist/utils/project-analysis.js +176 -16
  9. package/dist/utils/quality-gates.js +7 -6
  10. package/dist/utils/spec-resolver.js +4 -0
  11. package/dist/utils/yaml-validation.js +1 -0
  12. package/package.json +1 -1
  13. package/templates/.caws/tools/README.md +1 -0
  14. package/dist/budget-derivation.d.ts +0 -74
  15. package/dist/budget-derivation.d.ts.map +0 -1
  16. package/dist/cicd-optimizer.d.ts +0 -142
  17. package/dist/cicd-optimizer.d.ts.map +0 -1
  18. package/dist/commands/archive.d.ts +0 -50
  19. package/dist/commands/archive.d.ts.map +0 -1
  20. package/dist/commands/burnup.d.ts +0 -6
  21. package/dist/commands/burnup.d.ts.map +0 -1
  22. package/dist/commands/diagnose.d.ts +0 -52
  23. package/dist/commands/diagnose.d.ts.map +0 -1
  24. package/dist/commands/evaluate.d.ts +0 -8
  25. package/dist/commands/evaluate.d.ts.map +0 -1
  26. package/dist/commands/init.d.ts +0 -5
  27. package/dist/commands/init.d.ts.map +0 -1
  28. package/dist/commands/iterate.d.ts +0 -8
  29. package/dist/commands/iterate.d.ts.map +0 -1
  30. package/dist/commands/mode.d.ts +0 -24
  31. package/dist/commands/mode.d.ts.map +0 -1
  32. package/dist/commands/plan.d.ts +0 -49
  33. package/dist/commands/plan.d.ts.map +0 -1
  34. package/dist/commands/provenance.d.ts +0 -32
  35. package/dist/commands/provenance.d.ts.map +0 -1
  36. package/dist/commands/quality-gates.d.ts +0 -52
  37. package/dist/commands/quality-gates.d.ts.map +0 -1
  38. package/dist/commands/quality-monitor.d.ts +0 -17
  39. package/dist/commands/quality-monitor.d.ts.map +0 -1
  40. package/dist/commands/specs.d.ts +0 -71
  41. package/dist/commands/specs.d.ts.map +0 -1
  42. package/dist/commands/status.d.ts +0 -44
  43. package/dist/commands/status.d.ts.map +0 -1
  44. package/dist/commands/templates.d.ts +0 -74
  45. package/dist/commands/templates.d.ts.map +0 -1
  46. package/dist/commands/tool.d.ts +0 -13
  47. package/dist/commands/tool.d.ts.map +0 -1
  48. package/dist/commands/troubleshoot.d.ts +0 -8
  49. package/dist/commands/troubleshoot.d.ts.map +0 -1
  50. package/dist/commands/tutorial.d.ts +0 -55
  51. package/dist/commands/tutorial.d.ts.map +0 -1
  52. package/dist/commands/validate.d.ts +0 -15
  53. package/dist/commands/validate.d.ts.map +0 -1
  54. package/dist/commands/waivers.d.ts +0 -8
  55. package/dist/commands/waivers.d.ts.map +0 -1
  56. package/dist/commands/workflow.d.ts +0 -85
  57. package/dist/commands/workflow.d.ts.map +0 -1
  58. package/dist/config/index.d.ts +0 -29
  59. package/dist/config/index.d.ts.map +0 -1
  60. package/dist/config/modes.d.ts +0 -225
  61. package/dist/config/modes.d.ts.map +0 -1
  62. package/dist/constants/spec-types.d.ts +0 -41
  63. package/dist/constants/spec-types.d.ts.map +0 -1
  64. package/dist/error-handler.d.ts +0 -164
  65. package/dist/error-handler.d.ts.map +0 -1
  66. package/dist/generators/jest-config.d.ts +0 -32
  67. package/dist/generators/jest-config.d.ts.map +0 -1
  68. package/dist/generators/working-spec.d.ts +0 -13
  69. package/dist/generators/working-spec.d.ts.map +0 -1
  70. package/dist/index-new.d.ts +0 -5
  71. package/dist/index-new.d.ts.map +0 -1
  72. package/dist/index-new.js +0 -317
  73. package/dist/index.d.ts +0 -5
  74. package/dist/index.d.ts.map +0 -1
  75. package/dist/index.js.backup +0 -4711
  76. package/dist/minimal-cli.d.ts +0 -3
  77. package/dist/minimal-cli.d.ts.map +0 -1
  78. package/dist/policy/PolicyManager.d.ts +0 -104
  79. package/dist/policy/PolicyManager.d.ts.map +0 -1
  80. package/dist/scaffold/cursor-hooks.d.ts +0 -7
  81. package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
  82. package/dist/scaffold/git-hooks.d.ts +0 -20
  83. package/dist/scaffold/git-hooks.d.ts.map +0 -1
  84. package/dist/scaffold/index.d.ts +0 -20
  85. package/dist/scaffold/index.d.ts.map +0 -1
  86. package/dist/spec/SpecFileManager.d.ts +0 -146
  87. package/dist/spec/SpecFileManager.d.ts.map +0 -1
  88. package/dist/test-analysis.d.ts +0 -182
  89. package/dist/test-analysis.d.ts.map +0 -1
  90. package/dist/tool-interface.d.ts +0 -236
  91. package/dist/tool-interface.d.ts.map +0 -1
  92. package/dist/tool-loader.d.ts +0 -77
  93. package/dist/tool-loader.d.ts.map +0 -1
  94. package/dist/tool-validator.d.ts +0 -72
  95. package/dist/tool-validator.d.ts.map +0 -1
  96. package/dist/utils/detection.d.ts +0 -7
  97. package/dist/utils/detection.d.ts.map +0 -1
  98. package/dist/utils/finalization.d.ts +0 -17
  99. package/dist/utils/finalization.d.ts.map +0 -1
  100. package/dist/utils/project-analysis.d.ts +0 -14
  101. package/dist/utils/project-analysis.d.ts.map +0 -1
  102. package/dist/utils/quality-gates.d.ts +0 -49
  103. package/dist/utils/quality-gates.d.ts.map +0 -1
  104. package/dist/utils/spec-resolver.d.ts +0 -88
  105. package/dist/utils/spec-resolver.d.ts.map +0 -1
  106. package/dist/utils/typescript-detector.d.ts +0 -63
  107. package/dist/utils/typescript-detector.d.ts.map +0 -1
  108. package/dist/validation/spec-validation.d.ts +0 -43
  109. package/dist/validation/spec-validation.d.ts.map +0 -1
  110. package/dist/waivers-manager.d.ts +0 -167
  111. package/dist/waivers-manager.d.ts.map +0 -1
@@ -6,6 +6,7 @@
6
6
 
7
7
  const fs = require('fs-extra');
8
8
  const path = require('path');
9
+ const { getTodoAnalyzerSuggestion } = require('../utils/project-analysis');
9
10
 
10
11
  /**
11
12
  * Scaffold git hooks for CAWS provenance tracking
@@ -39,7 +40,7 @@ async function scaffoldGitHooks(projectDir, options = {}) {
39
40
  name: 'pre-commit',
40
41
  description: 'Pre-commit validation and quality checks',
41
42
  enabled: validation || qualityGates,
42
- content: generatePreCommitHook({ validation, qualityGates }),
43
+ content: generatePreCommitHook({ validation, qualityGates, projectDir }),
43
44
  },
44
45
  {
45
46
  name: 'post-commit',
@@ -120,7 +121,10 @@ async function scaffoldGitHooks(projectDir, options = {}) {
120
121
  * Implements fallback chain: Node script → CLI → Python scripts → Skip gracefully
121
122
  */
122
123
  function generatePreCommitHook(options) {
123
- const { qualityGates = true, stagedOnly = true } = options;
124
+ const { qualityGates = true, stagedOnly = true, projectDir = process.cwd() } = options;
125
+
126
+ // Get language-agnostic suggestions based on runtime availability
127
+ const todoSuggestion = getTodoAnalyzerSuggestion(projectDir);
124
128
 
125
129
  return `#!/bin/bash
126
130
  # CAWS Pre-commit Hook
@@ -144,13 +148,13 @@ if [ -f ".git/index.lock" ]; then
144
148
  LOCK_AGE_MINUTES=$((LOCK_AGE / 60))
145
149
 
146
150
  if [ $LOCK_AGE_MINUTES -gt 5 ]; then
147
- echo "⚠️ Stale git lock detected (${LOCK_AGE_MINUTES} minutes old)"
151
+ echo "⚠️ Stale git lock detected (\${LOCK_AGE_MINUTES} minutes old)"
148
152
  echo "💡 This may indicate a crashed git process"
149
153
  echo "💡 Remove stale lock: rm .git/index.lock"
150
154
  echo "⚠️ Warning: Check for running git/editor processes before removing"
151
155
  exit 1
152
156
  else
153
- echo "⚠️ Git lock detected (${LOCK_AGE_MINUTES} minutes old)"
157
+ echo "⚠️ Git lock detected (\${LOCK_AGE_MINUTES} minutes old)"
154
158
  echo "💡 Another git process may be running"
155
159
  echo "💡 Wait for the other process to complete, or check for running processes"
156
160
  exit 1
@@ -162,7 +166,7 @@ echo "🔍 Validating YAML syntax for CAWS spec files..."
162
166
  YAML_VALIDATION_FAILED=false
163
167
 
164
168
  # Find all staged .yaml/.yml files in .caws directory
165
- STAGED_YAML_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.caws/.*\.(yaml|yml)$' || true)
169
+ STAGED_YAML_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\\.caws/.*\\.(yaml|yml)$' || true)
166
170
 
167
171
  if [ -n "$STAGED_YAML_FILES" ]; then
168
172
  # Use Node.js to validate YAML if available
@@ -326,42 +330,49 @@ fi
326
330
  if [ "$QUALITY_GATES_RAN" = true ]; then
327
331
  echo "🔍 Checking for hidden TODOs in staged files..."
328
332
 
329
- # Find TODO analyzer .mjs file (preferred - no Python dependency)
330
- TODO_ANALYZER=""
333
+ TODO_CHECK_RAN=false
331
334
 
332
- # Try quality gates package TODO analyzer (published package)
333
- if [ -f "node_modules/@paths.design/quality-gates/todo-analyzer.mjs" ]; then
334
- TODO_ANALYZER="node_modules/@paths.design/quality-gates/todo-analyzer.mjs"
335
- # Try quality gates package TODO analyzer (monorepo/local copy)
336
- elif [ -f "node_modules/@caws/quality-gates/todo-analyzer.mjs" ]; then
337
- TODO_ANALYZER="node_modules/@caws/quality-gates/todo-analyzer.mjs"
338
- # Try monorepo structure (development)
339
- elif [ -f "packages/quality-gates/todo-analyzer.mjs" ]; then
340
- TODO_ANALYZER="packages/quality-gates/todo-analyzer.mjs"
341
- # Try local copy in scripts directory (if scaffolded)
342
- elif [ -f "scripts/todo-analyzer.mjs" ]; then
343
- TODO_ANALYZER="scripts/todo-analyzer.mjs"
335
+ # Option 1: Find TODO analyzer .mjs file (if installed locally)
336
+ if [ "$TODO_CHECK_RAN" = false ]; then
337
+ TODO_ANALYZER=""
338
+
339
+ # Try quality gates package TODO analyzer (published package)
340
+ if [ -f "node_modules/@paths.design/quality-gates/todo-analyzer.mjs" ]; then
341
+ TODO_ANALYZER="node_modules/@paths.design/quality-gates/todo-analyzer.mjs"
342
+ # Try quality gates package TODO analyzer (monorepo/local copy)
343
+ elif [ -f "node_modules/@caws/quality-gates/todo-analyzer.mjs" ]; then
344
+ TODO_ANALYZER="node_modules/@caws/quality-gates/todo-analyzer.mjs"
345
+ # Try monorepo structure (development)
346
+ elif [ -f "packages/quality-gates/todo-analyzer.mjs" ]; then
347
+ TODO_ANALYZER="packages/quality-gates/todo-analyzer.mjs"
348
+ # Try local copy in scripts directory (if scaffolded)
349
+ elif [ -f "scripts/todo-analyzer.mjs" ]; then
350
+ TODO_ANALYZER="scripts/todo-analyzer.mjs"
351
+ fi
352
+
353
+ # Run TODO analyzer if found
354
+ if [ -n "$TODO_ANALYZER" ] && command -v node >/dev/null 2>&1; then
355
+ if node "$TODO_ANALYZER" --staged-only --ci-mode --min-confidence 0.8 >/dev/null 2>&1; then
356
+ echo "✅ No critical hidden TODOs found in staged files"
357
+ TODO_CHECK_RAN=true
358
+ else
359
+ echo "❌ Critical hidden TODOs detected in staged files - commit blocked"
360
+ echo "💡 Fix stub implementations and placeholder code before committing"
361
+ echo "📖 See docs/PLACEHOLDER-DETECTION-GUIDE.md for classification"
362
+ echo ""
363
+ echo "🔍 Running detailed analysis on staged files..."
364
+ node "$TODO_ANALYZER" --staged-only --min-confidence 0.8
365
+ exit 1
366
+ fi
367
+ fi
344
368
  fi
345
369
 
346
- # Run TODO analyzer if found
347
- if [ -n "$TODO_ANALYZER" ] && command -v node >/dev/null 2>&1; then
348
- if node "$TODO_ANALYZER" --staged-only --ci-mode --min-confidence 0.8 >/dev/null 2>&1; then
349
- echo "✅ No critical hidden TODOs found in staged files"
350
- else
351
- echo "❌ Critical hidden TODOs detected in staged files - commit blocked"
352
- echo "💡 Fix stub implementations and placeholder code before committing"
353
- echo "📖 See docs/PLACEHOLDER-DETECTION-GUIDE.md for classification"
354
- echo ""
355
- echo "🔍 Running detailed analysis on staged files..."
356
- node "$TODO_ANALYZER" --staged-only --min-confidence 0.8
357
- exit 1
358
- fi
359
- # Fallback to legacy Python analyzer (deprecated - will be removed)
360
- elif command -v python3 >/dev/null 2>&1 && [ -f "scripts/v3/analysis/todo_analyzer.py" ]; then
370
+ # Option 2: Fallback to legacy Python analyzer (deprecated - will be removed)
371
+ if [ "$TODO_CHECK_RAN" = false ] && command -v python3 >/dev/null 2>&1 && [ -f "scripts/v3/analysis/todo_analyzer.py" ]; then
361
372
  echo "⚠️ Using legacy Python TODO analyzer (deprecated)"
362
- echo "💡 Install @paths.design/quality-gates for Node.js version: npm install --save-dev @paths.design/quality-gates"
363
373
  if python3 scripts/v3/analysis/todo_analyzer.py --staged-only --ci-mode --min-confidence 0.8 >/dev/null 2>&1; then
364
374
  echo "✅ No critical hidden TODOs found in staged files"
375
+ TODO_CHECK_RAN=true
365
376
  else
366
377
  echo "❌ Critical hidden TODOs detected in staged files - commit blocked"
367
378
  echo "💡 Fix stub implementations and placeholder code before committing"
@@ -371,9 +382,16 @@ if [ "$QUALITY_GATES_RAN" = true ]; then
371
382
  python3 scripts/v3/analysis/todo_analyzer.py --staged-only --min-confidence 0.8
372
383
  exit 1
373
384
  fi
374
- else
385
+ fi
386
+
387
+ # Option 3: No analyzer available - show language-aware suggestions
388
+ if [ "$TODO_CHECK_RAN" = false ]; then
375
389
  echo "⚠️ TODO analyzer not available - skipping hidden TODO check"
376
- echo "💡 Install @paths.design/quality-gates for TODO analysis: npm install --save-dev @paths.design/quality-gates"
390
+ echo "💡 Available options for TODO analysis:"
391
+ ${todoSuggestion
392
+ .split('\n')
393
+ .map((line) => ` echo "${line.replace(/"/g, '\\"')}"`)
394
+ .join('\n')}
377
395
  fi
378
396
  fi
379
397
 
@@ -444,8 +462,8 @@ for arg in "$@"; do
444
462
  echo "💡 To fix issues locally:"
445
463
  echo " 1. Run: caws validate"
446
464
  echo " 2. Fix reported issues"
447
- echo " 3. Commit fixes: git commit --no-verify (allowed)"
448
- echo " 4. Push again: git push (no --no-verify)"
465
+ echo " 3. Commit fixes: git commit --no-verify \\(allowed\\)"
466
+ echo " 4. Push again: git push \\(no --no-verify\\)"
449
467
  exit 1
450
468
  fi
451
469
  done
@@ -508,13 +526,13 @@ if command -v caws >/dev/null 2>&1; then
508
526
  echo " No active waivers found"
509
527
  echo ""
510
528
  echo "💡 If this is infrastructure/setup work, you can create a waiver:"
511
- echo " caws waivers create \\"
512
- echo " --title='Initial CAWS setup' \\"
513
- echo " --reason=infrastructure_limitation \\"
514
- echo " --gates=contracts \\"
515
- echo " --expires-at='2024-12-31T23:59:59Z' \\"
516
- echo " --approved-by='@your-team' \\"
517
- echo " --impact-level=low \\"
529
+ echo " caws waivers create \\\\"
530
+ echo " --title='Initial CAWS setup' \\\\"
531
+ echo " --reason=infrastructure_limitation \\\\"
532
+ echo " --gates=contracts \\\\"
533
+ echo " --expires-at='2024-12-31T23:59:59Z' \\\\"
534
+ echo " --approved-by='@your-team' \\\\"
535
+ echo " --impact-level=low \\\\"
518
536
  echo " --mitigation-plan='Contracts will be added as features are developed'"
519
537
  fi
520
538
 
@@ -523,43 +541,121 @@ if command -v caws >/dev/null 2>&1; then
523
541
  echo "Next Steps:"
524
542
  echo " 1. Review errors above"
525
543
  echo " 2. Fix issues in .caws/working-spec.yaml"
526
- echo " 3. Run: caws validate (to verify fixes)"
527
- echo " 4. Commit fixes: git commit --no-verify (allowed)"
544
+ echo " 3. Run: caws validate \\(to verify fixes\\)"
545
+ echo " 4. Commit fixes: git commit --no-verify \\(allowed\\)"
528
546
  echo " 5. Push again: git push"
529
547
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
530
548
  exit 1
531
549
  fi
532
550
  fi
533
551
 
534
- # Run security checks
552
+ # Run full pre-push checks (full test suite required before push)
553
+ # Note: Pre-commit uses filtered tests for speed, but push requires full suite
554
+ echo ""
555
+ echo "⚡ Running full pre-push checks (full test suite required)..."
556
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
557
+
558
+ QUICK_CHECKS_FAILED=false
559
+
560
+ # 1. Linting (fast)
561
+ if [ -f "package.json" ]; then
562
+ if command -v npm >/dev/null 2>&1; then
563
+ if grep -q '"lint"' package.json; then
564
+ echo "🔍 Running linting..."
565
+ if npm run lint >/dev/null 2>&1; then
566
+ echo "✅ Linting passed"
567
+ else
568
+ echo "❌ Linting failed"
569
+ echo "💡 Fix lint errors: npm run lint"
570
+ QUICK_CHECKS_FAILED=true
571
+ fi
572
+ fi
573
+ fi
574
+ fi
575
+
576
+ # 2. Type checking (fast for TypeScript/JavaScript)
577
+ if [ -f "package.json" ]; then
578
+ if command -v npm >/dev/null 2>&1; then
579
+ if grep -q '"typecheck"' package.json; then
580
+ echo "🔍 Running type checking..."
581
+ if npm run typecheck >/dev/null 2>&1; then
582
+ echo "✅ Type checking passed"
583
+ else
584
+ echo "❌ Type checking failed"
585
+ echo "💡 Fix type errors: npm run typecheck"
586
+ QUICK_CHECKS_FAILED=true
587
+ fi
588
+ fi
589
+ fi
590
+ fi
591
+
592
+ # 3. Run FULL test suite (required for push) - no filtering
593
+ # Pre-commit uses filtered tests for speed, but push requires full suite
594
+ if [ -f "package.json" ]; then
595
+ if command -v npm >/dev/null 2>&1 && grep -q '"test"' package.json; then
596
+ echo "🧪 Running FULL test suite (required for push)..."
597
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
598
+ if npm test 2>&1 | tee /tmp/pre-push-test-full.log; then
599
+ echo "✅ Full test suite passed"
600
+ rm -f /tmp/pre-push-test-full.log
601
+ else
602
+ FULL_TEST_EXIT_CODE=\${PIPESTATUS[0]}
603
+ echo "❌ Full test suite failed (exit code: \${FULL_TEST_EXIT_CODE})"
604
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
605
+ echo "Test output (last 100 lines):"
606
+ tail -100 /tmp/pre-push-test-full.log 2>/dev/null || echo "No test output captured"
607
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
608
+ echo "💡 Fix test failures before pushing: npm test"
609
+ rm -f /tmp/pre-push-test-full.log
610
+ QUICK_CHECKS_FAILED=true
611
+ fi
612
+ fi
613
+ fi
614
+
615
+ # 4. Security checks (non-blocking warnings)
616
+ echo ""
535
617
  echo "🔒 Running security checks..."
536
618
  if [ -f "package.json" ]; then
537
- # Check for vulnerabilities
538
619
  if command -v npm >/dev/null 2>&1; then
539
620
  echo "🔍 Checking for vulnerabilities..."
540
621
  if npm audit --audit-level moderate >/dev/null 2>&1; then
541
622
  echo "✅ Security audit passed"
542
623
  else
543
- echo "⚠️ Security vulnerabilities found"
624
+ echo "⚠️ Security vulnerabilities found (non-blocking)"
544
625
  echo "💡 Review with: npm audit"
545
- # Don't fail on warnings, just warn
546
626
  fi
547
627
  fi
548
628
  elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
549
- # Python project security checks
550
629
  if command -v pip-audit >/dev/null 2>&1; then
551
630
  echo "🔍 Checking Python vulnerabilities..."
552
- pip-audit --desc 2>/dev/null || echo "⚠️ Install pip-audit for vulnerability checks: pip install pip-audit"
631
+ pip-audit --desc 2>/dev/null || echo "⚠️ Install pip-audit: pip install pip-audit"
553
632
  fi
554
633
  elif [ -f "Cargo.toml" ]; then
555
- # Rust project security checks
556
634
  if command -v cargo-audit >/dev/null 2>&1; then
557
635
  echo "🔍 Checking Rust vulnerabilities..."
558
- cargo audit 2>/dev/null || echo "⚠️ Install cargo-audit for vulnerability checks: cargo install cargo-audit"
636
+ cargo audit 2>/dev/null || echo "⚠️ Install cargo-audit: cargo install cargo-audit"
559
637
  fi
560
638
  fi
561
639
 
562
- echo "🎉 Pre-push checks completed!"
640
+ # Fail if any checks failed
641
+ if [ "$QUICK_CHECKS_FAILED" = true ]; then
642
+ echo ""
643
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
644
+ echo "❌ Pre-push checks failed"
645
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
646
+ echo "All checks (linting/type checking/full test suite) must pass before push."
647
+ echo ""
648
+ echo "💡 Fix failures before pushing:"
649
+ echo " - Linting: npm run lint"
650
+ echo " - Type checking: npm run typecheck"
651
+ echo " - Tests: npm test"
652
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
653
+ exit 1
654
+ fi
655
+
656
+ echo ""
657
+ echo "✅ Pre-push checks completed!"
658
+ echo "💡 All quality gates passed - ready to push"
563
659
  `;
564
660
  }
565
661
 
@@ -583,7 +679,7 @@ fi
583
679
 
584
680
  # Basic commit message validation
585
681
  if [ \${#COMMIT_MSG} -lt 10 ]; then
586
- echo "❌ Commit message too short (minimum 10 characters)"
682
+ echo "❌ Commit message too short \\(minimum 10 characters\\)"
587
683
  echo "💡 Write descriptive commit messages"
588
684
  exit 1
589
685
  fi
@@ -700,4 +796,9 @@ module.exports = {
700
796
  scaffoldGitHooks,
701
797
  removeGitHooks,
702
798
  checkGitHooksStatus,
799
+ // Export generator functions for testing
800
+ generatePrePushHook,
801
+ generatePreCommitHook,
802
+ generatePostCommitHook,
803
+ generateCommitMsgHook,
703
804
  };
@@ -735,7 +735,7 @@ async function scaffoldProject(options) {
735
735
  }
736
736
 
737
737
  // Update .gitignore to exclude CAWS local runtime files
738
- const gitignoreUpdated = await updateGitignore(targetDir);
738
+ const gitignoreUpdated = await updateGitignore(currentDir);
739
739
  if (gitignoreUpdated) {
740
740
  console.log(chalk.green('\n✅ Updated .gitignore to exclude CAWS local runtime files'));
741
741
  console.log(
@@ -18,3 +18,4 @@ node .caws/tools/scope-guard.js check .caws/working-spec.yaml
18
18
 
19
19
  The `.cursor/hooks/scope-guard.sh` hook automatically uses this tool to validate file attachments against working spec scope boundaries.
20
20
 
21
+
@@ -116,3 +116,4 @@ module.exports = {
116
116
  formatGitLockError,
117
117
  };
118
118
 
119
+
@@ -111,9 +111,7 @@ function detectsPublishing(cwd = process.cwd()) {
111
111
  // Check package.json for npm publishing
112
112
  if (files.includes('package.json')) {
113
113
  try {
114
- const packageJson = JSON.parse(
115
- fs.readFileSync(path.join(cwd, 'package.json'), 'utf8')
116
- );
114
+ const packageJson = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
117
115
 
118
116
  // Indicators of publishing:
119
117
  // - Has publishConfig
@@ -123,9 +121,7 @@ function detectsPublishing(cwd = process.cwd()) {
123
121
  const hasPublishConfig = packageJson.publishConfig;
124
122
  const hasPublishScript =
125
123
  packageJson.scripts &&
126
- Object.keys(packageJson.scripts).some((key) =>
127
- key.toLowerCase().includes('publish')
128
- );
124
+ Object.keys(packageJson.scripts).some((key) => key.toLowerCase().includes('publish'));
129
125
  const hasScopedName = packageJson.name && packageJson.name.startsWith('@');
130
126
  const hasRepository = packageJson.repository;
131
127
 
@@ -140,16 +136,13 @@ function detectsPublishing(cwd = process.cwd()) {
140
136
  // Check pyproject.toml for PyPI publishing
141
137
  if (files.includes('pyproject.toml')) {
142
138
  try {
143
- const pyprojectContent = fs.readFileSync(
144
- path.join(cwd, 'pyproject.toml'),
145
- 'utf8'
146
- );
139
+ const pyprojectContent = fs.readFileSync(path.join(cwd, 'pyproject.toml'), 'utf8');
147
140
 
148
141
  // Check for build system and project metadata (indicates publishable package)
149
142
  const hasBuildSystem = pyprojectContent.includes('[build-system]');
150
143
  const hasProjectMetadata = pyprojectContent.includes('[project]');
151
- const hasToolPublish = pyprojectContent.includes('[tool.publish]') ||
152
- pyprojectContent.includes('[tool.twine]');
144
+ const hasToolPublish =
145
+ pyprojectContent.includes('[tool.publish]') || pyprojectContent.includes('[tool.twine]');
153
146
 
154
147
  if ((hasBuildSystem && hasProjectMetadata) || hasToolPublish) {
155
148
  return true;
@@ -177,10 +170,7 @@ function detectsPublishing(cwd = process.cwd()) {
177
170
  const workflowFiles = fs.readdirSync(workflowsPath);
178
171
  for (const workflowFile of workflowFiles) {
179
172
  if (workflowFile.endsWith('.yml') || workflowFile.endsWith('.yaml')) {
180
- const workflowContent = fs.readFileSync(
181
- path.join(workflowsPath, workflowFile),
182
- 'utf8'
183
- );
173
+ const workflowContent = fs.readFileSync(path.join(workflowsPath, workflowFile), 'utf8');
184
174
  // Check for common publishing actions/commands
185
175
  if (
186
176
  workflowContent.includes('npm publish') ||
@@ -201,8 +191,178 @@ function detectsPublishing(cwd = process.cwd()) {
201
191
  return false;
202
192
  }
203
193
 
194
+ /**
195
+ * Detect primary programming language(s) used in project
196
+ * @param {string} cwd - Current working directory
197
+ * @returns {Object} Language detection result with primary language and indicators
198
+ */
199
+ function detectProjectLanguage(cwd = process.cwd()) {
200
+ const files = fs.readdirSync(cwd);
201
+ const indicators = {
202
+ javascript: false,
203
+ typescript: false,
204
+ python: false,
205
+ rust: false,
206
+ go: false,
207
+ java: false,
208
+ csharp: false,
209
+ php: false,
210
+ };
211
+
212
+ // JavaScript/TypeScript indicators
213
+ if (files.includes('package.json')) {
214
+ indicators.javascript = true;
215
+ try {
216
+ const packageJson = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
217
+ const allDeps = {
218
+ ...(packageJson.dependencies || {}),
219
+ ...(packageJson.devDependencies || {}),
220
+ };
221
+ if ('typescript' in allDeps || files.includes('tsconfig.json')) {
222
+ indicators.typescript = true;
223
+ indicators.javascript = false; // TypeScript supersedes JavaScript
224
+ }
225
+ } catch (e) {
226
+ // Ignore parse errors
227
+ }
228
+ }
229
+
230
+ // Python indicators
231
+ if (
232
+ files.includes('requirements.txt') ||
233
+ files.includes('pyproject.toml') ||
234
+ files.includes('setup.py') ||
235
+ files.includes('Pipfile') ||
236
+ files.includes('poetry.lock') ||
237
+ files.some((f) => f.endsWith('.py'))
238
+ ) {
239
+ indicators.python = true;
240
+ }
241
+
242
+ // Rust indicators
243
+ if (files.includes('Cargo.toml') || files.some((f) => f.endsWith('.rs'))) {
244
+ indicators.rust = true;
245
+ }
246
+
247
+ // Go indicators
248
+ if (
249
+ files.includes('go.mod') ||
250
+ files.includes('go.sum') ||
251
+ files.some((f) => f.endsWith('.go'))
252
+ ) {
253
+ indicators.go = true;
254
+ }
255
+
256
+ // Java indicators
257
+ if (
258
+ files.includes('pom.xml') ||
259
+ files.includes('build.gradle') ||
260
+ files.some((f) => f.endsWith('.java'))
261
+ ) {
262
+ indicators.java = true;
263
+ }
264
+
265
+ // C# indicators
266
+ if (
267
+ files.some((f) => f.endsWith('.csproj')) ||
268
+ files.some((f) => f.endsWith('.sln')) ||
269
+ files.some((f) => f.endsWith('.cs'))
270
+ ) {
271
+ indicators.csharp = true;
272
+ }
273
+
274
+ // PHP indicators
275
+ if (
276
+ files.includes('composer.json') ||
277
+ files.includes('composer.lock') ||
278
+ files.some((f) => f.endsWith('.php'))
279
+ ) {
280
+ indicators.php = true;
281
+ }
282
+
283
+ // Determine primary language (priority order)
284
+ let primaryLanguage = 'unknown';
285
+ if (indicators.typescript) {
286
+ primaryLanguage = 'typescript';
287
+ } else if (indicators.javascript) {
288
+ primaryLanguage = 'javascript';
289
+ } else if (indicators.python) {
290
+ primaryLanguage = 'python';
291
+ } else if (indicators.rust) {
292
+ primaryLanguage = 'rust';
293
+ } else if (indicators.go) {
294
+ primaryLanguage = 'go';
295
+ } else if (indicators.java) {
296
+ primaryLanguage = 'java';
297
+ } else if (indicators.csharp) {
298
+ primaryLanguage = 'csharp';
299
+ } else if (indicators.php) {
300
+ primaryLanguage = 'php';
301
+ }
302
+
303
+ return {
304
+ primary: primaryLanguage,
305
+ indicators,
306
+ hasNodeJs: indicators.javascript || indicators.typescript,
307
+ hasPython: indicators.python,
308
+ };
309
+ }
310
+
311
+ /**
312
+ * Get language-agnostic suggestion for TODO analyzer installation
313
+ * Focuses on runtime availability (Node.js/npx) rather than project language
314
+ * @param {string} cwd - Current working directory
315
+ * @returns {string} Installation suggestion message
316
+ */
317
+ function getTodoAnalyzerSuggestion(cwd = process.cwd()) {
318
+ // Check runtime availability (language-agnostic)
319
+ let hasNodeJs = false;
320
+ let hasNpx = false;
321
+ try {
322
+ const { execSync } = require('child_process');
323
+ execSync('command -v node', { encoding: 'utf8', stdio: 'ignore' });
324
+ hasNodeJs = true;
325
+ execSync('command -v npx', { encoding: 'utf8', stdio: 'ignore' });
326
+ hasNpx = true;
327
+ } catch (e) {
328
+ // Node.js/npx not available
329
+ }
330
+
331
+ const suggestions = [];
332
+
333
+ if (hasNpx) {
334
+ // npx available - works for any language, no installation needed
335
+ suggestions.push(
336
+ ' • Use npx (no installation required): npx --yes @paths.design/quality-gates'
337
+ );
338
+ suggestions.push(' • Install package: npm install --save-dev @paths.design/quality-gates');
339
+ } else if (hasNodeJs) {
340
+ // Node.js available but npx not found (unusual)
341
+ suggestions.push(' • Install package: npm install --save-dev @paths.design/quality-gates');
342
+ suggestions.push(
343
+ ' • Install npx: npm install -g npx (then use: npx --yes @paths.design/quality-gates)'
344
+ );
345
+ } else {
346
+ // Node.js not available - suggest installation
347
+ suggestions.push(
348
+ ' • Install Node.js: https://nodejs.org/ (then use: npx --yes @paths.design/quality-gates)'
349
+ );
350
+ suggestions.push(' • Use CAWS MCP server: caws quality-gates (via MCP)');
351
+ }
352
+
353
+ // Check for project-specific scripts (language-agnostic - if they exist, suggest them)
354
+ const pythonScript = path.join(cwd, 'scripts', 'v3', 'analysis', 'todo_analyzer.py');
355
+ if (fs.existsSync(pythonScript)) {
356
+ suggestions.push(` • Use project script: python3 ${pythonScript}`);
357
+ }
358
+
359
+ return suggestions.join('\n');
360
+ }
361
+
204
362
  module.exports = {
205
363
  detectProjectType,
206
364
  shouldInitInCurrentDirectory,
207
365
  detectsPublishing,
366
+ detectProjectLanguage,
367
+ getTodoAnalyzerSuggestion,
208
368
  };
@@ -11,6 +11,7 @@ const fs = require('fs');
11
11
  const path = require('path');
12
12
  const yaml = require('js-yaml');
13
13
  const { execSync } = require('child_process');
14
+ const { getTodoAnalyzerSuggestion } = require('./project-analysis');
14
15
 
15
16
  /**
16
17
  * Quality Gate Configuration
@@ -229,17 +230,17 @@ function checkHiddenTodos(stagedFiles) {
229
230
 
230
231
  if (!analyzerPath) {
231
232
  console.warn('⚠️ TODO analyzer not found - skipping TODO analysis');
232
- console.warn(
233
- '💡 Install @paths.design/quality-gates: npm install --save-dev @paths.design/quality-gates'
234
- );
233
+ const suggestion = getTodoAnalyzerSuggestion(process.cwd());
234
+ console.warn('💡 Available options for TODO analysis:');
235
+ console.warn(suggestion);
235
236
  return { todos: [], blocking: 0, total: 0 };
236
237
  }
237
238
 
238
239
  if (usePython) {
239
240
  console.warn('⚠️ Using legacy Python TODO analyzer (deprecated)');
240
- console.warn(
241
- '💡 Install @paths.design/quality-gates for Node.js version: npm install --save-dev @paths.design/quality-gates'
242
- );
241
+ const suggestion = getTodoAnalyzerSuggestion(process.cwd());
242
+ console.warn('💡 Consider upgrading to Node.js version:');
243
+ console.warn(suggestion);
243
244
  }
244
245
 
245
246
  // Run the TODO analyzer with staged files
@@ -512,6 +512,10 @@ async function suggestMigration() {
512
512
  function suggestFeatureBreakdown(legacySpec) {
513
513
  const features = [];
514
514
 
515
+ if (!legacySpec) {
516
+ return features;
517
+ }
518
+
515
519
  if (legacySpec.acceptance && legacySpec.acceptance.length > 0) {
516
520
  // Group acceptance criteria by logical features
517
521
  const criteriaByFeature = {};
@@ -153,3 +153,4 @@ module.exports = {
153
153
  formatYamlError,
154
154
  };
155
155
 
156
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paths.design/caws-cli",
3
- "version": "7.0.3",
3
+ "version": "8.0.0",
4
4
  "description": "CAWS CLI - Coding Agent Workflow System command line tools",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -18,3 +18,4 @@ node .caws/tools/scope-guard.js check .caws/working-spec.yaml
18
18
 
19
19
  The `.cursor/hooks/scope-guard.sh` hook automatically uses this tool to validate file attachments against working spec scope boundaries.
20
20
 
21
+