claudex-setup 1.10.3 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +18 -4
- package/bin/cli.js +91 -23
- package/content/claude-native-integration.md +64 -0
- package/package.json +1 -1
- package/src/activity.js +130 -0
- package/src/domain-packs.js +8 -2
- package/src/techniques.js +191 -12
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,32 @@
|
|
|
15
15
|
- README and docs now reflect snapshot artifacts, governance export, and the Claude-native skill path
|
|
16
16
|
- packaged content and public-facing counts are now aligned with the current CLAUDEX state
|
|
17
17
|
|
|
18
|
+
## [1.12.0] - 2026-04-03
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- 12 new checks (62→74): test coverage, agent tool restrictions, auto-memory, sandbox, deny rule depth, git attribution, effort level, snapshot history, worktree, negative instructions, output style, CI variants
|
|
22
|
+
- 8 new stacks (22→30): Deno, Bun, Elixir, Astro, Remix, NestJS, Laravel, .NET
|
|
23
|
+
- Deeper domain detection: llamaindex, crewai, autogen, ollama for AI/ML; paypal, square, adyen, medusa for ecommerce; chromatic, style-dictionary for design; capacitor, ionic for mobile
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- `githubActionsOrCI` check used non-existent `ctx.hasFile()` — now uses `ctx.fileContent()`
|
|
27
|
+
- `.NET` stack detection no longer uses glob patterns
|
|
28
|
+
|
|
29
|
+
## [1.11.0] - 2026-04-03
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- `history` command — show score timeline from saved snapshots
|
|
33
|
+
- `compare` command — diff latest vs previous snapshot with delta, regressions, improvements
|
|
34
|
+
- `trend --out report.md` — export trend report as shareable markdown
|
|
35
|
+
- `--require A,B` CI flag — exit code 1 if named checks fail (policy guardrails)
|
|
36
|
+
- Agentic DX positioning in README
|
|
37
|
+
- Real results table (4 case studies) in README
|
|
38
|
+
- Claude-native integration guide (skill, hook, agent examples)
|
|
39
|
+
- Trust-first help text reordering
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
- Hook checks (hooksInSettings, preToolUse, postToolUse, sessionStart) now OR across settings.json and settings.local.json
|
|
43
|
+
|
|
18
44
|
## [1.10.2] - 2026-04-02
|
|
19
45
|
|
|
20
46
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# claudex-setup
|
|
2
2
|
|
|
3
|
-
> Score your repo's Claude Code setup against
|
|
3
|
+
> Score your repo's Claude Code setup against 74 checks. See what's missing, apply only what you approve with rollback, and benchmark the impact — without breaking existing config.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/claudex-setup)
|
|
6
6
|
[](https://www.npmjs.com/package/claudex-setup)
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
### What this is
|
|
10
10
|
|
|
11
|
+
- The **Agentic DX layer for Claude Code** — audit, improve, govern, and benchmark how Claude works with your repo
|
|
11
12
|
- A **Claude Code workflow audit and improvement tool** — not an MCP installer, not a code generator
|
|
12
13
|
- Scores your repo 0-100 across CLAUDE.md, hooks, commands, agents, skills, MCP, security, and more
|
|
13
14
|
- Proposes changes as diffs you review — applies only what you approve, with rollback for every change
|
|
@@ -37,6 +38,19 @@ npx claudex-setup --threshold 60 # Fail CI if score is below 60
|
|
|
37
38
|
|
|
38
39
|
No install. No config. No dependencies.
|
|
39
40
|
|
|
41
|
+
## Real Results
|
|
42
|
+
|
|
43
|
+
Tested on 4 real projects — not demos:
|
|
44
|
+
|
|
45
|
+
| Project | Type | Before | After | Delta |
|
|
46
|
+
|---------|------|--------|-------|-------|
|
|
47
|
+
| CLAUDEX | Research engine, Python | 62 | 90 | **+28** |
|
|
48
|
+
| VTCLE | Marketing automation, FastAPI | 46 | 64 | **+18** |
|
|
49
|
+
| Social | Mobile app, React Native | 40 | 48 | **+8** |
|
|
50
|
+
| Polymiro | Prediction system, Python/Docker | 35 | 48 | **+13** |
|
|
51
|
+
|
|
52
|
+
Most common gaps found: missing secrets protection, no deny rules, no mermaid diagram, no hooks in settings.
|
|
53
|
+
|
|
40
54
|
## What You Get
|
|
41
55
|
|
|
42
56
|
```
|
|
@@ -75,7 +89,7 @@ No install. No config. No dependencies.
|
|
|
75
89
|
design: none (0/2)
|
|
76
90
|
devops: none (0/4)
|
|
77
91
|
|
|
78
|
-
29/
|
|
92
|
+
29/74 checks passing
|
|
79
93
|
Next command: npx claudex-setup setup
|
|
80
94
|
```
|
|
81
95
|
|
|
@@ -91,7 +105,7 @@ That prints a compact top-3 quick scan with one clear next command.
|
|
|
91
105
|
|
|
92
106
|
| Command | What it does |
|
|
93
107
|
|---------|-------------|
|
|
94
|
-
| `npx claudex-setup` | **Discover** - Score 0-100 against
|
|
108
|
+
| `npx claudex-setup` | **Discover** - Score 0-100 against 74 checks |
|
|
95
109
|
| `npx claudex-setup discover` | **Discover** - Alias for audit mode |
|
|
96
110
|
| `npx claudex-setup setup` | **Starter** - Smart CLAUDE.md + hooks + commands + agents |
|
|
97
111
|
| `npx claudex-setup starter` | **Starter** - Alias for setup mode |
|
|
@@ -291,7 +305,7 @@ jobs:
|
|
|
291
305
|
runs-on: ubuntu-latest
|
|
292
306
|
steps:
|
|
293
307
|
- uses: actions/checkout@v4
|
|
294
|
-
- uses: DnaFin/claudex-setup@v1.
|
|
308
|
+
- uses: DnaFin/claudex-setup@v1.12.0
|
|
295
309
|
with:
|
|
296
310
|
threshold: 50
|
|
297
311
|
```
|
package/bin/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ const COMMAND_ALIASES = {
|
|
|
19
19
|
suggest: 'suggest-only',
|
|
20
20
|
gov: 'governance',
|
|
21
21
|
};
|
|
22
|
-
const KNOWN_COMMANDS = ['audit', 'setup', 'augment', 'suggest-only', 'plan', 'apply', 'governance', 'benchmark', 'deep-review', 'interactive', 'watch', 'badge', 'insights', 'help', 'version'];
|
|
22
|
+
const KNOWN_COMMANDS = ['audit', 'setup', 'augment', 'suggest-only', 'plan', 'apply', 'governance', 'benchmark', 'deep-review', 'interactive', 'watch', 'badge', 'insights', 'history', 'compare', 'trend', 'help', 'version'];
|
|
23
23
|
|
|
24
24
|
function levenshtein(a, b) {
|
|
25
25
|
const matrix = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0));
|
|
@@ -61,12 +61,13 @@ function parseArgs(rawArgs) {
|
|
|
61
61
|
let only = [];
|
|
62
62
|
let profile = 'safe-write';
|
|
63
63
|
let mcpPacks = [];
|
|
64
|
+
let requireChecks = [];
|
|
64
65
|
let commandSet = false;
|
|
65
66
|
|
|
66
67
|
for (let i = 0; i < rawArgs.length; i++) {
|
|
67
68
|
const arg = rawArgs[i];
|
|
68
69
|
|
|
69
|
-
if (arg === '--threshold' || arg === '--out' || arg === '--plan' || arg === '--only' || arg === '--profile' || arg === '--mcp-pack') {
|
|
70
|
+
if (arg === '--threshold' || arg === '--out' || arg === '--plan' || arg === '--only' || arg === '--profile' || arg === '--mcp-pack' || arg === '--require') {
|
|
70
71
|
const value = rawArgs[i + 1];
|
|
71
72
|
if (!value || value.startsWith('--')) {
|
|
72
73
|
throw new Error(`${arg} requires a value`);
|
|
@@ -77,10 +78,16 @@ function parseArgs(rawArgs) {
|
|
|
77
78
|
if (arg === '--only') only = value.split(',').map(item => item.trim()).filter(Boolean);
|
|
78
79
|
if (arg === '--profile') profile = value.trim();
|
|
79
80
|
if (arg === '--mcp-pack') mcpPacks = value.split(',').map(item => item.trim()).filter(Boolean);
|
|
81
|
+
if (arg === '--require') requireChecks = value.split(',').map(item => item.trim()).filter(Boolean);
|
|
80
82
|
i++;
|
|
81
83
|
continue;
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
if (arg.startsWith('--require=')) {
|
|
87
|
+
requireChecks = arg.split('=').slice(1).join('=').split(',').map(item => item.trim()).filter(Boolean);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
84
91
|
if (arg.startsWith('--threshold=')) {
|
|
85
92
|
threshold = arg.split('=')[1];
|
|
86
93
|
continue;
|
|
@@ -124,35 +131,41 @@ function parseArgs(rawArgs) {
|
|
|
124
131
|
|
|
125
132
|
const normalizedCommand = COMMAND_ALIASES[command] || command;
|
|
126
133
|
|
|
127
|
-
return { flags, command, normalizedCommand, threshold, out, planFile, only, profile, mcpPacks };
|
|
134
|
+
return { flags, command, normalizedCommand, threshold, out, planFile, only, profile, mcpPacks, requireChecks };
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
const HELP = `
|
|
131
138
|
claudex-setup v${version}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
npx claudex-setup
|
|
137
|
-
npx claudex-setup
|
|
138
|
-
npx claudex-setup
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
npx claudex-setup
|
|
142
|
-
npx claudex-setup
|
|
143
|
-
npx claudex-setup
|
|
144
|
-
npx claudex-setup
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
npx claudex-setup
|
|
148
|
-
npx claudex-setup
|
|
149
|
-
npx claudex-setup
|
|
139
|
+
Score your repo's Claude Code setup. Fix gaps safely. Benchmark the impact.
|
|
140
|
+
|
|
141
|
+
Start here (read-only, nothing changes):
|
|
142
|
+
npx claudex-setup Audit your project (10 seconds)
|
|
143
|
+
npx claudex-setup --lite Quick scan: top 3 gaps + next command
|
|
144
|
+
npx claudex-setup augment Repo-aware analysis, no writes
|
|
145
|
+
npx claudex-setup suggest-only Structured report, no writes
|
|
146
|
+
|
|
147
|
+
Plan and apply (when you're ready to change things):
|
|
148
|
+
npx claudex-setup plan Export proposal bundles with previews
|
|
149
|
+
npx claudex-setup apply Apply proposals selectively with rollback
|
|
150
|
+
npx claudex-setup setup Generate starter-safe baseline
|
|
151
|
+
npx claudex-setup setup --auto Apply all generated files without prompts
|
|
152
|
+
|
|
153
|
+
Track progress over time:
|
|
154
|
+
npx claudex-setup history Show score history from saved snapshots
|
|
155
|
+
npx claudex-setup compare Compare latest vs previous snapshot
|
|
156
|
+
npx claudex-setup trend --out r.md Export trend report as markdown
|
|
157
|
+
|
|
158
|
+
Advanced:
|
|
159
|
+
npx claudex-setup governance Permission profiles, hooks, policy packs
|
|
160
|
+
npx claudex-setup benchmark Before/after in isolated temp copy
|
|
161
|
+
npx claudex-setup deep-review AI-powered config review (opt-in, uses API)
|
|
150
162
|
npx claudex-setup interactive Step-by-step guided wizard
|
|
151
|
-
npx claudex-setup watch
|
|
163
|
+
npx claudex-setup watch Live monitoring on config changes
|
|
152
164
|
npx claudex-setup badge Generate shields.io badge markdown
|
|
153
165
|
|
|
154
166
|
Options:
|
|
155
167
|
--threshold N Exit with code 1 if score is below N (useful for CI)
|
|
168
|
+
--require A,B Exit with code 1 if named checks fail (e.g. --require secretsProtection,permissionDeny)
|
|
156
169
|
--out FILE Write JSON or markdown output to a file
|
|
157
170
|
--plan FILE Load a previously exported plan file
|
|
158
171
|
--only A,B Limit plan/apply to selected proposal ids or technique keys
|
|
@@ -227,6 +240,7 @@ async function main() {
|
|
|
227
240
|
only: parsed.only,
|
|
228
241
|
profile: parsed.profile,
|
|
229
242
|
mcpPacks: parsed.mcpPacks,
|
|
243
|
+
require: parsed.requireChecks,
|
|
230
244
|
dir: process.cwd()
|
|
231
245
|
};
|
|
232
246
|
|
|
@@ -261,7 +275,48 @@ async function main() {
|
|
|
261
275
|
}
|
|
262
276
|
|
|
263
277
|
try {
|
|
264
|
-
if (normalizedCommand === '
|
|
278
|
+
if (normalizedCommand === 'history') {
|
|
279
|
+
const { formatHistory } = require('../src/activity');
|
|
280
|
+
console.log('');
|
|
281
|
+
console.log(formatHistory(options.dir));
|
|
282
|
+
console.log('');
|
|
283
|
+
process.exit(0);
|
|
284
|
+
} else if (normalizedCommand === 'compare') {
|
|
285
|
+
const { compareLatest } = require('../src/activity');
|
|
286
|
+
const result = compareLatest(options.dir);
|
|
287
|
+
if (!result) {
|
|
288
|
+
console.log('\n Need at least 2 snapshots to compare. Run `npx claudex-setup --snapshot` twice.\n');
|
|
289
|
+
process.exit(0);
|
|
290
|
+
}
|
|
291
|
+
if (options.json) {
|
|
292
|
+
console.log(JSON.stringify(result, null, 2));
|
|
293
|
+
} else {
|
|
294
|
+
const sign = result.delta.score >= 0 ? '+' : '';
|
|
295
|
+
console.log('');
|
|
296
|
+
console.log(` Previous: ${result.previous.score}/100 (${result.previous.date?.split('T')[0]})`);
|
|
297
|
+
console.log(` Current: ${result.current.score}/100 (${result.current.date?.split('T')[0]})`);
|
|
298
|
+
console.log(` Delta: ${sign}${result.delta.score} points`);
|
|
299
|
+
console.log(` Trend: ${result.trend}`);
|
|
300
|
+
if (result.improvements.length > 0) console.log(` Fixed: ${result.improvements.join(', ')}`);
|
|
301
|
+
if (result.regressions.length > 0) console.log(` New gaps: ${result.regressions.join(', ')}`);
|
|
302
|
+
console.log('');
|
|
303
|
+
}
|
|
304
|
+
process.exit(0);
|
|
305
|
+
} else if (normalizedCommand === 'trend') {
|
|
306
|
+
const { exportTrendReport } = require('../src/activity');
|
|
307
|
+
const report = exportTrendReport(options.dir);
|
|
308
|
+
if (!report) {
|
|
309
|
+
console.log('\n No snapshots found. Run `npx claudex-setup --snapshot` to start tracking.\n');
|
|
310
|
+
process.exit(0);
|
|
311
|
+
}
|
|
312
|
+
if (options.out) {
|
|
313
|
+
require('fs').writeFileSync(options.out, report, 'utf8');
|
|
314
|
+
console.log(`\n Trend report exported to ${options.out}\n`);
|
|
315
|
+
} else {
|
|
316
|
+
console.log(report);
|
|
317
|
+
}
|
|
318
|
+
process.exit(0);
|
|
319
|
+
} else if (normalizedCommand === 'badge') {
|
|
265
320
|
const { getBadgeMarkdown } = require('../src/badge');
|
|
266
321
|
const result = await audit({ ...options, silent: true });
|
|
267
322
|
console.log(getBadgeMarkdown(result.score));
|
|
@@ -406,6 +461,19 @@ async function main() {
|
|
|
406
461
|
}
|
|
407
462
|
process.exit(1);
|
|
408
463
|
}
|
|
464
|
+
if (options.require && options.require.length > 0) {
|
|
465
|
+
const failedRequired = options.require.filter(key => {
|
|
466
|
+
const check = result.results.find(r => r.key === key);
|
|
467
|
+
return !check || check.passed !== true;
|
|
468
|
+
});
|
|
469
|
+
if (failedRequired.length > 0) {
|
|
470
|
+
if (!options.json) {
|
|
471
|
+
console.error(`\n Required checks failed: ${failedRequired.join(', ')}`);
|
|
472
|
+
console.error(' These must pass for CI to succeed.\n');
|
|
473
|
+
}
|
|
474
|
+
process.exit(1);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
409
477
|
}
|
|
410
478
|
} catch (err) {
|
|
411
479
|
console.error(`\n Error: ${err.message}`);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Using claudex-setup from inside Claude Code
|
|
2
|
+
|
|
3
|
+
## Skill: Audit Repo
|
|
4
|
+
|
|
5
|
+
Add this to `.claude/skills/audit-repo.md` in any project:
|
|
6
|
+
|
|
7
|
+
```markdown
|
|
8
|
+
---
|
|
9
|
+
name: audit-repo
|
|
10
|
+
description: Run claudex-setup audit on the current project and show score + top gaps
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Run `npx claudex-setup --json` on the current project directory.
|
|
14
|
+
Parse the JSON output and present:
|
|
15
|
+
1. Score X/100
|
|
16
|
+
2. Top 3 critical/high gaps with fix descriptions
|
|
17
|
+
3. Suggest next command based on score
|
|
18
|
+
|
|
19
|
+
$ARGUMENTS — optional: --lite for quick scan
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Hook: Auto-audit on SessionStart
|
|
23
|
+
|
|
24
|
+
Add to `.claude/settings.json`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"hooks": {
|
|
29
|
+
"SessionStart": [
|
|
30
|
+
{
|
|
31
|
+
"hooks": [
|
|
32
|
+
{
|
|
33
|
+
"type": "command",
|
|
34
|
+
"command": "node -e \"try{const r=require('child_process').execSync('npx claudex-setup --json 2>/dev/null',{timeout:15000}).toString();const d=JSON.parse(r);if(d.score<50)console.log(JSON.stringify({systemMessage:'⚠️ Claude Code setup score: '+d.score+'/100. Consider running: npx claudex-setup --lite'}))}catch(e){console.log('{}')}\"",
|
|
35
|
+
"timeout": 20,
|
|
36
|
+
"statusMessage": "Checking Claude Code setup..."
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Agent: Setup Advisor
|
|
46
|
+
|
|
47
|
+
Add to `.claude/agents/setup-advisor.md`:
|
|
48
|
+
|
|
49
|
+
```markdown
|
|
50
|
+
---
|
|
51
|
+
name: setup-advisor
|
|
52
|
+
description: Analyzes Claude Code setup and recommends improvements
|
|
53
|
+
tools: [Bash, Read, Glob, Grep]
|
|
54
|
+
model: haiku
|
|
55
|
+
maxTurns: 10
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
You are a Claude Code setup advisor.
|
|
59
|
+
|
|
60
|
+
1. Run `npx claudex-setup augment --json` on the current project
|
|
61
|
+
2. Analyze gaps and strengths
|
|
62
|
+
3. Recommend top 5 improvements with rationale
|
|
63
|
+
4. If user approves, guide them through applying changes
|
|
64
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudex-setup",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Score your repo's Claude Code setup against 62 checks. See gaps, apply fixes selectively with rollback, govern hooks and permissions, and benchmark impact — without breaking existing config.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
package/src/activity.js
CHANGED
|
@@ -163,9 +163,139 @@ function writeSnapshotArtifact(dir, snapshotKind, payload, meta = {}) {
|
|
|
163
163
|
};
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
function readSnapshotIndex(dir) {
|
|
167
|
+
const indexPath = path.join(dir, '.claude', 'claudex-setup', 'snapshots', 'index.json');
|
|
168
|
+
if (!fs.existsSync(indexPath)) return [];
|
|
169
|
+
try {
|
|
170
|
+
const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
|
171
|
+
return Array.isArray(entries) ? entries : [];
|
|
172
|
+
} catch {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function getHistory(dir, limit = 20) {
|
|
178
|
+
const entries = readSnapshotIndex(dir);
|
|
179
|
+
return entries
|
|
180
|
+
.filter(e => e.snapshotKind === 'audit')
|
|
181
|
+
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
|
|
182
|
+
.slice(0, limit);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function compareLatest(dir) {
|
|
186
|
+
const audits = getHistory(dir, 2);
|
|
187
|
+
if (audits.length < 2) return null;
|
|
188
|
+
|
|
189
|
+
const current = audits[0];
|
|
190
|
+
const previous = audits[1];
|
|
191
|
+
|
|
192
|
+
const delta = {
|
|
193
|
+
score: (current.summary?.score || 0) - (previous.summary?.score || 0),
|
|
194
|
+
organic: (current.summary?.organicScore || 0) - (previous.summary?.organicScore || 0),
|
|
195
|
+
passed: (current.summary?.passed || 0) - (previous.summary?.passed || 0),
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const regressions = [];
|
|
199
|
+
const improvements = [];
|
|
200
|
+
|
|
201
|
+
const prevKeys = new Set(previous.summary?.topActionKeys || []);
|
|
202
|
+
const currKeys = new Set(current.summary?.topActionKeys || []);
|
|
203
|
+
|
|
204
|
+
for (const key of currKeys) {
|
|
205
|
+
if (!prevKeys.has(key)) regressions.push(key);
|
|
206
|
+
}
|
|
207
|
+
for (const key of prevKeys) {
|
|
208
|
+
if (!currKeys.has(key)) improvements.push(key);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
current: { date: current.createdAt, score: current.summary?.score, passed: current.summary?.passed },
|
|
213
|
+
previous: { date: previous.createdAt, score: previous.summary?.score, passed: previous.summary?.passed },
|
|
214
|
+
delta,
|
|
215
|
+
regressions,
|
|
216
|
+
improvements,
|
|
217
|
+
trend: delta.score > 0 ? 'improving' : delta.score < 0 ? 'regressing' : 'stable',
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function formatHistory(dir) {
|
|
222
|
+
const history = getHistory(dir, 10);
|
|
223
|
+
if (history.length === 0) return 'No snapshots found. Run `npx claudex-setup --snapshot` to save one.';
|
|
224
|
+
|
|
225
|
+
const lines = ['Score history (most recent first):', ''];
|
|
226
|
+
for (const entry of history) {
|
|
227
|
+
const date = entry.createdAt?.split('T')[0] || 'unknown';
|
|
228
|
+
const score = entry.summary?.score ?? '?';
|
|
229
|
+
const passed = entry.summary?.passed ?? '?';
|
|
230
|
+
const total = entry.summary?.checkCount ?? '?';
|
|
231
|
+
lines.push(` ${date} ${score}/100 (${passed}/${total} passing)`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const comparison = compareLatest(dir);
|
|
235
|
+
if (comparison) {
|
|
236
|
+
lines.push('');
|
|
237
|
+
const sign = comparison.delta.score >= 0 ? '+' : '';
|
|
238
|
+
lines.push(` Trend: ${comparison.trend} (${sign}${comparison.delta.score} since previous)`);
|
|
239
|
+
if (comparison.improvements.length > 0) {
|
|
240
|
+
lines.push(` Fixed: ${comparison.improvements.join(', ')}`);
|
|
241
|
+
}
|
|
242
|
+
if (comparison.regressions.length > 0) {
|
|
243
|
+
lines.push(` New gaps: ${comparison.regressions.join(', ')}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return lines.join('\n');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function exportTrendReport(dir) {
|
|
251
|
+
const history = getHistory(dir, 50);
|
|
252
|
+
if (history.length === 0) return null;
|
|
253
|
+
|
|
254
|
+
const comparison = compareLatest(dir);
|
|
255
|
+
const lines = [
|
|
256
|
+
'# Claude Code Setup Trend Report',
|
|
257
|
+
'',
|
|
258
|
+
`**Project:** ${path.basename(dir)}`,
|
|
259
|
+
`**Generated:** ${new Date().toISOString().split('T')[0]}`,
|
|
260
|
+
`**Snapshots:** ${history.length}`,
|
|
261
|
+
'',
|
|
262
|
+
'## Score History',
|
|
263
|
+
'',
|
|
264
|
+
'| Date | Score | Passed | Checks |',
|
|
265
|
+
'|------|-------|--------|--------|',
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
for (const entry of history) {
|
|
269
|
+
const date = entry.createdAt?.split('T')[0] || '?';
|
|
270
|
+
lines.push(`| ${date} | ${entry.summary?.score ?? '?'}/100 | ${entry.summary?.passed ?? '?'} | ${entry.summary?.checkCount ?? '?'} |`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (comparison) {
|
|
274
|
+
lines.push('');
|
|
275
|
+
lines.push('## Latest Comparison');
|
|
276
|
+
lines.push('');
|
|
277
|
+
lines.push(`- **Previous:** ${comparison.previous.score}/100 (${comparison.previous.date?.split('T')[0]})`);
|
|
278
|
+
lines.push(`- **Current:** ${comparison.current.score}/100 (${comparison.current.date?.split('T')[0]})`);
|
|
279
|
+
lines.push(`- **Delta:** ${comparison.delta.score >= 0 ? '+' : ''}${comparison.delta.score} points`);
|
|
280
|
+
lines.push(`- **Trend:** ${comparison.trend}`);
|
|
281
|
+
if (comparison.improvements.length > 0) lines.push(`- **Fixed:** ${comparison.improvements.join(', ')}`);
|
|
282
|
+
if (comparison.regressions.length > 0) lines.push(`- **New gaps:** ${comparison.regressions.join(', ')}`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
lines.push('');
|
|
286
|
+
lines.push(`---`);
|
|
287
|
+
lines.push(`*Generated by claudex-setup v${version}*`);
|
|
288
|
+
return lines.join('\n');
|
|
289
|
+
}
|
|
290
|
+
|
|
166
291
|
module.exports = {
|
|
167
292
|
ensureArtifactDirs,
|
|
168
293
|
writeActivityArtifact,
|
|
169
294
|
writeRollbackArtifact,
|
|
170
295
|
writeSnapshotArtifact,
|
|
296
|
+
readSnapshotIndex,
|
|
297
|
+
getHistory,
|
|
298
|
+
compareLatest,
|
|
299
|
+
formatHistory,
|
|
300
|
+
exportTrendReport,
|
|
171
301
|
};
|
package/src/domain-packs.js
CHANGED
|
@@ -231,6 +231,7 @@ function detectDomainPacks(ctx, stacks, assets = null) {
|
|
|
231
231
|
|
|
232
232
|
// Mobile detection
|
|
233
233
|
const isMobile = deps['react-native'] || deps.expo || deps.flutter ||
|
|
234
|
+
deps['@capacitor/core'] || deps['@ionic/angular'] || deps['@ionic/react'] ||
|
|
234
235
|
ctx.files.includes('Podfile') || ctx.files.includes('build.gradle') ||
|
|
235
236
|
ctx.files.includes('build.gradle.kts') || ctx.hasDir('ios') || ctx.hasDir('android');
|
|
236
237
|
if (isMobile) {
|
|
@@ -257,7 +258,9 @@ function detectDomainPacks(ctx, stacks, assets = null) {
|
|
|
257
258
|
|
|
258
259
|
// E-commerce detection
|
|
259
260
|
const isEcommerce = deps.stripe || deps['@stripe/stripe-js'] || deps.shopify || deps['@shopify/shopify-api'] ||
|
|
260
|
-
deps.woocommerce ||
|
|
261
|
+
deps.woocommerce || deps.paypal || deps['@paypal/react-paypal-js'] || deps.square || deps['@adyen/adyen-web'] ||
|
|
262
|
+
deps.medusa || deps.saleor ||
|
|
263
|
+
ctx.hasDir('products') || ctx.hasDir('checkout') || ctx.hasDir('cart');
|
|
261
264
|
if (isEcommerce) {
|
|
262
265
|
addMatch('ecommerce', [
|
|
263
266
|
'Detected e-commerce dependencies or storefront structure.',
|
|
@@ -269,6 +272,8 @@ function detectDomainPacks(ctx, stacks, assets = null) {
|
|
|
269
272
|
// AI/ML detection
|
|
270
273
|
const isAiMl = deps.langchain || deps['@langchain/core'] || deps.openai || deps.anthropic ||
|
|
271
274
|
deps['@anthropic-ai/sdk'] || deps.transformers || deps.torch || deps.tensorflow ||
|
|
275
|
+
deps.llamaindex || deps['llama-index'] || deps.crewai || deps.autogen ||
|
|
276
|
+
deps['@ai-sdk/core'] || deps.ollama ||
|
|
272
277
|
ctx.hasDir('chains') || ctx.hasDir('agents') || ctx.hasDir('models') || ctx.hasDir('prompts');
|
|
273
278
|
if (isAiMl && !hasData) {
|
|
274
279
|
addMatch('ai-ml', [
|
|
@@ -292,8 +297,9 @@ function detectDomainPacks(ctx, stacks, assets = null) {
|
|
|
292
297
|
|
|
293
298
|
// Design system detection
|
|
294
299
|
const isDesignSystem = deps.storybook || deps['@storybook/react'] || deps['@storybook/vue3'] ||
|
|
300
|
+
deps.chromatic || deps['style-dictionary'] || deps['@tokens-studio/sd-transforms'] ||
|
|
295
301
|
ctx.hasDir('tokens') || ctx.hasDir('design-tokens') ||
|
|
296
|
-
(ctx.hasDir('components') && ctx.hasDir('.storybook'));
|
|
302
|
+
ctx.hasDir('.storybook') || (ctx.hasDir('components') && ctx.hasDir('.storybook'));
|
|
297
303
|
if (isDesignSystem) {
|
|
298
304
|
addMatch('design-system', [
|
|
299
305
|
'Detected design system or component library signals.',
|
package/src/techniques.js
CHANGED
|
@@ -425,9 +425,11 @@ const TECHNIQUES = {
|
|
|
425
425
|
id: 8801,
|
|
426
426
|
name: 'Hooks configured in settings',
|
|
427
427
|
check: (ctx) => {
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
428
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
429
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
430
|
+
const hasSharedHooks = shared && shared.hooks && Object.keys(shared.hooks).length > 0;
|
|
431
|
+
const hasLocalHooks = local && local.hooks && Object.keys(local.hooks).length > 0;
|
|
432
|
+
return hasSharedHooks || hasLocalHooks;
|
|
431
433
|
},
|
|
432
434
|
impact: 'high',
|
|
433
435
|
rating: 4,
|
|
@@ -440,9 +442,9 @@ const TECHNIQUES = {
|
|
|
440
442
|
id: 8802,
|
|
441
443
|
name: 'PreToolUse hook configured',
|
|
442
444
|
check: (ctx) => {
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
return !!
|
|
445
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
446
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
447
|
+
return !!(shared?.hooks?.PreToolUse || local?.hooks?.PreToolUse);
|
|
446
448
|
},
|
|
447
449
|
impact: 'high',
|
|
448
450
|
rating: 4,
|
|
@@ -455,9 +457,9 @@ const TECHNIQUES = {
|
|
|
455
457
|
id: 8803,
|
|
456
458
|
name: 'PostToolUse hook configured',
|
|
457
459
|
check: (ctx) => {
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
return !!
|
|
460
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
461
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
462
|
+
return !!(shared?.hooks?.PostToolUse || local?.hooks?.PostToolUse);
|
|
461
463
|
},
|
|
462
464
|
impact: 'high',
|
|
463
465
|
rating: 4,
|
|
@@ -470,9 +472,10 @@ const TECHNIQUES = {
|
|
|
470
472
|
id: 8804,
|
|
471
473
|
name: 'SessionStart hook configured',
|
|
472
474
|
check: (ctx) => {
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
return
|
|
475
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
476
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
477
|
+
if (!(shared?.hooks || local?.hooks)) return false;
|
|
478
|
+
return !!(shared?.hooks?.SessionStart || local?.hooks?.SessionStart);
|
|
476
479
|
},
|
|
477
480
|
impact: 'medium',
|
|
478
481
|
rating: 4,
|
|
@@ -943,6 +946,174 @@ const TECHNIQUES = {
|
|
|
943
946
|
template: null
|
|
944
947
|
},
|
|
945
948
|
|
|
949
|
+
// --- New checks: testing depth ---
|
|
950
|
+
testCoverage: {
|
|
951
|
+
id: 2010,
|
|
952
|
+
name: 'Test coverage or strategy mentioned',
|
|
953
|
+
check: (ctx) => {
|
|
954
|
+
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
955
|
+
return /coverage|test.*strateg|e2e|integration test|unit test/i.test(md);
|
|
956
|
+
},
|
|
957
|
+
impact: 'medium', rating: 3, category: 'quality',
|
|
958
|
+
fix: 'Mention your testing strategy in CLAUDE.md (unit, integration, E2E, coverage targets).',
|
|
959
|
+
template: null
|
|
960
|
+
},
|
|
961
|
+
|
|
962
|
+
// --- New checks: agent depth ---
|
|
963
|
+
agentHasAllowedTools: {
|
|
964
|
+
id: 2011,
|
|
965
|
+
name: 'At least one agent restricts tools',
|
|
966
|
+
check: (ctx) => {
|
|
967
|
+
if (!ctx.hasDir('.claude/agents')) return null;
|
|
968
|
+
const files = ctx.dirFiles('.claude/agents');
|
|
969
|
+
if (files.length === 0) return null;
|
|
970
|
+
for (const f of files) {
|
|
971
|
+
const content = ctx.fileContent(`.claude/agents/${f}`) || '';
|
|
972
|
+
if (/tools:\s*\[/.test(content)) return true;
|
|
973
|
+
}
|
|
974
|
+
return false;
|
|
975
|
+
},
|
|
976
|
+
impact: 'medium', rating: 3, category: 'workflow',
|
|
977
|
+
fix: 'Add a tools restriction to agent frontmatter (e.g. tools: [Read, Grep]) for safer delegation.',
|
|
978
|
+
template: null
|
|
979
|
+
},
|
|
980
|
+
|
|
981
|
+
// --- New checks: memory / auto-memory ---
|
|
982
|
+
autoMemoryAwareness: {
|
|
983
|
+
id: 2012,
|
|
984
|
+
name: 'Auto-memory or memory management mentioned',
|
|
985
|
+
check: (ctx) => {
|
|
986
|
+
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
987
|
+
return /auto.?memory|memory.*manage|remember|persistent.*context/i.test(md);
|
|
988
|
+
},
|
|
989
|
+
impact: 'low', rating: 3, category: 'memory',
|
|
990
|
+
fix: 'Claude Code supports auto-memory for cross-session learning. Mention your memory strategy if relevant.',
|
|
991
|
+
template: null
|
|
992
|
+
},
|
|
993
|
+
|
|
994
|
+
// --- New checks: sandbox / security depth ---
|
|
995
|
+
sandboxAwareness: {
|
|
996
|
+
id: 2013,
|
|
997
|
+
name: 'Sandbox or isolation mentioned',
|
|
998
|
+
check: (ctx) => {
|
|
999
|
+
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
1000
|
+
const settings = ctx.jsonFile('.claude/settings.json') || {};
|
|
1001
|
+
return /sandbox|isolat/i.test(md) || !!settings.sandbox;
|
|
1002
|
+
},
|
|
1003
|
+
impact: 'medium', rating: 3, category: 'security',
|
|
1004
|
+
fix: 'Claude Code supports sandboxed command execution. Consider enabling it for untrusted operations.',
|
|
1005
|
+
template: null
|
|
1006
|
+
},
|
|
1007
|
+
|
|
1008
|
+
denyRulesDepth: {
|
|
1009
|
+
id: 2014,
|
|
1010
|
+
name: 'Deny rules cover 3+ patterns',
|
|
1011
|
+
check: (ctx) => {
|
|
1012
|
+
const shared = ctx.jsonFile('.claude/settings.json');
|
|
1013
|
+
const local = ctx.jsonFile('.claude/settings.local.json');
|
|
1014
|
+
const deny = (shared?.permissions?.deny || []).concat(local?.permissions?.deny || []);
|
|
1015
|
+
return deny.length >= 3;
|
|
1016
|
+
},
|
|
1017
|
+
impact: 'high', rating: 4, category: 'security',
|
|
1018
|
+
fix: 'Add at least 3 deny rules: rm -rf, force-push, and .env reads. More patterns = safer Claude.',
|
|
1019
|
+
template: null
|
|
1020
|
+
},
|
|
1021
|
+
|
|
1022
|
+
// --- New checks: git depth ---
|
|
1023
|
+
gitAttributionDecision: {
|
|
1024
|
+
id: 2015,
|
|
1025
|
+
name: 'Git attribution configured',
|
|
1026
|
+
check: (ctx) => {
|
|
1027
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
1028
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
1029
|
+
return shared.attribution !== undefined || local.attribution !== undefined ||
|
|
1030
|
+
shared.includeCoAuthoredBy !== undefined || local.includeCoAuthoredBy !== undefined;
|
|
1031
|
+
},
|
|
1032
|
+
impact: 'low', rating: 3, category: 'git',
|
|
1033
|
+
fix: 'Decide on git attribution: set attribution.commit or includeCoAuthoredBy in settings.',
|
|
1034
|
+
template: null
|
|
1035
|
+
},
|
|
1036
|
+
|
|
1037
|
+
// --- New checks: performance ---
|
|
1038
|
+
effortLevelConfigured: {
|
|
1039
|
+
id: 2016,
|
|
1040
|
+
name: 'Effort level or thinking configuration',
|
|
1041
|
+
check: (ctx) => {
|
|
1042
|
+
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
1043
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
1044
|
+
const local = ctx.jsonFile('.claude/settings.local.json') || {};
|
|
1045
|
+
return /effort|thinking/i.test(md) || shared.effortLevel || local.effortLevel ||
|
|
1046
|
+
shared.alwaysThinkingEnabled !== undefined || local.alwaysThinkingEnabled !== undefined;
|
|
1047
|
+
},
|
|
1048
|
+
impact: 'low', rating: 3, category: 'performance',
|
|
1049
|
+
fix: 'Configure effortLevel or mention thinking strategy in CLAUDE.md for task-appropriate reasoning depth.',
|
|
1050
|
+
template: null
|
|
1051
|
+
},
|
|
1052
|
+
|
|
1053
|
+
// --- New checks: workflow depth ---
|
|
1054
|
+
hasSnapshotHistory: {
|
|
1055
|
+
id: 2017,
|
|
1056
|
+
name: 'Audit snapshot history exists',
|
|
1057
|
+
check: (ctx) => {
|
|
1058
|
+
return !!ctx.fileContent('.claude/claudex-setup/snapshots/index.json');
|
|
1059
|
+
},
|
|
1060
|
+
impact: 'low', rating: 3, category: 'workflow',
|
|
1061
|
+
fix: 'Run `npx claudex-setup --snapshot` to start tracking your setup score over time.',
|
|
1062
|
+
template: null
|
|
1063
|
+
},
|
|
1064
|
+
|
|
1065
|
+
worktreeAwareness: {
|
|
1066
|
+
id: 2018,
|
|
1067
|
+
name: 'Worktree or parallel sessions mentioned',
|
|
1068
|
+
check: (ctx) => {
|
|
1069
|
+
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
1070
|
+
const shared = ctx.jsonFile('.claude/settings.json') || {};
|
|
1071
|
+
return /worktree|parallel.*session/i.test(md) || !!shared.worktree;
|
|
1072
|
+
},
|
|
1073
|
+
impact: 'low', rating: 3, category: 'features',
|
|
1074
|
+
fix: 'Claude Code supports git worktrees for parallel isolated sessions. Mention if relevant.',
|
|
1075
|
+
template: null
|
|
1076
|
+
},
|
|
1077
|
+
|
|
1078
|
+
// --- New checks: prompting depth ---
|
|
1079
|
+
negativeInstructions: {
|
|
1080
|
+
id: 2019,
|
|
1081
|
+
name: 'CLAUDE.md includes "do not" instructions',
|
|
1082
|
+
check: (ctx) => {
|
|
1083
|
+
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
1084
|
+
return /do not|don't|never|avoid|must not/i.test(md);
|
|
1085
|
+
},
|
|
1086
|
+
impact: 'medium', rating: 4, category: 'prompting',
|
|
1087
|
+
fix: 'Add explicit "do not" rules to CLAUDE.md. Negative constraints reduce common mistakes.',
|
|
1088
|
+
template: null
|
|
1089
|
+
},
|
|
1090
|
+
|
|
1091
|
+
outputStyleGuidance: {
|
|
1092
|
+
id: 2020,
|
|
1093
|
+
name: 'CLAUDE.md includes output or style guidance',
|
|
1094
|
+
check: (ctx) => {
|
|
1095
|
+
const md = ctx.fileContent('CLAUDE.md') || '';
|
|
1096
|
+
return /style|format|convention|naming|pattern|prefer/i.test(md);
|
|
1097
|
+
},
|
|
1098
|
+
impact: 'medium', rating: 3, category: 'prompting',
|
|
1099
|
+
fix: 'Add coding style and naming conventions to CLAUDE.md so Claude matches your project patterns.',
|
|
1100
|
+
template: null
|
|
1101
|
+
},
|
|
1102
|
+
|
|
1103
|
+
// --- New checks: devops depth ---
|
|
1104
|
+
githubActionsOrCI: {
|
|
1105
|
+
id: 2021,
|
|
1106
|
+
name: 'GitHub Actions or CI configured',
|
|
1107
|
+
check: (ctx) => {
|
|
1108
|
+
return ctx.hasDir('.github/workflows') || !!ctx.fileContent('.circleci/config.yml') ||
|
|
1109
|
+
!!ctx.fileContent('.gitlab-ci.yml') || !!ctx.fileContent('Jenkinsfile') ||
|
|
1110
|
+
!!ctx.fileContent('.travis.yml') || !!ctx.fileContent('bitbucket-pipelines.yml');
|
|
1111
|
+
},
|
|
1112
|
+
impact: 'medium', rating: 3, category: 'devops',
|
|
1113
|
+
fix: 'Add CI pipeline for automated testing. Claude Code has a GitHub Action for audit gates.',
|
|
1114
|
+
template: null
|
|
1115
|
+
},
|
|
1116
|
+
|
|
946
1117
|
noDeprecatedPatterns: {
|
|
947
1118
|
id: 2009,
|
|
948
1119
|
name: 'No deprecated patterns detected',
|
|
@@ -989,6 +1160,14 @@ const STACKS = {
|
|
|
989
1160
|
kubernetes: { files: ['k8s', 'kubernetes', 'helm'], content: {}, label: 'Kubernetes' },
|
|
990
1161
|
cpp: { files: ['CMakeLists.txt', 'Makefile', '.clang-format'], content: {}, label: 'C++' },
|
|
991
1162
|
bazel: { files: ['BUILD', 'WORKSPACE', 'BUILD.bazel', 'WORKSPACE.bazel'], content: {}, label: 'Bazel' },
|
|
1163
|
+
deno: { files: ['deno.json', 'deno.jsonc', 'deno.lock'], content: {}, label: 'Deno' },
|
|
1164
|
+
bun: { files: ['bun.lockb', 'bunfig.toml'], content: {}, label: 'Bun' },
|
|
1165
|
+
elixir: { files: ['mix.exs'], content: {}, label: 'Elixir' },
|
|
1166
|
+
astro: { files: ['astro.config.mjs', 'astro.config.ts'], content: {}, label: 'Astro' },
|
|
1167
|
+
remix: { files: ['remix.config.js', 'remix.config.ts'], content: {}, label: 'Remix' },
|
|
1168
|
+
nestjs: { files: ['nest-cli.json'], content: {}, label: 'NestJS' },
|
|
1169
|
+
laravel: { files: ['artisan'], content: {}, label: 'Laravel' },
|
|
1170
|
+
dotnet: { files: ['global.json', 'Directory.Build.props'], content: {}, label: '.NET' },
|
|
992
1171
|
};
|
|
993
1172
|
|
|
994
1173
|
module.exports = { TECHNIQUES, STACKS };
|