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/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
+ }
@@ -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 };