clauderc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -0
- package/bin/cli.js +802 -0
- package/package.json +44 -0
- package/src/detector.js +387 -0
- package/src/project.js +435 -0
- package/src/stacks.js +299 -0
- package/templates/agents/project-setup-wizard.md +230 -0
- package/templates/commands/lint.md +33 -0
- package/templates/commands/pr.md +46 -0
- package/templates/commands/setup.md +51 -0
- package/templates/commands/test.md +33 -0
- package/templates/commands/verify.md +69 -0
- package/templates/manifest.json +93 -0
- package/templates/project-setup/AGENTS_TEMPLATE.md +100 -0
- package/templates/project-setup/CLAUDE_MD_TEMPLATE.md +49 -0
- package/templates/project-setup/COMMANDS_TEMPLATE.md +92 -0
- package/templates/project-setup/SKILLS_TEMPLATE.md +113 -0
- package/templates/project-setup/TEAM_DOCS_TEMPLATE.md +102 -0
- package/templates/skills/claude-code-templates/SKILL.md +260 -0
- package/templates/skills/project-analysis/SKILL.md +184 -0
package/src/project.js
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project setup wizard
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
6
|
+
import { join, basename } from 'path';
|
|
7
|
+
import { createInterface } from 'readline';
|
|
8
|
+
import { detectStack, generateCommands } from './detector.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Interactive prompt helper
|
|
12
|
+
*/
|
|
13
|
+
function createPrompt() {
|
|
14
|
+
const rl = createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
ask: (question) => new Promise((resolve) => {
|
|
21
|
+
rl.question(question, (answer) => resolve(answer.trim()));
|
|
22
|
+
}),
|
|
23
|
+
select: async (question, options) => {
|
|
24
|
+
console.log(`\n${question}\n`);
|
|
25
|
+
options.forEach((opt, i) => {
|
|
26
|
+
console.log(` ${i + 1}) ${opt.label}${opt.description ? ` - ${opt.description}` : ''}`);
|
|
27
|
+
});
|
|
28
|
+
const answer = await new Promise((resolve) => {
|
|
29
|
+
rl.question('\n Enter number: ', resolve);
|
|
30
|
+
});
|
|
31
|
+
const index = parseInt(answer) - 1;
|
|
32
|
+
return options[index] || options[0];
|
|
33
|
+
},
|
|
34
|
+
confirm: async (question) => {
|
|
35
|
+
const answer = await new Promise((resolve) => {
|
|
36
|
+
rl.question(`${question} (Y/n): `, resolve);
|
|
37
|
+
});
|
|
38
|
+
return answer.toLowerCase() !== 'n';
|
|
39
|
+
},
|
|
40
|
+
close: () => rl.close(),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate CLAUDE.md content
|
|
46
|
+
*/
|
|
47
|
+
function generateClaudeMd(config) {
|
|
48
|
+
const { projectName, stack, commands, customRules } = config;
|
|
49
|
+
|
|
50
|
+
let content = `# ${projectName}
|
|
51
|
+
|
|
52
|
+
## Stack
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
if (stack.stacks.length > 0) {
|
|
56
|
+
content += `- **Language**: ${stack.stacks.map(s => s.name).join(', ')}\n`;
|
|
57
|
+
}
|
|
58
|
+
if (stack.framework) {
|
|
59
|
+
content += `- **Framework**: ${stack.framework.name}\n`;
|
|
60
|
+
}
|
|
61
|
+
if (stack.packageManager) {
|
|
62
|
+
content += `- **Package Manager**: ${stack.packageManager.name}\n`;
|
|
63
|
+
}
|
|
64
|
+
if (stack.monorepo) {
|
|
65
|
+
content += `- **Monorepo**: ${stack.monorepo.name}\n`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
content += `
|
|
69
|
+
## Commands
|
|
70
|
+
|
|
71
|
+
\`\`\`bash
|
|
72
|
+
# Setup
|
|
73
|
+
${commands.setup || '# No setup command detected'}
|
|
74
|
+
|
|
75
|
+
# Development
|
|
76
|
+
${commands.dev || '# No dev command detected'}
|
|
77
|
+
|
|
78
|
+
# Test
|
|
79
|
+
${commands.test || '# No test command detected'}
|
|
80
|
+
|
|
81
|
+
# Lint
|
|
82
|
+
${commands.lint || '# No lint command detected'}
|
|
83
|
+
|
|
84
|
+
# Build
|
|
85
|
+
${commands.build || '# No build command detected'}
|
|
86
|
+
|
|
87
|
+
# Full verification
|
|
88
|
+
${commands.verify || '# No verify command detected'}
|
|
89
|
+
\`\`\`
|
|
90
|
+
|
|
91
|
+
## Workflow
|
|
92
|
+
|
|
93
|
+
### Plan Mode
|
|
94
|
+
Use Plan Mode (Shift+Tab 2x) for:
|
|
95
|
+
- Refactoring > 3 files
|
|
96
|
+
- New feature implementation
|
|
97
|
+
- Architecture changes
|
|
98
|
+
- Database migrations
|
|
99
|
+
|
|
100
|
+
### Verification
|
|
101
|
+
- ALWAYS run \`${commands.verify || 'tests'}\` before committing
|
|
102
|
+
- NEVER skip tests without explicit approval
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
if (customRules && customRules.length > 0) {
|
|
106
|
+
content += `
|
|
107
|
+
## Project Rules
|
|
108
|
+
|
|
109
|
+
${customRules.map(r => `- ${r}`).join('\n')}
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
content += `
|
|
114
|
+
## Documentation
|
|
115
|
+
@README.md
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
return content;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Generate settings.json
|
|
123
|
+
*/
|
|
124
|
+
function generateSettings(config) {
|
|
125
|
+
const { stack, commands } = config;
|
|
126
|
+
const pm = stack.packageManager?.name || 'npm';
|
|
127
|
+
|
|
128
|
+
const allowedCommands = [];
|
|
129
|
+
|
|
130
|
+
// Add package manager commands
|
|
131
|
+
switch (pm) {
|
|
132
|
+
case 'bun':
|
|
133
|
+
allowedCommands.push('Bash(bun:*)');
|
|
134
|
+
break;
|
|
135
|
+
case 'pnpm':
|
|
136
|
+
allowedCommands.push('Bash(pnpm:*)');
|
|
137
|
+
break;
|
|
138
|
+
case 'yarn':
|
|
139
|
+
allowedCommands.push('Bash(yarn:*)');
|
|
140
|
+
break;
|
|
141
|
+
case 'npm':
|
|
142
|
+
allowedCommands.push('Bash(npm:*)');
|
|
143
|
+
break;
|
|
144
|
+
case 'poetry':
|
|
145
|
+
allowedCommands.push('Bash(poetry:*)');
|
|
146
|
+
break;
|
|
147
|
+
case 'cargo':
|
|
148
|
+
allowedCommands.push('Bash(cargo:*)');
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Add common commands
|
|
153
|
+
allowedCommands.push('Bash(git:*)');
|
|
154
|
+
allowedCommands.push('Bash(gh:*)');
|
|
155
|
+
|
|
156
|
+
// Add stack-specific commands
|
|
157
|
+
const stackId = stack.stacks[0]?.id;
|
|
158
|
+
switch (stackId) {
|
|
159
|
+
case 'go':
|
|
160
|
+
allowedCommands.push('Bash(go:*)');
|
|
161
|
+
allowedCommands.push('Bash(golangci-lint:*)');
|
|
162
|
+
break;
|
|
163
|
+
case 'rust':
|
|
164
|
+
allowedCommands.push('Bash(cargo:*)');
|
|
165
|
+
break;
|
|
166
|
+
case 'python':
|
|
167
|
+
allowedCommands.push('Bash(pytest:*)');
|
|
168
|
+
allowedCommands.push('Bash(ruff:*)');
|
|
169
|
+
allowedCommands.push('Bash(mypy:*)');
|
|
170
|
+
break;
|
|
171
|
+
case 'dotnet':
|
|
172
|
+
allowedCommands.push('Bash(dotnet:*)');
|
|
173
|
+
break;
|
|
174
|
+
case 'elixir':
|
|
175
|
+
allowedCommands.push('Bash(mix:*)');
|
|
176
|
+
break;
|
|
177
|
+
case 'ruby':
|
|
178
|
+
allowedCommands.push('Bash(bundle:*)');
|
|
179
|
+
allowedCommands.push('Bash(rails:*)');
|
|
180
|
+
break;
|
|
181
|
+
case 'php':
|
|
182
|
+
allowedCommands.push('Bash(composer:*)');
|
|
183
|
+
allowedCommands.push('Bash(php:*)');
|
|
184
|
+
break;
|
|
185
|
+
case 'java':
|
|
186
|
+
allowedCommands.push('Bash(mvn:*)');
|
|
187
|
+
allowedCommands.push('Bash(gradle:*)');
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const settings = {
|
|
192
|
+
permissions: {
|
|
193
|
+
allow: allowedCommands,
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Add hooks for formatting
|
|
198
|
+
if (commands.format || commands.lint) {
|
|
199
|
+
const hookCommand = commands.format || `${commands.lint} --fix`;
|
|
200
|
+
settings.hooks = {
|
|
201
|
+
PostToolUse: [
|
|
202
|
+
{
|
|
203
|
+
matcher: 'Edit|Write',
|
|
204
|
+
hooks: [
|
|
205
|
+
{
|
|
206
|
+
type: 'command',
|
|
207
|
+
command: `${hookCommand} || true`,
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return settings;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Generate universal command files
|
|
220
|
+
*/
|
|
221
|
+
function generateCommandFile(name, config) {
|
|
222
|
+
const { commands, stack } = config;
|
|
223
|
+
const stackName = stack.stacks[0]?.name || 'Unknown';
|
|
224
|
+
|
|
225
|
+
const templates = {
|
|
226
|
+
test: `# Run ${stackName} tests
|
|
227
|
+
|
|
228
|
+
Runs the project test suite.
|
|
229
|
+
|
|
230
|
+
## Command
|
|
231
|
+
\`\`\`bash
|
|
232
|
+
${commands.test || '# No test command detected - configure in CLAUDE.md'}
|
|
233
|
+
\`\`\`
|
|
234
|
+
|
|
235
|
+
## Usage
|
|
236
|
+
Run this command to execute all project tests before committing changes.
|
|
237
|
+
`,
|
|
238
|
+
|
|
239
|
+
lint: `# Lint ${stackName} code
|
|
240
|
+
|
|
241
|
+
Runs linter and optionally fixes issues.
|
|
242
|
+
|
|
243
|
+
## Command
|
|
244
|
+
\`\`\`bash
|
|
245
|
+
${commands.lint || '# No lint command detected - configure in CLAUDE.md'}
|
|
246
|
+
\`\`\`
|
|
247
|
+
|
|
248
|
+
## Auto-fix
|
|
249
|
+
\`\`\`bash
|
|
250
|
+
${commands.format || commands.lint + ' --fix' || '# No format command detected'}
|
|
251
|
+
\`\`\`
|
|
252
|
+
`,
|
|
253
|
+
|
|
254
|
+
verify: `# Full verification
|
|
255
|
+
|
|
256
|
+
Runs all checks: lint, test, and build.
|
|
257
|
+
|
|
258
|
+
## Command
|
|
259
|
+
\`\`\`bash
|
|
260
|
+
${commands.verify || [commands.lint, commands.test, commands.build].filter(Boolean).join(' && ') || '# Configure verify steps in CLAUDE.md'}
|
|
261
|
+
\`\`\`
|
|
262
|
+
|
|
263
|
+
## When to use
|
|
264
|
+
- Before committing changes
|
|
265
|
+
- Before creating a PR
|
|
266
|
+
- After major refactoring
|
|
267
|
+
`,
|
|
268
|
+
|
|
269
|
+
setup: `# Project setup
|
|
270
|
+
|
|
271
|
+
Install dependencies and prepare the development environment.
|
|
272
|
+
|
|
273
|
+
## Command
|
|
274
|
+
\`\`\`bash
|
|
275
|
+
${commands.setup || '# No setup command detected - configure in CLAUDE.md'}
|
|
276
|
+
\`\`\`
|
|
277
|
+
`,
|
|
278
|
+
|
|
279
|
+
pr: `# Create Pull Request
|
|
280
|
+
|
|
281
|
+
Commit, push, and create a PR.
|
|
282
|
+
|
|
283
|
+
## Steps
|
|
284
|
+
1. Stage all changes
|
|
285
|
+
2. Commit with descriptive message
|
|
286
|
+
3. Push to remote
|
|
287
|
+
4. Create PR with \`gh pr create\`
|
|
288
|
+
|
|
289
|
+
## Prerequisites
|
|
290
|
+
- \`gh\` CLI installed and authenticated
|
|
291
|
+
- All tests passing (\`/verify\`)
|
|
292
|
+
`,
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
return templates[name] || '';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Run project setup wizard
|
|
300
|
+
*/
|
|
301
|
+
export async function runProjectWizard(options = {}) {
|
|
302
|
+
const { dryRun = false, silent = false } = options;
|
|
303
|
+
const projectPath = process.cwd();
|
|
304
|
+
const projectName = basename(projectPath);
|
|
305
|
+
|
|
306
|
+
const log = silent ? () => {} : console.log;
|
|
307
|
+
const prompt = createPrompt();
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
log('\n Analyzing project...\n');
|
|
311
|
+
|
|
312
|
+
// Detect stack
|
|
313
|
+
const stack = detectStack(projectPath);
|
|
314
|
+
const commands = generateCommands(stack);
|
|
315
|
+
|
|
316
|
+
// Show detection results
|
|
317
|
+
log(' Detected configuration:\n');
|
|
318
|
+
|
|
319
|
+
if (stack.stacks.length > 0) {
|
|
320
|
+
log(` Language: ${stack.stacks.map(s => s.name).join(', ')}`);
|
|
321
|
+
} else {
|
|
322
|
+
log(' Language: Not detected');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (stack.framework) {
|
|
326
|
+
log(` Framework: ${stack.framework.name}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (stack.packageManager) {
|
|
330
|
+
log(` Package Manager: ${stack.packageManager.name}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (stack.monorepo) {
|
|
334
|
+
log(` Monorepo: ${stack.monorepo.name}`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (stack.ci) {
|
|
338
|
+
log(` CI/CD: ${stack.ci.name}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
log('\n Generated commands:\n');
|
|
342
|
+
if (commands.setup) log(` Setup: ${commands.setup}`);
|
|
343
|
+
if (commands.dev) log(` Dev: ${commands.dev}`);
|
|
344
|
+
if (commands.test) log(` Test: ${commands.test}`);
|
|
345
|
+
if (commands.lint) log(` Lint: ${commands.lint}`);
|
|
346
|
+
if (commands.build) log(` Build: ${commands.build}`);
|
|
347
|
+
|
|
348
|
+
// Confirm or customize
|
|
349
|
+
log('');
|
|
350
|
+
const confirmed = await prompt.confirm(' Proceed with this configuration?');
|
|
351
|
+
|
|
352
|
+
if (!confirmed) {
|
|
353
|
+
log('\n Setup cancelled.\n');
|
|
354
|
+
prompt.close();
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Ask for custom rules
|
|
359
|
+
log('\n Any project-specific rules? (one per line, empty line to finish)\n');
|
|
360
|
+
const customRules = [];
|
|
361
|
+
let rule = await prompt.ask(' Rule: ');
|
|
362
|
+
while (rule) {
|
|
363
|
+
customRules.push(rule);
|
|
364
|
+
rule = await prompt.ask(' Rule: ');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Generate config
|
|
368
|
+
const config = {
|
|
369
|
+
projectName,
|
|
370
|
+
stack,
|
|
371
|
+
commands,
|
|
372
|
+
customRules,
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// Check existing .claude directory
|
|
376
|
+
const claudeDir = join(projectPath, '.claude');
|
|
377
|
+
const hasExisting = existsSync(claudeDir);
|
|
378
|
+
|
|
379
|
+
if (hasExisting) {
|
|
380
|
+
const overwrite = await prompt.confirm('\n .claude/ already exists. Overwrite?');
|
|
381
|
+
if (!overwrite) {
|
|
382
|
+
log('\n Setup cancelled.\n');
|
|
383
|
+
prompt.close();
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
prompt.close();
|
|
389
|
+
|
|
390
|
+
if (dryRun) {
|
|
391
|
+
log('\n DRY RUN - Would create:\n');
|
|
392
|
+
log(' .claude/');
|
|
393
|
+
log(' ├── commands/test.md');
|
|
394
|
+
log(' ├── commands/lint.md');
|
|
395
|
+
log(' ├── commands/verify.md');
|
|
396
|
+
log(' ├── commands/setup.md');
|
|
397
|
+
log(' ├── commands/pr.md');
|
|
398
|
+
log(' ├── settings.json');
|
|
399
|
+
log(' CLAUDE.md\n');
|
|
400
|
+
return config;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Create directories
|
|
404
|
+
mkdirSync(join(claudeDir, 'commands'), { recursive: true });
|
|
405
|
+
|
|
406
|
+
// Write files
|
|
407
|
+
const files = [
|
|
408
|
+
{ path: join(projectPath, 'CLAUDE.md'), content: generateClaudeMd(config) },
|
|
409
|
+
{ path: join(claudeDir, 'settings.json'), content: JSON.stringify(generateSettings(config), null, 2) },
|
|
410
|
+
{ path: join(claudeDir, 'commands', 'test.md'), content: generateCommandFile('test', config) },
|
|
411
|
+
{ path: join(claudeDir, 'commands', 'lint.md'), content: generateCommandFile('lint', config) },
|
|
412
|
+
{ path: join(claudeDir, 'commands', 'verify.md'), content: generateCommandFile('verify', config) },
|
|
413
|
+
{ path: join(claudeDir, 'commands', 'setup.md'), content: generateCommandFile('setup', config) },
|
|
414
|
+
{ path: join(claudeDir, 'commands', 'pr.md'), content: generateCommandFile('pr', config) },
|
|
415
|
+
];
|
|
416
|
+
|
|
417
|
+
for (const file of files) {
|
|
418
|
+
writeFileSync(file.path, file.content);
|
|
419
|
+
log(` + ${file.path.replace(projectPath, '.')}`);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
log('\n Project setup complete!\n');
|
|
423
|
+
log(' Next steps:');
|
|
424
|
+
log(' 1. Review CLAUDE.md and adjust as needed');
|
|
425
|
+
log(' 2. Commit .claude/ to your repository');
|
|
426
|
+
log(' 3. Run /test, /lint, /verify in Claude Code\n');
|
|
427
|
+
|
|
428
|
+
return config;
|
|
429
|
+
} catch (error) {
|
|
430
|
+
prompt.close();
|
|
431
|
+
throw error;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export default { runProjectWizard, detectStack, generateCommands };
|