@paths.design/caws-cli 2.0.1 → 3.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 (52) hide show
  1. package/dist/index.d.ts.map +1 -1
  2. package/dist/index.js +1463 -121
  3. package/package.json +3 -2
  4. package/templates/agents.md +820 -0
  5. package/templates/apps/tools/caws/COMPLETION_REPORT.md +331 -0
  6. package/templates/apps/tools/caws/MIGRATION_SUMMARY.md +360 -0
  7. package/templates/apps/tools/caws/README.md +463 -0
  8. package/templates/apps/tools/caws/TEST_STATUS.md +365 -0
  9. package/templates/apps/tools/caws/attest.js +357 -0
  10. package/templates/apps/tools/caws/ci-optimizer.js +642 -0
  11. package/templates/apps/tools/caws/config.ts +245 -0
  12. package/templates/apps/tools/caws/cross-functional.js +876 -0
  13. package/templates/apps/tools/caws/dashboard.js +1112 -0
  14. package/templates/apps/tools/caws/flake-detector.ts +362 -0
  15. package/templates/apps/tools/caws/gates.js +198 -0
  16. package/templates/apps/tools/caws/gates.ts +237 -0
  17. package/templates/apps/tools/caws/language-adapters.ts +381 -0
  18. package/templates/apps/tools/caws/language-support.d.ts +367 -0
  19. package/templates/apps/tools/caws/language-support.d.ts.map +1 -0
  20. package/templates/apps/tools/caws/language-support.js +585 -0
  21. package/templates/apps/tools/caws/legacy-assessment.ts +408 -0
  22. package/templates/apps/tools/caws/legacy-assessor.js +764 -0
  23. package/templates/apps/tools/caws/mutant-analyzer.js +734 -0
  24. package/templates/apps/tools/caws/perf-budgets.ts +349 -0
  25. package/templates/apps/tools/caws/prompt-lint.js.backup +274 -0
  26. package/templates/apps/tools/caws/property-testing.js +707 -0
  27. package/templates/apps/tools/caws/provenance.d.ts +14 -0
  28. package/templates/apps/tools/caws/provenance.d.ts.map +1 -0
  29. package/templates/apps/tools/caws/provenance.js +132 -0
  30. package/templates/apps/tools/caws/provenance.js.backup +73 -0
  31. package/templates/apps/tools/caws/provenance.ts +211 -0
  32. package/templates/apps/tools/caws/schemas/waivers.schema.json +30 -0
  33. package/templates/apps/tools/caws/schemas/working-spec.schema.json +115 -0
  34. package/templates/apps/tools/caws/scope-guard.js +208 -0
  35. package/templates/apps/tools/caws/security-provenance.ts +483 -0
  36. package/templates/apps/tools/caws/shared/base-tool.ts +281 -0
  37. package/templates/apps/tools/caws/shared/config-manager.ts +366 -0
  38. package/templates/apps/tools/caws/shared/gate-checker.ts +597 -0
  39. package/templates/apps/tools/caws/shared/types.ts +444 -0
  40. package/templates/apps/tools/caws/shared/validator.ts +305 -0
  41. package/templates/apps/tools/caws/shared/waivers-manager.ts +174 -0
  42. package/templates/apps/tools/caws/spec-test-mapper.ts +391 -0
  43. package/templates/apps/tools/caws/templates/working-spec.template.yml +60 -0
  44. package/templates/apps/tools/caws/test-quality.js +578 -0
  45. package/templates/apps/tools/caws/tools-allow.json +331 -0
  46. package/templates/apps/tools/caws/validate.js +76 -0
  47. package/templates/apps/tools/caws/validate.ts +228 -0
  48. package/templates/apps/tools/caws/waivers.js +344 -0
  49. package/templates/apps/tools/caws/waivers.yml +19 -0
  50. package/templates/codemod/README.md +1 -0
  51. package/templates/codemod/test.js +1 -0
  52. package/templates/docs/README.md +150 -0
package/dist/index.js CHANGED
@@ -37,7 +37,9 @@ try {
37
37
  }
38
38
  }
39
39
  } catch (error) {
40
- console.warn('⚠️ Language support tools not available');
40
+ console.warn(chalk.yellow('⚠️ Language support tools not available'));
41
+ console.warn(chalk.blue('💡 This may limit language-specific configuration features'));
42
+ console.warn(chalk.blue('💡 For full functionality, ensure caws-template package is available'));
41
43
  }
42
44
 
43
45
  const program = new Command();
