@nerviq/cli 0.0.1 → 0.9.0-beta.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/CHANGELOG.md +181 -0
- package/LICENSE +21 -0
- package/README.md +447 -0
- package/bin/cli.js +749 -0
- package/content/case-study-template.md +91 -0
- package/content/claims-governance.md +37 -0
- package/content/claude-code/audit-repo/SKILL.md +20 -0
- package/content/claude-native-integration.md +60 -0
- package/content/devto-article.json +9 -0
- package/content/launch-posts.md +226 -0
- package/content/pilot-rollout-kit.md +30 -0
- package/content/release-checklist.md +31 -0
- package/package.json +53 -4
- package/src/activity.js +529 -0
- package/src/aider/activity.js +226 -0
- package/src/aider/config-parser.js +166 -0
- package/src/aider/context.js +158 -0
- package/src/aider/deep-review.js +316 -0
- package/src/aider/domain-packs.js +278 -0
- package/src/aider/freshness.js +168 -0
- package/src/aider/governance.js +253 -0
- package/src/aider/interactive.js +334 -0
- package/src/aider/mcp-packs.js +98 -0
- package/src/aider/patch.js +214 -0
- package/src/aider/plans.js +186 -0
- package/src/aider/premium.js +360 -0
- package/src/aider/setup.js +404 -0
- package/src/aider/techniques.js +1323 -0
- package/src/analyze.js +821 -0
- package/src/audit.js +1003 -0
- package/src/badge.js +13 -0
- package/src/benchmark.js +339 -0
- package/src/claudex-sync.json +7 -0
- package/src/codex/activity.js +324 -0
- package/src/codex/config-parser.js +183 -0
- package/src/codex/context.js +221 -0
- package/src/codex/deep-review.js +493 -0
- package/src/codex/domain-packs.js +372 -0
- package/src/codex/freshness.js +167 -0
- package/src/codex/governance.js +192 -0
- package/src/codex/interactive.js +618 -0
- package/src/codex/mcp-packs.js +660 -0
- package/src/codex/patch.js +209 -0
- package/src/codex/plans.js +251 -0
- package/src/codex/premium.js +614 -0
- package/src/codex/setup.js +603 -0
- package/src/codex/techniques.js +2649 -0
- package/src/context.js +272 -0
- package/src/copilot/activity.js +309 -0
- package/src/copilot/config-parser.js +226 -0
- package/src/copilot/context.js +197 -0
- package/src/copilot/deep-review.js +346 -0
- package/src/copilot/domain-packs.js +350 -0
- package/src/copilot/freshness.js +197 -0
- package/src/copilot/governance.js +222 -0
- package/src/copilot/interactive.js +406 -0
- package/src/copilot/mcp-packs.js +572 -0
- package/src/copilot/patch.js +238 -0
- package/src/copilot/plans.js +253 -0
- package/src/copilot/premium.js +450 -0
- package/src/copilot/setup.js +488 -0
- package/src/copilot/techniques.js +1822 -0
- package/src/cursor/activity.js +301 -0
- package/src/cursor/config-parser.js +265 -0
- package/src/cursor/context.js +236 -0
- package/src/cursor/deep-review.js +334 -0
- package/src/cursor/domain-packs.js +346 -0
- package/src/cursor/freshness.js +214 -0
- package/src/cursor/governance.js +229 -0
- package/src/cursor/interactive.js +391 -0
- package/src/cursor/mcp-packs.js +571 -0
- package/src/cursor/patch.js +243 -0
- package/src/cursor/plans.js +254 -0
- package/src/cursor/premium.js +468 -0
- package/src/cursor/setup.js +488 -0
- package/src/cursor/techniques.js +1786 -0
- package/src/deep-review.js +345 -0
- package/src/domain-packs.js +364 -0
- package/src/formatters/sarif.js +115 -0
- package/src/gemini/activity.js +402 -0
- package/src/gemini/config-parser.js +275 -0
- package/src/gemini/context.js +221 -0
- package/src/gemini/deep-review.js +559 -0
- package/src/gemini/domain-packs.js +371 -0
- package/src/gemini/freshness.js +204 -0
- package/src/gemini/governance.js +201 -0
- package/src/gemini/interactive.js +860 -0
- package/src/gemini/mcp-packs.js +658 -0
- package/src/gemini/patch.js +229 -0
- package/src/gemini/plans.js +269 -0
- package/src/gemini/premium.js +759 -0
- package/src/gemini/setup.js +692 -0
- package/src/gemini/techniques.js +2084 -0
- package/src/governance.js +523 -0
- package/src/harmony/advisor.js +383 -0
- package/src/harmony/audit.js +303 -0
- package/src/harmony/canon.js +444 -0
- package/src/harmony/cli.js +331 -0
- package/src/harmony/drift.js +401 -0
- package/src/harmony/governance.js +313 -0
- package/src/harmony/memory.js +238 -0
- package/src/harmony/sync.js +458 -0
- package/src/harmony/watch.js +336 -0
- package/src/index.js +256 -0
- package/src/insights.js +119 -0
- package/src/interactive.js +118 -0
- package/src/mcp-packs.js +597 -0
- package/src/opencode/activity.js +286 -0
- package/src/opencode/config-parser.js +109 -0
- package/src/opencode/context.js +247 -0
- package/src/opencode/deep-review.js +313 -0
- package/src/opencode/domain-packs.js +240 -0
- package/src/opencode/freshness.js +158 -0
- package/src/opencode/governance.js +159 -0
- package/src/opencode/interactive.js +392 -0
- package/src/opencode/mcp-packs.js +474 -0
- package/src/opencode/patch.js +184 -0
- package/src/opencode/plans.js +231 -0
- package/src/opencode/premium.js +413 -0
- package/src/opencode/setup.js +449 -0
- package/src/opencode/techniques.js +1713 -0
- package/src/plans.js +655 -0
- package/src/secret-patterns.js +30 -0
- package/src/setup.js +1274 -0
- package/src/synergy/adaptive.js +261 -0
- package/src/synergy/compensation.js +156 -0
- package/src/synergy/evidence.js +193 -0
- package/src/synergy/learning.js +184 -0
- package/src/synergy/patterns.js +227 -0
- package/src/synergy/ranking.js +83 -0
- package/src/synergy/report.js +163 -0
- package/src/synergy/routing.js +152 -0
- package/src/techniques.js +1354 -0
- package/src/watch.js +229 -0
- package/src/windsurf/activity.js +302 -0
- package/src/windsurf/config-parser.js +267 -0
- package/src/windsurf/context.js +249 -0
- package/src/windsurf/deep-review.js +337 -0
- package/src/windsurf/domain-packs.js +348 -0
- package/src/windsurf/freshness.js +215 -0
- package/src/windsurf/governance.js +231 -0
- package/src/windsurf/interactive.js +388 -0
- package/src/windsurf/mcp-packs.js +535 -0
- package/src/windsurf/patch.js +231 -0
- package/src/windsurf/plans.js +247 -0
- package/src/windsurf/premium.js +467 -0
- package/src/windsurf/setup.js +471 -0
- package/src/windsurf/techniques.js +1758 -0
|
@@ -0,0 +1,1354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLAUDEX Technique Database
|
|
3
|
+
* Curated from 1107 verified techniques, filtered to actionable setup recommendations.
|
|
4
|
+
* Each technique includes: what to check, how to fix, impact level.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function hasFrontendSignals(ctx) {
|
|
8
|
+
const pkg = ctx.fileContent('package.json') || '';
|
|
9
|
+
return /react|vue|angular|next|svelte|tailwind|vite|astro/i.test(pkg) ||
|
|
10
|
+
ctx.files.some(f => /tailwind\.config|vite\.config|next\.config|svelte\.config|nuxt\.config|pages\/|components\/|app\//i.test(f));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { containsEmbeddedSecret } = require('./secret-patterns');
|
|
14
|
+
|
|
15
|
+
const TECHNIQUES = {
|
|
16
|
+
// ============================================================
|
|
17
|
+
// === MEMORY & CONTEXT (category: 'memory') ==================
|
|
18
|
+
// ============================================================
|
|
19
|
+
|
|
20
|
+
claudeMd: {
|
|
21
|
+
id: 1,
|
|
22
|
+
name: 'CLAUDE.md project instructions',
|
|
23
|
+
check: (ctx) => ctx.files.includes('CLAUDE.md') || ctx.files.includes('.claude/CLAUDE.md'),
|
|
24
|
+
impact: 'critical',
|
|
25
|
+
rating: 5,
|
|
26
|
+
category: 'memory',
|
|
27
|
+
fix: 'Create CLAUDE.md with project-specific instructions, build commands, and coding conventions.',
|
|
28
|
+
template: 'claude-md'
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
mermaidArchitecture: {
|
|
32
|
+
id: 51,
|
|
33
|
+
name: 'Mermaid architecture diagram',
|
|
34
|
+
check: (ctx) => {
|
|
35
|
+
const md = ctx.claudeMdContent() || '';
|
|
36
|
+
return md.includes('mermaid') || md.includes('graph ') || md.includes('flowchart ');
|
|
37
|
+
},
|
|
38
|
+
impact: 'high',
|
|
39
|
+
rating: 5,
|
|
40
|
+
category: 'memory',
|
|
41
|
+
fix: 'Add a Mermaid diagram to CLAUDE.md showing project architecture. Saves 73% tokens vs prose.',
|
|
42
|
+
template: 'mermaid'
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
pathRules: {
|
|
46
|
+
id: 3,
|
|
47
|
+
name: 'Path-specific rules',
|
|
48
|
+
check: (ctx) => ctx.hasDir('.claude/rules') && ctx.dirFiles('.claude/rules').length > 0,
|
|
49
|
+
impact: 'medium',
|
|
50
|
+
rating: 4,
|
|
51
|
+
category: 'memory',
|
|
52
|
+
fix: 'Add rules for different file types (frontend vs backend conventions).',
|
|
53
|
+
template: 'rules'
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
importSyntax: {
|
|
57
|
+
id: 763,
|
|
58
|
+
name: 'CLAUDE.md uses @import for modularity',
|
|
59
|
+
check: (ctx) => {
|
|
60
|
+
const md = ctx.claudeMdContent() || '';
|
|
61
|
+
return md.includes('@import');
|
|
62
|
+
},
|
|
63
|
+
impact: 'medium',
|
|
64
|
+
rating: 4,
|
|
65
|
+
category: 'memory',
|
|
66
|
+
fix: 'Use @import in CLAUDE.md to split instructions into focused modules (e.g. @import ./docs/coding-style.md).',
|
|
67
|
+
template: null
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
underlines200: {
|
|
71
|
+
id: 681,
|
|
72
|
+
name: 'CLAUDE.md under 200 lines (concise)',
|
|
73
|
+
check: (ctx) => {
|
|
74
|
+
const md = ctx.claudeMdContent() || '';
|
|
75
|
+
return md.split('\n').length <= 200;
|
|
76
|
+
},
|
|
77
|
+
impact: 'medium',
|
|
78
|
+
rating: 4,
|
|
79
|
+
category: 'memory',
|
|
80
|
+
fix: 'Keep CLAUDE.md under 200 lines. Use @import or .claude/rules/ to split large instructions.',
|
|
81
|
+
template: null
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
// ============================================================
|
|
85
|
+
// === QUALITY & TESTING (category: 'quality') ================
|
|
86
|
+
// ============================================================
|
|
87
|
+
|
|
88
|
+
verificationLoop: {
|
|
89
|
+
id: 93,
|
|
90
|
+
name: 'Verification criteria in CLAUDE.md',
|
|
91
|
+
check: (ctx) => {
|
|
92
|
+
const md = ctx.claudeMdContent() || '';
|
|
93
|
+
return /\b(npm test|yarn test|pnpm test|pytest|go test|make test|npm run lint|yarn lint|npx |ruff |eslint)\b/i.test(md) ||
|
|
94
|
+
/\b(test command|lint command|build command|verify|run tests|run lint)\b/i.test(md);
|
|
95
|
+
},
|
|
96
|
+
impact: 'critical',
|
|
97
|
+
rating: 5,
|
|
98
|
+
category: 'quality',
|
|
99
|
+
fix: 'Add test/lint/build commands to CLAUDE.md so Claude can verify its own work.',
|
|
100
|
+
template: null
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
testCommand: {
|
|
104
|
+
id: 93001,
|
|
105
|
+
name: 'CLAUDE.md contains a test command',
|
|
106
|
+
check: (ctx) => {
|
|
107
|
+
const md = ctx.claudeMdContent() || '';
|
|
108
|
+
return /npm test|pytest|jest|vitest|cargo test|go test|mix test|rspec/.test(md);
|
|
109
|
+
},
|
|
110
|
+
impact: 'high',
|
|
111
|
+
rating: 5,
|
|
112
|
+
category: 'quality',
|
|
113
|
+
fix: 'Add an explicit test command to CLAUDE.md (e.g. "Run `npm test` before committing").',
|
|
114
|
+
template: null
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
lintCommand: {
|
|
118
|
+
id: 93002,
|
|
119
|
+
name: 'CLAUDE.md contains a lint command',
|
|
120
|
+
check: (ctx) => {
|
|
121
|
+
const md = ctx.claudeMdContent() || '';
|
|
122
|
+
return /eslint|prettier|ruff|black|clippy|golangci-lint|rubocop|npm run lint|yarn lint|pnpm lint|bun lint/.test(md);
|
|
123
|
+
},
|
|
124
|
+
impact: 'high',
|
|
125
|
+
rating: 4,
|
|
126
|
+
category: 'quality',
|
|
127
|
+
fix: 'Add a lint command to CLAUDE.md so Claude auto-formats and checks code style.',
|
|
128
|
+
template: null
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
buildCommand: {
|
|
132
|
+
id: 93003,
|
|
133
|
+
name: 'CLAUDE.md contains a build command',
|
|
134
|
+
check: (ctx) => {
|
|
135
|
+
const md = ctx.claudeMdContent() || '';
|
|
136
|
+
return /npm run build|cargo build|go build|make|tsc|gradle build|mvn compile/.test(md);
|
|
137
|
+
},
|
|
138
|
+
impact: 'medium',
|
|
139
|
+
rating: 4,
|
|
140
|
+
category: 'quality',
|
|
141
|
+
fix: 'Add a build command to CLAUDE.md so Claude can verify compilation before committing.',
|
|
142
|
+
template: null
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// ============================================================
|
|
146
|
+
// === GIT SAFETY (category: 'git') ===========================
|
|
147
|
+
// ============================================================
|
|
148
|
+
|
|
149
|
+
gitIgnoreClaudeTracked: {
|
|
150
|
+
id: 976,
|
|
151
|
+
name: '.claude/ tracked in git',
|
|
152
|
+
check: (ctx) => {
|
|
153
|
+
if (!ctx.fileContent('.gitignore')) return true; // no gitignore = ok
|
|
154
|
+
const lines = ctx.fileContent('.gitignore')
|
|
155
|
+
.split(/\r?\n/)
|
|
156
|
+
.map(line => line.trim())
|
|
157
|
+
.filter(line => line && !line.startsWith('#'));
|
|
158
|
+
const ignoresClaudeDir = lines.some(line => /^(\/|\*\*\/)?\.claude\/?$/.test(line));
|
|
159
|
+
const unignoresClaudeDir = lines.some(line => /^!(\/)?\.claude(\/|\*\*)?$/.test(line));
|
|
160
|
+
return !ignoresClaudeDir || unignoresClaudeDir;
|
|
161
|
+
},
|
|
162
|
+
impact: 'high',
|
|
163
|
+
rating: 4,
|
|
164
|
+
category: 'git',
|
|
165
|
+
fix: 'Remove .claude/ from .gitignore (keep .claude/settings.local.json ignored).',
|
|
166
|
+
template: null
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
gitIgnoreEnv: {
|
|
170
|
+
id: 917,
|
|
171
|
+
name: '.gitignore blocks .env files',
|
|
172
|
+
check: (ctx) => {
|
|
173
|
+
const gitignore = ctx.fileContent('.gitignore') || '';
|
|
174
|
+
return gitignore.includes('.env');
|
|
175
|
+
},
|
|
176
|
+
impact: 'critical',
|
|
177
|
+
rating: 5,
|
|
178
|
+
category: 'git',
|
|
179
|
+
fix: 'Add .env to .gitignore to prevent leaking secrets.',
|
|
180
|
+
template: null
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
gitIgnoreNodeModules: {
|
|
184
|
+
id: 91701,
|
|
185
|
+
name: '.gitignore blocks node_modules',
|
|
186
|
+
check: (ctx) => {
|
|
187
|
+
const hasNodeSignals = ctx.files.includes('package.json') ||
|
|
188
|
+
ctx.files.includes('tsconfig.json') ||
|
|
189
|
+
ctx.files.some(f => /package-lock\.json|pnpm-lock\.yaml|yarn\.lock|next\.config|vite\.config/i.test(f));
|
|
190
|
+
if (!hasNodeSignals) return null;
|
|
191
|
+
const gitignore = ctx.fileContent('.gitignore') || '';
|
|
192
|
+
return gitignore.includes('node_modules');
|
|
193
|
+
},
|
|
194
|
+
impact: 'high',
|
|
195
|
+
rating: 4,
|
|
196
|
+
category: 'git',
|
|
197
|
+
fix: 'Add node_modules/ to .gitignore.',
|
|
198
|
+
template: null
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
noSecretsInClaude: {
|
|
202
|
+
id: 1039,
|
|
203
|
+
name: 'CLAUDE.md has no embedded API keys',
|
|
204
|
+
check: (ctx) => {
|
|
205
|
+
const md = ctx.claudeMdContent() || '';
|
|
206
|
+
return !containsEmbeddedSecret(md);
|
|
207
|
+
},
|
|
208
|
+
impact: 'critical',
|
|
209
|
+
rating: 5,
|
|
210
|
+
category: 'git',
|
|
211
|
+
fix: 'Remove API keys from CLAUDE.md. Use environment variables or .env files instead.',
|
|
212
|
+
template: null
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
// ============================================================
|
|
216
|
+
// === WORKFLOW (category: 'workflow') =========================
|
|
217
|
+
// ============================================================
|
|
218
|
+
|
|
219
|
+
customCommands: {
|
|
220
|
+
id: 20,
|
|
221
|
+
name: 'Custom slash commands',
|
|
222
|
+
check: (ctx) => ctx.hasDir('.claude/commands') && ctx.dirFiles('.claude/commands').length > 0,
|
|
223
|
+
impact: 'high',
|
|
224
|
+
rating: 4,
|
|
225
|
+
category: 'workflow',
|
|
226
|
+
fix: 'Create custom commands for repeated workflows (/test, /deploy, /review).',
|
|
227
|
+
template: 'commands'
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
multipleCommands: {
|
|
231
|
+
id: 20001,
|
|
232
|
+
name: '3+ slash commands for rich workflow',
|
|
233
|
+
check: (ctx) => ctx.hasDir('.claude/commands') && ctx.dirFiles('.claude/commands').length >= 3,
|
|
234
|
+
impact: 'medium',
|
|
235
|
+
rating: 4,
|
|
236
|
+
category: 'workflow',
|
|
237
|
+
fix: 'Add at least 3 slash commands to cover your main workflows (test, deploy, review, etc.).',
|
|
238
|
+
template: 'commands'
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
deployCommand: {
|
|
242
|
+
id: 20002,
|
|
243
|
+
name: 'Has /deploy or /release command',
|
|
244
|
+
check: (ctx) => {
|
|
245
|
+
if (!ctx.hasDir('.claude/commands')) return false;
|
|
246
|
+
const files = ctx.dirFiles('.claude/commands');
|
|
247
|
+
return files.some(f => /deploy|release/i.test(f));
|
|
248
|
+
},
|
|
249
|
+
impact: 'medium',
|
|
250
|
+
rating: 4,
|
|
251
|
+
category: 'workflow',
|
|
252
|
+
fix: 'Create a /deploy or /release command for one-click deployments.',
|
|
253
|
+
template: null
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
reviewCommand: {
|
|
257
|
+
id: 20003,
|
|
258
|
+
name: 'Has /review command',
|
|
259
|
+
check: (ctx) => {
|
|
260
|
+
if (!ctx.hasDir('.claude/commands')) return false;
|
|
261
|
+
const files = ctx.dirFiles('.claude/commands');
|
|
262
|
+
return files.some(f => /review/i.test(f));
|
|
263
|
+
},
|
|
264
|
+
impact: 'medium',
|
|
265
|
+
rating: 4,
|
|
266
|
+
category: 'workflow',
|
|
267
|
+
fix: 'Create a /review command for code review workflows.',
|
|
268
|
+
template: null
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
skills: {
|
|
272
|
+
id: 21,
|
|
273
|
+
name: 'Custom skills',
|
|
274
|
+
check: (ctx) => ctx.hasDir('.claude/skills') && ctx.dirFiles('.claude/skills').length > 0,
|
|
275
|
+
impact: 'medium',
|
|
276
|
+
rating: 4,
|
|
277
|
+
category: 'workflow',
|
|
278
|
+
fix: 'Add skills for domain-specific workflows.',
|
|
279
|
+
template: 'skills'
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
multipleSkills: {
|
|
283
|
+
id: 2101,
|
|
284
|
+
name: '2+ skills for specialization',
|
|
285
|
+
check: (ctx) => ctx.hasDir('.claude/skills') && ctx.dirFiles('.claude/skills').length >= 2,
|
|
286
|
+
impact: 'medium',
|
|
287
|
+
rating: 4,
|
|
288
|
+
category: 'workflow',
|
|
289
|
+
fix: 'Add at least 2 skills to cover different domain areas.',
|
|
290
|
+
template: 'skills'
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
agents: {
|
|
294
|
+
id: 22,
|
|
295
|
+
name: 'Custom agents',
|
|
296
|
+
check: (ctx) => ctx.hasDir('.claude/agents') && ctx.dirFiles('.claude/agents').length > 0,
|
|
297
|
+
impact: 'medium',
|
|
298
|
+
rating: 4,
|
|
299
|
+
category: 'workflow',
|
|
300
|
+
fix: 'Create specialized agents (security-reviewer, test-writer) in .claude/agents/.',
|
|
301
|
+
template: 'agents'
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
multipleAgents: {
|
|
305
|
+
id: 2201,
|
|
306
|
+
name: '2+ agents for delegation',
|
|
307
|
+
check: (ctx) => ctx.hasDir('.claude/agents') && ctx.dirFiles('.claude/agents').length >= 2,
|
|
308
|
+
impact: 'medium',
|
|
309
|
+
rating: 4,
|
|
310
|
+
category: 'workflow',
|
|
311
|
+
fix: 'Add at least 2 agents for specialized tasks (e.g. security-reviewer, test-writer).',
|
|
312
|
+
template: 'agents'
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
multipleRules: {
|
|
316
|
+
id: 301,
|
|
317
|
+
name: '2+ rules files for granular control',
|
|
318
|
+
check: (ctx) => ctx.hasDir('.claude/rules') && ctx.dirFiles('.claude/rules').length >= 2,
|
|
319
|
+
impact: 'medium',
|
|
320
|
+
rating: 4,
|
|
321
|
+
category: 'workflow',
|
|
322
|
+
fix: 'Add path-specific rules for different parts of the codebase (frontend, backend, tests).',
|
|
323
|
+
template: 'rules'
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
// ============================================================
|
|
327
|
+
// === SECURITY (category: 'security') ========================
|
|
328
|
+
// ============================================================
|
|
329
|
+
|
|
330
|
+
settingsPermissions: {
|
|
331
|
+
id: 24,
|
|
332
|
+
name: 'Permission configuration',
|
|
333
|
+
check: (ctx) => {
|
|
334
|
+
// Prefer local (effective config) — any settings file with permissions passes
|
|
335
|
+
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
336
|
+
return !!(settings && settings.permissions);
|
|
337
|
+
},
|
|
338
|
+
impact: 'medium',
|
|
339
|
+
rating: 4,
|
|
340
|
+
category: 'security',
|
|
341
|
+
fix: 'Configure allow/deny permission lists for safe tool usage.',
|
|
342
|
+
template: null
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
permissionDeny: {
|
|
346
|
+
id: 2401,
|
|
347
|
+
name: 'Deny rules configured in permissions',
|
|
348
|
+
check: (ctx) => {
|
|
349
|
+
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
350
|
+
if (!settings || !settings.permissions) return false;
|
|
351
|
+
const deny = settings.permissions.deny;
|
|
352
|
+
return Array.isArray(deny) && deny.length > 0;
|
|
353
|
+
},
|
|
354
|
+
impact: 'high',
|
|
355
|
+
rating: 5,
|
|
356
|
+
category: 'security',
|
|
357
|
+
fix: 'Add permissions.deny rules to block dangerous operations (e.g. rm -rf, dropping databases).',
|
|
358
|
+
template: null
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
noBypassPermissions: {
|
|
362
|
+
id: 2402,
|
|
363
|
+
name: 'Default mode is not bypassPermissions',
|
|
364
|
+
check: (ctx) => {
|
|
365
|
+
// Check shared settings first (committed to git) — if the shared baseline
|
|
366
|
+
// is safe, a personal settings.local.json override should not fail the audit.
|
|
367
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
368
|
+
if (shared && shared.permissions) {
|
|
369
|
+
return shared.permissions.defaultMode !== 'bypassPermissions';
|
|
370
|
+
}
|
|
371
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
372
|
+
if (!local || !local.permissions) return null;
|
|
373
|
+
return local.permissions.defaultMode !== 'bypassPermissions';
|
|
374
|
+
},
|
|
375
|
+
impact: 'critical',
|
|
376
|
+
rating: 5,
|
|
377
|
+
category: 'security',
|
|
378
|
+
fix: 'Do not set defaultMode to bypassPermissions. Use explicit allow rules instead.',
|
|
379
|
+
template: null
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
secretsProtection: {
|
|
383
|
+
id: 1096,
|
|
384
|
+
name: 'Secrets protection configured',
|
|
385
|
+
check: (ctx) => {
|
|
386
|
+
// Prefer shared settings.json (committed) over local override
|
|
387
|
+
const settings = ctx.jsonFile('.claude/settings.json') || ctx.jsonFile('.claude/settings.local.json');
|
|
388
|
+
if (!settings || !settings.permissions) return false;
|
|
389
|
+
const deny = JSON.stringify(settings.permissions.deny || []);
|
|
390
|
+
return deny.includes('.env') || deny.includes('secrets');
|
|
391
|
+
},
|
|
392
|
+
impact: 'critical',
|
|
393
|
+
rating: 5,
|
|
394
|
+
category: 'security',
|
|
395
|
+
fix: 'Add permissions.deny rules to block reading .env files and secrets directories.',
|
|
396
|
+
template: null
|
|
397
|
+
},
|
|
398
|
+
|
|
399
|
+
securityReview: {
|
|
400
|
+
id: 1031,
|
|
401
|
+
name: 'Security review command awareness',
|
|
402
|
+
check: (ctx) => {
|
|
403
|
+
const md = ctx.claudeMdContent() || '';
|
|
404
|
+
return md.includes('security') || md.includes('/security-review');
|
|
405
|
+
},
|
|
406
|
+
impact: 'high',
|
|
407
|
+
rating: 5,
|
|
408
|
+
category: 'security',
|
|
409
|
+
fix: 'Add /security-review to your workflow. Claude Code has built-in OWASP Top 10 scanning.',
|
|
410
|
+
template: null
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
// ============================================================
|
|
414
|
+
// === AUTOMATION (category: 'automation') =====================
|
|
415
|
+
// ============================================================
|
|
416
|
+
|
|
417
|
+
hooks: {
|
|
418
|
+
id: 19,
|
|
419
|
+
name: 'Hooks for automation',
|
|
420
|
+
check: (ctx) => {
|
|
421
|
+
if (ctx.hasDir('.claude/hooks') && ctx.dirFiles('.claude/hooks').length > 0) return true;
|
|
422
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
423
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
424
|
+
return !!(shared.hooks && Object.keys(shared.hooks).length > 0) || !!(local.hooks && Object.keys(local.hooks).length > 0);
|
|
425
|
+
},
|
|
426
|
+
impact: 'high',
|
|
427
|
+
rating: 4,
|
|
428
|
+
category: 'automation',
|
|
429
|
+
fix: 'Add hooks for auto-lint, auto-test, or file change tracking.',
|
|
430
|
+
template: 'hooks'
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
hooksInSettings: {
|
|
434
|
+
id: 8801,
|
|
435
|
+
name: 'Hooks configured in settings',
|
|
436
|
+
check: (ctx) => {
|
|
437
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
438
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
439
|
+
const hasSharedHooks = shared && shared.hooks && Object.keys(shared.hooks).length > 0;
|
|
440
|
+
const hasLocalHooks = local && local.hooks && Object.keys(local.hooks).length > 0;
|
|
441
|
+
return hasSharedHooks || hasLocalHooks;
|
|
442
|
+
},
|
|
443
|
+
impact: 'high',
|
|
444
|
+
rating: 4,
|
|
445
|
+
category: 'automation',
|
|
446
|
+
fix: 'Add hooks in .claude/settings.json for automated enforcement (lint-on-save, test-on-commit).',
|
|
447
|
+
template: 'hooks'
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
preToolUseHook: {
|
|
451
|
+
id: 8802,
|
|
452
|
+
name: 'PreToolUse hook configured',
|
|
453
|
+
check: (ctx) => {
|
|
454
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
455
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
456
|
+
return !!(shared?.hooks?.PreToolUse || local?.hooks?.PreToolUse);
|
|
457
|
+
},
|
|
458
|
+
impact: 'high',
|
|
459
|
+
rating: 4,
|
|
460
|
+
category: 'automation',
|
|
461
|
+
fix: 'Add PreToolUse hooks for validation before tool calls (e.g. block writes to protected files).',
|
|
462
|
+
template: null
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
postToolUseHook: {
|
|
466
|
+
id: 8803,
|
|
467
|
+
name: 'PostToolUse hook configured',
|
|
468
|
+
check: (ctx) => {
|
|
469
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
470
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
471
|
+
return !!(shared?.hooks?.PostToolUse || local?.hooks?.PostToolUse);
|
|
472
|
+
},
|
|
473
|
+
impact: 'high',
|
|
474
|
+
rating: 4,
|
|
475
|
+
category: 'automation',
|
|
476
|
+
fix: 'Add PostToolUse hooks for auto-lint or auto-format after file writes.',
|
|
477
|
+
template: null
|
|
478
|
+
},
|
|
479
|
+
|
|
480
|
+
sessionStartHook: {
|
|
481
|
+
id: 8804,
|
|
482
|
+
name: 'SessionStart hook configured',
|
|
483
|
+
check: (ctx) => {
|
|
484
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
485
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
486
|
+
if (!(shared?.hooks || local?.hooks)) return false;
|
|
487
|
+
return !!(shared?.hooks?.SessionStart || local?.hooks?.SessionStart);
|
|
488
|
+
},
|
|
489
|
+
impact: 'medium',
|
|
490
|
+
rating: 4,
|
|
491
|
+
category: 'automation',
|
|
492
|
+
fix: 'Add a SessionStart hook for initialization tasks (log rotation, state loading, etc.).',
|
|
493
|
+
template: null
|
|
494
|
+
},
|
|
495
|
+
|
|
496
|
+
// ============================================================
|
|
497
|
+
// === DESIGN (category: 'design') ============================
|
|
498
|
+
// ============================================================
|
|
499
|
+
|
|
500
|
+
frontendDesignSkill: {
|
|
501
|
+
id: 1025,
|
|
502
|
+
name: 'Frontend design skill for anti-AI-slop',
|
|
503
|
+
check: (ctx) => {
|
|
504
|
+
if (!hasFrontendSignals(ctx)) return null;
|
|
505
|
+
const md = ctx.claudeMdContent() || '';
|
|
506
|
+
return md.includes('frontend_aesthetics') || md.includes('anti-AI-slop') || md.includes('frontend-design');
|
|
507
|
+
},
|
|
508
|
+
impact: 'medium',
|
|
509
|
+
rating: 5,
|
|
510
|
+
category: 'design',
|
|
511
|
+
fix: 'Install the official frontend-design skill for better UI output quality.',
|
|
512
|
+
template: null
|
|
513
|
+
},
|
|
514
|
+
|
|
515
|
+
tailwindMention: {
|
|
516
|
+
id: 102501,
|
|
517
|
+
name: 'Tailwind CSS configured',
|
|
518
|
+
check: (ctx) => {
|
|
519
|
+
if (!hasFrontendSignals(ctx)) return null;
|
|
520
|
+
const pkg = ctx.fileContent('package.json') || '';
|
|
521
|
+
return pkg.includes('tailwind') ||
|
|
522
|
+
ctx.files.some(f => /tailwind\.config/.test(f));
|
|
523
|
+
},
|
|
524
|
+
impact: 'low',
|
|
525
|
+
rating: 3,
|
|
526
|
+
category: 'design',
|
|
527
|
+
fix: 'Consider adding Tailwind CSS for rapid, consistent UI styling with Claude.',
|
|
528
|
+
template: null
|
|
529
|
+
},
|
|
530
|
+
|
|
531
|
+
// ============================================================
|
|
532
|
+
// === DEVOPS (category: 'devops') ============================
|
|
533
|
+
// ============================================================
|
|
534
|
+
|
|
535
|
+
dockerfile: {
|
|
536
|
+
id: 399,
|
|
537
|
+
name: 'Has Dockerfile',
|
|
538
|
+
check: (ctx) => ctx.files.some(f => /^Dockerfile/i.test(f)),
|
|
539
|
+
impact: 'medium',
|
|
540
|
+
rating: 3,
|
|
541
|
+
category: 'devops',
|
|
542
|
+
fix: 'Add a Dockerfile for containerized builds and deployments.',
|
|
543
|
+
template: null
|
|
544
|
+
},
|
|
545
|
+
|
|
546
|
+
dockerCompose: {
|
|
547
|
+
id: 39901,
|
|
548
|
+
name: 'Has docker-compose.yml',
|
|
549
|
+
check: (ctx) => ctx.files.some(f => /^docker-compose\.(yml|yaml)$/i.test(f)),
|
|
550
|
+
impact: 'medium',
|
|
551
|
+
rating: 3,
|
|
552
|
+
category: 'devops',
|
|
553
|
+
fix: 'Add docker-compose.yml for multi-service local development.',
|
|
554
|
+
template: null
|
|
555
|
+
},
|
|
556
|
+
|
|
557
|
+
ciPipeline: {
|
|
558
|
+
id: 260,
|
|
559
|
+
name: 'CI pipeline configured',
|
|
560
|
+
check: (ctx) => ctx.hasDir('.github/workflows') || ctx.hasDir('.circleci') ||
|
|
561
|
+
ctx.files.includes('.gitlab-ci.yml') || ctx.files.includes('Jenkinsfile') ||
|
|
562
|
+
ctx.files.includes('.travis.yml') || ctx.files.includes('bitbucket-pipelines.yml'),
|
|
563
|
+
impact: 'high',
|
|
564
|
+
rating: 4,
|
|
565
|
+
category: 'devops',
|
|
566
|
+
fix: 'Add a CI pipeline (GitHub Actions, GitLab CI, CircleCI, etc.) for automated testing and deployment.',
|
|
567
|
+
template: null
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
terraformFiles: {
|
|
571
|
+
id: 397,
|
|
572
|
+
name: 'Infrastructure as Code (Terraform)',
|
|
573
|
+
check: (ctx) => ctx.files.some(f => /\.tf$/.test(f)) || ctx.files.includes('main.tf'),
|
|
574
|
+
impact: 'medium',
|
|
575
|
+
rating: 3,
|
|
576
|
+
category: 'devops',
|
|
577
|
+
fix: 'Add Terraform files for infrastructure-as-code management.',
|
|
578
|
+
template: null
|
|
579
|
+
},
|
|
580
|
+
|
|
581
|
+
// ============================================================
|
|
582
|
+
// === PROJECT HYGIENE (category: 'hygiene') ==================
|
|
583
|
+
// ============================================================
|
|
584
|
+
|
|
585
|
+
readme: {
|
|
586
|
+
id: 416,
|
|
587
|
+
name: 'Has README.md',
|
|
588
|
+
check: (ctx) => ctx.files.some(f => /^readme\.md$/i.test(f)),
|
|
589
|
+
impact: 'high',
|
|
590
|
+
rating: 4,
|
|
591
|
+
category: 'hygiene',
|
|
592
|
+
fix: 'Add a README.md with project overview, setup instructions, and usage.',
|
|
593
|
+
template: null
|
|
594
|
+
},
|
|
595
|
+
|
|
596
|
+
changelog: {
|
|
597
|
+
id: 417,
|
|
598
|
+
name: 'Has CHANGELOG.md',
|
|
599
|
+
check: (ctx) => ctx.files.some(f => /^changelog\.md$/i.test(f)),
|
|
600
|
+
impact: 'low',
|
|
601
|
+
rating: 3,
|
|
602
|
+
category: 'hygiene',
|
|
603
|
+
fix: 'Add a CHANGELOG.md to track notable changes across versions.',
|
|
604
|
+
template: null
|
|
605
|
+
},
|
|
606
|
+
|
|
607
|
+
contributing: {
|
|
608
|
+
id: 418,
|
|
609
|
+
name: 'Has CONTRIBUTING.md',
|
|
610
|
+
check: (ctx) => ctx.files.some(f => /^contributing\.md$/i.test(f)),
|
|
611
|
+
impact: 'low',
|
|
612
|
+
rating: 3,
|
|
613
|
+
category: 'hygiene',
|
|
614
|
+
fix: 'Add a CONTRIBUTING.md with contribution guidelines and code standards.',
|
|
615
|
+
template: null
|
|
616
|
+
},
|
|
617
|
+
|
|
618
|
+
license: {
|
|
619
|
+
id: 434,
|
|
620
|
+
name: 'Has LICENSE file',
|
|
621
|
+
check: (ctx) => ctx.files.some(f => /^license/i.test(f)),
|
|
622
|
+
impact: 'low',
|
|
623
|
+
rating: 3,
|
|
624
|
+
category: 'hygiene',
|
|
625
|
+
fix: 'Add a LICENSE file to clarify usage rights.',
|
|
626
|
+
template: null
|
|
627
|
+
},
|
|
628
|
+
|
|
629
|
+
editorconfig: {
|
|
630
|
+
id: 5001,
|
|
631
|
+
name: 'Has .editorconfig',
|
|
632
|
+
check: (ctx) => ctx.files.includes('.editorconfig'),
|
|
633
|
+
impact: 'low',
|
|
634
|
+
rating: 3,
|
|
635
|
+
category: 'hygiene',
|
|
636
|
+
fix: 'Add .editorconfig for consistent formatting across editors and Claude.',
|
|
637
|
+
template: null
|
|
638
|
+
},
|
|
639
|
+
|
|
640
|
+
nvmrc: {
|
|
641
|
+
id: 5002,
|
|
642
|
+
name: 'Node version pinned',
|
|
643
|
+
check: (ctx) => {
|
|
644
|
+
const hasNodeSignals = ctx.files.includes('package.json') ||
|
|
645
|
+
ctx.files.includes('tsconfig.json') ||
|
|
646
|
+
ctx.files.some(f => /package-lock\.json|pnpm-lock\.yaml|yarn\.lock|next\.config|vite\.config/i.test(f));
|
|
647
|
+
if (!hasNodeSignals) return null;
|
|
648
|
+
if (ctx.files.includes('.nvmrc') || ctx.files.includes('.node-version')) return true;
|
|
649
|
+
const pkg = ctx.jsonFile('package.json');
|
|
650
|
+
return !!(pkg && pkg.engines && pkg.engines.node);
|
|
651
|
+
},
|
|
652
|
+
impact: 'low',
|
|
653
|
+
rating: 3,
|
|
654
|
+
category: 'hygiene',
|
|
655
|
+
fix: 'Add .nvmrc, .node-version, or engines.node in package.json to pin Node version.',
|
|
656
|
+
template: null
|
|
657
|
+
},
|
|
658
|
+
|
|
659
|
+
// ============================================================
|
|
660
|
+
// === PERFORMANCE (category: 'performance') ==================
|
|
661
|
+
// ============================================================
|
|
662
|
+
|
|
663
|
+
compactionAwareness: {
|
|
664
|
+
id: 568,
|
|
665
|
+
name: 'CLAUDE.md mentions /compact or compaction',
|
|
666
|
+
check: (ctx) => {
|
|
667
|
+
const md = ctx.claudeMdContent() || '';
|
|
668
|
+
return /\/compact|compaction|context.*(limit|manage|budget)/i.test(md);
|
|
669
|
+
},
|
|
670
|
+
impact: 'medium',
|
|
671
|
+
rating: 4,
|
|
672
|
+
category: 'performance',
|
|
673
|
+
fix: 'Add compaction guidance to CLAUDE.md (e.g. "Run /compact when context is heavy").',
|
|
674
|
+
template: null
|
|
675
|
+
},
|
|
676
|
+
|
|
677
|
+
contextManagement: {
|
|
678
|
+
id: 45,
|
|
679
|
+
name: 'Context management awareness',
|
|
680
|
+
check: (ctx) => {
|
|
681
|
+
const md = ctx.claudeMdContent() || '';
|
|
682
|
+
return /context.*(manage|window|limit|budget|token)/i.test(md);
|
|
683
|
+
},
|
|
684
|
+
impact: 'medium',
|
|
685
|
+
rating: 4,
|
|
686
|
+
category: 'performance',
|
|
687
|
+
fix: 'Add context management tips to CLAUDE.md to help Claude stay within token limits.',
|
|
688
|
+
template: null
|
|
689
|
+
},
|
|
690
|
+
|
|
691
|
+
// ============================================================
|
|
692
|
+
// === MCP / TOOLS (category: 'tools') ========================
|
|
693
|
+
// ============================================================
|
|
694
|
+
|
|
695
|
+
mcpServers: {
|
|
696
|
+
id: 18,
|
|
697
|
+
name: 'MCP servers configured',
|
|
698
|
+
check: (ctx) => {
|
|
699
|
+
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
700
|
+
return !!(settings && settings.mcpServers && Object.keys(settings.mcpServers).length > 0);
|
|
701
|
+
},
|
|
702
|
+
impact: 'medium',
|
|
703
|
+
rating: 3,
|
|
704
|
+
category: 'tools',
|
|
705
|
+
fix: 'Configure MCP servers for external tool integration (database, APIs, etc).',
|
|
706
|
+
template: null
|
|
707
|
+
},
|
|
708
|
+
|
|
709
|
+
multipleMcpServers: {
|
|
710
|
+
id: 1801,
|
|
711
|
+
name: '2+ MCP servers for rich tooling',
|
|
712
|
+
check: (ctx) => {
|
|
713
|
+
let count = 0;
|
|
714
|
+
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
715
|
+
if (settings && settings.mcpServers) count += Object.keys(settings.mcpServers).length;
|
|
716
|
+
const mcpJson = ctx.jsonFile('.mcp.json');
|
|
717
|
+
if (mcpJson && mcpJson.mcpServers) count += Object.keys(mcpJson.mcpServers).length;
|
|
718
|
+
return count >= 2;
|
|
719
|
+
},
|
|
720
|
+
impact: 'medium',
|
|
721
|
+
rating: 4,
|
|
722
|
+
category: 'tools',
|
|
723
|
+
fix: 'Add at least 2 MCP servers for broader tool coverage (e.g. database + search).',
|
|
724
|
+
template: null
|
|
725
|
+
},
|
|
726
|
+
|
|
727
|
+
context7Mcp: {
|
|
728
|
+
id: 110,
|
|
729
|
+
name: 'Context7 MCP for real-time docs',
|
|
730
|
+
check: (ctx) => {
|
|
731
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
732
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
733
|
+
const mcp = ctx.jsonFile('.mcp.json') || {};
|
|
734
|
+
const all = { ...(shared.mcpServers || {}), ...(local.mcpServers || {}), ...(mcp.mcpServers || {}) };
|
|
735
|
+
if (Object.keys(all).length === 0) return false;
|
|
736
|
+
return Object.keys(all).some(k => /context7/i.test(k));
|
|
737
|
+
},
|
|
738
|
+
impact: 'medium',
|
|
739
|
+
rating: 4,
|
|
740
|
+
category: 'tools',
|
|
741
|
+
fix: 'Add Context7 MCP server for real-time documentation lookup (always up-to-date library docs).',
|
|
742
|
+
template: null
|
|
743
|
+
},
|
|
744
|
+
|
|
745
|
+
// ============================================================
|
|
746
|
+
// === PROMPTING (category: 'prompting') ======================
|
|
747
|
+
// ============================================================
|
|
748
|
+
|
|
749
|
+
xmlTags: {
|
|
750
|
+
id: 96,
|
|
751
|
+
name: 'XML tags for structured prompts',
|
|
752
|
+
check: (ctx) => {
|
|
753
|
+
const md = ctx.claudeMdContent() || '';
|
|
754
|
+
// Give credit for XML tags OR well-structured markdown with clear sections
|
|
755
|
+
const hasXml = md.includes('<constraints') || md.includes('<rules') ||
|
|
756
|
+
md.includes('<validation') || md.includes('<instructions');
|
|
757
|
+
const hasStructuredMd = (md.includes('## Rules') || md.includes('## Constraints') ||
|
|
758
|
+
md.includes('## Do not') || md.includes('## Never') || md.includes('## Important')) &&
|
|
759
|
+
md.split('\n').length > 20;
|
|
760
|
+
return hasXml || hasStructuredMd;
|
|
761
|
+
},
|
|
762
|
+
impact: 'medium',
|
|
763
|
+
rating: 4,
|
|
764
|
+
category: 'prompting',
|
|
765
|
+
fix: 'Add clear rules sections to CLAUDE.md. XML tags (<constraints>) are optional but improve clarity.',
|
|
766
|
+
template: null
|
|
767
|
+
},
|
|
768
|
+
|
|
769
|
+
fewShotExamples: {
|
|
770
|
+
id: 9,
|
|
771
|
+
name: 'CLAUDE.md contains code examples',
|
|
772
|
+
check: (ctx) => {
|
|
773
|
+
const md = ctx.claudeMdContent() || '';
|
|
774
|
+
return (md.match(/```/g) || []).length >= 2;
|
|
775
|
+
},
|
|
776
|
+
impact: 'high',
|
|
777
|
+
rating: 5,
|
|
778
|
+
category: 'prompting',
|
|
779
|
+
fix: 'Add code examples (few-shot) in CLAUDE.md to show preferred patterns and conventions.',
|
|
780
|
+
template: null
|
|
781
|
+
},
|
|
782
|
+
|
|
783
|
+
roleDefinition: {
|
|
784
|
+
id: 10,
|
|
785
|
+
name: 'CLAUDE.md defines a role or persona',
|
|
786
|
+
check: (ctx) => {
|
|
787
|
+
const md = ctx.claudeMdContent() || '';
|
|
788
|
+
return /^you are a |^your role is|^act as a |persona:|behave as a /im.test(md);
|
|
789
|
+
},
|
|
790
|
+
impact: 'medium',
|
|
791
|
+
rating: 4,
|
|
792
|
+
category: 'prompting',
|
|
793
|
+
fix: 'Define a role or persona in CLAUDE.md (e.g. "You are a senior backend engineer...").',
|
|
794
|
+
template: null
|
|
795
|
+
},
|
|
796
|
+
|
|
797
|
+
constraintBlocks: {
|
|
798
|
+
id: 9601,
|
|
799
|
+
name: 'XML constraint blocks in CLAUDE.md',
|
|
800
|
+
check: (ctx) => {
|
|
801
|
+
const md = ctx.claudeMdContent() || '';
|
|
802
|
+
return /<constraints|<rules|<requirements|<boundaries/i.test(md);
|
|
803
|
+
},
|
|
804
|
+
impact: 'high',
|
|
805
|
+
rating: 5,
|
|
806
|
+
category: 'prompting',
|
|
807
|
+
fix: 'Wrap critical rules in <constraints> XML blocks for 40% better adherence.',
|
|
808
|
+
template: null
|
|
809
|
+
},
|
|
810
|
+
|
|
811
|
+
// ============================================================
|
|
812
|
+
// === FEATURES (category: 'features') ========================
|
|
813
|
+
// ============================================================
|
|
814
|
+
|
|
815
|
+
channelsAwareness: {
|
|
816
|
+
id: 1102,
|
|
817
|
+
name: 'Claude Code Channels awareness',
|
|
818
|
+
check: (ctx) => {
|
|
819
|
+
const md = ctx.claudeMdContent() || '';
|
|
820
|
+
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
821
|
+
const settingsStr = JSON.stringify(settings || {});
|
|
822
|
+
return /\bchannels?\b.*\b(telegram|discord|imessage|slack|bridge)\b|\b(telegram|discord|imessage|slack|bridge)\b.*\bchannels?\b/i.test(md) || settingsStr.includes('channels');
|
|
823
|
+
},
|
|
824
|
+
impact: 'low',
|
|
825
|
+
rating: 3,
|
|
826
|
+
category: 'features',
|
|
827
|
+
fix: 'Claude Code Channels (v2.1.80+) bridges Telegram/Discord/iMessage to your session.',
|
|
828
|
+
template: null
|
|
829
|
+
},
|
|
830
|
+
|
|
831
|
+
// ============================================================
|
|
832
|
+
// === QUALITY CHECKS FOR VETERANS (category: 'quality-deep')
|
|
833
|
+
// These check HOW GOOD your config is, not just IF it exists.
|
|
834
|
+
// ============================================================
|
|
835
|
+
|
|
836
|
+
claudeMdFreshness: {
|
|
837
|
+
id: 2001,
|
|
838
|
+
name: 'CLAUDE.md mentions current Claude features',
|
|
839
|
+
check: (ctx) => {
|
|
840
|
+
const md = ctx.claudeMdContent() || '';
|
|
841
|
+
if (md.length < 50) return false; // too short to evaluate
|
|
842
|
+
// Check for awareness of features from 2025+
|
|
843
|
+
const modernFeatures = ['hook', 'skill', 'agent', 'subagent', 'mcp', 'compact', '/clear', 'extended thinking', 'tool_use', 'worktree'];
|
|
844
|
+
const found = modernFeatures.filter(f => md.toLowerCase().includes(f));
|
|
845
|
+
return found.length >= 2; // knows at least 2 modern features
|
|
846
|
+
},
|
|
847
|
+
impact: 'medium',
|
|
848
|
+
rating: 4,
|
|
849
|
+
category: 'quality-deep',
|
|
850
|
+
fix: 'Your CLAUDE.md may be outdated. Modern Claude Code supports hooks, skills, agents, MCP, worktrees, and extended thinking. Mention the ones you use.',
|
|
851
|
+
template: null
|
|
852
|
+
},
|
|
853
|
+
|
|
854
|
+
// claudeMdNotOverlong removed — duplicate of underlines200 (id 681)
|
|
855
|
+
|
|
856
|
+
claudeMdNotOverlong: {
|
|
857
|
+
id: 2002,
|
|
858
|
+
name: 'CLAUDE.md is concise (under 200 lines)',
|
|
859
|
+
check: (ctx) => {
|
|
860
|
+
// Defer to underlines200 — this check always returns null (skipped)
|
|
861
|
+
return null;
|
|
862
|
+
},
|
|
863
|
+
impact: 'medium',
|
|
864
|
+
rating: 4,
|
|
865
|
+
category: 'quality-deep',
|
|
866
|
+
fix: 'CLAUDE.md over 200 lines wastes tokens every session. Move detailed docs to .claude/rules/ or skills. Keep CLAUDE.md lean.',
|
|
867
|
+
template: null
|
|
868
|
+
},
|
|
869
|
+
|
|
870
|
+
claudeMdNoContradictions: {
|
|
871
|
+
id: 2003,
|
|
872
|
+
name: 'CLAUDE.md has no obvious contradictions',
|
|
873
|
+
check: (ctx) => {
|
|
874
|
+
const md = ctx.claudeMdContent();
|
|
875
|
+
if (!md || md.length < 50) return false; // no CLAUDE.md or too short = not passing
|
|
876
|
+
// Check for common contradictions
|
|
877
|
+
// Check for contradictions on the SAME topic (same line or adjacent sentence)
|
|
878
|
+
const lines = md.split('\n');
|
|
879
|
+
let hasContradiction = false;
|
|
880
|
+
for (const line of lines) {
|
|
881
|
+
if (/\balways\b.*\bnever\b|\bnever\b.*\balways\b/i.test(line)) {
|
|
882
|
+
hasContradiction = true;
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
const hasBothStyles = /\buse tabs\b/i.test(md) && /\buse spaces\b/i.test(md);
|
|
887
|
+
return !hasContradiction && !hasBothStyles;
|
|
888
|
+
},
|
|
889
|
+
impact: 'high',
|
|
890
|
+
rating: 4,
|
|
891
|
+
category: 'quality-deep',
|
|
892
|
+
fix: 'CLAUDE.md may contain contradictory instructions. Review for conflicting rules (e.g., "always X" and "never X" about the same topic).',
|
|
893
|
+
template: null
|
|
894
|
+
},
|
|
895
|
+
|
|
896
|
+
hooksAreSpecific: {
|
|
897
|
+
id: 2004,
|
|
898
|
+
name: 'Hooks use specific matchers (not catch-all)',
|
|
899
|
+
check: (ctx) => {
|
|
900
|
+
const settings = ctx.jsonFile('.claude/settings.local.json') || ctx.jsonFile('.claude/settings.json');
|
|
901
|
+
if (!settings || !settings.hooks) return null; // no hooks = not applicable
|
|
902
|
+
const hookStr = JSON.stringify(settings.hooks);
|
|
903
|
+
// Check that hooks have matchers, not just catch-all
|
|
904
|
+
return hookStr.includes('matcher');
|
|
905
|
+
},
|
|
906
|
+
impact: 'medium',
|
|
907
|
+
rating: 3,
|
|
908
|
+
category: 'quality-deep',
|
|
909
|
+
fix: 'Hooks without matchers run on every tool call. Use matchers like "Write|Edit" or "Bash" to target specific tools.',
|
|
910
|
+
template: null
|
|
911
|
+
},
|
|
912
|
+
|
|
913
|
+
// permissionsNotBypassed removed - duplicate of noBypassPermissions (#24)
|
|
914
|
+
|
|
915
|
+
commandsUseArguments: {
|
|
916
|
+
id: 2006,
|
|
917
|
+
name: 'Commands use $ARGUMENTS for flexibility',
|
|
918
|
+
check: (ctx) => {
|
|
919
|
+
if (!ctx.hasDir('.claude/commands')) return null; // not applicable
|
|
920
|
+
const files = ctx.dirFiles('.claude/commands');
|
|
921
|
+
if (files.length === 0) return null;
|
|
922
|
+
// Check if at least one command uses $ARGUMENTS
|
|
923
|
+
for (const f of files) {
|
|
924
|
+
const content = ctx.fileContent(`.claude/commands/${f}`) || '';
|
|
925
|
+
if (content.includes('$ARGUMENTS') || content.includes('$arguments')) return true;
|
|
926
|
+
}
|
|
927
|
+
return false;
|
|
928
|
+
},
|
|
929
|
+
impact: 'medium',
|
|
930
|
+
rating: 3,
|
|
931
|
+
category: 'quality-deep',
|
|
932
|
+
fix: 'Commands without $ARGUMENTS are static. Use $ARGUMENTS to make them flexible: "Fix the issue: $ARGUMENTS"',
|
|
933
|
+
template: null
|
|
934
|
+
},
|
|
935
|
+
|
|
936
|
+
agentsHaveMaxTurns: {
|
|
937
|
+
id: 2007,
|
|
938
|
+
name: 'Agents have maxTurns limit',
|
|
939
|
+
check: (ctx) => {
|
|
940
|
+
if (!ctx.hasDir('.claude/agents')) return null;
|
|
941
|
+
const files = ctx.dirFiles('.claude/agents');
|
|
942
|
+
if (files.length === 0) return null;
|
|
943
|
+
for (const f of files) {
|
|
944
|
+
const content = ctx.fileContent(`.claude/agents/${f}`) || '';
|
|
945
|
+
if (!content.includes('maxTurns')) return false;
|
|
946
|
+
}
|
|
947
|
+
return true;
|
|
948
|
+
},
|
|
949
|
+
impact: 'medium',
|
|
950
|
+
rating: 3,
|
|
951
|
+
category: 'quality-deep',
|
|
952
|
+
fix: 'Agents without maxTurns can run indefinitely. Add "maxTurns: 50" to agent frontmatter.',
|
|
953
|
+
template: null
|
|
954
|
+
},
|
|
955
|
+
|
|
956
|
+
securityReviewInWorkflow: {
|
|
957
|
+
id: 2008,
|
|
958
|
+
name: '/security-review command or workflow',
|
|
959
|
+
check: (ctx) => {
|
|
960
|
+
const hasCommand = ctx.hasDir('.claude/commands') &&
|
|
961
|
+
(ctx.dirFiles('.claude/commands') || []).some(f => f.includes('security') || f.includes('review'));
|
|
962
|
+
const md = ctx.claudeMdContent() || '';
|
|
963
|
+
const hasExplicitRef = /\/security-review|security review command|security workflow/i.test(md);
|
|
964
|
+
return hasCommand || hasExplicitRef;
|
|
965
|
+
},
|
|
966
|
+
impact: 'medium',
|
|
967
|
+
rating: 4,
|
|
968
|
+
category: 'quality-deep',
|
|
969
|
+
fix: 'Claude Code has built-in /security-review (OWASP Top 10). Add it to your workflow or create a /security command.',
|
|
970
|
+
template: null
|
|
971
|
+
},
|
|
972
|
+
|
|
973
|
+
// --- New checks: testing depth ---
|
|
974
|
+
testCoverage: {
|
|
975
|
+
id: 2010,
|
|
976
|
+
name: 'Test coverage or strategy mentioned',
|
|
977
|
+
check: (ctx) => {
|
|
978
|
+
const md = ctx.claudeMdContent() || '';
|
|
979
|
+
return /coverage|test.*strateg|e2e|integration test|unit test/i.test(md);
|
|
980
|
+
},
|
|
981
|
+
impact: 'medium', rating: 3, category: 'quality',
|
|
982
|
+
fix: 'Mention your testing strategy in CLAUDE.md (unit, integration, E2E, coverage targets).',
|
|
983
|
+
template: null
|
|
984
|
+
},
|
|
985
|
+
|
|
986
|
+
// --- New checks: agent depth ---
|
|
987
|
+
agentHasAllowedTools: {
|
|
988
|
+
id: 2011,
|
|
989
|
+
name: 'At least one agent restricts tools',
|
|
990
|
+
check: (ctx) => {
|
|
991
|
+
if (!ctx.hasDir('.claude/agents')) return null;
|
|
992
|
+
const files = ctx.dirFiles('.claude/agents');
|
|
993
|
+
if (files.length === 0) return null;
|
|
994
|
+
for (const f of files) {
|
|
995
|
+
const content = ctx.fileContent(`.claude/agents/${f}`) || '';
|
|
996
|
+
if (/tools:\s*\[/.test(content)) return true;
|
|
997
|
+
}
|
|
998
|
+
return false;
|
|
999
|
+
},
|
|
1000
|
+
impact: 'medium', rating: 3, category: 'workflow',
|
|
1001
|
+
fix: 'Add a tools restriction to agent frontmatter (e.g. tools: [Read, Grep]) for safer delegation.',
|
|
1002
|
+
template: null
|
|
1003
|
+
},
|
|
1004
|
+
|
|
1005
|
+
// --- New checks: memory / auto-memory ---
|
|
1006
|
+
autoMemoryAwareness: {
|
|
1007
|
+
id: 2012,
|
|
1008
|
+
name: 'Auto-memory or memory management mentioned',
|
|
1009
|
+
check: (ctx) => {
|
|
1010
|
+
const md = ctx.claudeMdContent() || '';
|
|
1011
|
+
return /auto.?memory|memory.*manage|remember|persistent.*context/i.test(md);
|
|
1012
|
+
},
|
|
1013
|
+
impact: 'low', rating: 3, category: 'memory',
|
|
1014
|
+
fix: 'Claude Code supports auto-memory for cross-session learning. Mention your memory strategy if relevant.',
|
|
1015
|
+
template: null
|
|
1016
|
+
},
|
|
1017
|
+
|
|
1018
|
+
// --- New checks: sandbox / security depth ---
|
|
1019
|
+
sandboxAwareness: {
|
|
1020
|
+
id: 2013,
|
|
1021
|
+
name: 'Sandbox or isolation mentioned',
|
|
1022
|
+
check: (ctx) => {
|
|
1023
|
+
const md = ctx.claudeMdContent() || '';
|
|
1024
|
+
const settings = ctx.jsonFile('.claude/settings.json') || {};
|
|
1025
|
+
return /sandbox|isolat/i.test(md) || !!settings.sandbox;
|
|
1026
|
+
},
|
|
1027
|
+
impact: 'medium', rating: 3, category: 'security',
|
|
1028
|
+
fix: 'Claude Code supports sandboxed command execution. Consider enabling it for untrusted operations.',
|
|
1029
|
+
template: null
|
|
1030
|
+
},
|
|
1031
|
+
|
|
1032
|
+
denyRulesDepth: {
|
|
1033
|
+
id: 2014,
|
|
1034
|
+
name: 'Deny rules cover 3+ patterns',
|
|
1035
|
+
check: (ctx) => {
|
|
1036
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
1037
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
1038
|
+
const deny = (shared?.permissions?.deny || []).concat(local?.permissions?.deny || []);
|
|
1039
|
+
return deny.length >= 3;
|
|
1040
|
+
},
|
|
1041
|
+
impact: 'high', rating: 4, category: 'security',
|
|
1042
|
+
fix: 'Add at least 3 deny rules: rm -rf, force-push, and .env reads. More patterns = safer Claude.',
|
|
1043
|
+
template: null
|
|
1044
|
+
},
|
|
1045
|
+
|
|
1046
|
+
// --- New checks: git depth ---
|
|
1047
|
+
gitAttributionDecision: {
|
|
1048
|
+
id: 2015,
|
|
1049
|
+
name: 'Git attribution configured',
|
|
1050
|
+
check: (ctx) => {
|
|
1051
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
1052
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
1053
|
+
return shared.attribution !== undefined || local.attribution !== undefined ||
|
|
1054
|
+
shared.includeCoAuthoredBy !== undefined || local.includeCoAuthoredBy !== undefined;
|
|
1055
|
+
},
|
|
1056
|
+
impact: 'low', rating: 3, category: 'git',
|
|
1057
|
+
fix: 'Decide on git attribution: set attribution.commit or includeCoAuthoredBy in settings.',
|
|
1058
|
+
template: null
|
|
1059
|
+
},
|
|
1060
|
+
|
|
1061
|
+
// --- New checks: performance ---
|
|
1062
|
+
effortLevelConfigured: {
|
|
1063
|
+
id: 2016,
|
|
1064
|
+
name: 'Effort level or thinking configuration',
|
|
1065
|
+
check: (ctx) => {
|
|
1066
|
+
const md = ctx.claudeMdContent() || '';
|
|
1067
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
1068
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
1069
|
+
return /effort|thinking/i.test(md) || shared.effortLevel || local.effortLevel ||
|
|
1070
|
+
shared.alwaysThinkingEnabled !== undefined || local.alwaysThinkingEnabled !== undefined;
|
|
1071
|
+
},
|
|
1072
|
+
impact: 'low', rating: 3, category: 'performance',
|
|
1073
|
+
fix: 'Configure effortLevel or mention thinking strategy in CLAUDE.md for task-appropriate reasoning depth.',
|
|
1074
|
+
template: null
|
|
1075
|
+
},
|
|
1076
|
+
|
|
1077
|
+
// --- New checks: workflow depth ---
|
|
1078
|
+
hasSnapshotHistory: {
|
|
1079
|
+
id: 2017,
|
|
1080
|
+
name: 'Audit snapshot history exists',
|
|
1081
|
+
check: (ctx) => {
|
|
1082
|
+
return !!ctx.fileContent('.claude/claudex-setup/snapshots/index.json');
|
|
1083
|
+
},
|
|
1084
|
+
impact: 'low', rating: 3, category: 'workflow',
|
|
1085
|
+
fix: 'Run `npx nerviq --snapshot` to start tracking your setup score over time.',
|
|
1086
|
+
template: null
|
|
1087
|
+
},
|
|
1088
|
+
|
|
1089
|
+
worktreeAwareness: {
|
|
1090
|
+
id: 2018,
|
|
1091
|
+
name: 'Worktree or parallel sessions mentioned',
|
|
1092
|
+
check: (ctx) => {
|
|
1093
|
+
const md = ctx.claudeMdContent() || '';
|
|
1094
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
1095
|
+
return /worktree|parallel.*session/i.test(md) || !!shared.worktree;
|
|
1096
|
+
},
|
|
1097
|
+
impact: 'low', rating: 3, category: 'features',
|
|
1098
|
+
fix: 'Claude Code supports git worktrees for parallel isolated sessions. Mention if relevant.',
|
|
1099
|
+
template: null
|
|
1100
|
+
},
|
|
1101
|
+
|
|
1102
|
+
// --- New checks: prompting depth ---
|
|
1103
|
+
negativeInstructions: {
|
|
1104
|
+
id: 2019,
|
|
1105
|
+
name: 'CLAUDE.md includes "do not" instructions',
|
|
1106
|
+
check: (ctx) => {
|
|
1107
|
+
const md = ctx.claudeMdContent() || '';
|
|
1108
|
+
return /do not|don't|never|avoid|must not/i.test(md);
|
|
1109
|
+
},
|
|
1110
|
+
impact: 'medium', rating: 4, category: 'prompting',
|
|
1111
|
+
fix: 'Add explicit "do not" rules to CLAUDE.md. Negative constraints reduce common mistakes.',
|
|
1112
|
+
template: null
|
|
1113
|
+
},
|
|
1114
|
+
|
|
1115
|
+
outputStyleGuidance: {
|
|
1116
|
+
id: 2020,
|
|
1117
|
+
name: 'CLAUDE.md includes output or style guidance',
|
|
1118
|
+
check: (ctx) => {
|
|
1119
|
+
const md = ctx.claudeMdContent() || '';
|
|
1120
|
+
return /coding style|naming convention|code style|style guide|formatting rules|\bprefer\b.*\b(single|double|tabs|spaces|camel|snake|kebab|named|default|const|let|arrow|function)\b/i.test(md);
|
|
1121
|
+
},
|
|
1122
|
+
impact: 'medium', rating: 3, category: 'prompting',
|
|
1123
|
+
fix: 'Add coding style and naming conventions to CLAUDE.md so Claude matches your project patterns.',
|
|
1124
|
+
template: null
|
|
1125
|
+
},
|
|
1126
|
+
|
|
1127
|
+
// --- New checks: devops depth ---
|
|
1128
|
+
githubActionsOrCI: {
|
|
1129
|
+
id: 2021,
|
|
1130
|
+
name: 'GitHub Actions or CI configured',
|
|
1131
|
+
check: (ctx) => {
|
|
1132
|
+
return ctx.hasDir('.github/workflows') || !!ctx.fileContent('.circleci/config.yml') ||
|
|
1133
|
+
!!ctx.fileContent('.gitlab-ci.yml') || !!ctx.fileContent('Jenkinsfile') ||
|
|
1134
|
+
!!ctx.fileContent('.travis.yml') || !!ctx.fileContent('bitbucket-pipelines.yml');
|
|
1135
|
+
},
|
|
1136
|
+
impact: 'medium', rating: 3, category: 'devops',
|
|
1137
|
+
fix: 'Add CI pipeline for automated testing. Claude Code has a GitHub Action for audit gates.',
|
|
1138
|
+
template: null
|
|
1139
|
+
},
|
|
1140
|
+
|
|
1141
|
+
// --- New checks: depth round 2 ---
|
|
1142
|
+
projectDescriptionInClaudeMd: {
|
|
1143
|
+
id: 2022,
|
|
1144
|
+
name: 'CLAUDE.md describes what the project does',
|
|
1145
|
+
check: (ctx) => {
|
|
1146
|
+
const md = ctx.claudeMdContent() || '';
|
|
1147
|
+
return /what.*does|overview|purpose|about|description|project.*is/i.test(md) && md.length > 100;
|
|
1148
|
+
},
|
|
1149
|
+
impact: 'high', rating: 4, category: 'memory',
|
|
1150
|
+
fix: 'Start CLAUDE.md with a clear project description. Claude needs to know what your project does.',
|
|
1151
|
+
template: null
|
|
1152
|
+
},
|
|
1153
|
+
|
|
1154
|
+
directoryStructureInClaudeMd: {
|
|
1155
|
+
id: 2023,
|
|
1156
|
+
name: 'CLAUDE.md documents directory structure',
|
|
1157
|
+
check: (ctx) => {
|
|
1158
|
+
const md = ctx.claudeMdContent() || '';
|
|
1159
|
+
return /src\/|app\/|lib\/|structure|director|folder/i.test(md);
|
|
1160
|
+
},
|
|
1161
|
+
impact: 'medium', rating: 4, category: 'memory',
|
|
1162
|
+
fix: 'Document your directory structure in CLAUDE.md so Claude navigates your codebase efficiently.',
|
|
1163
|
+
template: null
|
|
1164
|
+
},
|
|
1165
|
+
|
|
1166
|
+
multipleHookTypes: {
|
|
1167
|
+
id: 2024,
|
|
1168
|
+
name: '2+ hook event types configured',
|
|
1169
|
+
check: (ctx) => {
|
|
1170
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
1171
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
1172
|
+
const hooks = { ...(shared.hooks || {}), ...(local.hooks || {}) };
|
|
1173
|
+
return Object.keys(hooks).length >= 2;
|
|
1174
|
+
},
|
|
1175
|
+
impact: 'medium', rating: 3, category: 'automation',
|
|
1176
|
+
fix: 'Add at least 2 hook types (e.g. PostToolUse for linting + SessionStart for initialization).',
|
|
1177
|
+
template: null
|
|
1178
|
+
},
|
|
1179
|
+
|
|
1180
|
+
stopFailureHook: {
|
|
1181
|
+
id: 2025,
|
|
1182
|
+
name: 'StopFailure or error handling hook',
|
|
1183
|
+
check: (ctx) => {
|
|
1184
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
1185
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
1186
|
+
return !!(shared.hooks?.StopFailure || shared.hooks?.Stop || local.hooks?.StopFailure || local.hooks?.Stop);
|
|
1187
|
+
},
|
|
1188
|
+
impact: 'low', rating: 3, category: 'automation',
|
|
1189
|
+
fix: 'Add a StopFailure hook to log errors for debugging. Helps track why Claude stops unexpectedly.',
|
|
1190
|
+
template: null
|
|
1191
|
+
},
|
|
1192
|
+
|
|
1193
|
+
skillUsesPaths: {
|
|
1194
|
+
id: 2026,
|
|
1195
|
+
name: 'At least one skill uses paths for scoping',
|
|
1196
|
+
check: (ctx) => {
|
|
1197
|
+
if (!ctx.hasDir('.claude/skills')) return null;
|
|
1198
|
+
const entries = ctx.dirFiles('.claude/skills');
|
|
1199
|
+
if (entries.length === 0) return null;
|
|
1200
|
+
for (const entry of entries) {
|
|
1201
|
+
// Skills can be files or dirs with SKILL.md inside
|
|
1202
|
+
const direct = ctx.fileContent(`.claude/skills/${entry}`) || '';
|
|
1203
|
+
if (/paths:/i.test(direct)) return true;
|
|
1204
|
+
const nested = ctx.fileContent(`.claude/skills/${entry}/SKILL.md`) || '';
|
|
1205
|
+
if (/paths:/i.test(nested)) return true;
|
|
1206
|
+
}
|
|
1207
|
+
return false;
|
|
1208
|
+
},
|
|
1209
|
+
impact: 'low', rating: 3, category: 'workflow',
|
|
1210
|
+
fix: 'Add paths to skill frontmatter to scope when skills activate (e.g. paths: ["src/**/*.ts"]).',
|
|
1211
|
+
template: null
|
|
1212
|
+
},
|
|
1213
|
+
|
|
1214
|
+
mcpHasEnvConfig: {
|
|
1215
|
+
id: 2027,
|
|
1216
|
+
name: 'MCP servers have environment configuration',
|
|
1217
|
+
check: (ctx) => {
|
|
1218
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
1219
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
1220
|
+
const mcp = ctx.jsonFile('.mcp.json') || {};
|
|
1221
|
+
const allServers = { ...(shared.mcpServers || {}), ...(local.mcpServers || {}), ...(mcp.mcpServers || {}) };
|
|
1222
|
+
if (Object.keys(allServers).length === 0) return null;
|
|
1223
|
+
return Object.values(allServers).some(s => s.env && Object.keys(s.env).length > 0);
|
|
1224
|
+
},
|
|
1225
|
+
impact: 'low', rating: 3, category: 'tools',
|
|
1226
|
+
fix: 'Configure environment variables for MCP servers that need authentication (e.g. GITHUB_TOKEN).',
|
|
1227
|
+
template: null
|
|
1228
|
+
},
|
|
1229
|
+
|
|
1230
|
+
gitIgnoreClaudeLocal: {
|
|
1231
|
+
id: 2028,
|
|
1232
|
+
name: '.gitignore excludes settings.local.json',
|
|
1233
|
+
check: (ctx) => {
|
|
1234
|
+
const gitignore = ctx.fileContent('.gitignore') || '';
|
|
1235
|
+
return /settings\.local\.json|settings\.local/i.test(gitignore);
|
|
1236
|
+
},
|
|
1237
|
+
impact: 'medium', rating: 4, category: 'git',
|
|
1238
|
+
fix: 'Add .claude/settings.local.json to .gitignore. Personal overrides should not be committed.',
|
|
1239
|
+
template: null
|
|
1240
|
+
},
|
|
1241
|
+
|
|
1242
|
+
envExampleExists: {
|
|
1243
|
+
id: 2029,
|
|
1244
|
+
name: '.env.example or .env.template exists',
|
|
1245
|
+
check: (ctx) => {
|
|
1246
|
+
return !!(ctx.fileContent('.env.example') || ctx.fileContent('.env.template') || ctx.fileContent('.env.sample'));
|
|
1247
|
+
},
|
|
1248
|
+
impact: 'low', rating: 3, category: 'hygiene',
|
|
1249
|
+
fix: 'Add .env.example so new developers know which environment variables are needed.',
|
|
1250
|
+
template: null
|
|
1251
|
+
},
|
|
1252
|
+
|
|
1253
|
+
packageJsonHasScripts: {
|
|
1254
|
+
id: 2030,
|
|
1255
|
+
name: 'package.json has dev/test/build scripts',
|
|
1256
|
+
check: (ctx) => {
|
|
1257
|
+
const pkg = ctx.jsonFile('package.json');
|
|
1258
|
+
if (!pkg) return null;
|
|
1259
|
+
const scripts = pkg.scripts || {};
|
|
1260
|
+
const has = (k) => !!scripts[k];
|
|
1261
|
+
return has('test') || has('dev') || has('build') || has('start');
|
|
1262
|
+
},
|
|
1263
|
+
impact: 'medium', rating: 3, category: 'hygiene',
|
|
1264
|
+
fix: 'Add scripts to package.json (test, dev, build). Claude uses these for verification.',
|
|
1265
|
+
template: null
|
|
1266
|
+
},
|
|
1267
|
+
|
|
1268
|
+
typeCheckingConfigured: {
|
|
1269
|
+
id: 2031,
|
|
1270
|
+
name: 'Type checking configured (TypeScript or similar)',
|
|
1271
|
+
check: (ctx) => {
|
|
1272
|
+
return !!(ctx.fileContent('tsconfig.json') || ctx.fileContent('jsconfig.json') ||
|
|
1273
|
+
ctx.fileContent('pyrightconfig.json') || ctx.fileContent('mypy.ini'));
|
|
1274
|
+
},
|
|
1275
|
+
impact: 'medium', rating: 3, category: 'quality',
|
|
1276
|
+
fix: 'Add type checking configuration. Type-safe code produces fewer Claude errors.',
|
|
1277
|
+
template: null
|
|
1278
|
+
},
|
|
1279
|
+
|
|
1280
|
+
noDeprecatedPatterns: {
|
|
1281
|
+
id: 2009,
|
|
1282
|
+
name: 'No deprecated patterns detected',
|
|
1283
|
+
check: (ctx) => {
|
|
1284
|
+
const md = ctx.claudeMdContent();
|
|
1285
|
+
if (!md) return false; // no CLAUDE.md = not passing
|
|
1286
|
+
// Check for patterns deprecated in Claude 4.x
|
|
1287
|
+
const deprecatedPatterns = [
|
|
1288
|
+
/\bprefill\b/i, // deprecated API pattern in 4.6
|
|
1289
|
+
/\bclaude-3-opus\b/i, /\bclaude-3-sonnet\b/i, /\bclaude-3-haiku\b/i, // old model names
|
|
1290
|
+
/\bhuman_prompt\b/i, /\bassistant_prompt\b/i, // old API format
|
|
1291
|
+
];
|
|
1292
|
+
return !deprecatedPatterns.some(p => p.test(md));
|
|
1293
|
+
},
|
|
1294
|
+
impact: 'medium',
|
|
1295
|
+
rating: 3,
|
|
1296
|
+
category: 'quality-deep',
|
|
1297
|
+
fix: 'CLAUDE.md references deprecated patterns (old model names or API formats). Update to current Claude 4.x conventions.',
|
|
1298
|
+
template: null
|
|
1299
|
+
},
|
|
1300
|
+
|
|
1301
|
+
claudeMdQuality: {
|
|
1302
|
+
id: 102502,
|
|
1303
|
+
name: 'CLAUDE.md has substantive content',
|
|
1304
|
+
check: (ctx) => {
|
|
1305
|
+
const md = ctx.claudeMdContent();
|
|
1306
|
+
if (!md) return null;
|
|
1307
|
+
const lines = md.split('\n').filter(l => l.trim());
|
|
1308
|
+
const sections = (md.match(/^##\s/gm) || []).length;
|
|
1309
|
+
const hasCommand = /\b(npm|yarn|pnpm|pytest|go |make |ruff |cargo |dotnet )\b/i.test(md);
|
|
1310
|
+
return lines.length >= 15 && sections >= 2 && hasCommand;
|
|
1311
|
+
},
|
|
1312
|
+
impact: 'medium',
|
|
1313
|
+
rating: 4,
|
|
1314
|
+
category: 'quality-deep',
|
|
1315
|
+
fix: 'CLAUDE.md exists but lacks substance. Add at least 2 sections (## headings) and include your test/build/lint commands.',
|
|
1316
|
+
template: null
|
|
1317
|
+
},
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
// Stack detection
|
|
1321
|
+
const STACKS = {
|
|
1322
|
+
react: { files: ['package.json'], content: { 'package.json': 'react' }, label: 'React' },
|
|
1323
|
+
vue: { files: ['package.json'], content: { 'package.json': 'vue' }, label: 'Vue' },
|
|
1324
|
+
angular: { files: ['angular.json'], content: {}, label: 'Angular' },
|
|
1325
|
+
nextjs: { files: ['next.config'], content: {}, label: 'Next.js' },
|
|
1326
|
+
python: { files: ['requirements.txt', 'setup.py', 'pyproject.toml', 'Pipfile'], content: {}, label: 'Python' },
|
|
1327
|
+
django: { files: ['manage.py'], content: {}, label: 'Django' },
|
|
1328
|
+
fastapi: { files: ['requirements.txt'], content: { 'requirements.txt': 'fastapi' }, label: 'FastAPI' },
|
|
1329
|
+
node: { files: ['package.json'], content: {}, label: 'Node.js' },
|
|
1330
|
+
typescript: { files: ['tsconfig.json'], content: {}, label: 'TypeScript' },
|
|
1331
|
+
rust: { files: ['Cargo.toml'], content: {}, label: 'Rust' },
|
|
1332
|
+
go: { files: ['go.mod'], content: {}, label: 'Go' },
|
|
1333
|
+
docker: { files: ['Dockerfile', 'docker-compose.yml', 'docker-compose.yaml'], content: {}, label: 'Docker' },
|
|
1334
|
+
svelte: { files: ['svelte.config.js'], content: {}, label: 'Svelte' },
|
|
1335
|
+
flutter: { files: ['pubspec.yaml'], content: {}, label: 'Flutter' },
|
|
1336
|
+
ruby: { files: ['Gemfile'], content: {}, label: 'Ruby' },
|
|
1337
|
+
java: { files: ['pom.xml'], content: {}, label: 'Java' },
|
|
1338
|
+
kotlin: { files: ['build.gradle.kts'], content: {}, label: 'Kotlin' },
|
|
1339
|
+
swift: { files: ['Package.swift'], content: {}, label: 'Swift' },
|
|
1340
|
+
terraform: { files: ['main.tf', 'terraform'], content: {}, label: 'Terraform' },
|
|
1341
|
+
kubernetes: { files: ['k8s', 'kubernetes', 'helm'], content: {}, label: 'Kubernetes' },
|
|
1342
|
+
cpp: { files: ['CMakeLists.txt', 'Makefile', '.clang-format'], content: {}, label: 'C++' },
|
|
1343
|
+
bazel: { files: ['BUILD', 'WORKSPACE', 'BUILD.bazel', 'WORKSPACE.bazel'], content: {}, label: 'Bazel' },
|
|
1344
|
+
deno: { files: ['deno.json', 'deno.jsonc', 'deno.lock'], content: {}, label: 'Deno' },
|
|
1345
|
+
bun: { files: ['bun.lockb', 'bunfig.toml'], content: {}, label: 'Bun' },
|
|
1346
|
+
elixir: { files: ['mix.exs'], content: {}, label: 'Elixir' },
|
|
1347
|
+
astro: { files: ['astro.config.mjs', 'astro.config.ts'], content: {}, label: 'Astro' },
|
|
1348
|
+
remix: { files: ['remix.config.js', 'remix.config.ts'], content: {}, label: 'Remix' },
|
|
1349
|
+
nestjs: { files: ['nest-cli.json'], content: {}, label: 'NestJS' },
|
|
1350
|
+
laravel: { files: ['artisan'], content: {}, label: 'Laravel' },
|
|
1351
|
+
dotnet: { files: ['global.json', 'Directory.Build.props'], content: {}, label: '.NET' },
|
|
1352
|
+
};
|
|
1353
|
+
|
|
1354
|
+
module.exports = { TECHNIQUES, STACKS, containsEmbeddedSecret };
|