claudex-setup 1.10.3 → 1.11.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 +15 -0
- package/README.md +15 -1
- 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/techniques.js +15 -12
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,21 @@
|
|
|
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.11.0] - 2026-04-03
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- `history` command — show score timeline from saved snapshots
|
|
22
|
+
- `compare` command — diff latest vs previous snapshot with delta, regressions, improvements
|
|
23
|
+
- `trend --out report.md` — export trend report as shareable markdown
|
|
24
|
+
- `--require A,B` CI flag — exit code 1 if named checks fail (policy guardrails)
|
|
25
|
+
- Agentic DX positioning in README
|
|
26
|
+
- Real results table (4 case studies) in README
|
|
27
|
+
- Claude-native integration guide (skill, hook, agent examples)
|
|
28
|
+
- Trust-first help text reordering
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- Hook checks (hooksInSettings, preToolUse, postToolUse, sessionStart) now OR across settings.json and settings.local.json
|
|
32
|
+
|
|
18
33
|
## [1.10.2] - 2026-04-02
|
|
19
34
|
|
|
20
35
|
### Fixed
|
package/README.md
CHANGED
|
@@ -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
|
```
|
|
@@ -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.11.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.11.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/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,
|