claudex-setup 0.5.0 → 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 +4 -4
- package/bin/cli.js +3 -3
- package/package.json +1 -1
- package/src/deep-review.js +51 -11
- package/src/insights.js +4 -8
- package/src/interactive.js +1 -1
- package/src/setup.js +62 -20
- package/src/techniques.js +14 -6
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ No install. No config. No dependencies.
|
|
|
67
67
|
|------|--------|
|
|
68
68
|
| `--verbose` | Show all recommendations (not just critical/high) |
|
|
69
69
|
| `--json` | Machine-readable JSON output (for CI) |
|
|
70
|
-
| `--
|
|
70
|
+
| `--insights` | Enable anonymous usage insights (off by default) |
|
|
71
71
|
|
|
72
72
|
## Smart CLAUDE.md Generation
|
|
73
73
|
|
|
@@ -76,7 +76,7 @@ Not a generic template. The `setup` command actually analyzes your project:
|
|
|
76
76
|
- **Reads package.json** - includes your actual test, build, lint, dev commands
|
|
77
77
|
- **Detects framework** - Next.js Server Components, Django models, FastAPI Pydantic, React hooks
|
|
78
78
|
- **TypeScript-aware** - detects strict mode, adds TS-specific rules
|
|
79
|
-
- **Auto Mermaid diagram** - scans directories and generates architecture visualization (
|
|
79
|
+
- **Auto Mermaid diagram** - scans directories and generates architecture visualization (Mermaid diagrams are more token-efficient than prose descriptions, per Anthropic docs)
|
|
80
80
|
- **XML constraint blocks** - adds `<constraints>` and `<verification>` with context-aware rules
|
|
81
81
|
- **Verification criteria** - auto-generates checklist from your actual commands
|
|
82
82
|
|
|
@@ -145,7 +145,7 @@ Already have a solid CLAUDE.md and hooks? Two things for you:
|
|
|
145
145
|
### Deep Review (AI-powered)
|
|
146
146
|
|
|
147
147
|
```bash
|
|
148
|
-
|
|
148
|
+
npx claudex-setup deep-review
|
|
149
149
|
```
|
|
150
150
|
|
|
151
151
|
Claude reads your actual config and gives specific feedback: what's strong, what has issues, what's missing for your stack. Not pattern matching — real analysis. Your config goes to Anthropic API only, we never see it.
|
|
@@ -172,7 +172,7 @@ These checks evaluate **quality**, not just existence. A well-configured project
|
|
|
172
172
|
|
|
173
173
|
- **Zero dependencies** - nothing to audit
|
|
174
174
|
- **Runs 100% locally** - no cloud processing
|
|
175
|
-
- **Anonymous insights** - opt-in, no PII, no file contents (
|
|
175
|
+
- **Anonymous insights** - opt-in, no PII, no file contents (enable with `--insights`)
|
|
176
176
|
- **MIT Licensed** - use anywhere
|
|
177
177
|
|
|
178
178
|
## Backed by Research
|
package/bin/cli.js
CHANGED
|
@@ -11,14 +11,14 @@ const flags = args.filter(a => a.startsWith('--'));
|
|
|
11
11
|
const HELP = `
|
|
12
12
|
claudex-setup v${version}
|
|
13
13
|
Audit and optimize any project for Claude Code.
|
|
14
|
-
|
|
14
|
+
Backed by research from 1,107 cataloged Claude Code entries.
|
|
15
15
|
|
|
16
16
|
Usage:
|
|
17
17
|
npx claudex-setup Run audit on current directory
|
|
18
18
|
npx claudex-setup audit Same as above
|
|
19
19
|
npx claudex-setup setup Apply recommended configuration
|
|
20
20
|
npx claudex-setup setup --auto Apply all without prompts
|
|
21
|
-
npx claudex-setup deep-review AI-powered config
|
|
21
|
+
npx claudex-setup deep-review AI-powered config review (uses Claude Code or API key)
|
|
22
22
|
npx claudex-setup interactive Step-by-step guided wizard
|
|
23
23
|
npx claudex-setup watch Monitor changes and re-audit live
|
|
24
24
|
npx claudex-setup badge Generate shields.io badge markdown
|
|
@@ -26,7 +26,7 @@ const HELP = `
|
|
|
26
26
|
Options:
|
|
27
27
|
--verbose Show all recommendations (not just critical/high)
|
|
28
28
|
--json Output as JSON (for CI pipelines)
|
|
29
|
-
--
|
|
29
|
+
--insights Enable anonymous usage insights (off by default)
|
|
30
30
|
--help Show this help
|
|
31
31
|
--version Show version
|
|
32
32
|
`;
|
package/package.json
CHANGED
package/src/deep-review.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const https = require('https');
|
|
10
|
+
const path = require('path');
|
|
10
11
|
const { ProjectContext } = require('./context');
|
|
11
12
|
const { STACKS } = require('./techniques');
|
|
12
13
|
|
|
@@ -189,19 +190,45 @@ function callClaude(apiKey, prompt) {
|
|
|
189
190
|
});
|
|
190
191
|
}
|
|
191
192
|
|
|
193
|
+
function hasClaudeCode() {
|
|
194
|
+
try {
|
|
195
|
+
require('child_process').execSync('claude --version', { stdio: 'ignore' });
|
|
196
|
+
return true;
|
|
197
|
+
} catch { return false; }
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function callClaudeCode(prompt) {
|
|
201
|
+
const { execSync } = require('child_process');
|
|
202
|
+
const os = require('os');
|
|
203
|
+
const fs = require('fs');
|
|
204
|
+
const tmpFile = path.join(os.tmpdir(), `claudex-review-${Date.now()}.txt`);
|
|
205
|
+
fs.writeFileSync(tmpFile, prompt, 'utf8');
|
|
206
|
+
try {
|
|
207
|
+
const result = execSync(`claude -p --output-format text < "${tmpFile}"`, {
|
|
208
|
+
encoding: 'utf8',
|
|
209
|
+
maxBuffer: 1024 * 1024,
|
|
210
|
+
timeout: 120000,
|
|
211
|
+
shell: true,
|
|
212
|
+
});
|
|
213
|
+
return result;
|
|
214
|
+
} finally {
|
|
215
|
+
try { fs.unlinkSync(tmpFile); } catch {}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
192
219
|
async function deepReview(options) {
|
|
193
220
|
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
221
|
+
const hasClaude = hasClaudeCode();
|
|
222
|
+
|
|
223
|
+
if (!apiKey && !hasClaude) {
|
|
197
224
|
console.log('');
|
|
198
|
-
console.log('
|
|
199
|
-
console.log(c(' export ANTHROPIC_API_KEY=sk-ant-...', 'green'));
|
|
225
|
+
console.log(c(' Deep Review needs Claude Code or an API key.', 'bold'));
|
|
200
226
|
console.log('');
|
|
201
|
-
console.log(
|
|
202
|
-
console.log(c('
|
|
227
|
+
console.log(' Option A (recommended): Install Claude Code, then run this command.');
|
|
228
|
+
console.log(c(' npm install -g @anthropic-ai/claude-code', 'green'));
|
|
203
229
|
console.log('');
|
|
204
|
-
console.log(
|
|
230
|
+
console.log(' Option B: Set an API key:');
|
|
231
|
+
console.log(c(' export ANTHROPIC_API_KEY=sk-ant-...', 'green'));
|
|
205
232
|
console.log('');
|
|
206
233
|
process.exit(1);
|
|
207
234
|
}
|
|
@@ -236,7 +263,20 @@ async function deepReview(options) {
|
|
|
236
263
|
|
|
237
264
|
try {
|
|
238
265
|
const prompt = buildPrompt(config);
|
|
239
|
-
|
|
266
|
+
let review;
|
|
267
|
+
let method;
|
|
268
|
+
|
|
269
|
+
if (hasClaude) {
|
|
270
|
+
method = 'Claude Code (your existing subscription)';
|
|
271
|
+
console.log(c(' Using: Claude Code (no API key needed)', 'green'));
|
|
272
|
+
console.log('');
|
|
273
|
+
review = await callClaudeCode(prompt);
|
|
274
|
+
} else {
|
|
275
|
+
method = 'Anthropic API (your key)';
|
|
276
|
+
console.log(c(' Using: Anthropic API', 'dim'));
|
|
277
|
+
console.log('');
|
|
278
|
+
review = await callClaude(apiKey, prompt);
|
|
279
|
+
}
|
|
240
280
|
|
|
241
281
|
// Format output
|
|
242
282
|
const lines = review.split('\n');
|
|
@@ -264,8 +304,8 @@ async function deepReview(options) {
|
|
|
264
304
|
|
|
265
305
|
console.log('');
|
|
266
306
|
console.log(c(' ─────────────────────────────────────', 'dim'));
|
|
267
|
-
console.log(c(
|
|
268
|
-
console.log(c(' Your config
|
|
307
|
+
console.log(c(` Reviewed via ${method}`, 'dim'));
|
|
308
|
+
console.log(c(' Your config stays between you and Anthropic. We never see it.', 'dim'));
|
|
269
309
|
console.log('');
|
|
270
310
|
} catch (err) {
|
|
271
311
|
console.log(c(` Error: ${err.message}`, 'red'));
|
package/src/insights.js
CHANGED
|
@@ -25,14 +25,10 @@ const INSIGHTS_ENDPOINT = 'https://claudex-insights.claudex.workers.dev/v1/repor
|
|
|
25
25
|
const TIMEOUT_MS = 3000;
|
|
26
26
|
|
|
27
27
|
function shouldCollect() {
|
|
28
|
-
//
|
|
29
|
-
if (process.env.
|
|
30
|
-
if (process.argv.includes('--
|
|
31
|
-
|
|
32
|
-
// Don't collect in CI
|
|
33
|
-
if (process.env.CI || process.env.GITHUB_ACTIONS) return false;
|
|
34
|
-
|
|
35
|
-
return true;
|
|
28
|
+
// Opt-IN: only collect if user explicitly enables
|
|
29
|
+
if (process.env.CLAUDEX_INSIGHTS === '1') return true;
|
|
30
|
+
if (process.argv.includes('--insights')) return true;
|
|
31
|
+
return false;
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
function buildPayload(auditResult) {
|
package/src/interactive.js
CHANGED
|
@@ -102,7 +102,7 @@ async function interactive(options) {
|
|
|
102
102
|
console.log('');
|
|
103
103
|
|
|
104
104
|
// Run setup in auto mode
|
|
105
|
-
await setup({ ...options, auto: true });
|
|
105
|
+
await setup({ ...options, auto: true, only: toFix });
|
|
106
106
|
|
|
107
107
|
console.log('');
|
|
108
108
|
console.log(c(' Done! Run `npx claudex-setup` to see your new score.', 'green'));
|
package/src/setup.js
CHANGED
|
@@ -384,32 +384,29 @@ ${verificationSteps.join('\n')}
|
|
|
384
384
|
|
|
385
385
|
'hooks': () => ({
|
|
386
386
|
'on-edit-lint.sh': `#!/bin/bash
|
|
387
|
-
# PostToolUse hook -
|
|
388
|
-
#
|
|
389
|
-
|
|
390
|
-
|
|
387
|
+
# PostToolUse hook - runs linter after file edits
|
|
388
|
+
# Detects which linter is available and runs it
|
|
389
|
+
|
|
390
|
+
if command -v npx &>/dev/null; then
|
|
391
|
+
if [ -f "package.json" ] && grep -q '"lint"' package.json 2>/dev/null; then
|
|
392
|
+
npm run lint --silent 2>/dev/null
|
|
393
|
+
elif [ -f ".eslintrc" ] || [ -f ".eslintrc.js" ] || [ -f ".eslintrc.json" ] || [ -f "eslint.config.js" ]; then
|
|
394
|
+
npx eslint --fix . --quiet 2>/dev/null
|
|
395
|
+
fi
|
|
396
|
+
elif command -v ruff &>/dev/null; then
|
|
397
|
+
ruff check --fix . 2>/dev/null
|
|
398
|
+
fi
|
|
391
399
|
`,
|
|
392
400
|
'protect-secrets.sh': `#!/bin/bash
|
|
393
|
-
# PreToolUse hook -
|
|
394
|
-
# Prevents accidental reads/writes to files containing secrets
|
|
395
|
-
|
|
401
|
+
# PreToolUse hook - blocks reads of secret files
|
|
396
402
|
INPUT=$(cat -)
|
|
397
|
-
|
|
403
|
+
FILE_PATH=$(echo "$INPUT" | sed -n 's/.*"file_path"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')
|
|
398
404
|
|
|
399
|
-
if
|
|
405
|
+
if echo "$FILE_PATH" | grep -qiE '\\.env$|\\.env\\.|secrets/|credentials|\\.pem$|\\.key$'; then
|
|
406
|
+
echo '{"decision": "block", "reason": "Blocked: accessing secret/credential files is not allowed."}'
|
|
400
407
|
exit 0
|
|
401
408
|
fi
|
|
402
|
-
|
|
403
|
-
BASENAME=$(basename "$FILE")
|
|
404
|
-
|
|
405
|
-
case "$BASENAME" in
|
|
406
|
-
.env|.env.*|*.pem|*.key|credentials.json|secrets.yaml|secrets.yml)
|
|
407
|
-
echo "WARN: Attempting to access sensitive file: $BASENAME"
|
|
408
|
-
echo "This file may contain secrets. Proceed with caution."
|
|
409
|
-
;;
|
|
410
|
-
esac
|
|
411
|
-
|
|
412
|
-
exit 0
|
|
409
|
+
echo '{"decision": "allow"}'
|
|
413
410
|
`,
|
|
414
411
|
'log-changes.sh': `#!/bin/bash
|
|
415
412
|
# PostToolUse hook - logs all file changes with timestamps
|
|
@@ -571,9 +568,19 @@ async function setup(options) {
|
|
|
571
568
|
let created = 0;
|
|
572
569
|
let skipped = 0;
|
|
573
570
|
|
|
571
|
+
let failedWithTemplates = [];
|
|
574
572
|
for (const [key, technique] of Object.entries(TECHNIQUES)) {
|
|
575
573
|
if (technique.passed || technique.check(ctx)) continue;
|
|
576
574
|
if (!technique.template) continue;
|
|
575
|
+
failedWithTemplates.push({ key, technique });
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Filter by 'only' list if provided (interactive wizard selections)
|
|
579
|
+
if (options.only && options.only.length > 0) {
|
|
580
|
+
failedWithTemplates = failedWithTemplates.filter(r => options.only.includes(r.key));
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
for (const { key, technique } of failedWithTemplates) {
|
|
577
584
|
|
|
578
585
|
const template = TEMPLATES[technique.template];
|
|
579
586
|
if (!template) continue;
|
|
@@ -627,6 +634,41 @@ async function setup(options) {
|
|
|
627
634
|
}
|
|
628
635
|
}
|
|
629
636
|
|
|
637
|
+
// Auto-register hooks in settings if hooks were created but no settings exist
|
|
638
|
+
const hooksDir = path.join(options.dir, '.claude/hooks');
|
|
639
|
+
const settingsPath = path.join(options.dir, '.claude/settings.json');
|
|
640
|
+
if (fs.existsSync(hooksDir) && !fs.existsSync(settingsPath)) {
|
|
641
|
+
const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.sh'));
|
|
642
|
+
if (hookFiles.length > 0) {
|
|
643
|
+
const settings = {
|
|
644
|
+
hooks: {
|
|
645
|
+
PostToolUse: [{
|
|
646
|
+
matcher: "Write|Edit",
|
|
647
|
+
hooks: hookFiles.filter(f => f !== 'protect-secrets.sh').map(f => ({
|
|
648
|
+
type: "command",
|
|
649
|
+
command: `bash .claude/hooks/${f}`,
|
|
650
|
+
timeout: 10
|
|
651
|
+
}))
|
|
652
|
+
}]
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
// Add protect-secrets as PreToolUse if it exists
|
|
656
|
+
if (hookFiles.includes('protect-secrets.sh')) {
|
|
657
|
+
settings.hooks.PreToolUse = [{
|
|
658
|
+
matcher: "Read|Write|Edit",
|
|
659
|
+
hooks: [{
|
|
660
|
+
type: "command",
|
|
661
|
+
command: "bash .claude/hooks/protect-secrets.sh",
|
|
662
|
+
timeout: 5
|
|
663
|
+
}]
|
|
664
|
+
}];
|
|
665
|
+
}
|
|
666
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
667
|
+
console.log(` \x1b[32m✅\x1b[0m Created .claude/settings.json (hooks registered)`);
|
|
668
|
+
created++;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
630
672
|
console.log('');
|
|
631
673
|
if (created === 0 && skipped > 0) {
|
|
632
674
|
console.log(' \x1b[32m✅\x1b[0m Your project is already well configured!');
|
package/src/techniques.js
CHANGED
|
@@ -766,11 +766,16 @@ const TECHNIQUES = {
|
|
|
766
766
|
channelsAwareness: {
|
|
767
767
|
id: 1102,
|
|
768
768
|
name: 'Claude Code Channels awareness',
|
|
769
|
-
check: () =>
|
|
769
|
+
check: (ctx) => {
|
|
770
|
+
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
771
|
+
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
772
|
+
const settingsStr = JSON.stringify(settings || {});
|
|
773
|
+
return md.toLowerCase().includes('channel') || settingsStr.includes('channel');
|
|
774
|
+
},
|
|
770
775
|
impact: 'low',
|
|
771
|
-
rating:
|
|
776
|
+
rating: 3,
|
|
772
777
|
category: 'features',
|
|
773
|
-
fix: 'Claude Code Channels (v2.1.80+)
|
|
778
|
+
fix: 'Claude Code Channels (v2.1.80+) bridges Telegram/Discord/iMessage to your session.',
|
|
774
779
|
template: null
|
|
775
780
|
},
|
|
776
781
|
|
|
@@ -801,7 +806,8 @@ const TECHNIQUES = {
|
|
|
801
806
|
id: 2002,
|
|
802
807
|
name: 'CLAUDE.md is concise (under 200 lines)',
|
|
803
808
|
check: (ctx) => {
|
|
804
|
-
const md = ctx.fileContent('CLAUDE.md')
|
|
809
|
+
const md = ctx.fileContent('CLAUDE.md');
|
|
810
|
+
if (!md) return false; // no CLAUDE.md = not passing
|
|
805
811
|
return md.split('\n').length <= 200;
|
|
806
812
|
},
|
|
807
813
|
impact: 'medium',
|
|
@@ -815,7 +821,8 @@ const TECHNIQUES = {
|
|
|
815
821
|
id: 2003,
|
|
816
822
|
name: 'CLAUDE.md has no obvious contradictions',
|
|
817
823
|
check: (ctx) => {
|
|
818
|
-
const md = ctx.fileContent('CLAUDE.md')
|
|
824
|
+
const md = ctx.fileContent('CLAUDE.md');
|
|
825
|
+
if (!md || md.length < 50) return false; // no CLAUDE.md or too short = not passing
|
|
819
826
|
// Check for common contradictions
|
|
820
827
|
const hasNever = /never.*always|always.*never/i.test(md);
|
|
821
828
|
const hasBothStyles = /use tabs/i.test(md) && /use spaces/i.test(md);
|
|
@@ -921,7 +928,8 @@ const TECHNIQUES = {
|
|
|
921
928
|
id: 2009,
|
|
922
929
|
name: 'No deprecated patterns detected',
|
|
923
930
|
check: (ctx) => {
|
|
924
|
-
const md = ctx.fileContent('CLAUDE.md')
|
|
931
|
+
const md = ctx.fileContent('CLAUDE.md');
|
|
932
|
+
if (!md) return false; // no CLAUDE.md = not passing
|
|
925
933
|
// Check for patterns deprecated in Claude 4.x
|
|
926
934
|
const deprecated = [
|
|
927
935
|
'prefill', // deprecated in 4.6
|