@@ -124,39 +126,60 @@ function detectCAWSSetup(cwd = process.cwd()) {
124
126
  // Check for template directory - try multiple possible locations
125
127
  let templateDir = null;
126
128
  const possibleTemplatePaths = [
129
+ // FIRST: Try bundled templates (for npm-installed CLI)
130
+ { path: path.resolve(__dirname, '../templates'), source: 'bundled with CLI' },
131
+ { path: path.resolve(__dirname, 'templates'), source: 'bundled with CLI (fallback)' },
127
132
  // Try relative to current working directory (for monorepo setups)
128
- path.resolve(cwd, '../caws-template'),
129
- path.resolve(cwd, '../../caws-template'),
130
- path.resolve(cwd, '../../../caws-template'),
131
- path.resolve(cwd, 'packages/caws-template'),
132
- path.resolve(cwd, 'caws-template'),
133
+ { path: path.resolve(cwd, '../caws-template'), source: 'monorepo parent directory' },
134
+ { path: path.resolve(cwd, '../../caws-template'), source: 'monorepo grandparent' },
135
+ { path: path.resolve(cwd, '../../../caws-template'), source: 'workspace root' },
136
+ { path: path.resolve(cwd, 'packages/caws-template'), source: 'packages/ subdirectory' },
137
+ { path: path.resolve(cwd, 'caws-template'), source: 'caws-template/ subdirectory' },
133
138
  // Try relative to CLI location (for installed CLI)
134
- path.resolve(__dirname, '../caws-template'),
135
- path.resolve(__dirname, '../../caws-template'),
136
- path.resolve(__dirname, '../../../caws-template'),
139
+ { path: path.resolve(__dirname, '../caws-template'), source: 'CLI installation' },
140
+ { path: path.resolve(__dirname, '../../caws-template'), source: 'CLI parent directory' },
141
+ { path: path.resolve(__dirname, '../../../caws-template'), source: 'CLI workspace root' },
137
142
  // Try absolute paths for CI environments
138
- path.resolve(process.cwd(), 'packages/caws-template'),
139
- path.resolve(process.cwd(), '../packages/caws-template'),
140
- path.resolve(process.cwd(), '../../packages/caws-template'),
141
- path.resolve(process.cwd(), '../../../packages/caws-template'),
143
+ { path: path.resolve(process.cwd(), 'packages/caws-template'), source: 'current packages/' },
144
+ { path: path.resolve(process.cwd(), '../packages/caws-template'), source: 'parent packages/' },
145
+ {
146
+ path: path.resolve(process.cwd(), '../../packages/caws-template'),
147
+ source: 'grandparent packages/',
148
+ },
149
+ {
150
+ path: path.resolve(process.cwd(), '../../../packages/caws-template'),
151
+ source: 'workspace packages/',
152
+ },
142
153
  // Try from workspace root
143
- path.resolve(process.cwd(), 'caws-template'),
154
+ { path: path.resolve(process.cwd(), 'caws-template'), source: 'workspace caws-template/' },
144
155
  // Try various other common locations
145
- '/home/runner/work/coding-agent-working-standard/coding-agent-working-standard/packages/caws-template',
146
- '/workspace/packages/caws-template',
147
- '/caws/packages/caws-template',
156
+ {
157
+ path: '/home/runner/work/coding-agent-working-standard/coding-agent-working-standard/packages/caws-template',
158
+ source: 'GitHub Actions CI',
159
+ },
160
+ { path: '/workspace/packages/caws-template', source: 'Docker workspace' },
161
+ { path: '/caws/packages/caws-template', source: 'Container workspace' },
148
162
  ];
149
163
 
150
- for (const testPath of possibleTemplatePaths) {
164
+ for (const { path: testPath, source } of possibleTemplatePaths) {
151
165
  if (fs.existsSync(testPath)) {
152
166
  templateDir = testPath;
153
167
  if (!isQuietCommand) {
154
- console.log(`✅ Found template directory: ${testPath}`);
168
+ console.log(`✅ Found CAWS templates in ${source}:`);
169
+ console.log(` ${chalk.gray(testPath)}`);
155
170
  }
156
171
  break;
157
172
  }
158
173
  }
159
174
 
175
+ if (!templateDir && !isQuietCommand) {
176
+ console.warn(chalk.yellow('⚠️ CAWS templates not found in standard locations'));
177
+ console.warn(chalk.blue('💡 This may limit available scaffolding features'));
178
+ console.warn(
179
+ chalk.blue('💡 For full functionality, ensure caws-template package is available')
180
+ );
181
+ }
182
+
160
183
  const hasTemplateDir = templateDir !== null;
161
184
 
162
185
  return {
@@ -183,6 +206,22 @@ let cawsSetup = null;
183
206
  // Initialize global setup detection
184
207
  try {
185
208
  cawsSetup = detectCAWSSetup();
209
+
210
+ // If no template dir found in current directory, check CLI installation location
211
+ if (!cawsSetup.hasTemplateDir) {
212
+ const cliTemplatePaths = [
213
+ path.resolve(__dirname, '../templates'),
214
+ path.resolve(__dirname, 'templates'),
215
+ ];
216
+
217
+ for (const testPath of cliTemplatePaths) {
218
+ if (fs.existsSync(testPath)) {
219
+ cawsSetup.templateDir = testPath;
220
+ cawsSetup.hasTemplateDir = true;
221
+ break;
222
+ }
223
+ }
224
+ }
186
225
  } catch (error) {
187
226
  console.warn('⚠️ Failed to detect CAWS setup globally:', error.message);
188
227
  cawsSetup = {
@@ -358,76 +397,517 @@ const validateWorkingSpec = (spec) => {
358
397
  }
359
398
  };
360
399
 
361
- // Only log schema validation if not running quiet commands
362
- if (!process.argv.includes('--version') && !process.argv.includes('-V')) {
363
- console.log(chalk.green('✅ Schema validation initialized successfully'));
364
- }
365
-
366
400
  /**
367
- * Copy template files to destination
368
- * @param {string} templatePath - Source template path
369
- * @param {string} destPath - Destination path
370
- * @param {Object} replacements - Template variable replacements
401
+ * Enhanced validation with suggestions and auto-fix
371
402
  */
372
- async function copyTemplate(templatePath, destPath, replacements = {}) {
403
+ function validateWorkingSpecWithSuggestions(spec, options = {}) {
404
+ const { autoFix = false, suggestions = true } = options;
405
+
373
406
  try {
374
- // Ensure destination directory exists
375
- await fs.ensureDir(destPath);
376
-
377
- // Check if template directory exists
378
- if (!fs.existsSync(templatePath)) {
379
- console.error(chalk.red('❌ Template directory not found:'), templatePath);
380
- console.error(chalk.blue("💡 Make sure you're running the CLI from the correct directory"));
381
- throw new Error(`Template directory not found: ${templatePath}`);
407
+ // Basic structural validation for essential fields
408
+ const requiredFields = [
409
+ 'id',
410
+ 'title',
411
+ 'risk_tier',
412
+ 'mode',
413
+ 'change_budget',
414
+ 'blast_radius',
415
+ 'operational_rollback_slo',
416
+ 'scope',
417
+ 'invariants',
418
+ 'acceptance',
419
+ 'non_functional',
420
+ 'contracts',
421
+ ];
422
+
423
+ let errors = [];
424
+ let warnings = [];
425
+ let fixes = [];
426
+
427
+ for (const field of requiredFields) {
428
+ if (!spec[field]) {
429
+ errors.push({
430
+ instancePath: `/${field}`,
431
+ message: `Missing required field: ${field}`,
432
+ suggestion: getFieldSuggestion(field, spec),
433
+ canAutoFix: canAutoFixField(field, spec),
434
+ });
435
+ }
382
436
  }
383
437
 
384
- // Copy all files and directories
385
- await fs.copy(templatePath, destPath);
438
+ // Validate specific field formats
439
+ if (spec.id && !/^[A-Z]+-\d+$/.test(spec.id)) {
440
+ errors.push({
441
+ instancePath: '/id',
442
+ message: 'Project ID should be in format: PREFIX-NUMBER (e.g., FEAT-1234)',
443
+ suggestion: 'Use format like: PROJ-001, FEAT-002, FIX-003',
444
+ canAutoFix: false,
445
+ });
446
+ }
386
447
 
387
- // Replace template variables in text files
388
- const files = await fs.readdir(destPath, { recursive: true });
448
+ // Validate risk tier
449
+ if (spec.risk_tier !== undefined && (spec.risk_tier < 1 || spec.risk_tier > 3)) {
450
+ errors.push({
451
+ instancePath: '/risk_tier',
452
+ message: 'Risk tier must be 1, 2, or 3',
453
+ suggestion:
454
+ 'Tier 1: Critical (auth, billing), Tier 2: Standard (features), Tier 3: Low risk (UI)',
455
+ canAutoFix: true,
456
+ });
457
+ fixes.push({ field: 'risk_tier', value: Math.max(1, Math.min(3, spec.risk_tier || 2)) });
458
+ }
389
459
 
390
- for (const file of files) {
391
- const filePath = path.join(destPath, file);
392
- const stat = await fs.stat(filePath);
460
+ // Validate scope.in is not empty
461
+ if (!spec.scope || !spec.scope.in || spec.scope.in.length === 0) {
462
+ errors.push({
463
+ instancePath: '/scope/in',
464
+ message: 'Scope IN must not be empty',
465
+ suggestion: 'Specify directories/files that are included in changes',
466
+ canAutoFix: false,
467
+ });
468
+ }
393
469
 
394
- if (
395
- stat.isFile() &&
396
- (file.endsWith('.md') || file.endsWith('.yml') || file.endsWith('.yaml'))
397
- ) {
398
- try {
399
- let content = await fs.readFile(filePath, 'utf8');
400
- Object.entries(replacements).forEach(([key, value]) => {
401
- content = content.replace(new RegExp(`{{${key}}}`, 'g'), value);
470
+ // Check for common issues
471
+ if (!spec.invariants || spec.invariants.length === 0) {
472
+ warnings.push({
473
+ instancePath: '/invariants',
474
+ message: 'No system invariants defined',
475
+ suggestion: 'Add 1-3 statements about what must always remain true',
476
+ });
477
+ }
478
+
479
+ if (!spec.acceptance || spec.acceptance.length === 0) {
480
+ warnings.push({
481
+ instancePath: '/acceptance',
482
+ message: 'No acceptance criteria defined',
483
+ suggestion: 'Add acceptance criteria in GIVEN/WHEN/THEN format',
484
+ });
485
+ }
486
+
487
+ // Validate experimental mode
488
+ if (spec.experimental_mode) {
489
+ if (typeof spec.experimental_mode !== 'object') {
490
+ errors.push({
491
+ instancePath: '/experimental_mode',
492
+ message:
493
+ 'Experimental mode must be an object with enabled, rationale, and expires_at fields',
494
+ suggestion: 'Fix experimental_mode structure',
495
+ canAutoFix: false,
496
+ });
497
+ } else {
498
+ const requiredExpFields = ['enabled', 'rationale', 'expires_at'];
499
+ for (const field of requiredExpFields) {
500
+ if (!(field in spec.experimental_mode)) {
501
+ errors.push({
502
+ instancePath: `/experimental_mode/${field}`,
503
+ message: `Missing required experimental mode field: ${field}`,
504
+ suggestion: `Add ${field} to experimental_mode`,
505
+ canAutoFix: field === 'enabled' ? true : false,
506
+ });
507
+ if (field === 'enabled') {
508
+ fixes.push({ field: `experimental_mode.${field}`, value: true });
509
+ }
510
+ }
511
+ }
512
+
513
+ if (spec.experimental_mode.enabled && spec.risk_tier < 3) {
514
+ warnings.push({
515
+ instancePath: '/experimental_mode',
516
+ message: 'Experimental mode can only be used with Tier 3 (low risk) changes',
517
+ suggestion: 'Either set risk_tier to 3 or disable experimental mode',
402
518
  });
403
- await fs.writeFile(filePath, content);
404
- } catch (fileError) {
405
- console.warn(
406
- chalk.yellow(`⚠️ Warning: Could not process template file ${file}:`),
407
- fileError.message
408
- );
409
519
  }
410
520
  }
411
521
  }
412
522
 
413
- console.log(chalk.green('✅ Template files copied successfully'));
523
+ // Apply auto-fixes if requested
524
+ if (autoFix && fixes.length > 0) {
525
+ console.log(chalk.cyan('🔧 Applying auto-fixes...'));
526
+ for (const fix of fixes) {
527
+ if (fix.field.includes('.')) {
528
+ const [parent, child] = fix.field.split('.');
529
+ if (!spec[parent]) spec[parent] = {};
530
+ spec[parent][child] = fix.value;
531
+ } else {
532
+ spec[fix.field] = fix.value;
533
+ }
534
+ console.log(` Fixed: ${fix.field} = ${JSON.stringify(fix.value)}`);
535
+ }
536
+ }
537
+
538
+ // Display results
539
+ if (errors.length > 0) {
540
+ console.error(chalk.red('❌ Validation failed with errors:'));
541
+ errors.forEach((error, index) => {
542
+ console.error(`${index + 1}. ${error.instancePath || 'root'}: ${error.message}`);
543
+ if (suggestions && error.suggestion) {
544
+ console.error(` 💡 ${error.suggestion}`);
545
+ }
546
+ if (error.canAutoFix) {
547
+ console.error(` 🔧 Can auto-fix: ${autoFix ? 'applied' : 'run with --auto-fix'}`);
548
+ }
549
+ });
550
+ return { valid: false, errors, warnings };
551
+ }
552
+
553
+ if (warnings.length > 0 && suggestions) {
554
+ console.warn(chalk.yellow('⚠️ Validation passed with warnings:'));
555
+ warnings.forEach((warning, index) => {
556
+ console.warn(`${index + 1}. ${warning.instancePath || 'root'}: ${warning.message}`);
557
+ if (warning.suggestion) {
558
+ console.warn(` 💡 ${warning.suggestion}`);
559
+ }
560
+ });
561
+ }
562
+
563
+ console.log(chalk.green('✅ Working specification is valid'));
564
+ return { valid: true, errors: [], warnings };
414
565
  } catch (error) {
415
- console.error(chalk.red('❌ Error copying template:'), error.message);
566
+ console.error(chalk.red('❌ Error during validation:'), error.message);
567
+ return {
568
+ valid: false,
569
+ errors: [{ instancePath: '', message: `Validation error: ${error.message}` }],
570
+ warnings: [],
571
+ };
572
+ }
573
+ }
416
574
 
417
- if (error.code === 'EACCES') {
418
- console.error(
419
- chalk.blue('💡 This might be a permissions issue. Try running with elevated privileges.')
420
- );
421
- } else if (error.code === 'ENOENT') {
422
- console.error(
423
- chalk.blue('💡 Template directory not found. Make sure the caws-template directory exists.')
424
- );
425
- } else if (error.code === 'ENOSPC') {
426
- console.error(chalk.blue('💡 Not enough disk space to copy template files.'));
575
+ function getFieldSuggestion(field, _spec) {
576
+ const suggestions = {
577
+ id: 'Use format like: PROJ-001, FEAT-002, FIX-003',
578
+ title: 'Add a descriptive project title',
579
+ risk_tier: 'Choose: 1 (critical), 2 (standard), or 3 (low risk)',
580
+ mode: 'Choose: feature, refactor, fix, doc, or chore',
581
+ change_budget: 'Set max_files and max_loc based on risk tier',
582
+ blast_radius: 'List affected modules and data migration needs',
583
+ operational_rollback_slo: 'Choose: 1m, 5m, 15m, or 1h',
584
+ scope: "Define what's included (in) and excluded (out) from changes",
585
+ invariants: 'Add 1-3 statements about what must always remain true',
586
+ acceptance: 'Add acceptance criteria in GIVEN/WHEN/THEN format',
587
+ non_functional: 'Define accessibility, performance, and security requirements',
588
+ contracts: 'Specify API contracts (OpenAPI, GraphQL, etc.)',
589
+ };
590
+ return suggestions[field] || `Add the ${field} field`;
591
+ }
592
+
593
+ function canAutoFixField(field, _spec) {
594
+ const autoFixable = ['risk_tier'];
595
+ return autoFixable.includes(field);
596
+ }
597
+
598
+ /**
599
+ * Generate a getting started guide based on project analysis
600
+ */
601
+ function generateGettingStartedGuide(analysis) {
602
+ const { projectType, packageJson, hasTests, hasLinting } = analysis;
603
+
604
+ const projectName = packageJson.name || 'your-project';
605
+ const capitalizedType = projectType.charAt(0).toUpperCase() + projectType.slice(1);
606
+
607
+ let guide = `# Getting Started with CAWS - ${capitalizedType} Project
608
+
609
+ **Project**: ${projectName}
610
+ **Type**: ${capitalizedType}
611
+ **Generated**: ${new Date().toLocaleDateString()}
612
+
613
+ ---
614
+
615
+ ## Phase 1: Setup Verification (15 mins)
616
+
617
+ Complete these steps to ensure your CAWS setup is working:
618
+
619
+ ### ✅ Already Done
620
+ - [x] Initialize CAWS project
621
+ - [x] Generate working spec
622
+ - [x] Set up basic structure
623
+
624
+ ### Next Steps
625
+ - [ ] Review \`.caws/working-spec.yaml\` - customize for your needs
626
+ - [ ] Run validation: \`caws validate --suggestions\`
627
+ - [ ] Review tier policy in \`.caws/policy/\` (if applicable)
628
+ - [ ] Update \`.caws/templates/\` with project-specific examples
629
+
630
+ ---
631
+
632
+ ## Phase 2: First Feature (30 mins)
633
+
634
+ Time to create your first CAWS-managed feature:
635
+
636
+ ### Steps
637
+ 1. **Copy a template**:
638
+ \`\`\`bash
639
+ cp .caws/templates/feature.plan.md docs/plans/FEATURE-001.md
640
+ \`\`\`
641
+
642
+ 2. **Customize the plan**:
643
+ - Update title and description
644
+ - Fill in acceptance criteria (GIVEN/WHEN/THEN format)
645
+ - Set appropriate risk tier
646
+ - Define scope and invariants
647
+
648
+ 3. **Write tests first** (TDD approach):
649
+ \`\`\`bash
650
+ # For ${projectType} projects, focus on:
651
+ ${getTestingGuidance(projectType)}
652
+ \`\`\`
653
+
654
+ 4. **Implement the feature**:
655
+ - Stay within change budget limits
656
+ - Follow acceptance criteria
657
+ - Maintain system invariants
658
+
659
+ 5. **Run full verification**:
660
+ \`\`\`bash
661
+ caws validate --suggestions
662
+ ${hasTests ? 'npm test' : '# Add tests when ready'}
663
+ ${hasLinting ? 'npm run lint' : '# Add linting when ready'}
664
+ \`\`\`
665
+
666
+ ---
667
+
668
+ ## Phase 3: CI/CD Setup (20 mins)
669
+
670
+ Set up automated quality gates:
671
+
672
+ ### GitHub Actions (Recommended)
673
+ 1. **Create workflow**: \`.github/workflows/caws.yml\`
674
+ \`\`\`yaml
675
+ name: CAWS Quality Gates
676
+ on: [pull_request]
677
+
678
+ jobs:
679
+ validate:
680
+ runs-on: ubuntu-latest
681
+ steps:
682
+ - uses: actions/checkout@v4
683
+ - uses: actions/setup-node@v4
684
+ with:
685
+ node-version: '18'
686
+ - run: npm ci
687
+ - run: npx caws validate --quiet
688
+ - run: npm test # Add when ready
689
+ \`\`\`
690
+
691
+ 2. **Configure branch protection**:
692
+ - Require PR validation
693
+ - Require tests to pass
694
+ - Require CAWS spec validation
695
+
696
+ ### Other CI Systems
697
+ - **GitLab CI**: Use \`caws validate\` in \`.gitlab-ci.yml\`
698
+ - **Jenkins**: Add validation step to pipeline
699
+ - **CircleCI**: Include in \`.circleci/config.yml\`
700
+
701
+ ---
702
+
703
+ ## Phase 4: Team Onboarding (ongoing)
704
+
705
+ ### For Team Members
706
+ 1. **Read the basics**: Start with this guide
707
+ 2. **Learn by example**: Review completed features
708
+ 3. **Practice**: Create small features following the process
709
+ 4. **Contribute**: Help improve templates and processes
710
+
711
+ ### For Project Leads
712
+ 1. **Customize templates**: Adapt to team preferences
713
+ 2. **Set standards**: Define project-specific conventions
714
+ 3. **Monitor quality**: Review metrics and adjust gates
715
+ 4. **Scale practices**: Apply CAWS to more complex work
716
+
717
+ ---
718
+
719
+ ## Key Concepts Quick Reference
720
+
721
+ ### Risk Tiers
722
+ - **Tier 1**: Critical (auth, billing, migrations) - Max rigor
723
+ - **Tier 2**: Standard (features, APIs) - Standard rigor
724
+ - **Tier 3**: Low risk (UI, tooling) - Basic rigor
725
+
726
+ ### Change Budget
727
+ - Limits help maintain quality and reviewability
728
+ - Adjust based on risk tier and team experience
729
+ - Track actual vs. budgeted changes
730
+
731
+ ### System Invariants
732
+ - Core guarantees that must always hold true
733
+ - Examples: "Data integrity maintained", "API contracts honored"
734
+ - Define 2-4 key invariants for your system
735
+
736
+ ### Acceptance Criteria
737
+ - Use GIVEN/WHEN/THEN format
738
+ - Focus on observable behavior
739
+ - Include edge cases and error conditions
740
+
741
+ ---
742
+
743
+ ## Common Pitfalls to Avoid
744
+
745
+ ### For ${capitalizedType} Projects
746
+ ${getProjectSpecificPitfalls(projectType)}
747
+
748
+ ### General Issues
749
+ 1. **Over-customization**: Start with defaults, customize gradually
750
+ 2. **Missing invariants**: Define what must never break
751
+ 3. **Vague acceptance**: Make criteria measurable and testable
752
+ 4. **Large changes**: Break big features into smaller, reviewable pieces
753
+
754
+ ---
755
+
756
+ ## Resources
757
+
758
+ ### Documentation
759
+ - **Quick Reference**: This guide
760
+ - **Templates**: \`.caws/templates/\`
761
+ - **Examples**: \`.caws/examples/\` (when available)
762
+
763
+ ### Commands
764
+ - \`caws validate --suggestions\` - Get help with issues
765
+ - \`caws validate --auto-fix\` - Fix safe problems automatically
766
+ - \`caws init --interactive\` - Customize existing setup
767
+
768
+ ### Community
769
+ - **GitHub Issues**: Report problems and request features
770
+ - **Discussions**: Share experiences and best practices
771
+ - **Wiki**: Growing collection of examples and guides
772
+
773
+ ---
774
+
775
+ ## Next Steps
776
+
777
+ 1. **Right now**: Review your working spec and customize it
778
+ 2. **Today**: Create your first feature plan
779
+ 3. **This week**: Set up CI/CD and branch protection
780
+ 4. **Ongoing**: Refine processes based on team feedback
781
+
782
+ Remember: CAWS is a framework, not a straightjacket. Adapt it to your team's needs while maintaining the core principles of determinism and quality.
783
+
784
+ **Happy coding! 🎯**
785
+ `;
786
+
787
+ return guide;
788
+ }
789
+
790
+ function getTestingGuidance(projectType) {
791
+ const guidance = {
792
+ extension: `- Webview rendering tests\n- Command registration tests\n- Extension activation tests`,
793
+ library: `- Component rendering tests\n- API function tests\n- Type export tests`,
794
+ api: `- Endpoint response tests\n- Error handling tests\n- Authentication tests`,
795
+ cli: `- Command parsing tests\n- Output formatting tests\n- Error code tests`,
796
+ monorepo: `- Cross-package integration tests\n- Shared module tests\n- Build pipeline tests`,
797
+ application: `- User interaction tests\n- State management tests\n- Integration tests`,
798
+ };
799
+ return (
800
+ guidance[projectType] || `- Unit tests for core functions\n- Integration tests for workflows`
801
+ );
802
+ }
803
+
804
+ function getProjectSpecificPitfalls(projectType) {
805
+ const pitfalls = {
806
+ extension: `1. **Webview security**: Never use \`vscode.executeCommand\` from untrusted content
807
+ 2. **Activation timing**: Test cold start performance
808
+ 3. **API compatibility**: Check VS Code API version compatibility`,
809
+ library: `1. **Bundle size**: Monitor and limit package size
810
+ 2. **Type exports**: Ensure all public APIs are typed
811
+ 3. **Peer dependencies**: Handle React/Angular versions carefully`,
812
+ api: `1. **Backward compatibility**: Version APIs carefully
813
+ 2. **Rate limiting**: Test and document limits
814
+ 3. **Data validation**: Validate all inputs thoroughly`,
815
+ cli: `1. **Exit codes**: Use standard codes (0=success, 1=error)
816
+ 2. **Help text**: Keep it concise and helpful
817
+ 3. **Error messages**: Make them actionable`,
818
+ monorepo: `1. **Dependency cycles**: Avoid circular imports
819
+ 2. **Version consistency**: Keep package versions aligned
820
+ 3. **Build order**: Ensure correct build dependencies`,
821
+ application: `1. **State consistency**: Prevent invalid state transitions
822
+ 2. **Performance**: Monitor and optimize critical paths
823
+ 3. **Accessibility**: Test with screen readers and keyboard navigation`,
824
+ };
825
+ return (
826
+ pitfalls[projectType] ||
827
+ `1. **Test coverage**: Maintain adequate test coverage
828
+ 2. **Documentation**: Keep code and APIs documented
829
+ 3. **Dependencies**: Review and update regularly`
830
+ );
831
+ }
832
+
833
+ /**
834
+ * Generate smart .gitignore patterns for CAWS projects
835
+ */
836
+ function generateGitignorePatterns(existingGitignore = '') {
837
+ const cawsPatterns = `
838
+ # CAWS Configuration (tracked - these should be versioned)
839
+ # Note: .caws/ and .agent/ are tracked for provenance
840
+ # But we exclude temporary/generated files:
841
+
842
+ # CAWS temporary files (ignored)
843
+ .agent/temp/
844
+ .agent/cache/
845
+ .caws/.cache/
846
+ .caws/tmp/
847
+
848
+ # Build outputs (common patterns)
849
+ dist/
850
+ build/
851
+ *.tsbuildinfo
852
+ .next/
853
+ .nuxt/
854
+ .vite/
855
+
856
+ # Dependencies
857
+ node_modules/
858
+ .pnpm-store/
859
+
860
+ # Environment files
861
+ .env
862
+ .env.local
863
+ .env.development.local
864
+ .env.test.local
865
+ .env.production.local
866
+
867
+ # IDE files
868
+ .vscode/settings.json
869
+ .idea/
870
+ *.swp
871
+ *.swo
872
+
873
+ # OS files
874
+ .DS_Store
875
+ Thumbs.db
876
+
877
+ # Logs
878
+ logs/
879
+ *.log
880
+ npm-debug.log*
881
+ yarn-debug.log*
882
+ yarn-error.log*
883
+
884
+ # Coverage reports
885
+ coverage/
886
+ .nyc_output/
887
+
888
+ # Test results
889
+ test-results/
890
+ playwright-report/
891
+ `;
892
+
893
+ // If there's an existing .gitignore, merge intelligently
894
+ if (existingGitignore.trim()) {
895
+ // Check if CAWS patterns are already present
896
+ if (existingGitignore.includes('# CAWS Configuration')) {
897
+ console.log(chalk.blue('ℹ️ .gitignore already contains CAWS patterns - skipping'));
898
+ return existingGitignore;
427
899
  }
428
900
 
429
- process.exit(1);
901
+ // Append CAWS patterns to existing .gitignore
902
+ return existingGitignore.trim() + '\n\n' + cawsPatterns.trim() + '\n';
430
903
  }
904
+
905
+ return cawsPatterns.trim();
906
+ }
907
+
908
+ // Only log schema validation if not running quiet commands
909
+ if (!process.argv.includes('--version') && !process.argv.includes('-V')) {
910
+ console.log(chalk.green('✅ Schema validation initialized successfully'));
431
911
  }
432
912
 
433
913
  /**
@@ -618,11 +1098,317 @@ function validateGeneratedSpec(specContent, _answers) {
618
1098
  }
619
1099
  }
620
1100
 
1101
+ /**
1102
+ * Detect project type from existing files and structure
1103
+ */
1104
+ function detectProjectType(cwd = process.cwd()) {
1105
+ const files = fs.readdirSync(cwd);
1106
+
1107
+ // Check for various project indicators
1108
+ const hasPackageJson = files.includes('package.json');
1109
+ const hasPnpm = files.includes('pnpm-workspace.yaml');
1110
+ const hasYarn = files.includes('yarn.lock');
1111
+
1112
+ let packageJson = {};
1113
+ if (hasPackageJson) {
1114
+ try {
1115
+ packageJson = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
1116
+ } catch (e) {
1117
+ // Ignore parse errors
1118
+ }
1119
+ }
1120
+
1121
+ // VS Code Extension detection
1122
+ const isVscodeExtension =
1123
+ packageJson.engines?.vscode ||
1124
+ packageJson.contributes ||
1125
+ packageJson.activationEvents ||
1126
+ packageJson.main?.includes('extension.js');
1127
+
1128
+ // Monorepo detection
1129
+ const isMonorepo = hasPnpm || hasYarn || files.includes('packages') || files.includes('apps');
1130
+
1131
+ // Library detection
1132
+ const isLibrary = packageJson.main || packageJson.module || packageJson.exports;
1133
+
1134
+ // CLI detection
1135
+ const isCli = packageJson.bin || packageJson.name?.startsWith('@') === false;
1136
+
1137
+ // API detection
1138
+ const isApi =
1139
+ packageJson.scripts?.start ||
1140
+ packageJson.dependencies?.express ||
1141
+ packageJson.dependencies?.fastify ||
1142
+ packageJson.dependencies?.['@types/express'];
1143
+
1144
+ // Determine primary type
1145
+ if (isVscodeExtension) return 'extension';
1146
+ if (isMonorepo) return 'monorepo';
1147
+ if (isApi) return 'api';
1148
+ if (isLibrary) return 'library';
1149
+ if (isCli) return 'cli';
1150
+
1151
+ // Default fallback
1152
+ return 'application';
1153
+ }
1154
+
1155
+ /**
1156
+ * Generate working spec from project analysis
1157
+ */
1158
+ function generateWorkingSpecFromAnalysis(analysis) {
1159
+ const { projectType, packageJson } = analysis;
1160
+
1161
+ const templates = {
1162
+ extension: {
1163
+ risk_tier: 2,
1164
+ mode: 'feature',
1165
+ change_budget: { max_files: 25, max_loc: 1000 },
1166
+ invariants: [
1167
+ 'Webview only accesses workspace files via VS Code API',
1168
+ 'Extension activates in <1s on typical machine',
1169
+ 'All commands have keyboard shortcuts',
1170
+ ],
1171
+ scope: {
1172
+ in: ['src/', 'package.json', 'tsconfig.json'],
1173
+ out: ['node_modules/', '*.vsix'],
1174
+ },
1175
+ acceptance: [
1176
+ {
1177
+ id: 'A1',
1178
+ given: 'User has workspace open',
1179
+ when: 'Extension activates',
1180
+ then: 'Webview loads within 1 second',
1181
+ },
1182
+ ],
1183
+ non_functional: {
1184
+ a11y: ['keyboard navigation', 'screen reader support', 'high contrast theme'],
1185
+ perf: { api_p95_ms: 100 },
1186
+ security: ['CSP enforcement for webviews', 'No arbitrary filesystem access'],
1187
+ },
1188
+ },
1189
+ library: {
1190
+ risk_tier: 2,
1191
+ mode: 'feature',
1192
+ change_budget: { max_files: 20, max_loc: 800 },
1193
+ invariants: [
1194
+ 'No runtime dependencies except React',
1195
+ 'Tree-shakeable exports',
1196
+ 'TypeScript types exported',
1197
+ ],
1198
+ scope: {
1199
+ in: ['src/', 'lib/', 'package.json'],
1200
+ out: ['examples/', 'docs/', 'node_modules/'],
1201
+ },
1202
+ acceptance: [
1203
+ {
1204
+ id: 'A1',
1205
+ given: 'Library is imported',
1206
+ when: 'Component is rendered',
1207
+ then: 'No runtime errors occur',
1208
+ },
1209
+ ],
1210
+ non_functional: {
1211
+ a11y: ['WCAG 2.1 AA compliance', 'Semantic HTML'],
1212
+ perf: { bundle_size_kb: 50 },
1213
+ security: ['Input validation', 'XSS prevention'],
1214
+ },
1215
+ },
1216
+ api: {
1217
+ risk_tier: 1,
1218
+ mode: 'feature',
1219
+ change_budget: { max_files: 40, max_loc: 1500 },
1220
+ invariants: [
1221
+ 'API maintains backward compatibility',
1222
+ 'All endpoints respond within 100ms',
1223
+ 'Data consistency maintained across requests',
1224
+ ],
1225
+ scope: {
1226
+ in: ['src/', 'routes/', 'models/', 'tests/'],
1227
+ out: ['node_modules/', 'logs/', 'temp/'],
1228
+ },
1229
+ acceptance: [
1230
+ {
1231
+ id: 'A1',
1232
+ given: 'Valid request is made',
1233
+ when: 'Endpoint is called',
1234
+ then: 'Correct response returned within SLO',
1235
+ },
1236
+ ],
1237
+ non_functional: {
1238
+ a11y: ['API documentation accessible'],
1239
+ perf: { api_p95_ms: 100 },
1240
+ security: ['Input validation', 'Rate limiting', 'Authentication'],
1241
+ },
1242
+ },
1243
+ cli: {
1244
+ risk_tier: 3,
1245
+ mode: 'feature',
1246
+ change_budget: { max_files: 15, max_loc: 600 },
1247
+ invariants: [
1248
+ 'CLI exits with appropriate codes',
1249
+ 'Help text is informative',
1250
+ 'Error messages are clear',
1251
+ ],
1252
+ scope: {
1253
+ in: ['src/', 'bin/', 'lib/', 'tests/'],
1254
+ out: ['node_modules/', 'dist/'],
1255
+ },
1256
+ acceptance: [
1257
+ {
1258
+ id: 'A1',
1259
+ given: 'User runs command with --help',
1260
+ when: 'Help flag is provided',
1261
+ then: 'Help text displays clearly',
1262
+ },
1263
+ ],
1264
+ non_functional: {
1265
+ a11y: ['Color contrast in terminal output'],
1266
+ perf: { api_p95_ms: 50 },
1267
+ security: ['Input validation', 'No arbitrary execution'],
1268
+ },
1269
+ },
1270
+ monorepo: {
1271
+ risk_tier: 1,
1272
+ mode: 'feature',
1273
+ change_budget: { max_files: 50, max_loc: 2000 },
1274
+ invariants: [
1275
+ 'All packages remain compatible',
1276
+ 'Cross-package dependencies work',
1277
+ 'Build system remains stable',
1278
+ ],
1279
+ scope: {
1280
+ in: ['packages/', 'apps/', 'tools/', 'scripts/'],
1281
+ out: ['node_modules/', 'dist/', 'build/'],
1282
+ },
1283
+ acceptance: [
1284
+ {
1285
+ id: 'A1',
1286
+ given: 'Change is made to shared package',
1287
+ when: 'All dependent packages build',
1288
+ then: 'No breaking changes introduced',
1289
+ },
1290
+ ],
1291
+ non_functional: {
1292
+ a11y: ['Documentation accessible across packages'],
1293
+ perf: { api_p95_ms: 200 },
1294
+ security: ['Dependency audit passes', 'No vulnerable packages'],
1295
+ },
1296
+ },
1297
+ application: {
1298
+ risk_tier: 2,
1299
+ mode: 'feature',
1300
+ change_budget: { max_files: 30, max_loc: 1200 },
1301
+ invariants: [
1302
+ 'Application remains functional',
1303
+ 'User data is preserved',
1304
+ 'Performance does not degrade',
1305
+ ],
1306
+ scope: {
1307
+ in: ['src/', 'components/', 'pages/', 'lib/'],
1308
+ out: ['node_modules/', 'build/', 'dist/'],
1309
+ },
1310
+ acceptance: [
1311
+ {
1312
+ id: 'A1',
1313
+ given: 'User interacts with application',
1314
+ when: 'Feature is used',
1315
+ then: 'Expected behavior occurs',
1316
+ },
1317
+ ],
1318
+ non_functional: {
1319
+ a11y: ['WCAG 2.1 AA compliance', 'Keyboard navigation'],
1320
+ perf: { api_p95_ms: 250 },
1321
+ security: ['Input validation', 'Authentication', 'Authorization'],
1322
+ },
1323
+ },
1324
+ };
1325
+
1326
+ const baseSpec = templates[projectType] || templates.application;
1327
+
1328
+ return {
1329
+ id: `${packageJson.name?.toUpperCase().replace(/[^A-Z0-9]/g, '-') || 'PROJECT'}-001`,
1330
+ title: packageJson.name || 'Project',
1331
+ risk_tier: baseSpec.risk_tier,
1332
+ mode: baseSpec.mode,
1333
+ change_budget: baseSpec.change_budget,
1334
+ blast_radius: {
1335
+ modules: ['core', 'api', 'ui'],
1336
+ data_migration: false,
1337
+ },
1338
+ operational_rollback_slo: '5m',
1339
+ scope: baseSpec.scope,
1340
+ invariants: baseSpec.invariants,
1341
+ acceptance: baseSpec.acceptance,
1342
+ non_functional: baseSpec.non_functional,
1343
+ contracts: [
1344
+ {
1345
+ type: projectType === 'api' ? 'openapi' : 'none',
1346
+ path: projectType === 'api' ? 'docs/api.yaml' : '',
1347
+ },
1348
+ ],
1349
+ observability: {
1350
+ logs: ['error', 'warn', 'info'],
1351
+ metrics: ['requests_total', 'errors_total'],
1352
+ traces: ['request_flow'],
1353
+ },
1354
+ ai_assessment: {
1355
+ confidence_level: 8,
1356
+ uncertainty_areas: [],
1357
+ complexity_factors: [],
1358
+ risk_factors: [],
1359
+ },
1360
+ };
1361
+ }
1362
+
1363
+ /**
1364
+ * Detect if current directory appears to be a project that should be initialized directly
1365
+ */
1366
+ function shouldInitInCurrentDirectory(projectName, currentDir) {
1367
+ // If explicitly '.', always init in current directory
1368
+ if (projectName === '.') return true;
1369
+
1370
+ // Check for common project indicators
1371
+ const projectIndicators = [
1372
+ 'package.json',
1373
+ 'tsconfig.json',
1374
+ 'jest.config.js',
1375
+ 'eslint.config.js',
1376
+ 'README.md',
1377
+ 'src/',
1378
+ 'lib/',
1379
+ 'app/',
1380
+ 'packages/',
1381
+ '.git/',
1382
+ 'node_modules/', // Even if empty, suggests intent to be a project
1383
+ ];
1384
+
1385
+ const files = fs.readdirSync(currentDir);
1386
+ const hasProjectIndicators = projectIndicators.some((indicator) => {
1387
+ if (indicator.endsWith('/')) {
1388
+ return files.includes(indicator.slice(0, -1));
1389
+ }
1390
+ return files.includes(indicator);
1391
+ });
1392
+
1393
+ return hasProjectIndicators;
1394
+ }
1395
+
621
1396
  /**
622
1397
  * Initialize a new project with CAWS
623
1398
  */
624
1399
  async function initProject(projectName, options) {
625
- console.log(chalk.cyan(`🚀 Initializing new CAWS project: ${projectName}`));
1400
+ const currentDir = process.cwd();
1401
+ const isCurrentDirInit = shouldInitInCurrentDirectory(projectName, currentDir);
1402
+
1403
+ if (!isCurrentDirInit && projectName !== '.') {
1404
+ console.log(chalk.cyan(`🚀 Initializing new CAWS project: ${projectName}`));
1405
+ console.log(chalk.gray(` (Creating subdirectory: ${projectName}/)`));
1406
+ } else {
1407
+ console.log(
1408
+ chalk.cyan(`🚀 Initializing CAWS in current project: ${path.basename(currentDir)}`)
1409
+ );
1410
+ console.log(chalk.gray(` (Adding CAWS files to existing project)`));
1411
+ }
626
1412
 
627
1413
  let answers; // Will be set either interactively or with defaults
628
1414
 
@@ -634,11 +1420,14 @@ async function initProject(projectName, options) {
634
1420
  process.exit(1);
635
1421
  }
636
1422
 
637
- // Sanitize project name
638
- const sanitizedName = projectName.replace(/[^a-zA-Z0-9-_]/g, '-').toLowerCase();
639
- if (sanitizedName !== projectName) {
640
- console.warn(chalk.yellow(`⚠️ Project name sanitized to: ${sanitizedName}`));
641
- projectName = sanitizedName;
1423
+ // Special case: '.' means current directory, don't sanitize
1424
+ if (projectName !== '.') {
1425
+ // Sanitize project name
1426
+ const sanitizedName = projectName.replace(/[^a-zA-Z0-9-_]/g, '-').toLowerCase();
1427
+ if (sanitizedName !== projectName) {
1428
+ console.warn(chalk.yellow(`⚠️ Project name sanitized to: ${sanitizedName}`));
1429
+ projectName = sanitizedName;
1430
+ }
642
1431
  }
643
1432
 
644
1433
  // Validate project name length
@@ -663,53 +1452,478 @@ async function initProject(projectName, options) {
663
1452
  process.exit(1);
664
1453
  }
665
1454
 
666
- // Check if directory already exists
667
- if (fs.existsSync(projectName)) {
668
- console.error(chalk.red(`❌ Directory ${projectName} already exists`));
669
- console.error(chalk.blue('💡 Choose a different name or remove the existing directory'));
670
- process.exit(1);
1455
+ // Determine if initializing in current directory
1456
+ const initInCurrentDir = projectName === '.';
1457
+ const targetDir = initInCurrentDir ? process.cwd() : path.resolve(process.cwd(), projectName);
1458
+
1459
+ // Check if target directory already exists and has content (skip check for current directory)
1460
+ if (!initInCurrentDir && fs.existsSync(projectName)) {
1461
+ const existingFiles = fs.readdirSync(projectName);
1462
+ if (existingFiles.length > 0) {
1463
+ console.error(chalk.red(`❌ Directory '${projectName}' already exists and contains files`));
1464
+ console.error(chalk.blue('💡 To initialize CAWS in current directory instead:'));
1465
+ console.error(` ${chalk.cyan('caws init .')}`);
1466
+ console.error(chalk.blue('💡 Or choose a different name/remove existing directory'));
1467
+ process.exit(1);
1468
+ }
671
1469
  }
672
1470
 
673
- // Create project directory
674
- await fs.ensureDir(projectName);
675
- process.chdir(projectName);
1471
+ // Check if current directory has project files when trying to init in subdirectory
1472
+ if (!initInCurrentDir) {
1473
+ const currentDirFiles = fs.readdirSync(process.cwd());
1474
+ const hasProjectFiles = currentDirFiles.some(
1475
+ (file) => !file.startsWith('.') && file !== 'node_modules' && file !== '.git'
1476
+ );
1477
+
1478
+ if (hasProjectFiles) {
1479
+ console.warn(chalk.yellow('⚠️ Current directory contains project files'));
1480
+ console.warn(
1481
+ chalk.blue('💡 You might want to initialize CAWS in current directory instead:')
1482
+ );
1483
+ console.warn(` ${chalk.cyan('caws init .')}`);
1484
+ console.warn(chalk.blue(' Or continue to create subdirectory (Ctrl+C to cancel)'));
1485
+ }
1486
+ }
676
1487
 
677
- console.log(chalk.green(`📁 Created project directory: ${projectName}`));
1488
+ // Save the original template directory before changing directories
1489
+ const originalTemplateDir = cawsSetup?.hasTemplateDir ? cawsSetup.templateDir : null;
1490
+
1491
+ // Check for existing agents.md/caws.md in target directory
1492
+ const existingAgentsMd = fs.existsSync(path.join(targetDir, 'agents.md'));
1493
+ const existingCawsMd = fs.existsSync(path.join(targetDir, 'caws.md'));
1494
+
1495
+ // Create project directory and change to it (unless already in current directory)
1496
+ if (!initInCurrentDir) {
1497
+ await fs.ensureDir(projectName);
1498
+ process.chdir(projectName);
1499
+ console.log(chalk.green(`📁 Created project directory: ${projectName}`));
1500
+ } else {
1501
+ console.log(chalk.green(`📁 Initializing in current directory`));
1502
+ }
678
1503
 
679
1504
  // Detect and adapt to existing setup
680
1505
  const currentSetup = detectCAWSSetup(process.cwd());
681
1506
 
682
1507
  if (currentSetup.type === 'new') {
683
- // Copy template files from generic template
684
- if (cawsSetup && cawsSetup.hasTemplateDir && cawsSetup.templateDir) {
1508
+ // Create minimal CAWS structure
1509
+ await fs.ensureDir('.caws');
1510
+ await fs.ensureDir('.agent');
1511
+ console.log(chalk.blue('ℹ️ Created basic CAWS structure'));
1512
+
1513
+ // Copy agents.md guide if templates are available
1514
+ if (originalTemplateDir) {
685
1515
  try {
686
- await copyTemplate(cawsSetup.templateDir, '.');
687
- console.log(chalk.green('✅ Created CAWS project with templates'));
1516
+ const agentsMdSource = path.join(originalTemplateDir, 'agents.md');
1517
+ let targetFile = 'agents.md';
1518
+
1519
+ if (fs.existsSync(agentsMdSource)) {
1520
+ // Use the pre-checked values for conflicts
1521
+ if (existingAgentsMd) {
1522
+ // Conflict: user already has agents.md
1523
+ if (options.interactive && !options.nonInteractive) {
1524
+ // Interactive mode: ask user
1525
+ const overwriteAnswer = await inquirer.prompt([
1526
+ {
1527
+ type: 'confirm',
1528
+ name: 'overwrite',
1529
+ message: '⚠️ agents.md already exists. Overwrite with CAWS guide?',
1530
+ default: false,
1531
+ },
1532
+ ]);
1533
+
1534
+ if (overwriteAnswer.overwrite) {
1535
+ targetFile = 'agents.md';
1536
+ } else {
1537
+ targetFile = 'caws.md';
1538
+ }
1539
+ } else {
1540
+ // Non-interactive mode: use caws.md instead
1541
+ targetFile = 'caws.md';
1542
+ console.log(chalk.blue('ℹ️ agents.md exists, using caws.md for CAWS guide'));
1543
+ }
1544
+ }
1545
+
1546
+ // If caws.md also exists and that's our target, skip
1547
+ if (targetFile === 'caws.md' && existingCawsMd) {
1548
+ console.log(
1549
+ chalk.yellow('⚠️ Both agents.md and caws.md exist, skipping guide copy')
1550
+ );
1551
+ } else {
1552
+ const agentsMdDest = path.join(process.cwd(), targetFile);
1553
+ await fs.copyFile(agentsMdSource, agentsMdDest);
1554
+ console.log(chalk.green(`✅ Added ${targetFile} guide`));
1555
+ }
1556
+ }
688
1557
  } catch (templateError) {
1558
+ console.warn(chalk.yellow('⚠️ Could not copy agents guide:'), templateError.message);
689
1559
  console.warn(
690
- chalk.yellow('⚠️ Template directory not available, creating basic structure')
1560
+ chalk.blue('💡 You can manually copy the guide from the caws-template package')
691
1561
  );
692
- // Create minimal CAWS structure
693
- await fs.ensureDir('.caws');
694
- await fs.ensureDir('.agent');
695
- console.log(chalk.blue('ℹ️ Created basic CAWS structure'));
696
1562
  }
697
- } else {
698
- // Create minimal CAWS structure
699
- await fs.ensureDir('.caws');
700
- await fs.ensureDir('.agent');
701
- console.log(chalk.blue('ℹ️ Created basic CAWS structure'));
702
1563
  }
703
1564
  } else {
704
1565
  // Already has CAWS setup
705
1566
  console.log(chalk.green('✅ CAWS project detected - skipping template copy'));
706
1567
  }
707
1568
 
1569
+ // Handle interactive wizard or template-based setup
1570
+ if (options.interactive && !options.nonInteractive) {
1571
+ console.log(chalk.cyan('🎯 CAWS Interactive Setup Wizard'));
1572
+ console.log(chalk.blue('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
1573
+ console.log(chalk.gray('This wizard will guide you through creating a CAWS working spec\n'));
1574
+
1575
+ // Detect project type
1576
+ const detectedType = detectProjectType(process.cwd());
1577
+ console.log(chalk.blue(`📦 Detected project type: ${chalk.cyan(detectedType)}`));
1578
+
1579
+ // Get package.json info if available
1580
+ let packageJson = {};
1581
+ try {
1582
+ packageJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
1583
+ } catch (e) {
1584
+ // No package.json, that's fine
1585
+ }
1586
+
1587
+ const wizardQuestions = [
1588
+ {
1589
+ type: 'list',
1590
+ name: 'projectType',
1591
+ message: '❓ What type of project is this?',
1592
+ choices: [
1593
+ {
1594
+ name: '🔌 VS Code Extension (webview, commands, integrations)',
1595
+ value: 'extension',
1596
+ short: 'VS Code Extension',
1597
+ },
1598
+ {
1599
+ name: '📚 Library/Package (reusable components, utilities)',
1600
+ value: 'library',
1601
+ short: 'Library',
1602
+ },
1603
+ {
1604
+ name: '🌐 API Service (REST, GraphQL, microservices)',
1605
+ value: 'api',
1606
+ short: 'API Service',
1607
+ },
1608
+ {
1609
+ name: '💻 CLI Tool (command-line interface)',
1610
+ value: 'cli',
1611
+ short: 'CLI Tool',
1612
+ },
1613
+ {
1614
+ name: '🏗️ Monorepo (multiple packages/apps)',
1615
+ value: 'monorepo',
1616
+ short: 'Monorepo',
1617
+ },
1618
+ {
1619
+ name: '📱 Application (standalone app)',
1620
+ value: 'application',
1621
+ short: 'Application',
1622
+ },
1623
+ ],
1624
+ default: detectedType,
1625
+ },
1626
+ {
1627
+ type: 'input',
1628
+ name: 'projectTitle',
1629
+ message: '📝 Project Title (descriptive name):',
1630
+ default:
1631
+ packageJson.name ||
1632
+ projectName.charAt(0).toUpperCase() + projectName.slice(1).replace(/-/g, ' '),
1633
+ },
1634
+ {
1635
+ type: 'list',
1636
+ name: 'riskTier',
1637
+ message: '⚠️ Risk Tier (higher tier = more rigor):',
1638
+ choices: [
1639
+ {
1640
+ name: '🔴 Tier 1 - Critical (auth, billing, migrations) - Max rigor',
1641
+ value: 1,
1642
+ short: 'Critical',
1643
+ },
1644
+ {
1645
+ name: '🟡 Tier 2 - Standard (features, APIs) - Standard rigor',
1646
+ value: 2,
1647
+ short: 'Standard',
1648
+ },
1649
+ {
1650
+ name: '🟢 Tier 3 - Low Risk (UI, tooling) - Basic rigor',
1651
+ value: 3,
1652
+ short: 'Low Risk',
1653
+ },
1654
+ ],
1655
+ default: (answers) => {
1656
+ const typeDefaults = {
1657
+ extension: 2,
1658
+ library: 2,
1659
+ api: 1,
1660
+ cli: 3,
1661
+ monorepo: 1,
1662
+ application: 2,
1663
+ };
1664
+ return typeDefaults[answers.projectType] || 2;
1665
+ },
1666
+ },
1667
+ {
1668
+ type: 'list',
1669
+ name: 'projectMode',
1670
+ message: '🎯 Primary development mode:',
1671
+ choices: [
1672
+ { name: '✨ feature (new functionality)', value: 'feature' },
1673
+ { name: '🔄 refactor (code restructuring)', value: 'refactor' },
1674
+ { name: '🐛 fix (bug fixes)', value: 'fix' },
1675
+ { name: '📚 doc (documentation)', value: 'doc' },
1676
+ { name: '🧹 chore (maintenance)', value: 'chore' },
1677
+ ],
1678
+ default: 'feature',
1679
+ },
1680
+ {
1681
+ type: 'number',
1682
+ name: 'maxFiles',
1683
+ message: '📊 Max files to change per feature:',
1684
+ default: (answers) => {
1685
+ const tierDefaults = { 1: 40, 2: 25, 3: 15 };
1686
+ const typeAdjustments = {
1687
+ extension: -5,
1688
+ library: -10,
1689
+ api: 10,
1690
+ cli: -10,
1691
+ monorepo: 25,
1692
+ application: 0,
1693
+ };
1694
+ return Math.max(
1695
+ 5,
1696
+ tierDefaults[answers.riskTier] + (typeAdjustments[answers.projectType] || 0)
1697
+ );
1698
+ },
1699
+ },
1700
+ {
1701
+ type: 'number',
1702
+ name: 'maxLoc',
1703
+ message: '📏 Max lines of code to change per feature:',
1704
+ default: (answers) => {
1705
+ const tierDefaults = { 1: 1500, 2: 1000, 3: 600 };
1706
+ const typeAdjustments = {
1707
+ extension: -200,
1708
+ library: -300,
1709
+ api: 500,
1710
+ cli: -400,
1711
+ monorepo: 1000,
1712
+ application: 0,
1713
+ };
1714
+ return Math.max(
1715
+ 50,
1716
+ tierDefaults[answers.riskTier] + (typeAdjustments[answers.projectType] || 0)
1717
+ );
1718
+ },
1719
+ },
1720
+ {
1721
+ type: 'input',
1722
+ name: 'blastModules',
1723
+ message: '💥 Affected modules (comma-separated):',
1724
+ default: (answers) => {
1725
+ const typeDefaults = {
1726
+ extension: 'core,webview',
1727
+ library: 'components,utils',
1728
+ api: 'routes,models,controllers',
1729
+ cli: 'commands,utils',
1730
+ monorepo: 'shared,packages',
1731
+ application: 'ui,logic,data',
1732
+ };
1733
+ return typeDefaults[answers.projectType] || 'core,ui';
1734
+ },
1735
+ },
1736
+ {
1737
+ type: 'confirm',
1738
+ name: 'dataMigration',
1739
+ message: '🗄️ Requires data migration?',
1740
+ default: false,
1741
+ },
1742
+ {
1743
+ type: 'list',
1744
+ name: 'rollbackSlo',
1745
+ message: '⏱️ Operational rollback SLO:',
1746
+ choices: [
1747
+ { name: '⚡ 1 minute (critical systems)', value: '1m' },
1748
+ { name: '🟡 5 minutes (standard)', value: '5m' },
1749
+ { name: '🟠 15 minutes (complex)', value: '15m' },
1750
+ { name: '🔴 1 hour (data migration)', value: '1h' },
1751
+ ],
1752
+ default: '5m',
1753
+ },
1754
+ ];
1755
+
1756
+ console.log(chalk.cyan('⏳ Gathering project requirements...'));
1757
+
1758
+ let wizardAnswers;
1759
+ try {
1760
+ wizardAnswers = await inquirer.prompt(wizardQuestions);
1761
+ } catch (error) {
1762
+ if (error.isTtyError) {
1763
+ console.error(chalk.red('❌ Interactive prompts not supported in this environment'));
1764
+ console.error(chalk.blue('💡 Run with --non-interactive flag to use defaults'));
1765
+ process.exit(1);
1766
+ } else {
1767
+ console.error(chalk.red('❌ Error during interactive setup:'), error.message);
1768
+ process.exit(1);
1769
+ }
1770
+ }
1771
+
1772
+ console.log(chalk.green('✅ Project requirements gathered successfully!'));
1773
+
1774
+ // Show summary before generating spec
1775
+ console.log(chalk.bold('\n📋 Configuration Summary:'));
1776
+ console.log(` ${chalk.cyan('Type')}: ${wizardAnswers.projectType}`);
1777
+ console.log(` ${chalk.cyan('Project')}: ${wizardAnswers.projectTitle}`);
1778
+ console.log(
1779
+ ` ${chalk.cyan('Mode')}: ${wizardAnswers.projectMode} | ${chalk.cyan('Tier')}: ${wizardAnswers.riskTier}`
1780
+ );
1781
+ console.log(
1782
+ ` ${chalk.cyan('Budget')}: ${wizardAnswers.maxFiles} files, ${wizardAnswers.maxLoc} lines`
1783
+ );
1784
+ console.log(
1785
+ ` ${chalk.cyan('Data Migration')}: ${wizardAnswers.dataMigration ? 'Yes' : 'No'}`
1786
+ );
1787
+ console.log(` ${chalk.cyan('Rollback SLO')}: ${wizardAnswers.rollbackSlo}`);
1788
+
1789
+ // Generate working spec using the template system
1790
+ const analysis = {
1791
+ projectType: wizardAnswers.projectType,
1792
+ packageJson: { name: wizardAnswers.projectTitle },
1793
+ hasTests: false,
1794
+ hasLinting: false,
1795
+ hasCi: false,
1796
+ };
1797
+
1798
+ const workingSpecContent = yaml.dump(generateWorkingSpecFromAnalysis(analysis));
1799
+
1800
+ // Override template-generated values with wizard answers
1801
+ const spec = yaml.load(workingSpecContent);
1802
+ spec.title = wizardAnswers.projectTitle;
1803
+ spec.risk_tier = wizardAnswers.riskTier;
1804
+ spec.mode = wizardAnswers.projectMode;
1805
+ spec.change_budget = {
1806
+ max_files: wizardAnswers.maxFiles,
1807
+ max_loc: wizardAnswers.maxLoc,
1808
+ };
1809
+ spec.blast_radius = {
1810
+ modules: wizardAnswers.blastModules
1811
+ .split(',')
1812
+ .map((m) => m.trim())
1813
+ .filter((m) => m),
1814
+ data_migration: wizardAnswers.dataMigration,
1815
+ };
1816
+ spec.operational_rollback_slo = wizardAnswers.rollbackSlo;
1817
+
1818
+ // Validate the generated spec
1819
+ validateGeneratedSpec(yaml.dump(spec), wizardAnswers);
1820
+
1821
+ // Save the working spec
1822
+ await fs.writeFile('.caws/working-spec.yaml', yaml.dump(spec, { indent: 2 }));
1823
+
1824
+ console.log(chalk.green('✅ Working spec generated and validated'));
1825
+
1826
+ // Generate getting started guide
1827
+ const wizardAnalysis = {
1828
+ projectType: wizardAnswers.projectType,
1829
+ packageJson: { name: wizardAnswers.projectTitle },
1830
+ hasTests: false,
1831
+ hasLinting: false,
1832
+ hasCi: false,
1833
+ };
1834
+
1835
+ const guideContent = generateGettingStartedGuide(wizardAnalysis);
1836
+ await fs.writeFile('.caws/GETTING_STARTED.md', guideContent);
1837
+ console.log(chalk.green('✅ Getting started guide created'));
1838
+
1839
+ // Generate or update .gitignore with CAWS patterns
1840
+ const existingGitignore = fs.existsSync('.gitignore')
1841
+ ? fs.readFileSync('.gitignore', 'utf8')
1842
+ : '';
1843
+
1844
+ const updatedGitignore = generateGitignorePatterns(existingGitignore);
1845
+ if (updatedGitignore !== existingGitignore) {
1846
+ await fs.writeFile('.gitignore', updatedGitignore);
1847
+ const action = existingGitignore.trim() ? 'updated' : 'created';
1848
+ console.log(chalk.green(`✅ .gitignore ${action} with CAWS patterns`));
1849
+ }
1850
+
1851
+ // Finalize project with provenance and git initialization
1852
+ await finalizeProject(projectName, options, wizardAnswers);
1853
+
1854
+ continueToSuccess();
1855
+ return;
1856
+ }
1857
+
1858
+ // Handle template-based setup
1859
+ if (options.template) {
1860
+ console.log(chalk.cyan(`🎯 Using ${options.template} template`));
1861
+
1862
+ const validTemplates = ['extension', 'library', 'api', 'cli', 'monorepo'];
1863
+ if (!validTemplates.includes(options.template)) {
1864
+ console.error(chalk.red(`❌ Invalid template: ${options.template}`));
1865
+ console.error(chalk.blue(`💡 Valid templates: ${validTemplates.join(', ')}`));
1866
+ process.exit(1);
1867
+ }
1868
+
1869
+ const analysis = {
1870
+ projectType: options.template,
1871
+ packageJson: { name: projectName },
1872
+ hasTests: false,
1873
+ hasLinting: false,
1874
+ hasCi: false,
1875
+ };
1876
+
1877
+ const workingSpecContent = yaml.dump(generateWorkingSpecFromAnalysis(analysis));
1878
+
1879
+ // Validate the generated spec
1880
+ validateGeneratedSpec(workingSpecContent, { projectType: options.template });
1881
+
1882
+ // Save the working spec
1883
+ await fs.writeFile('.caws/working-spec.yaml', workingSpecContent);
1884
+
1885
+ console.log(chalk.green('✅ Working spec generated from template'));
1886
+
1887
+ // Generate getting started guide
1888
+ const templateAnalysis = {
1889
+ projectType: options.template,
1890
+ packageJson: { name: projectName },
1891
+ hasTests: false,
1892
+ hasLinting: false,
1893
+ hasCi: false,
1894
+ };
1895
+
1896
+ const guideContent = generateGettingStartedGuide(templateAnalysis);
1897
+ await fs.writeFile('.caws/GETTING_STARTED.md', guideContent);
1898
+ console.log(chalk.green('✅ Getting started guide created'));
1899
+
1900
+ // Generate or update .gitignore with CAWS patterns
1901
+ const existingGitignore = fs.existsSync('.gitignore')
1902
+ ? fs.readFileSync('.gitignore', 'utf8')
1903
+ : '';
1904
+
1905
+ const updatedGitignore = generateGitignorePatterns(existingGitignore);
1906
+ if (updatedGitignore !== existingGitignore) {
1907
+ await fs.writeFile('.gitignore', updatedGitignore);
1908
+ const action = existingGitignore.trim() ? 'updated' : 'created';
1909
+ console.log(chalk.green(`✅ .gitignore ${action} with CAWS patterns`));
1910
+ }
1911
+
1912
+ // Finalize project
1913
+ await finalizeProject(projectName, options, { projectType: options.template });
1914
+
1915
+ continueToSuccess();
1916
+ return;
1917
+ }
1918
+
708
1919
  // Set default answers for non-interactive mode
709
1920
  if (!options.interactive || options.nonInteractive) {
1921
+ // Use directory name for current directory init
1922
+ const displayName = initInCurrentDir ? path.basename(process.cwd()) : projectName;
1923
+
710
1924
  answers = {
711
- projectId: projectName.toUpperCase().replace(/[^A-Z0-9]/g, '-') + '-001',
712
- projectTitle: projectName.charAt(0).toUpperCase() + projectName.slice(1).replace(/-/g, ' '),
1925
+ projectId: displayName.toUpperCase().replace(/[^A-Z0-9]/g, '-') + '-001',
1926
+ projectTitle: displayName.charAt(0).toUpperCase() + displayName.slice(1).replace(/-/g, ' '),
713
1927
  riskTier: 2,
714
1928
  projectMode: 'feature',
715
1929
  maxFiles: 25,
@@ -757,6 +1971,32 @@ async function initProject(projectName, options) {
757
1971
 
758
1972
  console.log(chalk.green('✅ Working spec generated and validated'));
759
1973
 
1974
+ // Generate getting started guide (detect project type)
1975
+ const detectedType = detectProjectType(process.cwd());
1976
+ const defaultAnalysis = {
1977
+ projectType: detectedType,
1978
+ packageJson: { name: displayName },
1979
+ hasTests: false,
1980
+ hasLinting: false,
1981
+ hasCi: false,
1982
+ };
1983
+
1984
+ const guideContent = generateGettingStartedGuide(defaultAnalysis);
1985
+ await fs.writeFile('.caws/GETTING_STARTED.md', guideContent);
1986
+ console.log(chalk.green('✅ Getting started guide created'));
1987
+
1988
+ // Generate or update .gitignore with CAWS patterns
1989
+ const existingGitignore = fs.existsSync('.gitignore')
1990
+ ? fs.readFileSync('.gitignore', 'utf8')
1991
+ : '';
1992
+
1993
+ const updatedGitignore = generateGitignorePatterns(existingGitignore);
1994
+ if (updatedGitignore !== existingGitignore) {
1995
+ await fs.writeFile('.gitignore', updatedGitignore);
1996
+ const action = existingGitignore.trim() ? 'updated' : 'created';
1997
+ console.log(chalk.green(`✅ .gitignore ${action} with CAWS patterns`));
1998
+ }
1999
+
760
2000
  // Finalize project with provenance and git initialization
761
2001
  await finalizeProject(projectName, options, answers);
762
2002
 
@@ -1387,12 +2627,30 @@ async function finalizeProject(projectName, options, answers) {
1387
2627
  }
1388
2628
 
1389
2629
  function continueToSuccess() {
1390
- console.log(chalk.green('\n🎉 Project initialized successfully!'));
1391
- console.log(`📁 ${chalk.cyan('Project location')}: ${path.resolve(process.cwd())}`);
2630
+ const isCurrentDir =
2631
+ process.cwd() ===
2632
+ path.resolve(process.argv[3] === '.' ? process.cwd() : process.argv[3] || 'caws-project');
2633
+
2634
+ console.log(chalk.green('\n🎉 CAWS project initialized successfully!'));
2635
+
2636
+ if (isCurrentDir) {
2637
+ console.log(
2638
+ `📁 ${chalk.cyan('Initialized in current directory')}: ${path.resolve(process.cwd())}`
2639
+ );
2640
+ console.log(chalk.gray(' (CAWS files added to your existing project)'));
2641
+ } else {
2642
+ console.log(`📁 ${chalk.cyan('Project location')}: ${path.resolve(process.cwd())}`);
2643
+ console.log(chalk.gray(' (New subdirectory created with CAWS structure)'));
2644
+ }
2645
+
1392
2646
  console.log(chalk.bold('\nNext steps:'));
1393
2647
  console.log('1. Customize .caws/working-spec.yaml');
1394
- console.log('2. npm install (if using Node.js)');
1395
- console.log('3. Set up your CI/CD pipeline');
2648
+ console.log('2. Review added CAWS tools and documentation');
2649
+ if (!isCurrentDir) {
2650
+ console.log('3. Move CAWS files to your main project if needed');
2651
+ }
2652
+ console.log('4. npm install (if using Node.js)');
2653
+ console.log('5. Set up your CI/CD pipeline');
1396
2654
  console.log(chalk.blue('\nFor help: caws --help'));
1397
2655
  }
1398
2656
 
@@ -1403,12 +2661,23 @@ async function scaffoldProject(options) {
1403
2661
  const currentDir = process.cwd();
1404
2662
  const projectName = path.basename(currentDir);
1405
2663
 
1406
- console.log(chalk.cyan(`🔧 Enhancing existing project with CAWS: ${projectName}`));
1407
-
1408
2664
  try {
1409
- // Detect existing CAWS setup with current directory context
2665
+ // Detect existing CAWS setup FIRST before any logging
1410
2666
  const setup = detectCAWSSetup(currentDir);
1411
2667
 
2668
+ // Check for CAWS setup immediately and exit with helpful message if not found
2669
+ if (!setup.hasCAWSDir) {
2670
+ console.log(chalk.red('❌ CAWS not initialized in this project'));
2671
+ console.log(chalk.blue('\n💡 To get started:'));
2672
+ console.log(` 1. Initialize CAWS: ${chalk.cyan('caws init <project-name>')}`);
2673
+ console.log(` 2. Or initialize in current directory: ${chalk.cyan('caws init .')}`);
2674
+ console.log(chalk.blue('\n📚 For more help:'));
2675
+ console.log(` ${chalk.cyan('caws --help')}`);
2676
+ process.exit(1);
2677
+ }
2678
+
2679
+ console.log(chalk.cyan(`🔧 Enhancing existing CAWS project: ${projectName}`));
2680
+
1412
2681
  // Preserve the original template directory from global cawsSetup
1413
2682
  // (needed because detectCAWSSetup from within a new project won't find the template)
1414
2683
  if (cawsSetup?.templateDir && !setup.templateDir) {
@@ -1435,6 +2704,12 @@ async function scaffoldProject(options) {
1435
2704
 
1436
2705
  if (!setup.templateDir) {
1437
2706
  console.log(chalk.red(`❌ No template directory available!`));
2707
+ console.log(chalk.blue('💡 To fix this issue:'));
2708
+ console.log(` 1. Ensure caws-template package is installed`);
2709
+ console.log(` 2. Run from the monorepo root directory`);
2710
+ console.log(` 3. Check that CAWS CLI was installed correctly`);
2711
+ console.log(chalk.blue('\n📚 For installation help:'));
2712
+ console.log(` ${chalk.cyan('npm install -g @paths.design/caws-cli')}`);
1438
2713
  }
1439
2714
  }
1440
2715
 
@@ -1481,7 +2756,7 @@ async function scaffoldProject(options) {
1481
2756
  .update(JSON.stringify(scaffoldProvenance))
1482
2757
  .digest('hex');
1483
2758
 
1484
- // Determine what enhancements to add based on setup type
2759
+ // Determine what enhancements to add based on setup type and options
1485
2760
  const enhancements = [];
1486
2761
 
1487
2762
  // Add CAWS tools directory structure (matches test expectations)
@@ -1491,11 +2766,14 @@ async function scaffoldProject(options) {
1491
2766
  required: true,
1492
2767
  });
1493
2768
 
1494
- enhancements.push({
1495
- name: 'codemod',
1496
- description: 'Codemod transformation scripts',
1497
- required: true,
1498
- });
2769
+ // Add codemods if requested or not minimal
2770
+ if (options.withCodemods || (!options.minimal && !options.withCodemods)) {
2771
+ enhancements.push({
2772
+ name: 'codemod',
2773
+ description: 'Codemod transformation scripts',
2774
+ required: true,
2775
+ });
2776
+ }
1499
2777
 
1500
2778
  // Also add automated publishing for enhanced setups
1501
2779
  if (setup.isEnhanced) {
@@ -1521,8 +2799,11 @@ async function scaffoldProject(options) {
1521
2799
  });
1522
2800
  }
1523
2801
 
1524
- // Add OIDC setup guide for setups that need it
1525
- if (!setup.isEnhanced || !fs.existsSync(path.join(currentDir, 'OIDC_SETUP.md'))) {
2802
+ // Add OIDC setup guide if requested or not minimal
2803
+ if (
2804
+ (options.withOidc || (!options.minimal && !options.withOidc)) &&
2805
+ (!setup.isEnhanced || !fs.existsSync(path.join(currentDir, 'OIDC_SETUP.md')))
2806
+ ) {
1526
2807
  enhancements.push({
1527
2808
  name: 'OIDC_SETUP.md',
1528
2809
  description: 'OIDC trusted publisher setup guide',
@@ -1610,9 +2891,18 @@ async function scaffoldProject(options) {
1610
2891
  if (addedCount > 0) {
1611
2892
  console.log(chalk.bold('\n📝 Next steps:'));
1612
2893
  console.log('1. Review the added files');
1613
- console.log('2. Set up OIDC trusted publisher (see OIDC_SETUP.md)');
1614
- console.log('3. Push to trigger automated publishing');
1615
- console.log('4. Your existing CAWS tools remain unchanged');
2894
+
2895
+ // Check if OIDC was added
2896
+ const oidcAdded = addedFiles.some((file) => file.includes('OIDC_SETUP'));
2897
+ if (oidcAdded) {
2898
+ console.log('2. Set up OIDC trusted publisher (see OIDC_SETUP.md)');
2899
+ console.log('3. Push to trigger automated publishing');
2900
+ console.log('4. Your existing CAWS tools remain unchanged');
2901
+ } else {
2902
+ console.log('2. Customize your working spec in .caws/working-spec.yaml');
2903
+ console.log('3. Run validation: caws validate --suggestions');
2904
+ console.log('4. Your existing CAWS tools remain unchanged');
2905
+ }
1616
2906
  }
1617
2907
 
1618
2908
  if (setup.isEnhanced) {
@@ -1668,10 +2958,11 @@ program
1668
2958
  .alias('i')
1669
2959
  .description('Initialize a new project with CAWS')
1670
2960
  .argument('<project-name>', 'Name of the new project')
1671
- .option('-i, --interactive', 'Run interactive setup', true)
2961
+ .option('-i, --interactive', 'Run interactive setup wizard')
1672
2962
  .option('-g, --git', 'Initialize git repository', true)
1673
2963
  .option('-n, --non-interactive', 'Skip interactive prompts')
1674
2964
  .option('--no-git', "Don't initialize git repository")
2965
+ .option('-t, --template <type>', 'Use project template (extension|library|api|cli|monorepo)')
1675
2966
  .action(initProject);
1676
2967
 
1677
2968
  program
@@ -1679,8 +2970,59 @@ program
1679
2970
  .alias('s')
1680
2971
  .description('Add CAWS components to existing project')
1681
2972
  .option('-f, --force', 'Overwrite existing files')
2973
+ .option('--with-oidc', 'Include OIDC trusted publisher setup')
2974
+ .option('--with-codemods', 'Include codemod transformation scripts')
2975
+ .option('--minimal', 'Only essential components (no OIDC, no codemods)')
1682
2976
  .action(scaffoldProject);
1683
2977
 
2978
+ program
2979
+ .command('validate')
2980
+ .alias('v')
2981
+ .description('Validate CAWS working spec with suggestions')
2982
+ .argument('[spec-file]', 'Path to working spec file', '.caws/working-spec.yaml')
2983
+ .option('-s, --suggestions', 'Show helpful suggestions for issues', true)
2984
+ .option('-f, --auto-fix', 'Automatically fix safe issues', false)
2985
+ .option('-q, --quiet', 'Only show errors, no suggestions', false)
2986
+ .action(async (specFile, options) => {
2987
+ try {
2988
+ // Check if spec file exists
2989
+ if (!fs.existsSync(specFile)) {
2990
+ console.error(chalk.red(`❌ Working spec file not found: ${specFile}`));
2991
+ console.error(chalk.blue('💡 Initialize CAWS first:'));
2992
+ console.error(` ${chalk.cyan('caws init .')}`);
2993
+ process.exit(1);
2994
+ }
2995
+
2996
+ // Load and parse spec
2997
+ const specContent = fs.readFileSync(specFile, 'utf8');
2998
+ const spec = yaml.load(specContent);
2999
+
3000
+ if (!spec) {
3001
+ console.error(chalk.red('❌ Failed to parse working spec YAML'));
3002
+ process.exit(1);
3003
+ }
3004
+
3005
+ // Validate with suggestions
3006
+ const result = validateWorkingSpecWithSuggestions(spec, {
3007
+ autoFix: options.autoFix,
3008
+ suggestions: !options.quiet,
3009
+ });
3010
+
3011
+ // Save auto-fixed spec if changes were made
3012
+ if (options.autoFix && result.errors.length === 0) {
3013
+ const fixedContent = yaml.dump(spec, { indent: 2 });
3014
+ fs.writeFileSync(specFile, fixedContent);
3015
+ console.log(chalk.green(`✅ Saved auto-fixed spec to ${specFile}`));
3016
+ }
3017
+
3018
+ // Exit with appropriate code
3019
+ process.exit(result.valid ? 0 : 1);
3020
+ } catch (error) {
3021
+ console.error(chalk.red('❌ Error during validation:'), error.message);
3022
+ process.exit(1);
3023
+ }
3024
+ });
3025
+
1684
3026
  // Error handling
1685
3027
  program.exitOverride((err) => {
1686
3028
  if (