@paths.design/caws-cli 8.0.1 → 8.2.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 (112) hide show
  1. package/README.md +5 -6
  2. package/dist/commands/archive.d.ts +1 -0
  3. package/dist/commands/archive.d.ts.map +1 -1
  4. package/dist/commands/archive.js +114 -6
  5. package/dist/commands/burnup.d.ts.map +1 -1
  6. package/dist/commands/burnup.js +109 -10
  7. package/dist/commands/diagnose.js +1 -1
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +185 -39
  10. package/dist/commands/mode.d.ts +2 -1
  11. package/dist/commands/mode.d.ts.map +1 -1
  12. package/dist/commands/mode.js +24 -14
  13. package/dist/commands/provenance.d.ts.map +1 -1
  14. package/dist/commands/provenance.js +216 -93
  15. package/dist/commands/quality-gates.d.ts.map +1 -1
  16. package/dist/commands/quality-gates.js +3 -1
  17. package/dist/commands/specs.d.ts.map +1 -1
  18. package/dist/commands/specs.js +184 -6
  19. package/dist/commands/status.d.ts.map +1 -1
  20. package/dist/commands/status.js +134 -10
  21. package/dist/commands/templates.js +2 -2
  22. package/dist/commands/worktree.d.ts +7 -0
  23. package/dist/commands/worktree.d.ts.map +1 -0
  24. package/dist/commands/worktree.js +136 -0
  25. package/dist/config/lite-scope.d.ts +33 -0
  26. package/dist/config/lite-scope.d.ts.map +1 -0
  27. package/dist/config/lite-scope.js +158 -0
  28. package/dist/config/modes.d.ts +90 -51
  29. package/dist/config/modes.d.ts.map +1 -1
  30. package/dist/config/modes.js +26 -0
  31. package/dist/error-handler.d.ts +3 -16
  32. package/dist/error-handler.d.ts.map +1 -1
  33. package/dist/error-handler.js +6 -98
  34. package/dist/generators/jest-config-generator.d.ts +32 -0
  35. package/dist/generators/jest-config-generator.d.ts.map +1 -0
  36. package/dist/generators/jest-config-generator.js +242 -0
  37. package/dist/index.js +40 -7
  38. package/dist/minimal-cli.js +3 -1
  39. package/dist/scaffold/claude-hooks.d.ts +28 -0
  40. package/dist/scaffold/claude-hooks.d.ts.map +1 -0
  41. package/dist/scaffold/claude-hooks.js +344 -0
  42. package/dist/scaffold/index.d.ts +2 -0
  43. package/dist/scaffold/index.d.ts.map +1 -1
  44. package/dist/scaffold/index.js +96 -76
  45. package/dist/templates/.caws/schemas/scope.schema.json +52 -0
  46. package/dist/templates/.caws/schemas/working-spec.schema.json +1 -1
  47. package/dist/templates/.caws/schemas/worktrees.schema.json +36 -0
  48. package/dist/templates/.claude/README.md +190 -0
  49. package/dist/templates/.claude/hooks/audit.sh +96 -0
  50. package/dist/templates/.claude/hooks/block-dangerous.sh +123 -0
  51. package/dist/templates/.claude/hooks/lite-sprawl-check.sh +117 -0
  52. package/dist/templates/.claude/hooks/naming-check.sh +97 -0
  53. package/dist/templates/.claude/hooks/quality-check.sh +68 -0
  54. package/dist/templates/.claude/hooks/scan-secrets.sh +85 -0
  55. package/dist/templates/.claude/hooks/scope-guard.sh +192 -0
  56. package/dist/templates/.claude/hooks/simplification-guard.sh +92 -0
  57. package/dist/templates/.claude/hooks/validate-spec.sh +76 -0
  58. package/dist/templates/.claude/settings.json +95 -0
  59. package/dist/templates/.cursor/README.md +0 -3
  60. package/dist/templates/.github/copilot-instructions.md +82 -0
  61. package/dist/templates/.junie/guidelines.md +73 -0
  62. package/dist/templates/.vscode/launch.json +0 -27
  63. package/dist/templates/.windsurf/rules/caws-quality-standards.md +54 -0
  64. package/dist/templates/CLAUDE.md +101 -0
  65. package/dist/templates/agents.md +73 -1016
  66. package/dist/templates/docs/README.md +5 -5
  67. package/dist/test-analysis.d.ts +50 -1
  68. package/dist/test-analysis.d.ts.map +1 -1
  69. package/dist/test-analysis.js +203 -10
  70. package/dist/utils/error-categories.d.ts +52 -0
  71. package/dist/utils/error-categories.d.ts.map +1 -0
  72. package/dist/utils/error-categories.js +210 -0
  73. package/dist/utils/gitignore-updater.d.ts +1 -1
  74. package/dist/utils/gitignore-updater.d.ts.map +1 -1
  75. package/dist/utils/gitignore-updater.js +4 -0
  76. package/dist/utils/ide-detection.js +133 -0
  77. package/dist/utils/quality-gates-utils.d.ts +49 -0
  78. package/dist/utils/quality-gates-utils.d.ts.map +1 -0
  79. package/dist/utils/quality-gates-utils.js +402 -0
  80. package/dist/utils/typescript-detector.d.ts +8 -5
  81. package/dist/utils/typescript-detector.d.ts.map +1 -1
  82. package/dist/utils/typescript-detector.js +36 -90
  83. package/dist/validation/spec-validation.d.ts.map +1 -1
  84. package/dist/validation/spec-validation.js +59 -6
  85. package/dist/worktree/worktree-manager.d.ts +54 -0
  86. package/dist/worktree/worktree-manager.d.ts.map +1 -0
  87. package/dist/worktree/worktree-manager.js +378 -0
  88. package/package.json +9 -3
  89. package/templates/.caws/schemas/scope.schema.json +52 -0
  90. package/templates/.caws/schemas/working-spec.schema.json +1 -1
  91. package/templates/.caws/schemas/worktrees.schema.json +36 -0
  92. package/templates/.claude/README.md +190 -0
  93. package/templates/.claude/hooks/audit.sh +96 -0
  94. package/templates/.claude/hooks/block-dangerous.sh +123 -0
  95. package/templates/.claude/hooks/lite-sprawl-check.sh +117 -0
  96. package/templates/.claude/hooks/naming-check.sh +97 -0
  97. package/templates/.claude/hooks/quality-check.sh +68 -0
  98. package/templates/.claude/hooks/scan-secrets.sh +85 -0
  99. package/templates/.claude/hooks/scope-guard.sh +192 -0
  100. package/templates/.claude/hooks/simplification-guard.sh +92 -0
  101. package/templates/.claude/hooks/validate-spec.sh +76 -0
  102. package/templates/.claude/settings.json +95 -0
  103. package/templates/.cursor/README.md +0 -3
  104. package/templates/.github/copilot-instructions.md +82 -0
  105. package/templates/.junie/guidelines.md +73 -0
  106. package/templates/.vscode/launch.json +0 -27
  107. package/templates/.windsurf/rules/caws-quality-standards.md +54 -0
  108. package/templates/AGENTS.md +104 -0
  109. package/templates/CLAUDE.md +101 -0
  110. package/templates/docs/README.md +5 -5
  111. package/templates/.github/copilot/instructions.md +0 -311
  112. package/templates/agents.md +0 -1047
