claudex-setup 0.3.2 → 0.5.1

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
@@ -1,6 +1,6 @@
1
1
  # claudex-setup
2
2
 
3
- > Score your project 0-100 for Claude Code. Smart CLAUDE.md, 54 checks, wizard, watch mode, CI action. Powered by 1,107 verified techniques.
3
+ > Score your project 0-100 for Claude Code readiness. Smart CLAUDE.md generator, 63 audit checks, interactive wizard, watch mode, CI action. Never overwrites existing config.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/claudex-setup)](https://www.npmjs.com/package/claudex-setup)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/claudex-setup)](https://www.npmjs.com/package/claudex-setup)
@@ -44,7 +44,7 @@ No install. No config. No dependencies.
44
44
  design: none (0/2)
45
45
  devops: none (0/4)
46
46
 
47
- 29/54 checks passing
47
+ 29/63 checks passing
48
48
  Run npx claudex-setup setup to fix
49
49
  ```
50
50
 
@@ -52,12 +52,13 @@ No install. No config. No dependencies.
52
52
 
53
53
  | Command | What it does |
54
54
  |---------|-------------|
55
- | `npx claudex-setup` | **Audit** - Score 0-100 against 54 checks |
55
+ | `npx claudex-setup` | **Audit** - Score 0-100 against 63 checks |
56
56
  | `npx claudex-setup setup` | **Setup** - Smart CLAUDE.md + hooks + commands + agents |
57
57
  | `npx claudex-setup setup --auto` | **Auto-setup** - No prompts, apply all |
58
58
  | `npx claudex-setup interactive` | **Wizard** - Step-by-step guided tour |
59
59
  | `npx claudex-setup watch` | **Watch** - Live monitoring with score delta |
60
60
  | `npx claudex-setup badge` | **Badge** - Generate shields.io badge for README |
61
+ | `npx claudex-setup deep-review` | **Deep Review** - AI-powered config analysis (needs API key) |
61
62
  | `npx claudex-setup insights` | **Insights** - View community aggregate stats |
62
63
 
63
64
  ### Options
@@ -79,7 +80,7 @@ Not a generic template. The `setup` command actually analyzes your project:
79
80
  - **XML constraint blocks** - adds `<constraints>` and `<verification>` with context-aware rules
80
81
  - **Verification criteria** - auto-generates checklist from your actual commands
81
82
 
82
- ## 54 Checks Across 13 Categories
83
+ ## 63 Checks Across 14 Categories
83
84
 
84
85
  | Category | Checks | Key items |
85
86
  |----------|-------:|-----------|
@@ -96,6 +97,7 @@ Not a generic template. The `setup` command actually analyzes your project:
96
97
  | MCP | 3 | servers, Context7, integrations |
97
98
  | Prompting | 3 | constraints, validation, patterns |
98
99
  | Features | 2 | /security-review, Channels |
100
+ | **Quality Deep** | **9** | **freshness, contradictions, deprecated patterns, maxTurns, $ARGUMENTS** |
99
101
 
100
102
  ## Stack Detection
101
103
 
@@ -136,6 +138,36 @@ npx claudex-setup badge
136
138
  # Output: [![Claude Code Ready](https://img.shields.io/badge/...)](...)
137
139
  ```
138
140
 
141
+ ## For Veteran Claude Code Users
142
+
143
+ Already have a solid CLAUDE.md and hooks? Two things for you:
144
+
145
+ ### Deep Review (AI-powered)
146
+
147
+ ```bash
148
+ npx claudex-setup deep-review
149
+ ```
150
+
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.
152
+
153
+ ### Quality-Deep Checks
154
+
155
+ The v0.4.0 quality-deep checks catch what basic audits miss:
156
+
157
+ | Check | What it catches |
158
+ |-------|----------------|
159
+ | **Freshness** | CLAUDE.md that doesn't mention modern features (hooks, skills, MCP) |
160
+ | **Conciseness** | CLAUDE.md over 200 lines (wastes tokens every session) |
161
+ | **Contradictions** | Conflicting rules ("always X" + "never X") |
162
+ | **Hook specificity** | Hooks without matchers that fire on every tool call |
163
+ | **Permission hygiene** | bypassPermissions still enabled in production |
164
+ | **Command flexibility** | Commands without $ARGUMENTS (static, not reusable) |
165
+ | **Agent limits** | Agents without maxTurns (can run forever) |
166
+ | **Security workflow** | No /security-review in your process |
167
+ | **Deprecated patterns** | Old model names, prefill, deprecated API formats |
168
+
169
+ These checks evaluate **quality**, not just existence. A well-configured project with stale patterns will surface real improvements.
170
+
139
171
  ## Privacy
140
172
 
141
173
  - **Zero dependencies** - nothing to audit
@@ -151,7 +183,9 @@ Every check traces to a verified technique from a systematic audit of:
151
183
  - Anthropic blog posts and benchmark papers
152
184
  - 194 hands-on experiments with real evidence
153
185
 
154
- 1,107 techniques cataloged. 954 tested. Continuously updated.
186
+ The catalog includes 1,107 entries (features, techniques, patterns, tools, stats, and known limitations) — not all are actionable checks. 954 were verified with real evidence. Continuously updated.
187
+
188
+ **Note:** A hand-crafted CLAUDE.md that reflects your real conventions will always be better than a generated one. This tool is most useful for projects starting from zero, or as a checklist for what you might be missing.
155
189
 
156
190
  ## Requirements
157
191
 
package/bin/cli.js CHANGED
@@ -18,6 +18,7 @@ const HELP = `
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
22
  npx claudex-setup interactive Step-by-step guided wizard
22
23
  npx claudex-setup watch Monitor changes and re-audit live
23
24
  npx claudex-setup badge Generate shields.io badge markdown
@@ -91,6 +92,9 @@ async function main() {
91
92
  console.log(' Could not reach insights server. Run locally: npx claudex-setup');
92
93
  });
93
94
  return; // keep process alive for http
95
+ } else if (command === 'deep-review' || command === 'review') {
96
+ const { deepReview } = require('../src/deep-review');
97
+ await deepReview(options);
94
98
  } else if (command === 'interactive' || command === 'wizard') {
95
99
  const { interactive } = require('../src/interactive');
96
100
  await interactive(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudex-setup",
3
- "version": "0.3.2",
3
+ "version": "0.5.1",
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": {
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Deep Review - AI-powered analysis of Claude Code configuration quality.
3
+ * Uses Claude API to read and critique your actual config, not just pattern match.
4
+ *
5
+ * Requires: ANTHROPIC_API_KEY environment variable
6
+ * Usage: npx claudex-setup deep-review
7
+ */
8
+
9
+ const https = require('https');
10
+ const { ProjectContext } = require('./context');
11
+ const { STACKS } = require('./techniques');
12
+
13
+ const COLORS = {
14
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
15
+ red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m',
16
+ blue: '\x1b[36m', magenta: '\x1b[35m',
17
+ };
18
+ const c = (text, color) => `${COLORS[color] || ''}${text}${COLORS.reset}`;
19
+
20
+ function collectProjectConfig(ctx, stacks) {
21
+ const config = {};
22
+
23
+ // CLAUDE.md
24
+ config.claudeMd = ctx.fileContent('CLAUDE.md') || ctx.fileContent('.claude/CLAUDE.md');
25
+
26
+ // Settings
27
+ config.settings = ctx.fileContent('.claude/settings.local.json') || ctx.fileContent('.claude/settings.json');
28
+
29
+ // Commands
30
+ config.commands = {};
31
+ if (ctx.hasDir('.claude/commands')) {
32
+ for (const f of ctx.dirFiles('.claude/commands')) {
33
+ config.commands[f] = ctx.fileContent(`.claude/commands/${f}`);
34
+ }
35
+ }
36
+
37
+ // Agents
38
+ config.agents = {};
39
+ if (ctx.hasDir('.claude/agents')) {
40
+ for (const f of ctx.dirFiles('.claude/agents')) {
41
+ config.agents[f] = ctx.fileContent(`.claude/agents/${f}`);
42
+ }
43
+ }
44
+
45
+ // Rules
46
+ config.rules = {};
47
+ if (ctx.hasDir('.claude/rules')) {
48
+ for (const f of ctx.dirFiles('.claude/rules')) {
49
+ config.rules[f] = ctx.fileContent(`.claude/rules/${f}`);
50
+ }
51
+ }
52
+
53
+ // Hooks (from settings)
54
+ if (ctx.hasDir('.claude/hooks')) {
55
+ config.hookFiles = {};
56
+ for (const f of ctx.dirFiles('.claude/hooks')) {
57
+ config.hookFiles[f] = ctx.fileContent(`.claude/hooks/${f}`);
58
+ }
59
+ }
60
+
61
+ // Package.json (scripts only)
62
+ const pkg = ctx.jsonFile('package.json');
63
+ if (pkg) {
64
+ config.packageScripts = pkg.scripts || {};
65
+ config.packageName = pkg.name;
66
+ }
67
+
68
+ config.stacks = stacks.map(s => s.label);
69
+
70
+ return config;
71
+ }
72
+
73
+ function buildPrompt(config) {
74
+ const parts = [];
75
+
76
+ parts.push(`You are an expert Claude Code configuration reviewer. Analyze this project's Claude Code setup and provide specific, actionable feedback.
77
+
78
+ The project uses: ${config.stacks.join(', ') || 'unknown stack'}
79
+ ${config.packageName ? `Project name: ${config.packageName}` : ''}`);
80
+
81
+ if (config.claudeMd) {
82
+ parts.push(`\n<claude_md>\n${config.claudeMd.slice(0, 4000)}\n</claude_md>`);
83
+ } else {
84
+ parts.push('\nNo CLAUDE.md found.');
85
+ }
86
+
87
+ if (config.settings) {
88
+ parts.push(`\n<settings>\n${config.settings.slice(0, 2000)}\n</settings>`);
89
+ }
90
+
91
+ if (Object.keys(config.commands).length > 0) {
92
+ parts.push('\n<commands>');
93
+ for (const [name, content] of Object.entries(config.commands)) {
94
+ parts.push(`--- ${name} ---\n${(content || '').slice(0, 500)}`);
95
+ }
96
+ parts.push('</commands>');
97
+ }
98
+
99
+ if (Object.keys(config.agents).length > 0) {
100
+ parts.push('\n<agents>');
101
+ for (const [name, content] of Object.entries(config.agents)) {
102
+ parts.push(`--- ${name} ---\n${(content || '').slice(0, 500)}`);
103
+ }
104
+ parts.push('</agents>');
105
+ }
106
+
107
+ if (Object.keys(config.rules || {}).length > 0) {
108
+ parts.push('\n<rules>');
109
+ for (const [name, content] of Object.entries(config.rules)) {
110
+ parts.push(`--- ${name} ---\n${(content || '').slice(0, 300)}`);
111
+ }
112
+ parts.push('</rules>');
113
+ }
114
+
115
+ if (config.hookFiles && Object.keys(config.hookFiles).length > 0) {
116
+ parts.push('\n<hooks>');
117
+ for (const [name, content] of Object.entries(config.hookFiles)) {
118
+ parts.push(`--- ${name} ---\n${(content || '').slice(0, 300)}`);
119
+ }
120
+ parts.push('</hooks>');
121
+ }
122
+
123
+ parts.push(`
124
+ <task>
125
+ Provide a deep review with these exact sections:
126
+
127
+ ## Score: X/10
128
+
129
+ ## Strengths (what's done well)
130
+ - List 2-4 specific things this config does right, with WHY they're effective
131
+
132
+ ## Issues (what needs fixing)
133
+ - List 3-5 specific issues, each with:
134
+ - What's wrong (be specific, quote from the config)
135
+ - Why it matters
136
+ - Exact fix (show the corrected version or command)
137
+
138
+ ## Missing (what's not there but should be)
139
+ - List 2-3 things this project should add based on its stack
140
+ - Be specific to THIS project's stack and size, not generic advice
141
+
142
+ ## Quick Wins (fastest improvements)
143
+ - Top 3 changes that take under 2 minutes each
144
+
145
+ Be direct, specific, and honest. Don't pad with generic advice. Reference actual content from the config. If the setup is already excellent, say so and focus on micro-optimizations.
146
+ </task>`);
147
+
148
+ return parts.join('\n');
149
+ }
150
+
151
+ function callClaude(apiKey, prompt) {
152
+ return new Promise((resolve, reject) => {
153
+ const body = JSON.stringify({
154
+ model: 'claude-sonnet-4-6',
155
+ max_tokens: 2000,
156
+ messages: [{ role: 'user', content: prompt }],
157
+ });
158
+
159
+ const req = https.request({
160
+ hostname: 'api.anthropic.com',
161
+ path: '/v1/messages',
162
+ method: 'POST',
163
+ headers: {
164
+ 'Content-Type': 'application/json',
165
+ 'x-api-key': apiKey,
166
+ 'anthropic-version': '2023-06-01',
167
+ 'Content-Length': Buffer.byteLength(body),
168
+ },
169
+ }, (res) => {
170
+ let data = '';
171
+ res.on('data', chunk => data += chunk);
172
+ res.on('end', () => {
173
+ try {
174
+ const parsed = JSON.parse(data);
175
+ if (parsed.error) {
176
+ reject(new Error(parsed.error.message));
177
+ } else {
178
+ resolve(parsed.content[0].text);
179
+ }
180
+ } catch (e) {
181
+ reject(new Error(`API response parse error: ${data.slice(0, 200)}`));
182
+ }
183
+ });
184
+ });
185
+
186
+ req.on('error', reject);
187
+ req.write(body);
188
+ req.end();
189
+ });
190
+ }
191
+
192
+ function hasClaudeCode() {
193
+ try {
194
+ require('child_process').execSync('claude --version', { stdio: 'ignore' });
195
+ return true;
196
+ } catch { return false; }
197
+ }
198
+
199
+ async function callClaudeCode(prompt) {
200
+ 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;
209
+ }
210
+
211
+ async function deepReview(options) {
212
+ const apiKey = process.env.ANTHROPIC_API_KEY;
213
+ const hasClaude = hasClaudeCode();
214
+
215
+ if (!apiKey && !hasClaude) {
216
+ console.log('');
217
+ console.log(c(' Deep Review needs Claude Code or an API key.', 'bold'));
218
+ console.log('');
219
+ console.log(' Option A (recommended): Install Claude Code, then run this command.');
220
+ console.log(c(' npm install -g @anthropic-ai/claude-code', 'green'));
221
+ console.log('');
222
+ console.log(' Option B: Set an API key:');
223
+ console.log(c(' export ANTHROPIC_API_KEY=sk-ant-...', 'green'));
224
+ console.log('');
225
+ process.exit(1);
226
+ }
227
+
228
+ console.log('');
229
+ console.log(c(' claudex-setup deep review', 'bold'));
230
+ console.log(c(' ═══════════════════════════════════════', 'dim'));
231
+
232
+ const ctx = new ProjectContext(options.dir);
233
+ const stacks = ctx.detectStacks(STACKS);
234
+
235
+ console.log(c(` Scanning: ${options.dir}`, 'dim'));
236
+ if (stacks.length > 0) {
237
+ console.log(c(` Stack: ${stacks.map(s => s.label).join(', ')}`, 'blue'));
238
+ }
239
+
240
+ // Collect config
241
+ const config = collectProjectConfig(ctx, stacks);
242
+ const fileCount = [
243
+ config.claudeMd ? 1 : 0,
244
+ config.settings ? 1 : 0,
245
+ Object.keys(config.commands).length,
246
+ Object.keys(config.agents).length,
247
+ Object.keys(config.rules || {}).length,
248
+ Object.keys(config.hookFiles || {}).length,
249
+ ].reduce((a, b) => a + b, 0);
250
+
251
+ console.log(c(` Found ${fileCount} config files to analyze`, 'dim'));
252
+ console.log('');
253
+ console.log(c(' Sending to Claude for deep analysis...', 'magenta'));
254
+ console.log('');
255
+
256
+ try {
257
+ const prompt = buildPrompt(config);
258
+ let review;
259
+ let method;
260
+
261
+ if (hasClaude) {
262
+ method = 'Claude Code (your existing subscription)';
263
+ console.log(c(' Using: Claude Code (no API key needed)', 'green'));
264
+ console.log('');
265
+ review = await callClaudeCode(prompt);
266
+ } else {
267
+ method = 'Anthropic API (your key)';
268
+ console.log(c(' Using: Anthropic API', 'dim'));
269
+ console.log('');
270
+ review = await callClaude(apiKey, prompt);
271
+ }
272
+
273
+ // Format output
274
+ const lines = review.split('\n');
275
+ for (const line of lines) {
276
+ if (line.startsWith('## Score')) {
277
+ console.log(c(` ${line}`, 'bold'));
278
+ } else if (line.startsWith('## Strengths')) {
279
+ console.log(c(` ${line}`, 'green'));
280
+ } else if (line.startsWith('## Issues')) {
281
+ console.log(c(` ${line}`, 'yellow'));
282
+ } else if (line.startsWith('## Missing')) {
283
+ console.log(c(` ${line}`, 'red'));
284
+ } else if (line.startsWith('## Quick')) {
285
+ console.log(c(` ${line}`, 'magenta'));
286
+ } else if (line.startsWith('- ')) {
287
+ console.log(` ${line}`);
288
+ } else if (line.startsWith('```')) {
289
+ console.log(c(` ${line}`, 'dim'));
290
+ } else if (line.trim()) {
291
+ console.log(` ${line}`);
292
+ } else {
293
+ console.log('');
294
+ }
295
+ }
296
+
297
+ console.log('');
298
+ console.log(c(' ─────────────────────────────────────', 'dim'));
299
+ console.log(c(` Reviewed via ${method}`, 'dim'));
300
+ console.log(c(' Your config stays between you and Anthropic. We never see it.', 'dim'));
301
+ console.log('');
302
+ } catch (err) {
303
+ console.log(c(` Error: ${err.message}`, 'red'));
304
+ console.log('');
305
+ console.log(' Check your ANTHROPIC_API_KEY is valid.');
306
+ process.exit(1);
307
+ }
308
+ }
309
+
310
+ module.exports = { deepReview };
package/src/setup.js CHANGED
@@ -569,6 +569,7 @@ async function setup(options) {
569
569
  console.log('');
570
570
 
571
571
  let created = 0;
572
+ let skipped = 0;
572
573
 
573
574
  for (const [key, technique] of Object.entries(TECHNIQUES)) {
574
575
  if (technique.passed || technique.check(ctx)) continue;
@@ -589,6 +590,9 @@ async function setup(options) {
589
590
  fs.writeFileSync(fullPath, result, 'utf8');
590
591
  console.log(` \x1b[32m✅\x1b[0m Created ${filePath}`);
591
592
  created++;
593
+ } else {
594
+ console.log(` \x1b[2m⏭️ Skipped ${filePath} (already exists — your version is kept)\x1b[0m`);
595
+ skipped++;
592
596
  }
593
597
  } else if (typeof result === 'object') {
594
598
  // Multiple files template (hooks, commands, etc)
@@ -616,16 +620,23 @@ async function setup(options) {
616
620
  fs.writeFileSync(filePath, content, 'utf8');
617
621
  console.log(` \x1b[32m✅\x1b[0m Created ${path.relative(options.dir, filePath)}`);
618
622
  created++;
623
+ } else {
624
+ skipped++;
619
625
  }
620
626
  }
621
627
  }
