claudex-setup 0.5.1 → 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
 
@@ -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.1",
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
 
@@ -198,14 +199,21 @@ function hasClaudeCode() {
198
199
 
199
200
  async function callClaudeCode(prompt) {
200
201
  const { execSync } = require('child_process');
201
- // Use claude -p (headless/print mode) - uses the user's existing Claude Code auth
202
- const escaped = prompt.replace(/'/g, "'\\''");
203
- const result = execSync(`claude -p '${escaped}' --output-format text`, {
204
- encoding: 'utf8',
205
- maxBuffer: 1024 * 1024,
206
- timeout: 120000,
207
- });
208
- return result;
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
+ }
209
217
  }
210
218
 
211
219
  async function deepReview(options) {
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