package/README.md CHANGED
@@ -13,7 +13,7 @@ The CAWS CLI serves as the central control point for:
13
13
  - **Agent Integration**: Programmatic APIs for AI agents to evaluate and guide development
14
14
  - **Waiver Management**: Fast-lane escape hatches with full audit trails
15
15
  - **CI/CD Optimization**: Pipeline generation and optimization tools
16
- - **Experimental Features**: Dry-run capabilities for cutting-edge functionality
16
+ - **Experimental Features**: Dry-run capabilities for experimental functionality
17
17
 
18
18
  ## Installation
19
19
 
@@ -157,15 +157,14 @@ caws-cli/
157
157
  │ │
158
158
  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
159
159
  │
160
- ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
161
- │ caws-mcp-server │────│ caws-vscode-ext │
162
- │ (Agent Bridge) │ │ (IDE Integration)
163
- ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
160
+ ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
161
+ │ caws-mcp-server │
162
+ │ (Agent Bridge) │
163
+ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
164
164
  ```
165
165
 
166
166
  - **caws-template**: Provides the tools and configurations that CLI manages
167
167
  - **caws-mcp-server**: Exposes CLI functionality to AI agents via MCP protocol
168
- - **caws-vscode-extension**: Provides IDE integration using CLI capabilities
169
168
 
170
169
  ### Quality Gates Integration
171
170
 
@@ -18,6 +18,7 @@ export function loadChange(changeId: string): Promise<any | null>;
18
18
  export function validateAcceptanceCriteria(workingSpec: any): Promise<any>;
19
19
  /**
20
20
  * Validate change meets quality gates
21
+ * Runs the actual quality gates runner and checks for violations
21
22
  * @param {string} changeId - Change identifier
22
23
  * @returns {Promise<Object>} Quality gate result
23
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/commands/archive.js"],"names":[],"mappings":"AA8OA;;;;GAIG;AACH,yCAHW,MAAM,+BAqGhB;AAtUD;;;;GAIG;AACH,qCAHW,MAAM,GACJ,OAAO,CAAC,MAAO,IAAI,CAAC,CAgChC;AAED;;;;GAIG;AACH,8DAFa,OAAO,KAAQ,CA8B3B;AAED;;;;GAIG;AACH,sDAFa,OAAO,KAAQ,CAS3B;AAED;;;;GAIG;AACH,oDAFa,OAAO,CAAC,MAAM,CAAC,CAgC3B;AAED;;;;GAIG;AACH,4CAFa,OAAO,CAAC,IAAI,CAAC,CAazB;AAED;;;;GAIG;AACH,+CAFa,OAAO,CAAC,IAAI,CAAC,CAoCzB;AAED;;;;;GAKG;AACH,6FAiCC"}
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/commands/archive.js"],"names":[],"mappings":"AA0VA;;;;GAIG;AACH,yCAHW,MAAM,+BAqGhB;AAjbD;;;;GAIG;AACH,qCAHW,MAAM,GACJ,OAAO,CAAC,MAAO,IAAI,CAAC,CAgChC;AAED;;;;GAIG;AACH,8DAFa,OAAO,KAAQ,CA8B3B;AAED;;;;;GAKG;AACH,sDAFa,OAAO,KAAQ,CAmH3B;AAED;;;;GAIG;AACH,oDAFa,OAAO,CAAC,MAAM,CAAC,CAgC3B;AAED;;;;GAIG;AACH,4CAFa,OAAO,CAAC,IAAI,CAAC,CAazB;AAED;;;;GAIG;AACH,+CAFa,OAAO,CAAC,IAAI,CAAC,CAoCzB;AAED;;;;;GAKG;AACH,6FAiCC"}
@@ -8,6 +8,7 @@ const fs = require('fs-extra');
8
8
  const path = require('path');
9
9
  const yaml = require('js-yaml');
10
10
  const chalk = require('chalk');
11
+ const { execSync } = require('child_process');
11
12
  const { safeAsync, outputResult } = require('../error-handler');
12
13
 
13
14
  // Import spec resolution system
@@ -87,16 +88,123 @@ async function validateAcceptanceCriteria(workingSpec) {
87
88
 
88
89
  /**
89
90
  * Validate change meets quality gates
91
+ * Runs the actual quality gates runner and checks for violations
90
92
  * @param {string} changeId - Change identifier
91
93
  * @returns {Promise<Object>} Quality gate result
92
94
  */
93
95
  async function validateQualityGates(_changeId) {
94
- // For now, return success - in full implementation would run actual gate checks
95
- return {
96
- valid: true,
97
- message: 'Quality gates passed (implementation pending)',
98
- gates: ['test-coverage', 'linting', 'type-checking'],
99
- };
96
+ const gates = [];
97
+ const violations = [];
98
+ const warnings = [];
99
+
100
+ try {
101
+ // Try to run the quality gates runner
102
+ const qualityGatesPath = path.join(
103
+ __dirname,
104
+ '..',
105
+ '..',
106
+ '..',
107
+ 'quality-gates',
108
+ 'run-quality-gates.mjs'
109
+ );
110
+
111
+ // Check if quality gates runner exists
112
+ if (await fs.pathExists(qualityGatesPath)) {
113
+ try {
114
+ // Run quality gates in CI mode (checks all files, not just staged)
115
+ const result = execSync(`node "${qualityGatesPath}" --context=ci --json 2>&1`, {
116
+ encoding: 'utf8',
117
+ timeout: 60000, // 60 second timeout
118
+ cwd: process.cwd(),
119
+ });
120
+
121
+ // Try to parse JSON output
122
+ try {
123
+ const jsonMatch = result.match(/\{[\s\S]*\}$/);
124
+ if (jsonMatch) {
125
+ const parsed = JSON.parse(jsonMatch[0]);
126
+ if (parsed.violations) {
127
+ violations.push(...parsed.violations);
128
+ }
129
+ if (parsed.warnings) {
130
+ warnings.push(...parsed.warnings);
131
+ }
132
+ gates.push(...(parsed.gates || []));
133
+ }
134
+ } catch {
135
+ // JSON parsing failed, check for error indicators in output
136
+ if (result.includes('āŒ') || result.includes('FAIL')) {
137
+ violations.push({ message: 'Quality gates reported failures', output: result });
138
+ }
139
+ }
140
+ } catch (execError) {
141
+ // Command failed - check exit code and output
142
+ if (execError.status !== 0) {
143
+ const output = execError.stdout || execError.message;
144
+ if (output.includes('violations') || output.includes('āŒ')) {
145
+ violations.push({ message: 'Quality gates failed', output: output.substring(0, 500) });
146
+ }
147
+ }
148
+ }
149
+ }
150
+
151
+ // Also check for active waivers that might cover violations
152
+ const waiversPath = path.join(process.cwd(), '.caws', 'waivers', 'active-waivers.yaml');
153
+ let hasActiveWaivers = false;
154
+ if (await fs.pathExists(waiversPath)) {
155
+ const waiversContent = await fs.readFile(waiversPath, 'utf8');
156
+ const waivers = yaml.load(waiversContent);
157
+ if (waivers && waivers.waivers) {
158
+ const activeWaiverCount = Object.keys(waivers.waivers).length;
159
+ if (activeWaiverCount > 0) {
160
+ hasActiveWaivers = true;
161
+ gates.push(`${activeWaiverCount} active waiver(s)`);
162
+ }
163
+ }
164
+ }
165
+
166
+ // Determine overall validity
167
+ const hasBlockingViolations = violations.some(
168
+ (v) => v.severity === 'block' || v.severity === 'fail'
169
+ );
170
+
171
+ if (violations.length === 0) {
172
+ return {
173
+ valid: true,
174
+ message: 'All quality gates passed',
175
+ gates: gates.length > 0 ? gates : ['naming', 'duplication', 'god-objects', 'hidden-todo'],
176
+ violations: [],
177
+ warnings,
178
+ };
179
+ } else if (hasActiveWaivers && !hasBlockingViolations) {
180
+ return {
181
+ valid: true,
182
+ message: `Quality gates passed with ${violations.length} waived violation(s)`,
183
+ gates,
184
+ violations,
185
+ warnings,
186
+ waived: true,
187
+ };
188
+ } else {
189
+ return {
190
+ valid: false,
191
+ message: `${violations.length} quality gate violation(s) found`,
192
+ gates,
193
+ violations,
194
+ warnings,
195
+ };
196
+ }
197
+ } catch (error) {
198
+ // If quality gates can't be run, warn but don't block
199
+ return {
200
+ valid: true,
201
+ message: `Quality gates check skipped: ${error.message}`,
202
+ gates: [],
203
+ violations: [],
204
+ warnings: [{ message: `Could not run quality gates: ${error.message}` }],
205
+ skipped: true,
206
+ };
207
+ }
100
208
  }
101
209
 
102
210
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"burnup.d.ts","sourceRoot":"","sources":["../../src/commands/burnup.js"],"names":[],"mappings":"AAaA;;;GAGG;AACH,wCAFW,MAAM,iBAsEhB"}
1
+ {"version":3,"file":"burnup.d.ts","sourceRoot":"","sources":["../../src/commands/burnup.js"],"names":[],"mappings":"AAsFA;;;GAGG;AACH,wCAFW,MAAM,iBAgGhB"}
@@ -8,9 +8,82 @@ const fs = require('fs-extra');
8
8
  const path = require('path');
9
9
  const yaml = require('js-yaml');
10
10
  const chalk = require('chalk');
11
+ const { execSync } = require('child_process');
11
12
 
12
13
  const { deriveBudget, generateBurnupReport } = require('../budget-derivation');
13
14
 
15
+ /**
16
+ * Get actual git change statistics from the repository
17
+ * Analyzes changes since the last tag or initial commit
18
+ * @param {string} specDir - Directory containing the spec file
19
+ * @returns {Object} Stats with files_changed, lines_added, lines_removed, lines_changed
20
+ */
21
+ function getGitChangeStats(specDir) {
22
+ try {
23
+ const cwd = specDir || process.cwd();
24
+
25
+ // Find the base reference - prefer last tag, fall back to first commit
26
+ let baseRef;
27
+ try {
28
+ baseRef = execSync('git describe --tags --abbrev=0 2>/dev/null', {
29
+ cwd,
30
+ encoding: 'utf8',
31
+ }).trim();
32
+ } catch {
33
+ // No tags, use first commit
34
+ try {
35
+ baseRef = execSync('git rev-list --max-parents=0 HEAD', {
36
+ cwd,
37
+ encoding: 'utf8',
38
+ }).trim();
39
+ } catch {
40
+ // Not a git repo or no commits
41
+ return null;
42
+ }
43
+ }
44
+
45
+ // Get file change count
46
+ const filesOutput = execSync(`git diff --name-only ${baseRef}..HEAD`, {
47
+ cwd,
48
+ encoding: 'utf8',
49
+ });
50
+ const filesChanged = filesOutput.trim().split('\n').filter(Boolean).length;
51
+
52
+ // Get line statistics using --numstat
53
+ const numstatOutput = execSync(`git diff --numstat ${baseRef}..HEAD`, {
54
+ cwd,
55
+ encoding: 'utf8',
56
+ });
57
+
58
+ let linesAdded = 0;
59
+ let linesRemoved = 0;
60
+
61
+ numstatOutput
62
+ .trim()
63
+ .split('\n')
64
+ .filter(Boolean)
65
+ .forEach((line) => {
66
+ const [added, removed] = line.split('\t');
67
+ // Skip binary files (shown as '-')
68
+ if (added !== '-' && removed !== '-') {
69
+ linesAdded += parseInt(added, 10) || 0;
70
+ linesRemoved += parseInt(removed, 10) || 0;
71
+ }
72
+ });
73
+
74
+ return {
75
+ files_changed: filesChanged,
76
+ lines_added: linesAdded,
77
+ lines_removed: linesRemoved,
78
+ lines_changed: linesAdded + linesRemoved,
79
+ base_ref: baseRef,
80
+ };
81
+ } catch (error) {
82
+ // Return null if git analysis fails
83
+ return null;
84
+ }
85
+ }
86
+
14
87
  /**
15
88
  * Burn-up command handler
16
89
  * @param {string} specFile - Path to spec file
@@ -32,15 +105,34 @@ async function burnupCommand(specFile) {
32
105
  // Derive budget
33
106
  const derivedBudget = deriveBudget(spec, path.dirname(specPath));
34
107
 
35
- // Mock current stats - in real implementation this would analyze actual git changes
36
- const mockStats = {
37
- files_changed: 50, // This would be calculated from actual changes
38
- lines_changed: 5000,
39
- risk_tier: spec.risk_tier,
40
- };
108
+ // Get actual git change statistics
109
+ const gitStats = getGitChangeStats(path.dirname(specPath));
110
+
111
+ let currentStats;
112
+ if (gitStats) {
113
+ currentStats = {
114
+ files_changed: gitStats.files_changed,
115
+ lines_changed: gitStats.lines_changed,
116
+ lines_added: gitStats.lines_added,
117
+ lines_removed: gitStats.lines_removed,
118
+ risk_tier: spec.risk_tier,
119
+ base_ref: gitStats.base_ref,
120
+ };
121
+ console.log(chalk.gray(` Analyzing changes since: ${gitStats.base_ref}`));
122
+ } else {
123
+ // Fallback if git analysis fails (not in a repo or no commits)
124
+ console.log(chalk.yellow(' āš ļø Could not analyze git history, using zero values'));
125
+ currentStats = {
126
+ files_changed: 0,
127
+ lines_changed: 0,
128
+ lines_added: 0,
129
+ lines_removed: 0,
130
+ risk_tier: spec.risk_tier,
131
+ };
132
+ }
41
133
 
42
134
  // Generate report
43
- const report = generateBurnupReport(derivedBudget, mockStats);
135
+ const report = generateBurnupReport(derivedBudget, currentStats);
44
136
 
45
137
  console.log(report);
46
138
 
@@ -63,15 +155,22 @@ async function burnupCommand(specFile) {
63
155
 
64
156
  console.log(
65
157
  chalk.gray(
66
- ` Current Usage: ${mockStats.files_changed} files, ${mockStats.lines_changed} LOC`
158
+ ` Current Usage: ${currentStats.files_changed} files, ${currentStats.lines_changed} LOC`
67
159
  )
68
160
  );
161
+ if (currentStats.lines_added !== undefined) {
162
+ console.log(
163
+ chalk.gray(
164
+ ` Breakdown: +${currentStats.lines_added} added, -${currentStats.lines_removed} removed`
165
+ )
166
+ );
167
+ }
69
168
 
70
169
  const filePercent = Math.round(
71
- (mockStats.files_changed / derivedBudget.effective.max_files) * 100
170
+ (currentStats.files_changed / derivedBudget.effective.max_files) * 100
72
171
  );
73
172
  const locPercent = Math.round(
74
- (mockStats.lines_changed / derivedBudget.effective.max_loc) * 100
173
+ (currentStats.lines_changed / derivedBudget.effective.max_loc) * 100
75
174
  );
76
175
 
77
176
  if (filePercent > 90 || locPercent > 90) {
@@ -11,7 +11,7 @@ const chalk = require('chalk');
11
11
 
12
12
  // Import utilities
13
13
  const { checkTypeScriptTestConfig } = require('../utils/typescript-detector');
14
- const { configureJestForTypeScript } = require('../generators/jest-config');
14
+ const { configureJestForTypeScript } = require('../generators/jest-config-generator');
15
15
 
16
16
  /**
17
17
  * Health check: Working spec validity
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.js"],"names":[],"mappings":"AAqBA;;GAEG;AACH,2EAwiBC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.js"],"names":[],"mappings":"AAwBA;;GAEG;AACH,2EA6mBC"}
@@ -18,6 +18,15 @@ const { finalizeProject } = require('../utils/finalization');
18
18
  const { scaffoldCursorHooks } = require('../scaffold/cursor-hooks');
19
19
  const { scaffoldIDEIntegrations } = require('../scaffold/index');
20
20
  const { updateGitignore } = require('../utils/gitignore-updater');
21
+ const { getLiteScopeDefaults } = require('../config/lite-scope');
22
+ const { scaffoldClaudeHooks } = require('../scaffold/claude-hooks');
23
+ const { setCurrentMode } = require('../config/modes');
24
+ const {
25
+ IDE_REGISTRY,
26
+ detectActiveIDEs,
27
+ getRecommendedIDEs,
28
+ parseIDESelection,
29
+ } = require('../utils/ide-detection');
21
30
 
22
31
  /**
23
32
  * Initialize a new project with CAWS
@@ -115,8 +124,9 @@ async function initProject(projectName, options) {
115
124
  const cawsSetup = detectCAWSSetup(targetDir);
116
125
  const originalTemplateDir = cawsSetup?.hasTemplateDir ? cawsSetup.templateDir : null;
117
126
 
118
- // Check for existing agents.md/caws.md in target directory
119
- const existingAgentsMd = fs.existsSync(path.join(targetDir, 'agents.md'));
127
+ // Check for existing AGENTS.md/agents.md/caws.md in target directory
128
+ const existingAgentsMd = fs.existsSync(path.join(targetDir, 'AGENTS.md')) ||
129
+ fs.existsSync(path.join(targetDir, 'agents.md'));
120
130
  const existingCawsMd = fs.existsSync(path.join(targetDir, 'caws.md'));
121
131
 
122
132
  // Create project directory and change to it (unless already in current directory)
@@ -137,55 +147,45 @@ async function initProject(projectName, options) {
137
147
  await fs.ensureDir('.agent');
138
148
  console.log(chalk.blue('ā„¹ļø Created basic CAWS structure'));
139
149
 
140
- // Copy agents.md guide if templates are available
150
+ // Copy AGENTS.md guide if templates are available
141
151
  if (originalTemplateDir) {
142
152
  try {
143
- const agentsMdSource = path.join(originalTemplateDir, 'agents.md');
144
- let targetFile = 'agents.md';
153
+ const agentsMdSource = path.join(originalTemplateDir, 'AGENTS.md');
154
+ let targetFile = 'AGENTS.md';
145
155
 
146
156
  if (fs.existsSync(agentsMdSource)) {
147
- // Use the pre-checked values for conflicts
148
157
  if (existingAgentsMd) {
149
- // Conflict: user already has agents.md
150
158
  if (options.interactive && !options.nonInteractive) {
151
- // Interactive mode: ask user
152
159
  const overwriteAnswer = await inquirer.prompt([
153
160
  {
154
161
  type: 'confirm',
155
162
  name: 'overwrite',
156
- message: 'āš ļø agents.md already exists. Overwrite with CAWS guide?',
163
+ message: 'AGENTS.md already exists. Overwrite with CAWS guide?',
157
164
  default: false,
158
165
  },
159
166
  ]);
160
167
 
161
- if (overwriteAnswer.overwrite) {
162
- targetFile = 'agents.md';
163
- } else {
168
+ if (!overwriteAnswer.overwrite) {
164
169
  targetFile = 'caws.md';
165
170
  }
166
171
  } else {
167
- // Non-interactive mode: use caws.md instead
168
172
  targetFile = 'caws.md';
169
- console.log(chalk.blue('ā„¹ļø agents.md exists, using caws.md for CAWS guide'));
173
+ console.log(chalk.blue('AGENTS.md exists, using caws.md for CAWS guide'));
170
174
  }
171
175
  }
172
176
 
173
- // If caws.md also exists and that's our target, skip
174
177
  if (targetFile === 'caws.md' && existingCawsMd) {
175
178
  console.log(
176
- chalk.yellow('āš ļø Both agents.md and caws.md exist, skipping guide copy')
179
+ chalk.yellow('Both AGENTS.md and caws.md exist, skipping guide copy')
177
180
  );
178
181
  } else {
179
182
  const agentsMdDest = path.join(process.cwd(), targetFile);
180
183
  await fs.copyFile(agentsMdSource, agentsMdDest);
181
- console.log(chalk.green(`āœ… Added ${targetFile} guide`));
184
+ console.log(chalk.green(`Added ${targetFile} guide`));
182
185
  }
183
186
  }
184
187
  } catch (templateError) {
185
- console.warn(chalk.yellow('āš ļø Could not copy agents guide:'), templateError.message);
186
- console.warn(
187
- chalk.blue('šŸ’” You can manually copy the guide from the caws-template package')
188
- );
188
+ console.warn(chalk.yellow('Could not copy agents guide:'), templateError.message);
189
189
  }
190
190
  }
191
191
  } else {
@@ -193,12 +193,92 @@ async function initProject(projectName, options) {
193
193
  console.log(chalk.green('āœ… CAWS project detected - skipping template copy'));
194
194
  }
195
195
 
196
+ // Handle lite mode init path
197
+ if (options.mode === 'lite') {
198
+ console.log(chalk.magenta('šŸ›”ļø CAWS Lite Mode — guardrails without YAML specs'));
199
+
200
+ // Detect allowed directories
201
+ const detectedDirs = [];
202
+ const commonDirs = ['src/', 'lib/', 'app/', 'tests/', 'test/', 'docs/'];
203
+ for (const dir of commonDirs) {
204
+ if (fs.existsSync(path.join(process.cwd(), dir.replace(/\/$/, '')))) {
205
+ detectedDirs.push(dir);
206
+ }
207
+ }
208
+
209
+ let allowedDirs = detectedDirs.length > 0 ? detectedDirs : ['src/', 'tests/', 'docs/'];
210
+
211
+ if (options.interactive && !options.nonInteractive) {
212
+ const liteAnswers = await inquirer.prompt([
213
+ {
214
+ type: 'input',
215
+ name: 'projectName',
216
+ message: 'šŸ“ Project name:',
217
+ default: path.basename(process.cwd()),
218
+ },
219
+ {
220
+ type: 'input',
221
+ name: 'allowedDirs',
222
+ message: 'šŸ“ Allowed directories (comma-separated):',
223
+ default: allowedDirs.join(', '),
224
+ },
225
+ ]);
226
+ allowedDirs = liteAnswers.allowedDirs.split(',').map((d) => d.trim()).filter(Boolean);
227
+ }
228
+
229
+ // Generate .caws/scope.json
230
+ await fs.ensureDir('.caws');
231
+ const scopeConfig = getLiteScopeDefaults();
232
+ scopeConfig.allowedDirectories = allowedDirs;
233
+ await fs.writeFile(
234
+ path.join('.caws', 'scope.json'),
235
+ JSON.stringify(scopeConfig, null, 2)
236
+ );
237
+ console.log(chalk.green('āœ… Created .caws/scope.json'));
238
+
239
+ // Set mode to lite
240
+ await setCurrentMode('lite');
241
+ console.log(chalk.green('āœ… Set mode to lite in .caws/mode.json'));
242
+
243
+ // Scaffold hooks: block-dangerous + scope-guard + lite-sprawl-check + simplification-guard
244
+ const liteIDEs = options.ide ? parseIDESelection(options.ide) : ['claude'];
245
+ if (liteIDEs.includes('claude')) {
246
+ console.log(chalk.blue('šŸ”§ Setting up lite-mode hooks...'));
247
+ await scaffoldClaudeHooks(process.cwd(), ['safety', 'scope', 'lite']);
248
+ }
249
+
250
+ // Update .gitignore
251
+ console.log(chalk.blue('šŸ“ Updating .gitignore...'));
252
+ await updateGitignore(process.cwd());
253
+
254
+ // Success
255
+ console.log(chalk.green('\nšŸŽ‰ CAWS Lite mode initialized!'));
256
+ console.log(chalk.blue('\nGuardrails active:'));
257
+ console.log(' - Destructive command blocking (git push --force, rm -rf, etc.)');
258
+ console.log(' - Scope fencing (edits outside allowed directories require confirmation)');
259
+ console.log(' - File sprawl detection (banned patterns like *-enhanced.*, *-final.*)');
260
+ console.log(' - Simplification guard (prevents stubbing out implementations)');
261
+ console.log(chalk.blue('\nNext steps:'));
262
+ console.log(' 1. Review .caws/scope.json and customize for your project');
263
+ console.log(' 2. Start coding — hooks will protect against common AI mistakes');
264
+ console.log(' 3. Use `caws worktree create <name>` for isolated agent workspaces');
265
+ return;
266
+ }
267
+
196
268
  // Handle interactive wizard or template-based setup
197
269
  if (options.interactive && !options.nonInteractive) {
198
270
  console.log(chalk.cyan('šŸŽÆ CAWS Interactive Setup Wizard'));
199
271
  console.log(chalk.blue('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
200
272
  console.log(chalk.gray('This wizard will guide you through creating a CAWS working spec\n'));
201
273
 
274
+ // Detect active IDEs for pre-selecting the IDE prompt
275
+ const detectedIDEs = detectActiveIDEs();
276
+ const recommendedIDEs = getRecommendedIDEs();
277
+ if (detectedIDEs.length > 0) {
278
+ console.log(chalk.blue(`Detected IDE: ${detectedIDEs.map((id) => IDE_REGISTRY[id].name).join(', ')}`));
279
+ console.log(chalk.gray(' (Pre-selected based on your environment)\n'));
280
+ }
281
+
202
282
  // Detect project type
203
283
  const detectedType = detectProjectType(process.cwd());
204
284
  console.log(chalk.blue(`šŸ“¦ Detected project type: ${chalk.cyan(detectedType)}`));
@@ -337,10 +417,65 @@ async function initProject(projectName, options) {
337
417
  },
338
418
  },
339
419
  {
340
- type: 'confirm',
341
- name: 'useCursorHooks',
342
- message: 'šŸŽÆ Enable Cursor IDE hooks for real-time quality gates?',
343
- default: true,
420
+ type: 'checkbox',
421
+ name: 'selectedIDEs',
422
+ message: 'Which IDE integrations do you want to install?',
423
+ choices: [
424
+ {
425
+ name: `Cursor (hooks, rules, audit) - AI-first IDE`,
426
+ value: 'cursor',
427
+ checked: options.ide
428
+ ? parseIDESelection(options.ide).includes('cursor')
429
+ : recommendedIDEs.includes('cursor'),
430
+ },
431
+ {
432
+ name: `Claude Code (safety hooks, settings)`,
433
+ value: 'claude',
434
+ checked: options.ide
435
+ ? parseIDESelection(options.ide).includes('claude')
436
+ : recommendedIDEs.includes('claude'),
437
+ },
438
+ {
439
+ name: `VS Code (settings, debug configs)`,
440
+ value: 'vscode',
441
+ checked: options.ide
442
+ ? parseIDESelection(options.ide).includes('vscode')
443
+ : recommendedIDEs.includes('vscode'),
444
+ },
445
+ {
446
+ name: `IntelliJ IDEA (run configurations)`,
447
+ value: 'intellij',
448
+ checked: options.ide
449
+ ? parseIDESelection(options.ide).includes('intellij')
450
+ : recommendedIDEs.includes('intellij'),
451
+ },
452
+ {
453
+ name: `Windsurf (CAWS workflow)`,
454
+ value: 'windsurf',
455
+ checked: options.ide
456
+ ? parseIDESelection(options.ide).includes('windsurf')
457
+ : recommendedIDEs.includes('windsurf'),
458
+ },
459
+ {
460
+ name: `GitHub Copilot (instructions)`,
461
+ value: 'copilot',
462
+ checked: options.ide
463
+ ? parseIDESelection(options.ide).includes('copilot')
464
+ : recommendedIDEs.includes('copilot'),
465
+ },
466
+ {
467
+ name: `JetBrains Junie (AI agent guidelines)`,
468
+ value: 'junie',
469
+ checked: options.ide
470
+ ? parseIDESelection(options.ide).includes('junie')
471
+ : recommendedIDEs.includes('junie'),
472
+ },
473
+ new inquirer.Separator(),
474
+ {
475
+ name: 'All IDEs (install everything)',
476
+ value: 'all',
477
+ },
478
+ ],
344
479
  },
345
480
  {
346
481
  type: 'confirm',
@@ -412,15 +547,20 @@ Happy coding! šŸŽÆ
412
547
  console.log(chalk.green('āœ… Created test and docs directories'));
413
548
  }
414
549
 
415
- // Setup Cursor hooks if requested
416
- if (answers.useCursorHooks) {
417
- console.log(chalk.blue('šŸŽÆ Setting up Cursor hooks...'));
550
+ // Setup selected IDE integrations
551
+ const selectedIDEs = parseIDESelection(answers.selectedIDEs || []);
552
+
553
+ if (selectedIDEs.includes('cursor')) {
554
+ console.log(chalk.blue('Setting up Cursor hooks...'));
418
555
  await scaffoldCursorHooks(process.cwd());
419
556
  }
420
557
 
421
- // Setup IDE integrations for comprehensive development experience
422
- console.log(chalk.blue('šŸŽØ Setting up IDE integrations...'));
423
- await scaffoldIDEIntegrations(process.cwd(), { force: false });
558
+ if (selectedIDEs.length > 0) {
559
+ console.log(chalk.blue('Setting up IDE integrations...'));
560
+ await scaffoldIDEIntegrations(process.cwd(), { force: false, ides: selectedIDEs });
561
+ } else {
562
+ console.log(chalk.gray('Skipping IDE setup (none selected, run `caws scaffold --ide <ides>` later)'));
563
+ }
424
564
 
425
565
  // Update .gitignore to exclude CAWS local runtime files
426
566
  console.log(chalk.blue('šŸ“ Updating .gitignore...'));
@@ -500,13 +640,18 @@ Happy coding! šŸŽÆ
500
640
  console.log(chalk.green('āœ… Created .caws/policy.yaml (optional - defaults work fine)'));
501
641
  }
502
642
 
503
- // Setup Cursor hooks by default in non-interactive mode
504
- console.log(chalk.blue('šŸŽÆ Setting up Cursor hooks...'));
505
- await scaffoldCursorHooks(process.cwd());
643
+ // Setup IDE integrations based on --ide flag or auto-detection
644
+ const selectedIDEs = options.ide ? parseIDESelection(options.ide) : getRecommendedIDEs();
506
645
 
507
- // Setup IDE integrations by default in non-interactive mode
508
- console.log(chalk.blue('šŸŽØ Setting up IDE integrations...'));
509
- await scaffoldIDEIntegrations(process.cwd(), { force: false });
646
+ if (selectedIDEs.includes('cursor')) {
647
+ console.log(chalk.blue('Setting up Cursor hooks...'));
648
+ await scaffoldCursorHooks(process.cwd());
649
+ }
650
+
651
+ if (selectedIDEs.length > 0) {
652
+ console.log(chalk.blue(`Setting up IDE integrations: ${selectedIDEs.map((id) => IDE_REGISTRY[id].name).join(', ')}...`));
653
+ await scaffoldIDEIntegrations(process.cwd(), { force: false, ides: selectedIDEs });
654
+ }
510
655
 
511
656
  // Update .gitignore to exclude CAWS local runtime files
512
657
  console.log(chalk.blue('šŸ“ Updating .gitignore...'));
@@ -549,8 +694,9 @@ Happy coding! šŸŽÆ
549
694
  console.log(' 3. Run: caws validate (verify setup)');
550
695
  console.log(' 4. Run: caws diagnose (check health)');
551
696
  console.log(' 5. Optional: Create .caws/policy.yaml for custom budgets');
552
- if (answers?.useCursorHooks || options.interactive === false) {
553
- console.log(' 6. Restart Cursor IDE to activate quality gates');
697
+ const finalIDEs = answers?.selectedIDEs || [];
698
+ if (finalIDEs.includes('cursor') || finalIDEs.includes('claude') || options.interactive === false) {
699
+ console.log(' 6. Restart your IDE to activate quality gates');
554
700
  }
555
701
  console.log('\nšŸ’” Quick start: caws scaffold && caws validate && caws diagnose');
556
702
  } catch (error) {