claudex-setup 1.1.0 → 1.3.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 +1 -1
- package/src/audit.js +18 -0
- package/src/setup.js +195 -31
- package/src/techniques.js +24 -22
package/package.json
CHANGED
package/src/audit.js
CHANGED
|
@@ -76,6 +76,21 @@ async function audit(options) {
|
|
|
76
76
|
const earnedScore = passed.reduce((sum, r) => sum + (weights[r.impact] || 5), 0);
|
|
77
77
|
const score = maxScore > 0 ? Math.round((earnedScore / maxScore) * 100) : 0;
|
|
78
78
|
|
|
79
|
+
// Detect scaffolded vs organic: if CLAUDE.md contains our version stamp, some checks
|
|
80
|
+
// are passing because WE generated them, not the user
|
|
81
|
+
const claudeMd = ctx.fileContent('CLAUDE.md') || '';
|
|
82
|
+
const isScaffolded = claudeMd.includes('Generated by claudex-setup') ||
|
|
83
|
+
claudeMd.includes('claudex-setup');
|
|
84
|
+
// Scaffolded checks: things our setup creates (CLAUDE.md, hooks, commands, agents, rules, skills)
|
|
85
|
+
const scaffoldedKeys = new Set(['claudeMd', 'mermaidArchitecture', 'verificationLoop',
|
|
86
|
+
'hooks', 'customCommands', 'multipleCommands', 'agents', 'pathRules', 'multipleRules',
|
|
87
|
+
'skills', 'hooksConfigured', 'preToolUseHook', 'postToolUseHook', 'fewShotExamples',
|
|
88
|
+
'constraintBlocks', 'xmlTags']);
|
|
89
|
+
const organicPassed = passed.filter(r => !scaffoldedKeys.has(r.key));
|
|
90
|
+
const scaffoldedPassed = passed.filter(r => scaffoldedKeys.has(r.key));
|
|
91
|
+
const organicEarned = organicPassed.reduce((sum, r) => sum + (weights[r.impact] || 5), 0);
|
|
92
|
+
const organicScore = maxScore > 0 ? Math.round((organicEarned / maxScore) * 100) : 0;
|
|
93
|
+
|
|
79
94
|
// Silent mode: skip all output, just return result
|
|
80
95
|
if (silent) {
|
|
81
96
|
return { score, passed: passed.length, failed: failed.length, stacks, results };
|
|
@@ -100,6 +115,9 @@ async function audit(options) {
|
|
|
100
115
|
|
|
101
116
|
// Score
|
|
102
117
|
console.log(` ${progressBar(score)} ${colorize(`${score}/100`, 'bold')}`);
|
|
118
|
+
if (isScaffolded && scaffoldedPassed.length > 0) {
|
|
119
|
+
console.log(colorize(` Organic: ${organicScore}/100 (without claudex-setup generated files)`, 'dim'));
|
|
120
|
+
}
|
|
103
121
|
console.log('');
|
|
104
122
|
|
|
105
123
|
// Passed
|
package/src/setup.js
CHANGED
|
@@ -96,10 +96,47 @@ function detectDependencies(ctx) {
|
|
|
96
96
|
guidelines.push('- Use Playwright for E2E tests. Keep tests in tests/ or e2e/');
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
// tRPC
|
|
100
|
+
if (allDeps['@trpc/server'] || allDeps['@trpc/client']) {
|
|
101
|
+
guidelines.push('- Use tRPC for type-safe API calls. Define routers in server, use client hooks in components');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Stripe
|
|
105
|
+
if (allDeps['stripe']) {
|
|
106
|
+
guidelines.push('- Use Stripe SDK for payments. Always verify webhooks with stripe.webhooks.constructEvent()');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Resend
|
|
110
|
+
if (allDeps['resend']) {
|
|
111
|
+
guidelines.push('- Use Resend for transactional email. Define templates as React components');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Express security
|
|
115
|
+
if (allDeps['helmet']) {
|
|
116
|
+
guidelines.push('- Helmet is configured — ensure all middleware is applied before routes');
|
|
117
|
+
}
|
|
118
|
+
if (allDeps['jsonwebtoken']) {
|
|
119
|
+
guidelines.push('- Use JWT for authentication. Always verify tokens with the correct secret/algorithm');
|
|
120
|
+
}
|
|
121
|
+
if (allDeps['bcrypt']) {
|
|
122
|
+
guidelines.push('- Use bcrypt for password hashing. Never store plaintext passwords');
|
|
123
|
+
}
|
|
124
|
+
if (allDeps['cors']) {
|
|
125
|
+
guidelines.push('- CORS is configured — restrict origins to known domains in production');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Monorepo
|
|
129
|
+
if (allDeps['turbo'] || allDeps['turborepo']) {
|
|
130
|
+
guidelines.push('- Turborepo monorepo — use `turbo run` for all tasks. Respect package boundaries');
|
|
131
|
+
}
|
|
132
|
+
if (allDeps['nx']) {
|
|
133
|
+
guidelines.push('- Nx monorepo — use `nx affected` for incremental builds and tests');
|
|
134
|
+
}
|
|
135
|
+
|
|
99
136
|
// Python
|
|
100
137
|
const reqTxt = ctx.fileContent('requirements.txt') || '';
|
|
101
138
|
if (reqTxt.includes('sqlalchemy')) {
|
|
102
|
-
guidelines.push('- Use SQLAlchemy for
|
|
139
|
+
guidelines.push('- Use SQLAlchemy for database operations. Define models in models/');
|
|
103
140
|
}
|
|
104
141
|
if (reqTxt.includes('pydantic')) {
|
|
105
142
|
guidelines.push('- Use Pydantic for data validation and serialization');
|
|
@@ -107,6 +144,15 @@ function detectDependencies(ctx) {
|
|
|
107
144
|
if (reqTxt.includes('pytest')) {
|
|
108
145
|
guidelines.push('- Use pytest for testing. Run with `python -m pytest`');
|
|
109
146
|
}
|
|
147
|
+
if (reqTxt.includes('alembic')) {
|
|
148
|
+
guidelines.push('- Use Alembic for database migrations. Run `alembic upgrade head` after model changes');
|
|
149
|
+
}
|
|
150
|
+
if (reqTxt.includes('celery')) {
|
|
151
|
+
guidelines.push('- Use Celery for background tasks. Define tasks in tasks/ or services/');
|
|
152
|
+
}
|
|
153
|
+
if (reqTxt.includes('redis')) {
|
|
154
|
+
guidelines.push('- Redis is available for caching and task queues');
|
|
155
|
+
}
|
|
110
156
|
|
|
111
157
|
return guidelines;
|
|
112
158
|
}
|
|
@@ -342,18 +388,29 @@ function getFrameworkInstructions(stacks) {
|
|
|
342
388
|
|
|
343
389
|
if (stackKeys.includes('rust')) {
|
|
344
390
|
sections.push(`### Rust
|
|
345
|
-
-
|
|
346
|
-
-
|
|
347
|
-
-
|
|
348
|
-
-
|
|
391
|
+
- Use Result<T, E> for error handling, avoid unwrap() in production code
|
|
392
|
+
- Prefer &str over String for function parameters
|
|
393
|
+
- Use clippy: \`cargo clippy -- -D warnings\`
|
|
394
|
+
- Structure: src/lib.rs for library, src/main.rs for binary`);
|
|
349
395
|
}
|
|
350
396
|
|
|
351
397
|
if (stackKeys.includes('go')) {
|
|
352
398
|
sections.push(`### Go
|
|
353
|
-
- Follow standard project layout
|
|
354
|
-
-
|
|
355
|
-
-
|
|
356
|
-
-
|
|
399
|
+
- Follow standard Go project layout (cmd/, internal/, pkg/)
|
|
400
|
+
- Use interfaces for dependency injection and testability
|
|
401
|
+
- Handle all errors explicitly — never ignore err returns
|
|
402
|
+
- Use context.Context for cancellation and timeouts
|
|
403
|
+
- Prefer table-driven tests
|
|
404
|
+
- Run \`go vet\` and \`golangci-lint\` before committing`);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (stackKeys.includes('terraform')) {
|
|
408
|
+
sections.push(`### Terraform
|
|
409
|
+
- Use modules for reusable infrastructure components
|
|
410
|
+
- Always run \`terraform plan\` before \`terraform apply\`
|
|
411
|
+
- Store state remotely (S3 + DynamoDB, or Terraform Cloud)
|
|
412
|
+
- Use variables.tf for all configurable values
|
|
413
|
+
- Tag all resources consistently`);
|
|
357
414
|
}
|
|
358
415
|
|
|
359
416
|
const hasJS = stackKeys.some(k => ['react', 'vue', 'angular', 'nextjs', 'node', 'svelte'].includes(k));
|
|
@@ -569,38 +626,109 @@ exit 0
|
|
|
569
626
|
`,
|
|
570
627
|
}),
|
|
571
628
|
|
|
572
|
-
'commands': () =>
|
|
573
|
-
|
|
629
|
+
'commands': (stacks) => {
|
|
630
|
+
const stackKeys = stacks.map(s => s.key);
|
|
631
|
+
const isNext = stackKeys.includes('nextjs');
|
|
632
|
+
const isDjango = stackKeys.includes('django');
|
|
633
|
+
const isFastApi = stackKeys.includes('fastapi');
|
|
634
|
+
const isPython = stackKeys.includes('python') || isDjango || isFastApi;
|
|
635
|
+
const hasDocker = stackKeys.includes('docker');
|
|
636
|
+
|
|
637
|
+
const cmds = {};
|
|
638
|
+
|
|
639
|
+
// Test command - stack-specific
|
|
640
|
+
if (isNext) {
|
|
641
|
+
cmds['test.md'] = `Run the test suite for this Next.js project.
|
|
642
|
+
|
|
643
|
+
## Steps:
|
|
644
|
+
1. Run \`npm test\` (or \`npx vitest run\`)
|
|
645
|
+
2. If tests fail, check for missing mocks or async issues
|
|
646
|
+
3. For component tests, ensure React Testing Library patterns are used
|
|
647
|
+
4. For API route tests, check request/response handling
|
|
648
|
+
5. Report: total, passed, failed, coverage if available
|
|
649
|
+
`;
|
|
650
|
+
} else if (isPython) {
|
|
651
|
+
cmds['test.md'] = `Run the test suite for this Python project.
|
|
652
|
+
|
|
653
|
+
## Steps:
|
|
654
|
+
1. Run \`python -m pytest -v\` (or the project's test command)
|
|
655
|
+
2. Check for fixture issues, missing test database, or import errors
|
|
656
|
+
3. If using Django: \`python manage.py test\`
|
|
657
|
+
4. Report: total, passed, failed, and any tracebacks
|
|
658
|
+
`;
|
|
659
|
+
} else {
|
|
660
|
+
cmds['test.md'] = `Run the test suite and report results.
|
|
574
661
|
|
|
575
662
|
## Steps:
|
|
576
663
|
1. Run the project's test command
|
|
577
664
|
2. If tests fail, analyze the failures
|
|
578
665
|
3. Report: total, passed, failed, and any error details
|
|
579
|
-
|
|
580
|
-
|
|
666
|
+
`;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Review - always generic (works well as-is)
|
|
670
|
+
cmds['review.md'] = `Review the current changes for quality and correctness.
|
|
581
671
|
|
|
582
672
|
## Steps:
|
|
583
673
|
1. Run \`git diff\` to see all changes
|
|
584
674
|
2. Check for: bugs, security issues, missing tests, code style
|
|
585
675
|
3. Provide actionable feedback
|
|
586
|
-
|
|
587
|
-
|
|
676
|
+
`;
|
|
677
|
+
|
|
678
|
+
// Deploy - stack-specific
|
|
679
|
+
if (isNext) {
|
|
680
|
+
cmds['deploy.md'] = `Pre-deployment checklist for Next.js.
|
|
681
|
+
|
|
682
|
+
## Pre-deploy:
|
|
683
|
+
1. Run \`git status\` — working tree must be clean
|
|
684
|
+
2. Run \`npm run build\` — must succeed with no errors
|
|
685
|
+
3. Run \`npm test\` — all tests pass
|
|
686
|
+
4. Run \`npm run lint\` — no lint errors
|
|
687
|
+
5. Check for \`console.log\` in production code
|
|
688
|
+
6. Verify environment variables are set in deployment platform
|
|
689
|
+
|
|
690
|
+
## Deploy:
|
|
691
|
+
1. If Vercel: \`git push\` triggers auto-deploy
|
|
692
|
+
2. If self-hosted: \`npm run build && npm start\`
|
|
693
|
+
3. Verify: check /api/health or main page loads
|
|
694
|
+
4. Tag: \`git tag -a vX.Y.Z -m "Release vX.Y.Z"\`
|
|
695
|
+
`;
|
|
696
|
+
} else if (hasDocker) {
|
|
697
|
+
cmds['deploy.md'] = `Pre-deployment checklist with Docker.
|
|
588
698
|
|
|
589
|
-
## Pre-deploy
|
|
699
|
+
## Pre-deploy:
|
|
700
|
+
1. Run \`git status\` — working tree must be clean
|
|
701
|
+
2. Run full test suite — all tests pass
|
|
702
|
+
3. Run \`docker build -t app .\` — must succeed
|
|
703
|
+
4. Run \`docker run app\` locally — smoke test
|
|
704
|
+
|
|
705
|
+
## Deploy:
|
|
706
|
+
1. Build: \`docker build -t registry/app:latest .\`
|
|
707
|
+
2. Push: \`docker push registry/app:latest\`
|
|
708
|
+
3. Deploy to target environment
|
|
709
|
+
4. Verify health endpoint responds
|
|
710
|
+
5. Tag: \`git tag -a vX.Y.Z -m "Release vX.Y.Z"\`
|
|
711
|
+
`;
|
|
712
|
+
} else {
|
|
713
|
+
cmds['deploy.md'] = `Pre-deployment checklist.
|
|
714
|
+
|
|
715
|
+
## Pre-deploy:
|
|
590
716
|
1. Run \`git status\` — working tree must be clean
|
|
591
717
|
2. Run full test suite — all tests must pass
|
|
592
|
-
3. Run linter — no errors
|
|
593
|
-
4.
|
|
594
|
-
5.
|
|
595
|
-
|
|
596
|
-
## Deploy
|
|
597
|
-
1. Confirm target environment
|
|
598
|
-
2.
|
|
599
|
-
3.
|
|
600
|
-
4.
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
718
|
+
3. Run linter — no errors
|
|
719
|
+
4. Verify no secrets in staged changes
|
|
720
|
+
5. Review diff since last deploy
|
|
721
|
+
|
|
722
|
+
## Deploy:
|
|
723
|
+
1. Confirm target environment
|
|
724
|
+
2. Run deployment command
|
|
725
|
+
3. Verify deployment (health check)
|
|
726
|
+
4. Tag: \`git tag -a vX.Y.Z -m "Release vX.Y.Z"\`
|
|
727
|
+
`;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Fix - always generic with $ARGUMENTS
|
|
731
|
+
cmds['fix.md'] = `Fix the issue described: $ARGUMENTS
|
|
604
732
|
|
|
605
733
|
## Steps:
|
|
606
734
|
1. Understand the issue — read relevant code and error messages
|
|
@@ -609,8 +737,32 @@ exit 0
|
|
|
609
737
|
4. Write or update tests to cover the fix
|
|
610
738
|
5. Run the full test suite to verify no regressions
|
|
611
739
|
6. Summarize what was wrong and how the fix addresses it
|
|
612
|
-
|
|
613
|
-
|
|
740
|
+
`;
|
|
741
|
+
|
|
742
|
+
// Stack-specific bonus commands
|
|
743
|
+
if (isNext) {
|
|
744
|
+
cmds['check-build.md'] = `Run Next.js build check without deploying.
|
|
745
|
+
|
|
746
|
+
1. Run \`npx next build\`
|
|
747
|
+
2. Check for: TypeScript errors, missing pages, broken imports
|
|
748
|
+
3. Verify no "Dynamic server usage" errors in static pages
|
|
749
|
+
4. Report build output size and any warnings
|
|
750
|
+
`;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
if (isPython && (isDjango || isFastApi)) {
|
|
754
|
+
cmds['migrate.md'] = `Run database migrations safely.
|
|
755
|
+
|
|
756
|
+
1. Check current migration status${isDjango ? ': `python manage.py showmigrations`' : ''}
|
|
757
|
+
2. Create new migration if schema changed${isDjango ? ': `python manage.py makemigrations`' : ''}
|
|
758
|
+
3. Review the generated migration file
|
|
759
|
+
4. Apply: ${isDjango ? '`python manage.py migrate`' : '`alembic upgrade head`'}
|
|
760
|
+
5. Verify: check that the app starts and queries work
|
|
761
|
+
`;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return cmds;
|
|
765
|
+
},
|
|
614
766
|
|
|
615
767
|
'skills': () => ({
|
|
616
768
|
'fix-issue/SKILL.md': `---
|
|
@@ -631,8 +783,10 @@ Fix the GitHub issue: $ARGUMENTS
|
|
|
631
783
|
const rules = {};
|
|
632
784
|
const hasTS = stacks.some(s => s.key === 'typescript');
|
|
633
785
|
const hasPython = stacks.some(s => s.key === 'python');
|
|
786
|
+
const hasFrontend = stacks.some(s => ['react', 'vue', 'angular', 'svelte', 'nextjs'].includes(s.key));
|
|
787
|
+
const hasBackend = stacks.some(s => ['go', 'python', 'django', 'fastapi', 'rust', 'java'].includes(s.key));
|
|
634
788
|
|
|
635
|
-
if (
|
|
789
|
+
if (hasFrontend || (hasTS && !hasBackend)) {
|
|
636
790
|
rules['frontend.md'] = `When editing JavaScript/TypeScript files (*.ts, *.tsx, *.js, *.jsx, *.vue):
|
|
637
791
|
- Use functional components with hooks (React/Vue 3)
|
|
638
792
|
- Add TypeScript interfaces for all props and function params
|
|
@@ -640,6 +794,16 @@ Fix the GitHub issue: $ARGUMENTS
|
|
|
640
794
|
- Use named exports over default exports
|
|
641
795
|
- Handle errors explicitly — no empty catch blocks
|
|
642
796
|
- Keep component files under 200 lines; extract sub-components
|
|
797
|
+
`;
|
|
798
|
+
}
|
|
799
|
+
if (hasBackend) {
|
|
800
|
+
rules['backend.md'] = `When editing backend code:
|
|
801
|
+
- Handle all errors explicitly — never swallow exceptions silently
|
|
802
|
+
- Validate all external input at API boundaries
|
|
803
|
+
- Use dependency injection for testability
|
|
804
|
+
- Keep route handlers thin — delegate to service/business logic layers
|
|
805
|
+
- Log errors with sufficient context for debugging
|
|
806
|
+
- Never hardcode secrets or credentials
|
|
643
807
|
`;
|
|
644
808
|
}
|
|
645
809
|
if (hasPython) {
|
package/src/techniques.js
CHANGED
|
@@ -92,7 +92,7 @@ const TECHNIQUES = {
|
|
|
92
92
|
},
|
|
93
93
|
|
|
94
94
|
testCommand: {
|
|
95
|
-
id:
|
|
95
|
+
id: 93001,
|
|
96
96
|
name: 'CLAUDE.md contains a test command',
|
|
97
97
|
check: (ctx) => {
|
|
98
98
|
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
@@ -106,7 +106,7 @@ const TECHNIQUES = {
|
|
|
106
106
|
},
|
|
107
107
|
|
|
108
108
|
lintCommand: {
|
|
109
|
-
id:
|
|
109
|
+
id: 93002,
|
|
110
110
|
name: 'CLAUDE.md contains a lint command',
|
|
111
111
|
check: (ctx) => {
|
|
112
112
|
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
@@ -120,7 +120,7 @@ const TECHNIQUES = {
|
|
|
120
120
|
},
|
|
121
121
|
|
|
122
122
|
buildCommand: {
|
|
123
|
-
id:
|
|
123
|
+
id: 93003,
|
|
124
124
|
name: 'CLAUDE.md contains a build command',
|
|
125
125
|
check: (ctx) => {
|
|
126
126
|
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
@@ -167,7 +167,7 @@ const TECHNIQUES = {
|
|
|
167
167
|
},
|
|
168
168
|
|
|
169
169
|
gitIgnoreNodeModules: {
|
|
170
|
-
id:
|
|
170
|
+
id: 91701,
|
|
171
171
|
name: '.gitignore blocks node_modules',
|
|
172
172
|
check: (ctx) => {
|
|
173
173
|
const gitignore = ctx.fileContent('.gitignore') || '';
|
|
@@ -210,7 +210,7 @@ const TECHNIQUES = {
|
|
|
210
210
|
},
|
|
211
211
|
|
|
212
212
|
multipleCommands: {
|
|
213
|
-
id:
|
|
213
|
+
id: 20001,
|
|
214
214
|
name: '3+ slash commands for rich workflow',
|
|
215
215
|
check: (ctx) => ctx.hasDir('.claude/commands') && ctx.dirFiles('.claude/commands').length >= 3,
|
|
216
216
|
impact: 'medium',
|
|
@@ -221,7 +221,7 @@ const TECHNIQUES = {
|
|
|
221
221
|
},
|
|
222
222
|
|
|
223
223
|
deployCommand: {
|
|
224
|
-
id:
|
|
224
|
+
id: 20002,
|
|
225
225
|
name: 'Has /deploy or /release command',
|
|
226
226
|
check: (ctx) => {
|
|
227
227
|
if (!ctx.hasDir('.claude/commands')) return false;
|
|
@@ -236,7 +236,7 @@ const TECHNIQUES = {
|
|
|
236
236
|
},
|
|
237
237
|
|
|
238
238
|
reviewCommand: {
|
|
239
|
-
id:
|
|
239
|
+
id: 20003,
|
|
240
240
|
name: 'Has /review command',
|
|
241
241
|
check: (ctx) => {
|
|
242
242
|
if (!ctx.hasDir('.claude/commands')) return false;
|
|
@@ -262,7 +262,7 @@ const TECHNIQUES = {
|
|
|
262
262
|
},
|
|
263
263
|
|
|
264
264
|
multipleSkills: {
|
|
265
|
-
id:
|
|
265
|
+
id: 2101,
|
|
266
266
|
name: '2+ skills for specialization',
|
|
267
267
|
check: (ctx) => ctx.hasDir('.claude/skills') && ctx.dirFiles('.claude/skills').length >= 2,
|
|
268
268
|
impact: 'medium',
|
|
@@ -284,7 +284,7 @@ const TECHNIQUES = {
|
|
|
284
284
|
},
|
|
285
285
|
|
|
286
286
|
multipleAgents: {
|
|
287
|
-
id:
|
|
287
|
+
id: 2201,
|
|
288
288
|
name: '2+ agents for delegation',
|
|
289
289
|
check: (ctx) => ctx.hasDir('.claude/agents') && ctx.dirFiles('.claude/agents').length >= 2,
|
|
290
290
|
impact: 'medium',
|
|
@@ -295,7 +295,7 @@ const TECHNIQUES = {
|
|
|
295
295
|
},
|
|
296
296
|
|
|
297
297
|
multipleRules: {
|
|
298
|
-
id:
|
|
298
|
+
id: 301,
|
|
299
299
|
name: '2+ rules files for granular control',
|
|
300
300
|
check: (ctx) => ctx.hasDir('.claude/rules') && ctx.dirFiles('.claude/rules').length >= 2,
|
|
301
301
|
impact: 'medium',
|
|
@@ -324,7 +324,7 @@ const TECHNIQUES = {
|
|
|
324
324
|
},
|
|
325
325
|
|
|
326
326
|
permissionDeny: {
|
|
327
|
-
id:
|
|
327
|
+
id: 2401,
|
|
328
328
|
name: 'Deny rules configured in permissions',
|
|
329
329
|
check: (ctx) => {
|
|
330
330
|
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
@@ -340,7 +340,7 @@ const TECHNIQUES = {
|
|
|
340
340
|
},
|
|
341
341
|
|
|
342
342
|
noBypassPermissions: {
|
|
343
|
-
id:
|
|
343
|
+
id: 2402,
|
|
344
344
|
name: 'Default mode is not bypassPermissions',
|
|
345
345
|
check: (ctx) => {
|
|
346
346
|
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
@@ -400,7 +400,7 @@ const TECHNIQUES = {
|
|
|
400
400
|
},
|
|
401
401
|
|
|
402
402
|
hooksInSettings: {
|
|
403
|
-
id:
|
|
403
|
+
id: 8801,
|
|
404
404
|
name: 'Hooks configured in settings',
|
|
405
405
|
check: (ctx) => {
|
|
406
406
|
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
@@ -415,7 +415,7 @@ const TECHNIQUES = {
|
|
|
415
415
|
},
|
|
416
416
|
|
|
417
417
|
preToolUseHook: {
|
|
418
|
-
id:
|
|
418
|
+
id: 8802,
|
|
419
419
|
name: 'PreToolUse hook configured',
|
|
420
420
|
check: (ctx) => {
|
|
421
421
|
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
@@ -430,7 +430,7 @@ const TECHNIQUES = {
|
|
|
430
430
|
},
|
|
431
431
|
|
|
432
432
|
postToolUseHook: {
|
|
433
|
-
id:
|
|
433
|
+
id: 8803,
|
|
434
434
|
name: 'PostToolUse hook configured',
|
|
435
435
|
check: (ctx) => {
|
|
436
436
|
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
@@ -445,7 +445,7 @@ const TECHNIQUES = {
|
|
|
445
445
|
},
|
|
446
446
|
|
|
447
447
|
sessionStartHook: {
|
|
448
|
-
id:
|
|
448
|
+
id: 8804,
|
|
449
449
|
name: 'SessionStart hook configured',
|
|
450
450
|
check: (ctx) => {
|
|
451
451
|
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
@@ -478,7 +478,7 @@ const TECHNIQUES = {
|
|
|
478
478
|
},
|
|
479
479
|
|
|
480
480
|
tailwindMention: {
|
|
481
|
-
id:
|
|
481
|
+
id: 102501,
|
|
482
482
|
name: 'Tailwind CSS configured',
|
|
483
483
|
check: (ctx) => {
|
|
484
484
|
const pkg = ctx.fileContent('package.json') || '';
|
|
@@ -508,7 +508,7 @@ const TECHNIQUES = {
|
|
|
508
508
|
},
|
|
509
509
|
|
|
510
510
|
dockerCompose: {
|
|
511
|
-
id:
|
|
511
|
+
id: 39901,
|
|
512
512
|
name: 'Has docker-compose.yml',
|
|
513
513
|
check: (ctx) => ctx.files.some(f => /^docker-compose\.(yml|yaml)$/i.test(f)),
|
|
514
514
|
impact: 'medium',
|
|
@@ -589,7 +589,7 @@ const TECHNIQUES = {
|
|
|
589
589
|
},
|
|
590
590
|
|
|
591
591
|
editorconfig: {
|
|
592
|
-
id:
|
|
592
|
+
id: 5001,
|
|
593
593
|
name: 'Has .editorconfig',
|
|
594
594
|
check: (ctx) => ctx.files.includes('.editorconfig'),
|
|
595
595
|
impact: 'low',
|
|
@@ -600,7 +600,7 @@ const TECHNIQUES = {
|
|
|
600
600
|
},
|
|
601
601
|
|
|
602
602
|
nvmrc: {
|
|
603
|
-
id:
|
|
603
|
+
id: 5002,
|
|
604
604
|
name: 'Node version pinned',
|
|
605
605
|
check: (ctx) => {
|
|
606
606
|
if (ctx.files.includes('.nvmrc') || ctx.files.includes('.node-version')) return true;
|
|
@@ -665,7 +665,7 @@ const TECHNIQUES = {
|
|
|
665
665
|
},
|
|
666
666
|
|
|
667
667
|
multipleMcpServers: {
|
|
668
|
-
id:
|
|
668
|
+
id: 1801,
|
|
669
669
|
name: '2+ MCP servers for rich tooling',
|
|
670
670
|
check: (ctx) => {
|
|
671
671
|
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
@@ -746,7 +746,7 @@ const TECHNIQUES = {
|
|
|
746
746
|
},
|
|
747
747
|
|
|
748
748
|
constraintBlocks: {
|
|
749
|
-
id:
|
|
749
|
+
id: 9601,
|
|
750
750
|
name: 'XML constraint blocks in CLAUDE.md',
|
|
751
751
|
check: (ctx) => {
|
|
752
752
|
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
@@ -953,6 +953,8 @@ const STACKS = {
|
|
|
953
953
|
java: { files: ['pom.xml'], content: {}, label: 'Java' },
|
|
954
954
|
kotlin: { files: ['build.gradle.kts'], content: {}, label: 'Kotlin' },
|
|
955
955
|
swift: { files: ['Package.swift'], content: {}, label: 'Swift' },
|
|
956
|
+
terraform: { files: ['main.tf', 'terraform'], content: {}, label: 'Terraform' },
|
|
957
|
+
kubernetes: { files: ['k8s', 'kubernetes', 'helm'], content: {}, label: 'Kubernetes' },
|
|
956
958
|
};
|
|
957
959
|
|
|
958
960
|
module.exports = { TECHNIQUES, STACKS };
|