claudex-setup 1.16.0 → 1.16.2
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 +21 -0
- package/README.md +52 -23
- package/bin/cli.js +92 -5
- package/content/launch-posts.md +159 -92
- package/package.json +2 -2
- package/src/activity.js +195 -1
- package/src/analyze.js +11 -6
- package/src/audit.js +49 -14
- package/src/context.js +106 -0
- package/src/deep-review.js +95 -68
- package/src/domain-packs.js +13 -4
- package/src/index.js +4 -0
- package/src/mcp-packs.js +16 -0
- package/src/secret-patterns.js +30 -0
- package/src/techniques.js +4 -2
- package/src/watch.js +170 -42
package/content/launch-posts.md
CHANGED
|
@@ -1,159 +1,226 @@
|
|
|
1
|
-
# Launch Posts —
|
|
1
|
+
# Launch Posts — Proof-Backed Distribution Assets
|
|
2
|
+
|
|
3
|
+
**Status:** Complete — every asset below is anchored in measured proof, a canonical artifact, or a verified runtime surface
|
|
4
|
+
**Date:** 2026-04-03
|
|
5
|
+
|
|
6
|
+
## Shared Proof Anchors
|
|
7
|
+
|
|
8
|
+
Use these links as the canonical sources behind public claims:
|
|
9
|
+
|
|
10
|
+
- Proof artifact index: https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/README.md
|
|
11
|
+
- CLAUDEX self-dogfood trace: https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/claudex-self-dogfood-proof-trace-2026-04-03.md
|
|
12
|
+
- VTCLE case study: https://github.com/DnaFin/claudex/blob/main/research/case-study-vtcle-2026-04-03.md
|
|
13
|
+
- Social case study: https://github.com/DnaFin/claudex/blob/main/research/case-study-social-2026-04-03.md
|
|
14
|
+
- Polymiro case study: https://github.com/DnaFin/claudex/blob/main/research/case-study-polymiro-2026-04-03.md
|
|
15
|
+
- Public proof metrics source: https://github.com/DnaFin/claudex/blob/main/research/claudex-proof-metrics-source-2026-04-03.md
|
|
16
|
+
|
|
17
|
+
Measured-result boundary to preserve:
|
|
18
|
+
|
|
19
|
+
- before/after scores were measured with `claudex-setup@1.10.3` on `2026-04-03`
|
|
20
|
+
- current npm latest is `1.16.1`
|
|
21
|
+
- current product surface is `85 checks`
|
|
2
22
|
|
|
3
23
|
## Post 1: Reddit r/ClaudeAI
|
|
4
24
|
|
|
5
|
-
**Title:** I built a
|
|
25
|
+
**Title:** I built a CLI that audits your Claude Code setup — 85 checks, measured on 4 real repos
|
|
6
26
|
|
|
7
27
|
**Body:**
|
|
8
|
-
|
|
28
|
+
I built a zero-dependency CLI that audits how well a repo is set up for Claude Code.
|
|
9
29
|
|
|
10
|
-
|
|
30
|
+
It checks `85` things across `CLAUDE.md`, hooks, commands, agents, skills, MCP config, permissions, diagrams, and verification loops.
|
|
11
31
|
|
|
12
|
-
|
|
32
|
+
Measured on `2026-04-03` with `claudex-setup@1.10.3`:
|
|
33
|
+
- CLAUDEX: `62 -> 90`
|
|
34
|
+
- VTCLE: `46 -> 64`
|
|
35
|
+
- Social: `40 -> 48`
|
|
36
|
+
- Polymiro: `35 -> 48`
|
|
37
|
+
|
|
38
|
+
```bash
|
|
13
39
|
npx claudex-setup
|
|
14
40
|
```
|
|
15
41
|
|
|
16
|
-
|
|
42
|
+
It starts trust-first:
|
|
43
|
+
- audit first
|
|
44
|
+
- plan / suggest-only before writes
|
|
45
|
+
- apply only what you approve
|
|
46
|
+
- rollback artifacts for every applied batch
|
|
17
47
|
|
|
18
|
-
Zero dependencies. No API keys. Runs
|
|
48
|
+
Zero dependencies. No API keys. Runs local.
|
|
19
49
|
|
|
20
50
|
GitHub: https://github.com/DnaFin/claudex-setup
|
|
21
51
|
|
|
22
|
-
|
|
52
|
+
Proof and case studies:
|
|
53
|
+
- https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/README.md
|
|
54
|
+
- https://github.com/DnaFin/claudex/blob/main/research/case-study-vtcle-2026-04-03.md
|
|
55
|
+
- https://github.com/DnaFin/claudex/blob/main/research/case-study-social-2026-04-03.md
|
|
56
|
+
- https://github.com/DnaFin/claudex/blob/main/research/case-study-polymiro-2026-04-03.md
|
|
57
|
+
|
|
58
|
+
Would love feedback on what checks or rollout surfaces are still missing.
|
|
59
|
+
|
|
60
|
+
**Evidence anchor:** proof artifact index + 3 external case studies + current proof source
|
|
23
61
|
|
|
24
62
|
---
|
|
25
63
|
|
|
26
64
|
## Post 2: Reddit r/ChatGPTCoding
|
|
27
65
|
|
|
28
|
-
**Title:**
|
|
66
|
+
**Title:** Most Claude Code repos are missing the safety layer, not the model
|
|
29
67
|
|
|
30
68
|
**Body:**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
69
|
+
The interesting problem with Claude Code is not "can it write code?".
|
|
70
|
+
It's "is the repo actually set up so Claude can work safely and predictably?".
|
|
71
|
+
|
|
72
|
+
I built `claudex-setup` to audit that surface:
|
|
73
|
+
- `85` checks
|
|
74
|
+
- zero dependencies
|
|
75
|
+
- local-only by default
|
|
76
|
+
- trust-first flow: audit -> plan -> apply -> rollback
|
|
77
|
+
|
|
78
|
+
Measured on 4 real repos:
|
|
79
|
+
- FastAPI repo: `46 -> 64`
|
|
80
|
+
- React Native repo: `40 -> 48`
|
|
81
|
+
- Python/Docker repo: `35 -> 48`
|
|
82
|
+
- research engine repo: `62 -> 90`
|
|
83
|
+
|
|
84
|
+
```bash
|
|
42
85
|
npx claudex-setup
|
|
43
86
|
```
|
|
44
87
|
|
|
45
|
-
|
|
88
|
+
The most common misses were not exotic:
|
|
89
|
+
- no deny rules
|
|
90
|
+
- no secrets protection
|
|
91
|
+
- no mermaid architecture
|
|
92
|
+
- no hooks registered in settings
|
|
93
|
+
|
|
94
|
+
Proof:
|
|
95
|
+
https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/README.md
|
|
46
96
|
|
|
47
|
-
|
|
97
|
+
**Evidence anchor:** measured before/after traces + common gap summary from public proof set
|
|
48
98
|
|
|
49
99
|
---
|
|
50
100
|
|
|
51
101
|
## Post 3: Dev.to Article
|
|
52
102
|
|
|
53
|
-
**Title:**
|
|
103
|
+
**Title:** What 4 Real Repos Taught Me About Claude Code Readiness
|
|
54
104
|
|
|
55
105
|
**Body (excerpt):**
|
|
56
|
-
I
|
|
106
|
+
I tested `claudex-setup` on 4 real repos and the pattern was clear:
|
|
57
107
|
|
|
58
|
-
|
|
108
|
+
- the best teams still miss permission deny rules
|
|
109
|
+
- mature repos often have hooks in files but not actually registered
|
|
110
|
+
- non-standard settings formats are a real adoption trap
|
|
111
|
+
- shared `settings.json` matters more than personal local overrides
|
|
59
112
|
|
|
60
|
-
|
|
113
|
+
Measured on `2026-04-03` with `claudex-setup@1.10.3`:
|
|
114
|
+
- CLAUDEX: `62 -> 90`
|
|
115
|
+
- VTCLE: `46 -> 64`
|
|
116
|
+
- Social: `40 -> 48`
|
|
117
|
+
- Polymiro: `35 -> 48`
|
|
61
118
|
|
|
62
|
-
|
|
119
|
+
The product today is strongest as:
|
|
63
120
|
|
|
64
|
-
|
|
121
|
+
`audit -> plan -> safe apply -> governance -> benchmark`
|
|
65
122
|
|
|
66
|
-
|
|
123
|
+
Not a code generator. Not an MCP installer. A trust layer for Claude Code repos.
|
|
67
124
|
|
|
68
|
-
|
|
125
|
+
Proof packet:
|
|
126
|
+
https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/README.md
|
|
69
127
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
7. **XML tags** — `<constraints>`, `<validation>` in CLAUDE.md = unambiguous instructions.
|
|
73
|
-
|
|
74
|
-
8. **Custom agents** — Security reviewer, test writer — specialized subagents for focused tasks.
|
|
75
|
-
|
|
76
|
-
9. **Skills** — Domain-specific workflows that load on demand, not every session.
|
|
77
|
-
|
|
78
|
-
10. **MCP servers** — Connect Claude to your database, ticket system, Slack.
|
|
79
|
-
|
|
80
|
-
I packaged this into a CLI that checks your project:
|
|
81
|
-
```
|
|
82
|
-
npx claudex-setup
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Full catalog: https://github.com/DnaFin/claudex-setup
|
|
128
|
+
**Evidence anchor:** proof artifact index + case-study docs + current proof source
|
|
86
129
|
|
|
87
130
|
---
|
|
88
131
|
|
|
89
132
|
## Post 4: Twitter/X Thread
|
|
90
133
|
|
|
91
134
|
**Tweet 1:**
|
|
92
|
-
I
|
|
93
|
-
|
|
94
|
-
Most projects use less than 5% of what Claude Code can do.
|
|
135
|
+
I built a zero-dependency CLI that audits Claude Code readiness across `85` checks.
|
|
95
136
|
|
|
96
|
-
|
|
137
|
+
Measured on 4 real repos:
|
|
138
|
+
- `62 -> 90`
|
|
139
|
+
- `46 -> 64`
|
|
140
|
+
- `40 -> 48`
|
|
141
|
+
- `35 -> 48`
|
|
97
142
|
|
|
98
|
-
npx claudex-setup
|
|
143
|
+
`npx claudex-setup`
|
|
99
144
|
|
|
100
|
-
|
|
145
|
+
Proof: github.com/DnaFin/claudex/blob/main/research/proof-artifacts/README.md
|
|
101
146
|
|
|
102
147
|
**Tweet 2:**
|
|
103
|
-
The
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
- Testing framework
|
|
109
|
-
- Project architecture
|
|
148
|
+
The most common misses were boring and important:
|
|
149
|
+
- no deny rules
|
|
150
|
+
- no secrets protection
|
|
151
|
+
- no mermaid diagram
|
|
152
|
+
- no hooks registered in settings
|
|
110
153
|
|
|
111
|
-
|
|
154
|
+
It is much more "trust layer" than "AI magic".
|
|
112
155
|
|
|
113
156
|
**Tweet 3:**
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
157
|
+
What it does well today:
|
|
158
|
+
- audit first
|
|
159
|
+
- suggest / plan before writes
|
|
160
|
+
- apply selectively
|
|
161
|
+
- emit rollback artifacts
|
|
162
|
+
- benchmark on isolated copy
|
|
119
163
|
|
|
120
164
|
**Tweet 4:**
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
CLAUDE.md instructions = ~80% compliance
|
|
124
|
-
Hooks = 100% enforcement
|
|
165
|
+
Best result so far:
|
|
166
|
+
- CLAUDEX self-dogfood: `62 -> 90`
|
|
125
167
|
|
|
126
|
-
|
|
168
|
+
Best external proof:
|
|
169
|
+
- VTCLE: `46 -> 64`
|
|
127
170
|
|
|
128
|
-
|
|
171
|
+
Case studies:
|
|
172
|
+
- github.com/DnaFin/claudex/blob/main/research/case-study-vtcle-2026-04-03.md
|
|
173
|
+
- github.com/DnaFin/claudex/blob/main/research/case-study-social-2026-04-03.md
|
|
174
|
+
- github.com/DnaFin/claudex/blob/main/research/case-study-polymiro-2026-04-03.md
|
|
129
175
|
|
|
130
176
|
**Tweet 5:**
|
|
131
|
-
|
|
177
|
+
Measured results were captured on `claudex-setup@1.10.3` on `2026-04-03`.
|
|
178
|
+
Current npm latest is `1.16.1`, so exact scores can move slightly, but the proof packet is explicit about that boundary.
|
|
132
179
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
Scores 0-100. Shows what's missing. Auto-fixes with `setup`.
|
|
136
|
-
|
|
137
|
-
Free. Open source. Zero dependencies.
|
|
138
|
-
|
|
139
|
-
https://github.com/DnaFin/claudex-setup
|
|
180
|
+
**Evidence anchor:** proof artifact index + per-repo traces
|
|
140
181
|
|
|
141
182
|
---
|
|
142
183
|
|
|
143
184
|
## Post 5: Hacker News (Show HN)
|
|
144
185
|
|
|
145
|
-
**Title:** Show HN: claudex-setup
|
|
186
|
+
**Title:** Show HN: claudex-setup — audit Claude Code readiness with 85 checks
|
|
146
187
|
|
|
147
188
|
**Body:**
|
|
148
|
-
I built a CLI
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
189
|
+
I built a CLI that audits how well a repo is set up for Claude Code.
|
|
190
|
+
|
|
191
|
+
This is not a code-quality linter and not an MCP installer.
|
|
192
|
+
It focuses on Claude workflow quality:
|
|
193
|
+
- `CLAUDE.md`
|
|
194
|
+
- hooks
|
|
195
|
+
- commands
|
|
196
|
+
- agents
|
|
197
|
+
- skills
|
|
198
|
+
- MCP config
|
|
199
|
+
- permissions / deny rules
|
|
200
|
+
- diagrams
|
|
201
|
+
- verification loops
|
|
202
|
+
|
|
203
|
+
Core workflow:
|
|
204
|
+
- `npx claudex-setup`
|
|
205
|
+
- `npx claudex-setup suggest-only`
|
|
206
|
+
- `npx claudex-setup plan`
|
|
207
|
+
- `npx claudex-setup apply`
|
|
208
|
+
- `npx claudex-setup benchmark`
|
|
209
|
+
|
|
210
|
+
Measured on 4 real repos on `2026-04-03` with `claudex-setup@1.10.3`:
|
|
211
|
+
- CLAUDEX: `62 -> 90`
|
|
212
|
+
- VTCLE: `46 -> 64`
|
|
213
|
+
- Social: `40 -> 48`
|
|
214
|
+
- Polymiro: `35 -> 48`
|
|
215
|
+
|
|
216
|
+
Trust decisions that mattered:
|
|
217
|
+
- zero dependencies
|
|
218
|
+
- audit before write
|
|
219
|
+
- rollback artifacts
|
|
220
|
+
- cross-platform Node hooks
|
|
221
|
+
- explicit proof packets instead of vague claims
|
|
222
|
+
|
|
223
|
+
Proof packet:
|
|
224
|
+
https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/README.md
|
|
225
|
+
|
|
226
|
+
**Evidence anchor:** proof artifact index + current npm proof source
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudex-setup",
|
|
3
|
-
"version": "1.16.
|
|
4
|
-
"description": "Score your repo's Claude Code setup against
|
|
3
|
+
"version": "1.16.2",
|
|
4
|
+
"description": "Score your repo's Claude Code setup against 85 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": {
|
|
7
7
|
"claudex-setup": "bin/cli.js"
|
package/src/activity.js
CHANGED
|
@@ -21,10 +21,12 @@ function ensureArtifactDirs(dir) {
|
|
|
21
21
|
const activityDir = path.join(root, 'activity');
|
|
22
22
|
const rollbackDir = path.join(root, 'rollbacks');
|
|
23
23
|
const snapshotDir = path.join(root, 'snapshots');
|
|
24
|
+
const outcomesDir = path.join(root, 'outcomes');
|
|
24
25
|
fs.mkdirSync(activityDir, { recursive: true });
|
|
25
26
|
fs.mkdirSync(rollbackDir, { recursive: true });
|
|
26
27
|
fs.mkdirSync(snapshotDir, { recursive: true });
|
|
27
|
-
|
|
28
|
+
fs.mkdirSync(outcomesDir, { recursive: true });
|
|
29
|
+
return { root, activityDir, rollbackDir, snapshotDir, outcomesDir };
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
function writeJson(filePath, payload) {
|
|
@@ -322,6 +324,192 @@ function exportTrendReport(dir) {
|
|
|
322
324
|
return lines.join('\n');
|
|
323
325
|
}
|
|
324
326
|
|
|
327
|
+
function readOutcomeIndex(dir) {
|
|
328
|
+
const indexPath = path.join(dir, '.claude', 'claudex-setup', 'outcomes', 'index.json');
|
|
329
|
+
if (!fs.existsSync(indexPath)) return [];
|
|
330
|
+
try {
|
|
331
|
+
const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
|
332
|
+
return Array.isArray(entries) ? entries : [];
|
|
333
|
+
} catch {
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function updateOutcomeIndex(outcomesDir, record) {
|
|
339
|
+
const indexPath = path.join(outcomesDir, 'index.json');
|
|
340
|
+
let entries = [];
|
|
341
|
+
|
|
342
|
+
if (fs.existsSync(indexPath)) {
|
|
343
|
+
try {
|
|
344
|
+
entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
|
345
|
+
if (!Array.isArray(entries)) entries = [];
|
|
346
|
+
} catch {
|
|
347
|
+
entries = [];
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
entries.push(record);
|
|
352
|
+
const MAX_INDEX_ENTRIES = 500;
|
|
353
|
+
if (entries.length > MAX_INDEX_ENTRIES) {
|
|
354
|
+
entries = entries.slice(entries.length - MAX_INDEX_ENTRIES);
|
|
355
|
+
}
|
|
356
|
+
fs.writeFileSync(indexPath, JSON.stringify(entries, null, 2), 'utf8');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function normalizeOutcomeStatus(value) {
|
|
360
|
+
const normalized = `${value || ''}`.trim().toLowerCase();
|
|
361
|
+
if (!['accepted', 'rejected', 'deferred'].includes(normalized)) {
|
|
362
|
+
throw new Error('feedback status must be one of: accepted, rejected, deferred');
|
|
363
|
+
}
|
|
364
|
+
return normalized;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function normalizeOutcomeEffect(value) {
|
|
368
|
+
const normalized = `${value || ''}`.trim().toLowerCase();
|
|
369
|
+
if (!['positive', 'neutral', 'negative'].includes(normalized)) {
|
|
370
|
+
throw new Error('feedback effect must be one of: positive, neutral, negative');
|
|
371
|
+
}
|
|
372
|
+
return normalized;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function recordRecommendationOutcome(dir, payload) {
|
|
376
|
+
const key = `${payload.key || ''}`.trim();
|
|
377
|
+
if (!key) {
|
|
378
|
+
throw new Error('feedback requires a recommendation key');
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const status = normalizeOutcomeStatus(payload.status);
|
|
382
|
+
const effect = normalizeOutcomeEffect(payload.effect || 'neutral');
|
|
383
|
+
const scoreDelta = Number.isFinite(payload.scoreDelta) ? payload.scoreDelta : (
|
|
384
|
+
payload.scoreDelta === null || payload.scoreDelta === undefined || payload.scoreDelta === ''
|
|
385
|
+
? null
|
|
386
|
+
: Number(payload.scoreDelta)
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
if (scoreDelta !== null && !Number.isFinite(scoreDelta)) {
|
|
390
|
+
throw new Error('feedback scoreDelta must be a number when provided');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const id = timestampId();
|
|
394
|
+
const { outcomesDir } = ensureArtifactDirs(dir);
|
|
395
|
+
const filePath = path.join(outcomesDir, `${id}.json`);
|
|
396
|
+
const record = {
|
|
397
|
+
id,
|
|
398
|
+
createdAt: new Date().toISOString(),
|
|
399
|
+
key,
|
|
400
|
+
status,
|
|
401
|
+
effect,
|
|
402
|
+
source: `${payload.source || 'manual-cli'}`.trim() || 'manual-cli',
|
|
403
|
+
notes: `${payload.notes || ''}`.trim(),
|
|
404
|
+
scoreDelta,
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
writeJson(filePath, record);
|
|
408
|
+
updateOutcomeIndex(outcomesDir, {
|
|
409
|
+
...record,
|
|
410
|
+
relativePath: path.relative(dir, filePath),
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
id,
|
|
415
|
+
filePath,
|
|
416
|
+
relativePath: path.relative(dir, filePath),
|
|
417
|
+
record,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function summarizeOutcomeEntries(entries = []) {
|
|
422
|
+
const byKey = {};
|
|
423
|
+
|
|
424
|
+
for (const entry of entries) {
|
|
425
|
+
if (!entry || !entry.key) continue;
|
|
426
|
+
const bucket = byKey[entry.key] || {
|
|
427
|
+
key: entry.key,
|
|
428
|
+
total: 0,
|
|
429
|
+
accepted: 0,
|
|
430
|
+
rejected: 0,
|
|
431
|
+
deferred: 0,
|
|
432
|
+
positive: 0,
|
|
433
|
+
neutral: 0,
|
|
434
|
+
negative: 0,
|
|
435
|
+
scoreDeltaTotal: 0,
|
|
436
|
+
scoreDeltaCount: 0,
|
|
437
|
+
latestAt: null,
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
bucket.total += 1;
|
|
441
|
+
if (bucket[entry.status] !== undefined) bucket[entry.status] += 1;
|
|
442
|
+
if (bucket[entry.effect] !== undefined) bucket[entry.effect] += 1;
|
|
443
|
+
if (Number.isFinite(entry.scoreDelta)) {
|
|
444
|
+
bucket.scoreDeltaTotal += entry.scoreDelta;
|
|
445
|
+
bucket.scoreDeltaCount += 1;
|
|
446
|
+
}
|
|
447
|
+
if (!bucket.latestAt || new Date(entry.createdAt) > new Date(bucket.latestAt)) {
|
|
448
|
+
bucket.latestAt = entry.createdAt;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
byKey[entry.key] = bucket;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
for (const bucket of Object.values(byKey)) {
|
|
455
|
+
bucket.avgScoreDelta = bucket.scoreDeltaCount > 0
|
|
456
|
+
? Number((bucket.scoreDeltaTotal / bucket.scoreDeltaCount).toFixed(2))
|
|
457
|
+
: null;
|
|
458
|
+
bucket.evidenceClass = bucket.total > 0 ? 'measured' : 'estimated';
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
totalEntries: entries.length,
|
|
463
|
+
byKey,
|
|
464
|
+
keys: Object.keys(byKey).sort(),
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function getRecommendationOutcomeSummary(dir) {
|
|
469
|
+
return summarizeOutcomeEntries(readOutcomeIndex(dir));
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function getRecommendationAdjustment(summaryByKey, key) {
|
|
473
|
+
const bucket = summaryByKey && summaryByKey[key];
|
|
474
|
+
if (!bucket) return 0;
|
|
475
|
+
|
|
476
|
+
let adjustment = 0;
|
|
477
|
+
adjustment += bucket.accepted * 2;
|
|
478
|
+
adjustment += bucket.positive * 3;
|
|
479
|
+
adjustment -= bucket.rejected * 3;
|
|
480
|
+
adjustment -= bucket.negative * 4;
|
|
481
|
+
|
|
482
|
+
if (Number.isFinite(bucket.avgScoreDelta)) {
|
|
483
|
+
if (bucket.avgScoreDelta > 0) adjustment += Math.min(4, Math.round(bucket.avgScoreDelta / 4));
|
|
484
|
+
if (bucket.avgScoreDelta < 0) adjustment -= Math.min(4, Math.round(Math.abs(bucket.avgScoreDelta) / 4));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (adjustment > 8) return 8;
|
|
488
|
+
if (adjustment < -8) return -8;
|
|
489
|
+
return adjustment;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function formatRecommendationOutcomeSummary(dir) {
|
|
493
|
+
const summary = getRecommendationOutcomeSummary(dir);
|
|
494
|
+
if (summary.totalEntries === 0) {
|
|
495
|
+
return 'No recommendation outcomes recorded yet. Use `npx claudex-setup feedback --key permissionDeny --status accepted --effect positive` after a real run.';
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const lines = [
|
|
499
|
+
'Recommendation outcome summary:',
|
|
500
|
+
'',
|
|
501
|
+
];
|
|
502
|
+
|
|
503
|
+
for (const key of summary.keys) {
|
|
504
|
+
const bucket = summary.byKey[key];
|
|
505
|
+
const avg = Number.isFinite(bucket.avgScoreDelta) ? ` | avg score delta ${bucket.avgScoreDelta >= 0 ? '+' : ''}${bucket.avgScoreDelta}` : '';
|
|
506
|
+
const adjustment = getRecommendationAdjustment(summary.byKey, key);
|
|
507
|
+
lines.push(` ${key}: total ${bucket.total} | accepted ${bucket.accepted} | rejected ${bucket.rejected} | deferred ${bucket.deferred} | positive ${bucket.positive} | negative ${bucket.negative}${avg} | ranking ${adjustment >= 0 ? '+' : ''}${adjustment}`);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return lines.join('\n');
|
|
511
|
+
}
|
|
512
|
+
|
|
325
513
|
module.exports = {
|
|
326
514
|
ensureArtifactDirs,
|
|
327
515
|
writeActivityArtifact,
|
|
@@ -332,4 +520,10 @@ module.exports = {
|
|
|
332
520
|
compareLatest,
|
|
333
521
|
formatHistory,
|
|
334
522
|
exportTrendReport,
|
|
523
|
+
readOutcomeIndex,
|
|
524
|
+
recordRecommendationOutcome,
|
|
525
|
+
summarizeOutcomeEntries,
|
|
526
|
+
getRecommendationOutcomeSummary,
|
|
527
|
+
getRecommendationAdjustment,
|
|
528
|
+
formatRecommendationOutcomeSummary,
|
|
335
529
|
};
|
package/src/analyze.js
CHANGED
|
@@ -244,12 +244,15 @@ function toGaps(results) {
|
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
function toRecommendations(auditResult) {
|
|
247
|
-
const failed = auditResult.results
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
247
|
+
const failed = auditResult.results.filter(r => r.passed === false);
|
|
248
|
+
const topActionOrder = new Map((auditResult.topNextActions || []).map((item, index) => [item.key, index]));
|
|
249
|
+
failed.sort((a, b) => {
|
|
250
|
+
const rankedA = topActionOrder.has(a.key) ? topActionOrder.get(a.key) : Number.MAX_SAFE_INTEGER;
|
|
251
|
+
const rankedB = topActionOrder.has(b.key) ? topActionOrder.get(b.key) : Number.MAX_SAFE_INTEGER;
|
|
252
|
+
if (rankedA !== rankedB) return rankedA - rankedB;
|
|
253
|
+
const order = { critical: 3, high: 2, medium: 1, low: 0 };
|
|
254
|
+
return (order[b.impact] || 0) - (order[a.impact] || 0);
|
|
255
|
+
});
|
|
253
256
|
|
|
254
257
|
return failed.slice(0, 10).map((r, index) => ({
|
|
255
258
|
priority: index + 1,
|
|
@@ -259,6 +262,8 @@ function toRecommendations(auditResult) {
|
|
|
259
262
|
module: moduleFromCategory(r.category),
|
|
260
263
|
risk: riskFromImpact(r.impact),
|
|
261
264
|
why: r.fix,
|
|
265
|
+
evidenceClass: (auditResult.topNextActions || []).find(item => item.key === r.key)?.evidenceClass || 'estimated',
|
|
266
|
+
rankingAdjustment: (auditResult.topNextActions || []).find(item => item.key === r.key)?.rankingAdjustment || 0,
|
|
262
267
|
}));
|
|
263
268
|
}
|
|
264
269
|
|