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 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
- | `--no-insights` | Disable anonymous usage insights |
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 (73% token savings)
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
- ANTHROPIC_API_KEY=sk-ant-... npx claudex-setup deep-review
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 (disable with `--no-insights`)
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
- Powered by 1,107 verified techniques.
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 analysis (needs API key)
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
- --no-insights Disable anonymous usage insights
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudex-setup",
3
- "version": "0.5.0",
3
+ "version": "1.0.0",
4
4
  "description": "Audit and optimize any project for Claude Code. Powered by 1107 verified techniques.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -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
- if (!apiKey) {
195
- console.log('');
196
- console.log(c(' Deep Review requires an Anthropic API key.', 'bold'));
221
+ const hasClaude = hasClaudeCode();
222
+
223
+ if (!apiKey && !hasClaude) {
197
224
  console.log('');
198
- console.log(' Set it with:');
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(c(' Or on Windows:', 'dim'));
202
- console.log(c(' set ANTHROPIC_API_KEY=sk-ant-...', 'green'));
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(c(' Your key is sent directly to Anthropic API. We never see or store it.', 'dim'));
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
- const review = await callClaude(apiKey, prompt);
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(' Reviewed by Claude Sonnet 4.6 via Anthropic API', 'dim'));
268
- console.log(c(' Your config was sent to api.anthropic.com only. We never see it.', 'dim'));
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
- // Respect opt-out
29
- if (process.env.CLAUDEX_NO_INSIGHTS === '1') return false;
30
- if (process.argv.includes('--no-insights')) return false;
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) {
@@ -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 - auto-check after file edits
388
- # Customize the linter command for your project
389
- TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
390
- echo "[$TIMESTAMP] File changed: $(cat -)" >> .claude/logs/changes.txt
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 - warn before touching sensitive files
394
- # Prevents accidental reads/writes to files containing secrets
395
-
401
+ # PreToolUse hook - blocks reads of secret files
396
402
  INPUT=$(cat -)
397
- FILE=$(echo "$INPUT" | grep -oP '"file_path"\\s*:\\s*"\\K[^"]+' 2>/dev/null || echo "")
403
+ FILE_PATH=$(echo "$INPUT" | sed -n 's/.*"file_path"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')
398
404
 
399
- if [ -z "$FILE" ]; then
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: () => true, // informational
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: 4,
776
+ rating: 3,
772
777
  category: 'features',
773
- fix: 'Claude Code Channels (v2.1.80+) lets you control sessions from Telegram/Discord/iMessage.',
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