@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.
- package/README.md +5 -6
- package/dist/commands/archive.d.ts +1 -0
- package/dist/commands/archive.d.ts.map +1 -1
- package/dist/commands/archive.js +114 -6
- package/dist/commands/burnup.d.ts.map +1 -1
- package/dist/commands/burnup.js +109 -10
- package/dist/commands/diagnose.js +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +185 -39
- package/dist/commands/mode.d.ts +2 -1
- package/dist/commands/mode.d.ts.map +1 -1
- package/dist/commands/mode.js +24 -14
- package/dist/commands/provenance.d.ts.map +1 -1
- package/dist/commands/provenance.js +216 -93
- package/dist/commands/quality-gates.d.ts.map +1 -1
- package/dist/commands/quality-gates.js +3 -1
- package/dist/commands/specs.d.ts.map +1 -1
- package/dist/commands/specs.js +184 -6
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +134 -10
- package/dist/commands/templates.js +2 -2
- package/dist/commands/worktree.d.ts +7 -0
- package/dist/commands/worktree.d.ts.map +1 -0
- package/dist/commands/worktree.js +136 -0
- package/dist/config/lite-scope.d.ts +33 -0
- package/dist/config/lite-scope.d.ts.map +1 -0
- package/dist/config/lite-scope.js +158 -0
- package/dist/config/modes.d.ts +90 -51
- package/dist/config/modes.d.ts.map +1 -1
- package/dist/config/modes.js +26 -0
- package/dist/error-handler.d.ts +3 -16
- package/dist/error-handler.d.ts.map +1 -1
- package/dist/error-handler.js +6 -98
- package/dist/generators/jest-config-generator.d.ts +32 -0
- package/dist/generators/jest-config-generator.d.ts.map +1 -0
- package/dist/generators/jest-config-generator.js +242 -0
- package/dist/index.js +40 -7
- package/dist/minimal-cli.js +3 -1
- package/dist/scaffold/claude-hooks.d.ts +28 -0
- package/dist/scaffold/claude-hooks.d.ts.map +1 -0
- package/dist/scaffold/claude-hooks.js +344 -0
- package/dist/scaffold/index.d.ts +2 -0
- package/dist/scaffold/index.d.ts.map +1 -1
- package/dist/scaffold/index.js +96 -76
- package/dist/templates/.caws/schemas/scope.schema.json +52 -0
- package/dist/templates/.caws/schemas/working-spec.schema.json +1 -1
- package/dist/templates/.caws/schemas/worktrees.schema.json +36 -0
- package/dist/templates/.claude/README.md +190 -0
- package/dist/templates/.claude/hooks/audit.sh +96 -0
- package/dist/templates/.claude/hooks/block-dangerous.sh +123 -0
- package/dist/templates/.claude/hooks/lite-sprawl-check.sh +117 -0
- package/dist/templates/.claude/hooks/naming-check.sh +97 -0
- package/dist/templates/.claude/hooks/quality-check.sh +68 -0
- package/dist/templates/.claude/hooks/scan-secrets.sh +85 -0
- package/dist/templates/.claude/hooks/scope-guard.sh +192 -0
- package/dist/templates/.claude/hooks/simplification-guard.sh +92 -0
- package/dist/templates/.claude/hooks/validate-spec.sh +76 -0
- package/dist/templates/.claude/settings.json +95 -0
- package/dist/templates/.cursor/README.md +0 -3
- package/dist/templates/.github/copilot-instructions.md +82 -0
- package/dist/templates/.junie/guidelines.md +73 -0
- package/dist/templates/.vscode/launch.json +0 -27
- package/dist/templates/.windsurf/rules/caws-quality-standards.md +54 -0
- package/dist/templates/CLAUDE.md +101 -0
- package/dist/templates/agents.md +73 -1016
- package/dist/templates/docs/README.md +5 -5
- package/dist/test-analysis.d.ts +50 -1
- package/dist/test-analysis.d.ts.map +1 -1
- package/dist/test-analysis.js +203 -10
- package/dist/utils/error-categories.d.ts +52 -0
- package/dist/utils/error-categories.d.ts.map +1 -0
- package/dist/utils/error-categories.js +210 -0
- package/dist/utils/gitignore-updater.d.ts +1 -1
- package/dist/utils/gitignore-updater.d.ts.map +1 -1
- package/dist/utils/gitignore-updater.js +4 -0
- package/dist/utils/ide-detection.js +133 -0
- package/dist/utils/quality-gates-utils.d.ts +49 -0
- package/dist/utils/quality-gates-utils.d.ts.map +1 -0
- package/dist/utils/quality-gates-utils.js +402 -0
- package/dist/utils/typescript-detector.d.ts +8 -5
- package/dist/utils/typescript-detector.d.ts.map +1 -1
- package/dist/utils/typescript-detector.js +36 -90
- package/dist/validation/spec-validation.d.ts.map +1 -1
- package/dist/validation/spec-validation.js +59 -6
- package/dist/worktree/worktree-manager.d.ts +54 -0
- package/dist/worktree/worktree-manager.d.ts.map +1 -0
- package/dist/worktree/worktree-manager.js +378 -0
- package/package.json +9 -3
- package/templates/.caws/schemas/scope.schema.json +52 -0
- package/templates/.caws/schemas/working-spec.schema.json +1 -1
- package/templates/.caws/schemas/worktrees.schema.json +36 -0
- package/templates/.claude/README.md +190 -0
- package/templates/.claude/hooks/audit.sh +96 -0
- package/templates/.claude/hooks/block-dangerous.sh +123 -0
- package/templates/.claude/hooks/lite-sprawl-check.sh +117 -0
- package/templates/.claude/hooks/naming-check.sh +97 -0
- package/templates/.claude/hooks/quality-check.sh +68 -0
- package/templates/.claude/hooks/scan-secrets.sh +85 -0
- package/templates/.claude/hooks/scope-guard.sh +192 -0
- package/templates/.claude/hooks/simplification-guard.sh +92 -0
- package/templates/.claude/hooks/validate-spec.sh +76 -0
- package/templates/.claude/settings.json +95 -0
- package/templates/.cursor/README.md +0 -3
- package/templates/.github/copilot-instructions.md +82 -0
- package/templates/.junie/guidelines.md +73 -0
- package/templates/.vscode/launch.json +0 -27
- package/templates/.windsurf/rules/caws-quality-standards.md +54 -0
- package/templates/AGENTS.md +104 -0
- package/templates/CLAUDE.md +101 -0
- package/templates/docs/README.md +5 -5
- package/templates/.github/copilot/instructions.md +0 -311
- package/templates/agents.md +0 -1047
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Claude Code Hooks Scaffolding
|
|
3
|
+
* Functions for setting up Claude Code hooks for CAWS projects
|
|
4
|
+
* @author @darianrosebrook
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
|
|
11
|
+
// Import detection utilities
|
|
12
|
+
const { detectCAWSSetup, findPackageRoot } = require('../utils/detection');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Scaffold Claude Code hooks for a CAWS project
|
|
16
|
+
* Creates .claude/settings.json with hooks and .claude/hooks/ directory with scripts
|
|
17
|
+
*
|
|
18
|
+
* @param {string} projectDir - Project directory path
|
|
19
|
+
* @param {string[]} levels - Hook levels to enable: 'safety', 'quality', 'scope', 'audit'
|
|
20
|
+
*/
|
|
21
|
+
async function scaffoldClaudeHooks(projectDir, levels = ['safety', 'quality', 'scope', 'audit']) {
|
|
22
|
+
try {
|
|
23
|
+
const claudeDir = path.join(projectDir, '.claude');
|
|
24
|
+
const claudeHooksDir = path.join(claudeDir, 'hooks');
|
|
25
|
+
|
|
26
|
+
// Create .claude directory structure
|
|
27
|
+
await fs.ensureDir(claudeDir);
|
|
28
|
+
await fs.ensureDir(claudeHooksDir);
|
|
29
|
+
|
|
30
|
+
// Determine template directory - prefer bundled templates
|
|
31
|
+
const setup = detectCAWSSetup(projectDir);
|
|
32
|
+
|
|
33
|
+
// Find package root using shared utility
|
|
34
|
+
const packageRoot = findPackageRoot(__dirname);
|
|
35
|
+
|
|
36
|
+
// Try templates relative to package root first (works in both dev and global install)
|
|
37
|
+
const bundledTemplateDir = path.join(packageRoot, 'templates');
|
|
38
|
+
const fallbackTemplateDir = path.join(__dirname, '../../templates');
|
|
39
|
+
const templateDir = fs.existsSync(bundledTemplateDir)
|
|
40
|
+
? bundledTemplateDir
|
|
41
|
+
: fs.existsSync(fallbackTemplateDir)
|
|
42
|
+
? fallbackTemplateDir
|
|
43
|
+
: setup.templateDir || path.resolve(__dirname, '../templates');
|
|
44
|
+
|
|
45
|
+
const claudeTemplateDir = path.join(templateDir, '.claude');
|
|
46
|
+
const claudeHooksTemplateDir = path.join(claudeTemplateDir, 'hooks');
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(claudeTemplateDir)) {
|
|
49
|
+
console.warn(chalk.yellow('⚠️ Claude Code hooks templates not found'));
|
|
50
|
+
console.warn(chalk.blue('💡 Skipping Claude Code hooks setup'));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Map levels to hook scripts
|
|
55
|
+
const hookMapping = {
|
|
56
|
+
safety: ['block-dangerous.sh', 'scan-secrets.sh'],
|
|
57
|
+
quality: ['quality-check.sh', 'validate-spec.sh'],
|
|
58
|
+
scope: ['scope-guard.sh', 'naming-check.sh'],
|
|
59
|
+
audit: ['audit.sh'],
|
|
60
|
+
lite: ['block-dangerous.sh', 'scope-guard.sh', 'lite-sprawl-check.sh', 'simplification-guard.sh'],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Determine which hooks to enable
|
|
64
|
+
const enabledHooks = new Set();
|
|
65
|
+
levels.forEach((level) => {
|
|
66
|
+
const hooks = hookMapping[level] || [];
|
|
67
|
+
hooks.forEach((hook) => enabledHooks.add(hook));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Always enable audit.sh if any hooks are enabled
|
|
71
|
+
if (enabledHooks.size > 0) {
|
|
72
|
+
enabledHooks.add('audit.sh');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Copy enabled hook scripts
|
|
76
|
+
const allHookScripts = [
|
|
77
|
+
'audit.sh',
|
|
78
|
+
'validate-spec.sh',
|
|
79
|
+
'quality-check.sh',
|
|
80
|
+
'scan-secrets.sh',
|
|
81
|
+
'block-dangerous.sh',
|
|
82
|
+
'scope-guard.sh',
|
|
83
|
+
'naming-check.sh',
|
|
84
|
+
'lite-sprawl-check.sh',
|
|
85
|
+
'simplification-guard.sh',
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
for (const script of allHookScripts) {
|
|
89
|
+
if (enabledHooks.has(script)) {
|
|
90
|
+
const sourcePath = path.join(claudeHooksTemplateDir, script);
|
|
91
|
+
const destPath = path.join(claudeHooksDir, script);
|
|
92
|
+
|
|
93
|
+
if (fs.existsSync(sourcePath)) {
|
|
94
|
+
await fs.copy(sourcePath, destPath);
|
|
95
|
+
// Make executable
|
|
96
|
+
await fs.chmod(destPath, 0o755);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Generate settings.json with hooks configuration
|
|
102
|
+
const settings = generateClaudeSettings(levels, enabledHooks);
|
|
103
|
+
|
|
104
|
+
// Check for existing settings and merge
|
|
105
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
106
|
+
if (fs.existsSync(settingsPath)) {
|
|
107
|
+
try {
|
|
108
|
+
const existingSettings = await fs.readJSON(settingsPath);
|
|
109
|
+
// Merge hooks, preserving existing non-hook settings
|
|
110
|
+
settings.hooks = {
|
|
111
|
+
...existingSettings.hooks,
|
|
112
|
+
...settings.hooks,
|
|
113
|
+
};
|
|
114
|
+
// Preserve other settings
|
|
115
|
+
Object.keys(existingSettings).forEach((key) => {
|
|
116
|
+
if (key !== 'hooks' && !(key in settings)) {
|
|
117
|
+
settings[key] = existingSettings[key];
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.warn(chalk.yellow('⚠️ Could not merge existing settings:'), error.message);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Write settings.json
|
|
126
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
127
|
+
|
|
128
|
+
// Copy README if it exists
|
|
129
|
+
const readmePath = path.join(claudeTemplateDir, 'README.md');
|
|
130
|
+
if (fs.existsSync(readmePath)) {
|
|
131
|
+
await fs.copy(readmePath, path.join(claudeDir, 'README.md'));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log(chalk.green('✅ Claude Code hooks configured'));
|
|
135
|
+
console.log(chalk.gray(` Enabled: ${levels.join(', ')}`));
|
|
136
|
+
console.log(
|
|
137
|
+
chalk.gray(` Scripts: ${Array.from(enabledHooks).length} hook scripts installed`)
|
|
138
|
+
);
|
|
139
|
+
console.log(chalk.blue('💡 Hooks will activate on next Claude Code session'));
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(chalk.yellow('⚠️ Failed to setup Claude Code hooks:'), error.message);
|
|
142
|
+
console.log(chalk.blue('💡 You can manually copy .claude/ directory later'));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Generate Claude Code settings with hooks configuration
|
|
148
|
+
* @param {string[]} levels - Enabled hook levels
|
|
149
|
+
* @param {Set<string>} enabledHooks - Set of enabled hook script names
|
|
150
|
+
* @returns {Object} Settings object for settings.json
|
|
151
|
+
*/
|
|
152
|
+
function generateClaudeSettings(levels, _enabledHooks) {
|
|
153
|
+
const settings = {
|
|
154
|
+
hooks: {},
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Build hooks configuration based on enabled levels
|
|
158
|
+
// Claude Code uses different event names and matcher patterns
|
|
159
|
+
|
|
160
|
+
if (levels.includes('safety')) {
|
|
161
|
+
// Block dangerous bash commands
|
|
162
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse || [];
|
|
163
|
+
settings.hooks.PreToolUse.push({
|
|
164
|
+
matcher: 'Bash',
|
|
165
|
+
hooks: [
|
|
166
|
+
{
|
|
167
|
+
type: 'command',
|
|
168
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/block-dangerous.sh',
|
|
169
|
+
timeout: 10,
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Scan for secrets on file read
|
|
175
|
+
settings.hooks.PreToolUse.push({
|
|
176
|
+
matcher: 'Read',
|
|
177
|
+
hooks: [
|
|
178
|
+
{
|
|
179
|
+
type: 'command',
|
|
180
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/scan-secrets.sh',
|
|
181
|
+
timeout: 10,
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (levels.includes('quality')) {
|
|
188
|
+
// Run quality checks after file edits
|
|
189
|
+
settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
|
|
190
|
+
settings.hooks.PostToolUse.push({
|
|
191
|
+
matcher: 'Write|Edit',
|
|
192
|
+
hooks: [
|
|
193
|
+
{
|
|
194
|
+
type: 'command',
|
|
195
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/quality-check.sh',
|
|
196
|
+
timeout: 30,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
type: 'command',
|
|
200
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/validate-spec.sh',
|
|
201
|
+
timeout: 15,
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (levels.includes('scope')) {
|
|
208
|
+
// Scope guard before file writes
|
|
209
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse || [];
|
|
210
|
+
settings.hooks.PreToolUse.push({
|
|
211
|
+
matcher: 'Write|Edit',
|
|
212
|
+
hooks: [
|
|
213
|
+
{
|
|
214
|
+
type: 'command',
|
|
215
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/scope-guard.sh',
|
|
216
|
+
timeout: 10,
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Naming check after edits
|
|
222
|
+
settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
|
|
223
|
+
settings.hooks.PostToolUse.push({
|
|
224
|
+
matcher: 'Write|Edit',
|
|
225
|
+
hooks: [
|
|
226
|
+
{
|
|
227
|
+
type: 'command',
|
|
228
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/naming-check.sh',
|
|
229
|
+
timeout: 10,
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (levels.includes('lite')) {
|
|
236
|
+
// Lite mode: sprawl check on Write, simplification guard on Edit
|
|
237
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse || [];
|
|
238
|
+
settings.hooks.PreToolUse.push({
|
|
239
|
+
matcher: 'Write',
|
|
240
|
+
hooks: [
|
|
241
|
+
{
|
|
242
|
+
type: 'command',
|
|
243
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/lite-sprawl-check.sh',
|
|
244
|
+
timeout: 10,
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
});
|
|
248
|
+
settings.hooks.PreToolUse.push({
|
|
249
|
+
matcher: 'Edit',
|
|
250
|
+
hooks: [
|
|
251
|
+
{
|
|
252
|
+
type: 'command',
|
|
253
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/simplification-guard.sh',
|
|
254
|
+
timeout: 10,
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (levels.includes('audit')) {
|
|
261
|
+
// Session audit logging
|
|
262
|
+
settings.hooks.SessionStart = settings.hooks.SessionStart || [];
|
|
263
|
+
settings.hooks.SessionStart.push({
|
|
264
|
+
hooks: [
|
|
265
|
+
{
|
|
266
|
+
type: 'command',
|
|
267
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/audit.sh session-start',
|
|
268
|
+
timeout: 5,
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
settings.hooks.Stop = settings.hooks.Stop || [];
|
|
274
|
+
settings.hooks.Stop.push({
|
|
275
|
+
hooks: [
|
|
276
|
+
{
|
|
277
|
+
type: 'command',
|
|
278
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/audit.sh stop',
|
|
279
|
+
timeout: 5,
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Audit tool usage
|
|
285
|
+
settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
|
|
286
|
+
settings.hooks.PostToolUse.push({
|
|
287
|
+
matcher: 'Write|Edit|Bash',
|
|
288
|
+
hooks: [
|
|
289
|
+
{
|
|
290
|
+
type: 'command',
|
|
291
|
+
command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/audit.sh tool-use',
|
|
292
|
+
timeout: 5,
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return settings;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Check if Claude Code hooks are already configured
|
|
303
|
+
* @param {string} projectDir - Project directory
|
|
304
|
+
* @returns {boolean} True if hooks are configured
|
|
305
|
+
*/
|
|
306
|
+
function hasClaudeHooks(projectDir) {
|
|
307
|
+
const settingsPath = path.join(projectDir, '.claude', 'settings.json');
|
|
308
|
+
if (!fs.existsSync(settingsPath)) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
const settings = fs.readJSONSync(settingsPath);
|
|
314
|
+
return settings.hooks && Object.keys(settings.hooks).length > 0;
|
|
315
|
+
} catch {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* List configured Claude Code hooks
|
|
322
|
+
* @param {string} projectDir - Project directory
|
|
323
|
+
* @returns {Object} Hook configuration or null
|
|
324
|
+
*/
|
|
325
|
+
function getClaudeHooksConfig(projectDir) {
|
|
326
|
+
const settingsPath = path.join(projectDir, '.claude', 'settings.json');
|
|
327
|
+
if (!fs.existsSync(settingsPath)) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
try {
|
|
332
|
+
const settings = fs.readJSONSync(settingsPath);
|
|
333
|
+
return settings.hooks || null;
|
|
334
|
+
} catch {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
module.exports = {
|
|
340
|
+
scaffoldClaudeHooks,
|
|
341
|
+
generateClaudeSettings,
|
|
342
|
+
hasClaudeHooks,
|
|
343
|
+
getClaudeHooksConfig,
|
|
344
|
+
};
|
package/dist/scaffold/index.d.ts
CHANGED
|
@@ -7,9 +7,11 @@ export function scaffoldIDEIntegrations(targetDir: any, options: any): Promise<{
|
|
|
7
7
|
added: number;
|
|
8
8
|
skipped: number;
|
|
9
9
|
}>;
|
|
10
|
+
import { scaffoldClaudeHooks } from "./claude-hooks";
|
|
10
11
|
/**
|
|
11
12
|
* Set dependencies for scaffold module
|
|
12
13
|
* @param {Object} deps - Dependencies object
|
|
13
14
|
*/
|
|
14
15
|
export function setScaffoldDependencies(deps: any): void;
|
|
16
|
+
export { scaffoldClaudeHooks };
|
|
15
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scaffold/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scaffold/index.js"],"names":[],"mappings":"AA8MA;;;GAGG;AACH,6DA6jBC;AAztBD;;;GAyIC;;AAMD;;;GAGG;AACH,yDAGC"}
|
package/dist/scaffold/index.js
CHANGED
|
@@ -16,6 +16,12 @@ const { detectsPublishing } = require('../utils/project-analysis');
|
|
|
16
16
|
const { scaffoldGitHooks } = require('./git-hooks');
|
|
17
17
|
const { updateGitignore } = require('../utils/gitignore-updater');
|
|
18
18
|
|
|
19
|
+
// Import Claude Code hooks scaffolding
|
|
20
|
+
const { scaffoldClaudeHooks } = require('./claude-hooks');
|
|
21
|
+
|
|
22
|
+
// Import IDE detection utilities
|
|
23
|
+
const { IDE_REGISTRY, parseIDESelection, getRecommendedIDEs } = require('../utils/ide-detection');
|
|
24
|
+
|
|
19
25
|
// CLI version from package.json
|
|
20
26
|
const CLI_VERSION = require('../../package.json').version;
|
|
21
27
|
|
|
@@ -52,12 +58,20 @@ function findTemplateDir() {
|
|
|
52
58
|
async function scaffoldIDEIntegrations(targetDir, options) {
|
|
53
59
|
const templateDir = findTemplateDir() || path.join(__dirname, '../../templates');
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
// Determine which IDEs to install
|
|
62
|
+
const selectedIDEs = options.ides || [];
|
|
63
|
+
if (selectedIDEs.length === 0) {
|
|
64
|
+
console.log(chalk.gray('Skipping IDE setup (none selected)'));
|
|
65
|
+
return { added: 0, skipped: 0 };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const ideNames = selectedIDEs.map((id) => IDE_REGISTRY[id]?.name || id).join(', ');
|
|
69
|
+
console.log(chalk.cyan(`Setting up IDE integrations: ${ideNames}`));
|
|
56
70
|
|
|
57
71
|
let addedCount = 0;
|
|
58
72
|
let skippedCount = 0;
|
|
59
73
|
|
|
60
|
-
// Setup git hooks with provenance integration
|
|
74
|
+
// Setup git hooks with provenance integration (always -- not IDE-specific)
|
|
61
75
|
try {
|
|
62
76
|
const gitHooksResult = await scaffoldGitHooks(targetDir, {
|
|
63
77
|
provenance: true,
|
|
@@ -69,106 +83,107 @@ async function scaffoldIDEIntegrations(targetDir, options) {
|
|
|
69
83
|
addedCount += gitHooksResult.added;
|
|
70
84
|
skippedCount += gitHooksResult.skipped;
|
|
71
85
|
} catch (error) {
|
|
72
|
-
console.log(chalk.yellow(
|
|
86
|
+
console.log(chalk.yellow(`Warning: Git hooks setup failed: ${error.message}`));
|
|
73
87
|
}
|
|
74
88
|
|
|
75
|
-
//
|
|
76
|
-
const ideTemplates = [
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
dest: '.vscode/settings.json',
|
|
81
|
-
desc: 'VS Code
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
src: '.
|
|
120
|
-
dest: '.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
89
|
+
// Build IDE templates list dynamically based on selection
|
|
90
|
+
const ideTemplates = [];
|
|
91
|
+
|
|
92
|
+
if (selectedIDEs.includes('vscode')) {
|
|
93
|
+
ideTemplates.push(
|
|
94
|
+
{ src: '.vscode/settings.json', dest: '.vscode/settings.json', desc: 'VS Code workspace settings' },
|
|
95
|
+
{ src: '.vscode/launch.json', dest: '.vscode/launch.json', desc: 'VS Code debug configurations' }
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (selectedIDEs.includes('intellij')) {
|
|
100
|
+
ideTemplates.push(
|
|
101
|
+
{ src: '.idea/runConfigurations/CAWS_Validate.xml', dest: '.idea/runConfigurations/CAWS_Validate.xml', desc: 'IntelliJ run configuration for CAWS validate' },
|
|
102
|
+
{ src: '.idea/runConfigurations/CAWS_Evaluate.xml', dest: '.idea/runConfigurations/CAWS_Evaluate.xml', desc: 'IntelliJ run configuration for CAWS evaluate' }
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (selectedIDEs.includes('junie')) {
|
|
107
|
+
ideTemplates.push(
|
|
108
|
+
{ src: '.junie/guidelines.md', dest: '.junie/guidelines.md', desc: 'JetBrains Junie AI agent guidelines' }
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (selectedIDEs.includes('windsurf')) {
|
|
113
|
+
ideTemplates.push(
|
|
114
|
+
{ src: '.windsurf/workflows/caws-guided-development.md', dest: '.windsurf/workflows/caws-guided-development.md', desc: 'Windsurf workflow for CAWS-guided development' },
|
|
115
|
+
{ src: '.windsurf/rules/caws-quality-standards.md', dest: '.windsurf/rules/caws-quality-standards.md', desc: 'Windsurf CAWS quality rules' }
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (selectedIDEs.includes('copilot')) {
|
|
120
|
+
ideTemplates.push(
|
|
121
|
+
{ src: '.github/copilot-instructions.md', dest: '.github/copilot-instructions.md', desc: 'GitHub Copilot CAWS integration instructions' }
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (selectedIDEs.includes('cursor')) {
|
|
126
|
+
ideTemplates.push(
|
|
127
|
+
{ src: '.cursor/README.md', dest: '.cursor/README.md', desc: 'Cursor integration documentation' }
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (selectedIDEs.includes('claude')) {
|
|
132
|
+
ideTemplates.push(
|
|
133
|
+
{ src: '.claude/README.md', dest: '.claude/README.md', desc: 'Claude Code integration documentation' },
|
|
134
|
+
{ src: 'CLAUDE.md', dest: 'CLAUDE.md', desc: 'Claude Code project instructions' }
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// Setup Claude Code hooks
|
|
138
|
+
try {
|
|
139
|
+
await scaffoldClaudeHooks(targetDir, ['safety', 'quality', 'scope', 'audit']);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.log(chalk.yellow(`Warning: Claude Code hooks setup failed: ${error.message}`));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
124
144
|
|
|
125
145
|
for (const template of ideTemplates) {
|
|
126
146
|
const srcPath = path.join(templateDir, template.src);
|
|
127
147
|
const destPath = path.join(targetDir, template.dest);
|
|
128
148
|
|
|
129
149
|
try {
|
|
130
|
-
// Check if source exists
|
|
131
150
|
if (!(await fs.pathExists(srcPath))) {
|
|
132
151
|
if (!template.optional) {
|
|
133
|
-
console.log(chalk.yellow(
|
|
152
|
+
console.log(chalk.yellow(`Warning: Template not found: ${template.src}`));
|
|
134
153
|
}
|
|
135
154
|
continue;
|
|
136
155
|
}
|
|
137
156
|
|
|
138
|
-
// Check if destination already exists
|
|
139
157
|
const destExists = await fs.pathExists(destPath);
|
|
140
158
|
|
|
141
159
|
if (destExists && !options.force) {
|
|
142
|
-
console.log(chalk.gray(
|
|
160
|
+
console.log(chalk.gray(`Skipped ${template.desc} (already exists)`));
|
|
143
161
|
skippedCount++;
|
|
144
162
|
continue;
|
|
145
163
|
}
|
|
146
164
|
|
|
147
|
-
// Ensure destination directory exists
|
|
148
165
|
await fs.ensureDir(path.dirname(destPath));
|
|
149
|
-
|
|
150
|
-
// Copy the file
|
|
151
166
|
await fs.copy(srcPath, destPath);
|
|
152
167
|
|
|
153
|
-
|
|
154
|
-
if (destPath.includes('.git/hooks/') || destPath.includes('.cursor/hooks/')) {
|
|
168
|
+
if (destPath.includes('.git/hooks/') || destPath.includes('.cursor/hooks/') || destPath.includes('.claude/hooks/')) {
|
|
155
169
|
try {
|
|
156
170
|
await fs.chmod(destPath, '755');
|
|
157
|
-
} catch (
|
|
171
|
+
} catch (_) {
|
|
158
172
|
// Ignore chmod errors on some systems
|
|
159
173
|
}
|
|
160
174
|
}
|
|
161
175
|
|
|
162
|
-
console.log(chalk.green(
|
|
176
|
+
console.log(chalk.green(`Added ${template.desc}`));
|
|
163
177
|
addedCount++;
|
|
164
178
|
} catch (error) {
|
|
165
|
-
console.log(chalk.red(
|
|
179
|
+
console.log(chalk.red(`Failed to add ${template.desc}: ${error.message}`));
|
|
166
180
|
}
|
|
167
181
|
}
|
|
168
182
|
|
|
169
183
|
if (addedCount > 0) {
|
|
170
|
-
console.log(chalk.green(`\
|
|
171
|
-
console.log(chalk.
|
|
184
|
+
console.log(chalk.green(`\nIDE integrations: ${addedCount} added, ${skippedCount} skipped`));
|
|
185
|
+
console.log(chalk.gray(` Installed: ${ideNames}`));
|
|
186
|
+
console.log(chalk.blue('Restart your IDE to activate the new integrations'));
|
|
172
187
|
}
|
|
173
188
|
|
|
174
189
|
return { added: addedCount, skipped: skippedCount };
|
|
@@ -353,15 +368,19 @@ async function scaffoldProject(options) {
|
|
|
353
368
|
});
|
|
354
369
|
}
|
|
355
370
|
|
|
356
|
-
// Add IDE integrations for
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
371
|
+
// Add IDE integrations for selected IDEs
|
|
372
|
+
const selectedIDEs = options.ide ? parseIDESelection(options.ide) : getRecommendedIDEs();
|
|
373
|
+
if (selectedIDEs.length > 0) {
|
|
374
|
+
const ideNames = selectedIDEs.map((id) => IDE_REGISTRY[id]?.name || id).join(', ');
|
|
375
|
+
enhancements.push({
|
|
376
|
+
name: 'ide-integrations',
|
|
377
|
+
description: `IDE integrations (${ideNames})`,
|
|
378
|
+
required: false,
|
|
379
|
+
customHandler: async (targetDir, opts) => {
|
|
380
|
+
return await scaffoldIDEIntegrations(targetDir, { ...opts, ides: selectedIDEs });
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
}
|
|
365
384
|
|
|
366
385
|
// Add quality gates package and configuration if requested
|
|
367
386
|
// Note: These are optional - git hooks fall back to CAWS CLI if package isn't installed
|
|
@@ -541,7 +560,7 @@ async function scaffoldProject(options) {
|
|
|
541
560
|
!fs.existsSync(path.join(currentDir, 'caws.md'))
|
|
542
561
|
) {
|
|
543
562
|
enhancements.push({
|
|
544
|
-
name: '
|
|
563
|
+
name: 'AGENTS.md',
|
|
545
564
|
description: 'CAWS agent workflow guide',
|
|
546
565
|
required: false,
|
|
547
566
|
});
|
|
@@ -769,5 +788,6 @@ async function scaffoldProject(options) {
|
|
|
769
788
|
module.exports = {
|
|
770
789
|
scaffoldProject,
|
|
771
790
|
scaffoldIDEIntegrations,
|
|
791
|
+
scaffoldClaudeHooks,
|
|
772
792
|
setScaffoldDependencies,
|
|
773
793
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "CAWS Lite Scope Configuration",
|
|
4
|
+
"description": "Scope configuration for CAWS lite mode — guardrails without YAML specs",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["version", "allowedDirectories"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"version": {
|
|
9
|
+
"type": "integer",
|
|
10
|
+
"const": 1,
|
|
11
|
+
"description": "Schema version"
|
|
12
|
+
},
|
|
13
|
+
"allowedDirectories": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": { "type": "string" },
|
|
16
|
+
"minItems": 1,
|
|
17
|
+
"description": "Directories the agent is allowed to modify (e.g., src/, tests/)"
|
|
18
|
+
},
|
|
19
|
+
"bannedPatterns": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"files": {
|
|
23
|
+
"type": "array",
|
|
24
|
+
"items": { "type": "string" },
|
|
25
|
+
"description": "Glob patterns for banned file names (e.g., *-enhanced.*, *-final.*)"
|
|
26
|
+
},
|
|
27
|
+
"directories": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": { "type": "string" },
|
|
30
|
+
"description": "Glob patterns for banned directory names (e.g., *venv*, .venv)"
|
|
31
|
+
},
|
|
32
|
+
"docs": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"items": { "type": "string" },
|
|
35
|
+
"description": "Glob patterns for banned doc file names (e.g., *-summary.md)"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"additionalProperties": false
|
|
39
|
+
},
|
|
40
|
+
"maxNewFilesPerCommit": {
|
|
41
|
+
"type": "integer",
|
|
42
|
+
"minimum": 1,
|
|
43
|
+
"maximum": 100,
|
|
44
|
+
"description": "Maximum number of new files allowed per commit (prevents file sprawl)"
|
|
45
|
+
},
|
|
46
|
+
"designatedVenvPath": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "The only allowed virtual environment path (e.g., .venv)"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"additionalProperties": false
|
|
52
|
+
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"contracts"
|
|
17
17
|
],
|
|
18
18
|
"properties": {
|
|
19
|
-
"id": { "type": "string", "pattern": "^
|
|
19
|
+
"id": { "type": "string", "pattern": "^[A-Z]{2,6}-\\d{3,4}$" },
|
|
20
20
|
"title": { "type": "string", "minLength": 10, "maxLength": 200 },
|
|
21
21
|
"risk_tier": { "type": ["integer", "string"], "enum": [1, 2, 3, "1", "2", "3"] },
|
|
22
22
|
"mode": { "type": "string", "enum": ["feature", "refactor", "fix", "doc", "chore"] },
|