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/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clauderc",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Setup Claude Code with best practices - agents, skills, commands, and templates",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"clauderc": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "node bin/cli.js",
|
|
11
|
+
"test": "node test/test-detector.js",
|
|
12
|
+
"test:cli": "node bin/cli.js --help && node bin/cli.js list"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"claude",
|
|
16
|
+
"claude-code",
|
|
17
|
+
"anthropic",
|
|
18
|
+
"ai",
|
|
19
|
+
"developer-tools",
|
|
20
|
+
"cli",
|
|
21
|
+
"setup",
|
|
22
|
+
"productivity"
|
|
23
|
+
],
|
|
24
|
+
"author": "Matheus Kindrazki",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/matheuskindrazki/clauderc.git"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"bin",
|
|
32
|
+
"src",
|
|
33
|
+
"templates",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=16.7.0"
|
|
38
|
+
},
|
|
39
|
+
"os": [
|
|
40
|
+
"darwin",
|
|
41
|
+
"linux",
|
|
42
|
+
"win32"
|
|
43
|
+
]
|
|
44
|
+
}
|
package/src/detector.js
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project stack detector
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { STACKS, MONOREPO_TOOLS, CI_PLATFORMS } from './stacks.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Detect project stack from current directory
|
|
11
|
+
*/
|
|
12
|
+
export function detectStack(projectPath = process.cwd()) {
|
|
13
|
+
const result = {
|
|
14
|
+
stacks: [],
|
|
15
|
+
packageManager: null,
|
|
16
|
+
framework: null,
|
|
17
|
+
monorepo: null,
|
|
18
|
+
ci: null,
|
|
19
|
+
testFramework: null,
|
|
20
|
+
linter: null,
|
|
21
|
+
formatter: null,
|
|
22
|
+
typechecker: null,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Detect primary stacks
|
|
26
|
+
for (const [stackId, stack] of Object.entries(STACKS)) {
|
|
27
|
+
for (const detectFile of stack.detect) {
|
|
28
|
+
if (detectFile.includes('*')) {
|
|
29
|
+
// Glob pattern
|
|
30
|
+
const pattern = detectFile.replace('*', '');
|
|
31
|
+
const files = safeReadDir(projectPath);
|
|
32
|
+
if (files.some(f => f.endsWith(pattern))) {
|
|
33
|
+
result.stacks.push({ id: stackId, ...stack });
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
} else if (existsSync(join(projectPath, detectFile))) {
|
|
37
|
+
result.stacks.push({ id: stackId, ...stack });
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Detect package manager for Node.js
|
|
44
|
+
const nodeStack = result.stacks.find(s => s.id === 'node');
|
|
45
|
+
if (nodeStack) {
|
|
46
|
+
result.packageManager = detectNodePackageManager(projectPath);
|
|
47
|
+
result.framework = detectNodeFramework(projectPath);
|
|
48
|
+
const tools = detectNodeTools(projectPath);
|
|
49
|
+
result.testFramework = tools.test;
|
|
50
|
+
result.linter = tools.linter;
|
|
51
|
+
result.formatter = tools.formatter;
|
|
52
|
+
result.typechecker = tools.typechecker;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Detect package manager for Python
|
|
56
|
+
const pythonStack = result.stacks.find(s => s.id === 'python');
|
|
57
|
+
if (pythonStack) {
|
|
58
|
+
result.packageManager = detectPythonPackageManager(projectPath);
|
|
59
|
+
result.framework = detectPythonFramework(projectPath);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Detect package manager for Java
|
|
63
|
+
const javaStack = result.stacks.find(s => s.id === 'java');
|
|
64
|
+
if (javaStack) {
|
|
65
|
+
result.packageManager = detectJavaPackageManager(projectPath);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Detect framework for Ruby
|
|
69
|
+
const rubyStack = result.stacks.find(s => s.id === 'ruby');
|
|
70
|
+
if (rubyStack) {
|
|
71
|
+
result.framework = detectRubyFramework(projectPath);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Detect framework for PHP
|
|
75
|
+
const phpStack = result.stacks.find(s => s.id === 'php');
|
|
76
|
+
if (phpStack) {
|
|
77
|
+
result.framework = detectPHPFramework(projectPath);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Detect monorepo
|
|
81
|
+
result.monorepo = detectMonorepo(projectPath);
|
|
82
|
+
|
|
83
|
+
// Detect CI
|
|
84
|
+
result.ci = detectCI(projectPath);
|
|
85
|
+
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function safeReadDir(path) {
|
|
90
|
+
try {
|
|
91
|
+
return readdirSync(path);
|
|
92
|
+
} catch {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function safeReadFile(path) {
|
|
98
|
+
try {
|
|
99
|
+
return readFileSync(path, 'utf-8');
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function safeParseJSON(path) {
|
|
106
|
+
const content = safeReadFile(path);
|
|
107
|
+
if (!content) return null;
|
|
108
|
+
try {
|
|
109
|
+
return JSON.parse(content);
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function detectNodePackageManager(projectPath) {
|
|
116
|
+
const managers = STACKS.node.packageManagers;
|
|
117
|
+
for (const [name, config] of Object.entries(managers)) {
|
|
118
|
+
if (existsSync(join(projectPath, config.lockfile))) {
|
|
119
|
+
return { name, ...config };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Default to npm if package.json exists but no lockfile
|
|
123
|
+
if (existsSync(join(projectPath, 'package.json'))) {
|
|
124
|
+
return { name: 'npm', ...managers.npm };
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function detectNodeFramework(projectPath) {
|
|
130
|
+
const pkg = safeParseJSON(join(projectPath, 'package.json'));
|
|
131
|
+
if (!pkg) return null;
|
|
132
|
+
|
|
133
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
134
|
+
|
|
135
|
+
for (const [name, config] of Object.entries(STACKS.node.frameworks)) {
|
|
136
|
+
if (deps[config.detect]) {
|
|
137
|
+
return { name, ...config };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function detectNodeTools(projectPath) {
|
|
144
|
+
const pkg = safeParseJSON(join(projectPath, 'package.json'));
|
|
145
|
+
if (!pkg) return {};
|
|
146
|
+
|
|
147
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
148
|
+
const result = {};
|
|
149
|
+
|
|
150
|
+
// Test framework
|
|
151
|
+
if (deps.vitest) result.test = 'vitest';
|
|
152
|
+
else if (deps.jest) result.test = 'jest';
|
|
153
|
+
else if (deps.mocha) result.test = 'mocha';
|
|
154
|
+
else if (deps.ava) result.test = 'ava';
|
|
155
|
+
else if (deps.playwright) result.test = 'playwright';
|
|
156
|
+
else if (deps.cypress) result.test = 'cypress';
|
|
157
|
+
|
|
158
|
+
// Linter
|
|
159
|
+
if (deps['@biomejs/biome'] || deps.biome) result.linter = 'biome';
|
|
160
|
+
else if (deps.eslint) result.linter = 'eslint';
|
|
161
|
+
else if (deps.oxlint) result.linter = 'oxlint';
|
|
162
|
+
|
|
163
|
+
// Formatter
|
|
164
|
+
if (deps['@biomejs/biome'] || deps.biome) result.formatter = 'biome';
|
|
165
|
+
else if (deps.prettier) result.formatter = 'prettier';
|
|
166
|
+
else if (deps.dprint) result.formatter = 'dprint';
|
|
167
|
+
|
|
168
|
+
// Typechecker
|
|
169
|
+
if (deps.typescript) result.typechecker = 'tsc';
|
|
170
|
+
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function detectPythonPackageManager(projectPath) {
|
|
175
|
+
const managers = STACKS.python.packageManagers;
|
|
176
|
+
for (const [name, config] of Object.entries(managers)) {
|
|
177
|
+
if (existsSync(join(projectPath, config.lockfile))) {
|
|
178
|
+
return { name, ...config };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Check pyproject.toml for poetry
|
|
182
|
+
const pyproject = safeReadFile(join(projectPath, 'pyproject.toml'));
|
|
183
|
+
if (pyproject?.includes('[tool.poetry]')) {
|
|
184
|
+
return { name: 'poetry', ...managers.poetry };
|
|
185
|
+
}
|
|
186
|
+
if (existsSync(join(projectPath, 'requirements.txt'))) {
|
|
187
|
+
return { name: 'pip', ...managers.pip };
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function detectPythonFramework(projectPath) {
|
|
193
|
+
const pyproject = safeReadFile(join(projectPath, 'pyproject.toml'));
|
|
194
|
+
const requirements = safeReadFile(join(projectPath, 'requirements.txt'));
|
|
195
|
+
const content = (pyproject || '') + (requirements || '');
|
|
196
|
+
|
|
197
|
+
for (const [name, config] of Object.entries(STACKS.python.frameworks)) {
|
|
198
|
+
if (content.includes(config.detect)) {
|
|
199
|
+
return { name, ...config };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function detectJavaPackageManager(projectPath) {
|
|
206
|
+
if (existsSync(join(projectPath, 'pom.xml'))) {
|
|
207
|
+
return { name: 'maven', ...STACKS.java.packageManagers.maven };
|
|
208
|
+
}
|
|
209
|
+
if (existsSync(join(projectPath, 'build.gradle.kts'))) {
|
|
210
|
+
return { name: 'gradle-kts', ...STACKS.java.packageManagers.gradleKts };
|
|
211
|
+
}
|
|
212
|
+
if (existsSync(join(projectPath, 'build.gradle'))) {
|
|
213
|
+
return { name: 'gradle', ...STACKS.java.packageManagers.gradle };
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function detectRubyFramework(projectPath) {
|
|
219
|
+
const gemfile = safeReadFile(join(projectPath, 'Gemfile'));
|
|
220
|
+
if (!gemfile) return null;
|
|
221
|
+
|
|
222
|
+
for (const [name, config] of Object.entries(STACKS.ruby.frameworks)) {
|
|
223
|
+
if (gemfile.includes(config.detect)) {
|
|
224
|
+
return { name, ...config };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function detectPHPFramework(projectPath) {
|
|
231
|
+
const composerJson = safeParseJSON(join(projectPath, 'composer.json'));
|
|
232
|
+
if (!composerJson) return null;
|
|
233
|
+
|
|
234
|
+
const deps = { ...composerJson.require, ...composerJson['require-dev'] };
|
|
235
|
+
|
|
236
|
+
for (const [name, config] of Object.entries(STACKS.php.frameworks)) {
|
|
237
|
+
if (deps[config.detect]) {
|
|
238
|
+
return { name, ...config };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function detectMonorepo(projectPath) {
|
|
245
|
+
for (const [name, config] of Object.entries(MONOREPO_TOOLS)) {
|
|
246
|
+
if (existsSync(join(projectPath, config.detect))) {
|
|
247
|
+
// Special check for yarn workspaces
|
|
248
|
+
if (name === 'yarnWorkspaces') {
|
|
249
|
+
const pkg = safeParseJSON(join(projectPath, 'package.json'));
|
|
250
|
+
if (!pkg?.workspaces) continue;
|
|
251
|
+
}
|
|
252
|
+
return { name, ...config };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function detectCI(projectPath) {
|
|
259
|
+
for (const [name, config] of Object.entries(CI_PLATFORMS)) {
|
|
260
|
+
if (existsSync(join(projectPath, config.detect))) {
|
|
261
|
+
return { name, ...config };
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Generate commands based on detected stack
|
|
269
|
+
*/
|
|
270
|
+
export function generateCommands(detection) {
|
|
271
|
+
const commands = {
|
|
272
|
+
setup: null,
|
|
273
|
+
dev: null,
|
|
274
|
+
test: null,
|
|
275
|
+
lint: null,
|
|
276
|
+
format: null,
|
|
277
|
+
typecheck: null,
|
|
278
|
+
build: null,
|
|
279
|
+
verify: null,
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const stack = detection.stacks[0];
|
|
283
|
+
if (!stack) return commands;
|
|
284
|
+
|
|
285
|
+
const pm = detection.packageManager;
|
|
286
|
+
|
|
287
|
+
switch (stack.id) {
|
|
288
|
+
case 'node': {
|
|
289
|
+
const run = pm?.run || 'npm run';
|
|
290
|
+
commands.setup = pm?.install || 'npm install';
|
|
291
|
+
commands.dev = `${run} dev`;
|
|
292
|
+
commands.test = `${run} test`;
|
|
293
|
+
commands.lint = `${run} lint`;
|
|
294
|
+
commands.format = `${run} format`;
|
|
295
|
+
commands.typecheck = `${run} typecheck`;
|
|
296
|
+
commands.build = `${run} build`;
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
case 'python': {
|
|
300
|
+
const run = pm?.run || '';
|
|
301
|
+
const prefix = run ? `${run} ` : '';
|
|
302
|
+
commands.setup = pm?.install || 'pip install -r requirements.txt';
|
|
303
|
+
commands.dev = detection.framework?.dev || `${prefix}python -m app`;
|
|
304
|
+
commands.test = `${prefix}pytest`;
|
|
305
|
+
commands.lint = `${prefix}ruff check .`;
|
|
306
|
+
commands.format = `${prefix}ruff format .`;
|
|
307
|
+
commands.typecheck = `${prefix}mypy .`;
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
case 'go':
|
|
311
|
+
commands.setup = 'go mod download';
|
|
312
|
+
commands.dev = 'go run .';
|
|
313
|
+
commands.test = 'go test ./...';
|
|
314
|
+
commands.lint = 'golangci-lint run';
|
|
315
|
+
commands.format = 'gofmt -w .';
|
|
316
|
+
commands.build = 'go build ./...';
|
|
317
|
+
break;
|
|
318
|
+
case 'rust':
|
|
319
|
+
commands.setup = 'cargo build';
|
|
320
|
+
commands.dev = 'cargo run';
|
|
321
|
+
commands.test = 'cargo test';
|
|
322
|
+
commands.lint = 'cargo clippy';
|
|
323
|
+
commands.format = 'cargo fmt';
|
|
324
|
+
commands.build = 'cargo build --release';
|
|
325
|
+
break;
|
|
326
|
+
case 'java': {
|
|
327
|
+
const isMaven = pm?.name === 'maven';
|
|
328
|
+
commands.setup = isMaven ? 'mvn install -DskipTests' : 'gradle build -x test';
|
|
329
|
+
commands.dev = isMaven ? 'mvn spring-boot:run' : 'gradle bootRun';
|
|
330
|
+
commands.test = isMaven ? 'mvn test' : 'gradle test';
|
|
331
|
+
commands.lint = isMaven ? 'mvn checkstyle:check' : 'gradle checkstyleMain';
|
|
332
|
+
commands.build = isMaven ? 'mvn package' : 'gradle build';
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case 'php':
|
|
336
|
+
commands.setup = 'composer install';
|
|
337
|
+
commands.test = 'vendor/bin/phpunit';
|
|
338
|
+
commands.lint = 'vendor/bin/phpcs';
|
|
339
|
+
commands.format = 'vendor/bin/php-cs-fixer fix';
|
|
340
|
+
commands.dev = detection.framework?.dev || 'php -S localhost:8000';
|
|
341
|
+
break;
|
|
342
|
+
case 'ruby':
|
|
343
|
+
commands.setup = 'bundle install';
|
|
344
|
+
commands.test = 'bundle exec rspec';
|
|
345
|
+
commands.lint = 'bundle exec rubocop';
|
|
346
|
+
commands.format = 'bundle exec rubocop -a';
|
|
347
|
+
commands.dev = detection.framework?.dev || 'bundle exec ruby app.rb';
|
|
348
|
+
break;
|
|
349
|
+
case 'dotnet':
|
|
350
|
+
commands.setup = 'dotnet restore';
|
|
351
|
+
commands.test = 'dotnet test';
|
|
352
|
+
commands.build = 'dotnet build';
|
|
353
|
+
commands.dev = 'dotnet run';
|
|
354
|
+
commands.format = 'dotnet format';
|
|
355
|
+
break;
|
|
356
|
+
case 'elixir':
|
|
357
|
+
commands.setup = 'mix deps.get';
|
|
358
|
+
commands.test = 'mix test';
|
|
359
|
+
commands.lint = 'mix credo';
|
|
360
|
+
commands.format = 'mix format';
|
|
361
|
+
commands.dev = 'mix phx.server';
|
|
362
|
+
break;
|
|
363
|
+
case 'dart':
|
|
364
|
+
const isFlutter = detection.framework?.name === 'flutter';
|
|
365
|
+
commands.setup = isFlutter ? 'flutter pub get' : 'dart pub get';
|
|
366
|
+
commands.test = isFlutter ? 'flutter test' : 'dart test';
|
|
367
|
+
commands.lint = 'dart analyze';
|
|
368
|
+
commands.format = 'dart format .';
|
|
369
|
+
commands.dev = isFlutter ? 'flutter run' : 'dart run';
|
|
370
|
+
break;
|
|
371
|
+
case 'swift':
|
|
372
|
+
commands.build = 'swift build';
|
|
373
|
+
commands.test = 'swift test';
|
|
374
|
+
commands.dev = 'swift run';
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Generate verify command (lint + test + build)
|
|
379
|
+
const verifyParts = [commands.lint, commands.test, commands.build].filter(Boolean);
|
|
380
|
+
if (verifyParts.length > 0) {
|
|
381
|
+
commands.verify = verifyParts.join(' && ');
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return commands;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export default { detectStack, generateCommands };
|