622
628
  }
623
629
 
624
- if (created === 0) {
625
- console.log(' \x1b[32m✅\x1b[0m Project already well configured!');
626
- } else {
627
- console.log('');
630
+ console.log('');
631
+ if (created === 0 && skipped > 0) {
632
+ console.log(' \x1b[32m✅\x1b[0m Your project is already well configured!');
633
+ console.log(` \x1b[2m ${skipped} files already exist and were preserved.\x1b[0m`);
634
+ console.log(' \x1b[2m We never overwrite your existing config — your setup is kept.\x1b[0m');
635
+ } else if (created > 0) {
628
636
  console.log(` \x1b[1m${created} files created.\x1b[0m`);
637
+ if (skipped > 0) {
638
+ console.log(` \x1b[2m${skipped} existing files preserved (not overwritten).\x1b[0m`);
639
+ }
629
640
  }
630
641
 
631
642
  console.log('');
package/src/techniques.js CHANGED
@@ -702,15 +702,18 @@ const TECHNIQUES = {
702
702
  name: 'XML tags for structured prompts',
703
703
  check: (ctx) => {
704
704
  const md = ctx.fileContent('CLAUDE.md') || '';
705
- return md.includes('<') && md.includes('>') && (
706
- md.includes('<constraints') || md.includes('<rules') ||
707
- md.includes('<validation') || md.includes('<instructions')
708
- );
705
+ // Give credit for XML tags OR well-structured markdown with clear sections
706
+ const hasXml = md.includes('<constraints') || md.includes('<rules') ||
707
+ md.includes('<validation') || md.includes('<instructions');
708
+ const hasStructuredMd = (md.includes('## Rules') || md.includes('## Constraints') ||
709
+ md.includes('## Do not') || md.includes('## Never') || md.includes('## Important')) &&
710
+ md.split('\n').length > 20;
711
+ return hasXml || hasStructuredMd;
709
712
  },
710
- impact: 'high',
711
- rating: 5,
713
+ impact: 'medium',
714
+ rating: 4,
712
715
  category: 'prompting',
713
- fix: 'Use XML tags (<constraints>, <validation>) in CLAUDE.md for unambiguous instructions.',
716
+ fix: 'Add clear rules sections to CLAUDE.md. XML tags (<constraints>) are optional but improve clarity.',
714
717
  template: null
715
718
  },
716
719
 
@@ -770,6 +773,169 @@ const TECHNIQUES = {
770
773
  fix: 'Claude Code Channels (v2.1.80+) lets you control sessions from Telegram/Discord/iMessage.',
771
774
  template: null
772
775
  },
776
+
777
+ // ============================================================
778
+ // === QUALITY CHECKS FOR VETERANS (category: 'quality-deep')
779
+ // These check HOW GOOD your config is, not just IF it exists.
780
+ // ============================================================
781
+
782
+ claudeMdFreshness: {
783
+ id: 2001,
784
+ name: 'CLAUDE.md mentions current Claude features',
785
+ check: (ctx) => {
786
+ const md = ctx.fileContent('CLAUDE.md') || '';
787
+ if (md.length < 50) return false; // too short to evaluate
788
+ // Check for awareness of features from 2025+
789
+ const modernFeatures = ['hook', 'skill', 'agent', 'subagent', 'mcp', 'compact', '/clear', 'extended thinking', 'tool_use', 'worktree'];
790
+ const found = modernFeatures.filter(f => md.toLowerCase().includes(f));
791
+ return found.length >= 2; // knows at least 2 modern features
792
+ },
793
+ impact: 'medium',
794
+ rating: 4,
795
+ category: 'quality-deep',
796
+ fix: 'Your CLAUDE.md may be outdated. Modern Claude Code supports hooks, skills, agents, MCP, worktrees, and extended thinking. Mention the ones you use.',
797
+ template: null
798
+ },
799
+
800
+ claudeMdNotOverlong: {
801
+ id: 2002,
802
+ name: 'CLAUDE.md is concise (under 200 lines)',
803
+ check: (ctx) => {
804
+ const md = ctx.fileContent('CLAUDE.md') || '';
805
+ return md.split('\n').length <= 200;
806
+ },
807
+ impact: 'medium',
808
+ rating: 4,
809
+ category: 'quality-deep',
810
+ fix: 'CLAUDE.md over 200 lines wastes tokens every session. Move detailed docs to .claude/rules/ or skills. Keep CLAUDE.md lean.',
811
+ template: null
812
+ },
813
+
814
+ claudeMdNoContradictions: {
815
+ id: 2003,
816
+ name: 'CLAUDE.md has no obvious contradictions',
817
+ check: (ctx) => {
818
+ const md = ctx.fileContent('CLAUDE.md') || '';
819
+ // Check for common contradictions
820
+ const hasNever = /never.*always|always.*never/i.test(md);
821
+ const hasBothStyles = /use tabs/i.test(md) && /use spaces/i.test(md);
822
+ return !hasNever && !hasBothStyles;
823
+ },
824
+ impact: 'high',
825
+ rating: 4,
826
+ category: 'quality-deep',
827
+ fix: 'CLAUDE.md may contain contradictory instructions. Review for conflicting rules (e.g., "always X" and "never X" about the same topic).',
828
+ template: null
829
+ },
830
+
831
+ hooksAreSpecific: {
832
+ id: 2004,
833
+ name: 'Hooks use specific matchers (not catch-all)',
834
+ check: (ctx) => {
835
+ const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
836
+ if (!settings || !settings.hooks) return true; // no hooks = skip
837
+ const hookStr = JSON.stringify(settings.hooks);
838
+ // Check that hooks have matchers, not just catch-all
839
+ return hookStr.includes('matcher');
840
+ },
841
+ impact: 'medium',
842
+ rating: 3,
843
+ category: 'quality-deep',
844
+ fix: 'Hooks without matchers run on every tool call. Use matchers like "Write|Edit" or "Bash" to target specific tools.',
845
+ template: null
846
+ },
847
+
848
+ permissionsNotBypassed: {
849
+ id: 2005,
850
+ name: 'Default mode is not bypassPermissions',
851
+ check: (ctx) => {
852
+ const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
853
+ if (!settings || !settings.permissions) return true;
854
+ return settings.permissions.defaultMode !== 'bypassPermissions';
855
+ },
856
+ impact: 'high',
857
+ rating: 4,
858
+ category: 'quality-deep',
859
+ fix: 'bypassPermissions skips all safety checks. Use "default" or "auto" mode with targeted allow rules instead.',
860
+ template: null
861
+ },
862
+
863
+ commandsUseArguments: {
864
+ id: 2006,
865
+ name: 'Commands use $ARGUMENTS for flexibility',
866
+ check: (ctx) => {
867
+ if (!ctx.hasDir('.claude/commands')) return true;
868
+ const files = ctx.dirFiles('.claude/commands');
869
+ if (files.length === 0) return true;
870
+ // Check if at least one command uses $ARGUMENTS
871
+ for (const f of files) {
872
+ const content = ctx.fileContent(`.claude/commands/${f}`) || '';
873
+ if (content.includes('$ARGUMENTS') || content.includes('$arguments')) return true;
874
+ }
875
+ return false;
876
+ },
877
+ impact: 'medium',
878
+ rating: 3,
879
+ category: 'quality-deep',
880
+ fix: 'Commands without $ARGUMENTS are static. Use $ARGUMENTS to make them flexible: "Fix the issue: $ARGUMENTS"',
881
+ template: null
882
+ },
883
+
884
+ agentsHaveMaxTurns: {
885
+ id: 2007,
886
+ name: 'Agents have maxTurns limit',
887
+ check: (ctx) => {
888
+ if (!ctx.hasDir('.claude/agents')) return true;
889
+ const files = ctx.dirFiles('.claude/agents');
890
+ if (files.length === 0) return true;
891
+ for (const f of files) {
892
+ const content = ctx.fileContent(`.claude/agents/${f}`) || '';
893
+ if (!content.includes('maxTurns')) return false;
894
+ }
895
+ return true;
896
+ },
897
+ impact: 'medium',
898
+ rating: 3,
899
+ category: 'quality-deep',
900
+ fix: 'Agents without maxTurns can run indefinitely. Add "maxTurns: 50" to agent frontmatter.',
901
+ template: null
902
+ },
903
+
904
+ securityReviewInWorkflow: {
905
+ id: 2008,
906
+ name: '/security-review in workflow',
907
+ check: (ctx) => {
908
+ const md = ctx.fileContent('CLAUDE.md') || '';
909
+ const hasCommand = ctx.hasDir('.claude/commands') &&
910
+ (ctx.dirFiles('.claude/commands') || []).some(f => f.includes('security') || f.includes('review'));
911
+ return md.toLowerCase().includes('security') || hasCommand;
912
+ },
913
+ impact: 'high',
914
+ rating: 4,
915
+ category: 'quality-deep',
916
+ fix: 'Claude Code has built-in /security-review (OWASP Top 10). Add it to your workflow or create a /security command.',
917
+ template: null
918
+ },
919
+
920
+ noDeprecatedPatterns: {
921
+ id: 2009,
922
+ name: 'No deprecated patterns detected',
923
+ check: (ctx) => {
924
+ const md = ctx.fileContent('CLAUDE.md') || '';
925
+ // Check for patterns deprecated in Claude 4.x
926
+ const deprecated = [
927
+ 'prefill', // deprecated in 4.6
928
+ 'claude-3-opus', 'claude-3-sonnet', 'claude-3-haiku', // old model names
929
+ 'human_prompt', 'assistant_prompt', // old API format
930
+ ];
931
+ return !deprecated.some(d => md.toLowerCase().includes(d));
932
+ },
933
+ impact: 'medium',
934
+ rating: 3,
935
+ category: 'quality-deep',
936
+ fix: 'CLAUDE.md references deprecated patterns (old model names or API formats). Update to current Claude 4.x conventions.',
937
+ template: null
938
+ },
773
939
  };
774
940
 
775
941
  // Stack detection