claudex-setup 1.15.1 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # claudex-setup
2
2
 
3
- > Score your repo's Claude Code setup against 84 checks. See what's missing, apply only what you approve with rollback, and benchmark the impact — without breaking existing config.
3
+ > Score your repo's Claude Code setup against 85 checks. See what's missing, apply only what you approve with rollback, and benchmark the impact — without breaking existing config.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/claudex-setup)](https://www.npmjs.com/package/claudex-setup)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/claudex-setup)](https://www.npmjs.com/package/claudex-setup)
@@ -51,6 +51,12 @@ Tested on 4 real projects — not demos:
51
51
 
52
52
  Most common gaps found: missing secrets protection, no deny rules, no mermaid diagram, no hooks in settings.
53
53
 
54
+ > Scores measured with claudex-setup@1.10.3 on 2026-04-03. Current npm latest: 1.16.0, so exact scores may differ slightly on the newer release.
55
+ >
56
+ > Canonical proof artifacts: [Index](https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/README.md) | [CLAUDEX trace](https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/claudex-self-dogfood-proof-trace-2026-04-03.md) | [VTCLE trace](https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/vtcle-proof-trace-2026-04-03.md) | [Social trace](https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/social-proof-trace-2026-04-03.md) | [Polymiro trace](https://github.com/DnaFin/claudex/blob/main/research/proof-artifacts/polymiro-proof-trace-2026-04-03.md)
57
+ >
58
+ > Narrative case studies: [VTCLE](https://github.com/DnaFin/claudex/blob/main/research/case-study-vtcle-2026-04-03.md) | [Social](https://github.com/DnaFin/claudex/blob/main/research/case-study-social-2026-04-03.md) | [Polymiro](https://github.com/DnaFin/claudex/blob/main/research/case-study-polymiro-2026-04-03.md)
59
+
54
60
  ## What You Get
55
61
 
56
62
  ```
@@ -89,7 +95,7 @@ Most common gaps found: missing secrets protection, no deny rules, no mermaid di
89
95
  design: none (0/2)
90
96
  devops: none (0/4)
91
97
 
92
- 29/84 checks passing
98
+ 29/85 checks passing
93
99
  Next command: npx claudex-setup setup
94
100
  ```
95
101
 
@@ -105,7 +111,7 @@ That prints a compact top-3 quick scan with one clear next command.
105
111
 
106
112
  | Command | What it does |
107
113
  |---------|-------------|
108
- | `npx claudex-setup` | **Discover** - Score 0-100 against 84 checks |
114
+ | `npx claudex-setup` | **Discover** - Score 0-100 against 85 checks |
109
115
  | `npx claudex-setup discover` | **Discover** - Alias for audit mode |
110
116
  | `npx claudex-setup setup` | **Starter** - Smart CLAUDE.md + hooks + commands + agents |
111
117
  | `npx claudex-setup starter` | **Starter** - Alias for setup mode |
@@ -117,11 +123,14 @@ That prints a compact top-3 quick scan with one clear next command.
117
123
  | `npx claudex-setup governance` | **Governance** - Permission profiles, hook registry, policy packs, pilot kit |
118
124
  | `npx claudex-setup benchmark` | **Benchmark** - Before/after evidence from an isolated temp copy |
119
125
  | `npx claudex-setup interactive` | **Wizard** - Step-by-step guided tour |
120
- | `npx claudex-setup watch` | **Watch** - Live monitoring with score delta |
126
+ | `npx claudex-setup watch` | **Watch** - Live monitoring with score delta and cross-platform directory fallback |
121
127
  | `npx claudex-setup badge` | **Badge** - Generate shields.io badge for README |
122
- | `npx claudex-setup deep-review` | **Deep Review** - AI-powered config analysis (needs API key) |
128
+ | `npx claudex-setup feedback` | **Feedback** - Record local recommendation outcomes or show outcome summary |
129
+ | `npx claudex-setup deep-review` | **Deep Review** - AI-powered config analysis (Claude Code or API key, selected config only) |
123
130
  | `npx claudex-setup insights` | **Insights** - View community aggregate stats |
124
131
 
132
+ > Note: the `feedback` command is currently validated on the main working tree for the next release. If your installed npm build does not expose it yet, use the rest of the trust-first flow and pick it up on the next publish.
133
+
125
134
  ### Options
126
135
 
127
136
  | Flag | Effect |
@@ -132,6 +141,10 @@ That prints a compact top-3 quick scan with one clear next command.
132
141
  | `--only A,B` | Limit plan/apply to selected proposal ids |
133
142
  | `--profile NAME` | Choose a permission profile for write-capable flows |
134
143
  | `--mcp-pack A,B` | Merge named MCP packs into generated or patched settings |
144
+ | `--key NAME` | Recommendation key for `feedback` logging |
145
+ | `--status VALUE` | Outcome status: `accepted`, `rejected`, or `deferred` |
146
+ | `--effect VALUE` | Outcome effect: `positive`, `neutral`, or `negative` |
147
+ | `--score-delta N` | Optional observed score delta tied to the feedback event |
135
148
  | `--snapshot` | Save a normalized artifact under `.claude/claudex-setup/snapshots/` |
136
149
  | `--lite` | Show a short top-3 quick scan with one clear next command |
137
150
  | `--dry-run` | Preview apply without writing files |
@@ -244,6 +257,19 @@ npx claudex-setup benchmark --snapshot
244
257
 
245
258
  Snapshots are written to `.claude/claudex-setup/snapshots/` with a shared envelope and an `index.json` history file.
246
259
 
260
+ If you want a local-first recommendation loop, record what actually helped:
261
+
262
+ ```bash
263
+ npx claudex-setup feedback --key permissionDeny --status accepted --effect positive --score-delta 12
264
+ npx claudex-setup feedback
265
+ ```
266
+
267
+ Feedback stays under `.claude/claudex-setup/outcomes/` and is used only as a local ranking signal. Recommendations with repeated positive outcomes get a measured boost; recommendations with repeated negative or rejected outcomes get pushed down.
268
+
269
+ If your currently installed npm build does not expose `feedback` yet, treat this as next-release behavior rather than current npm-latest behavior.
270
+
271
+ `watch` uses native `fs.watch` with recursive directory watches where the platform supports them, and an expanded directory fallback elsewhere. That keeps nested `.claude/` and `.github/` changes visible on Linux too, while staying zero-dependency. Native filesystem watch semantics can still be noisier on very large repos or network filesystems, so the command is best treated as fast local feedback rather than a CI-grade signal.
272
+
247
273
  ## Use Inside Claude Code
248
274
 
249
275
  If you want the first Claude-native entry point, copy the shipped skill template into your repo.
@@ -259,39 +285,39 @@ If you are using `npx` only, copy the same file from the GitHub repo at `content
259
285
 
260
286
  The skill runs `npx claudex-setup --json`, summarizes the score, shows the top next actions, and points to the right next command without applying changes.
261
287
 
262
- ## 62 Checks Across 14 Categories
288
+ ## 85 Checks Across 14 Categories
263
289
 
264
290
  The exact applicable count can be lower on a given repo because stack-specific checks are skipped when they do not apply.
265
291
 
266
292
  | Category | Checks | Key items |
267
293
  |----------|-------:|-----------|
268
- | Memory | 8 | CLAUDE.md, architecture, conventions |
269
- | Quality | 7 | verification loops, self-correction |
270
- | Git Safety | 5 | hooks, force-push protection |
271
- | Workflow | 6 | commands, skills, rules, agents |
272
- | Security | 5 | permissions, secrets, deny rules |
273
- | Automation | 5 | PreToolUse, PostToolUse, SessionStart |
274
- | Design | 4 | Mermaid, XML tags, structured prompts |
275
- | DevOps | 4 | Docker, CI, Terraform, K8s |
276
- | Hygiene | 6 | .gitignore, cleanup, structure |
277
- | Performance | 3 | context management, compaction |
278
- | MCP | 3 | servers, Context7, integrations |
279
- | Prompting | 3 | constraints, validation, patterns |
280
- | Features | 2 | /security-review, Channels |
281
- | **Quality Deep** | **9** | **freshness, contradictions, deprecated patterns, maxTurns, $ARGUMENTS** |
294
+ | Memory | 8 | CLAUDE.md, architecture, conventions, imports |
295
+ | Quality | 6 | verification loops, test/lint/build, testing strategy |
296
+ | Git Safety | 6 | .gitignore, env protection, attribution, secret detection |
297
+ | Workflow | 12 | commands, skills, rules, agents, snapshots |
298
+ | Security | 7 | permissions, secrets, deny rules, sandbox awareness |
299
+ | Automation | 7 | hook coverage, specificity, session and error hooks |
300
+ | Design | 2 | frontend anti-slop guidance, styling signals |
301
+ | DevOps | 5 | Docker, CI, Terraform, infra signals |
302
+ | Hygiene | 8 | README, changelog, license, env example, version pinning |
303
+ | Performance | 3 | context management, compaction, effort level |
304
+ | MCP & Tools | 4 | servers, Context7, tool companions, env config |
305
+ | Prompting | 6 | constraints, examples, negative rules, style guidance |
306
+ | Features | 2 | channels, worktrees |
307
+ | **Quality Deep** | **9** | **freshness, contradictions, deprecated patterns, maxTurns, $ARGUMENTS, hook specificity** |
282
308
 
283
309
  ## Stack Detection
284
310
 
285
- Auto-detects and tailors output for 22 stacks:
311
+ Auto-detects and tailors output for 30 stacks:
286
312
 
287
313
  | | |
288
314
  |--|--|
289
- | **Frontend** | React, Vue, Angular, Next.js, Svelte |
290
- | **Backend** | Node.js, Python, Django, FastAPI |
291
- | **Mobile** | Flutter, Swift, Kotlin |
292
- | **Systems** | Rust, Go, Java, Ruby, C++, Bazel |
315
+ | **Frontend** | React, Vue, Angular, Next.js, Svelte, Astro |
316
+ | **Backend** | Node.js, Python, Django, FastAPI, Express, NestJS, Spring Boot |
317
+ | **Mobile** | React Native, Expo, Flutter, Swift, Kotlin |
318
+ | **Systems** | Rust, Go, Java, Ruby, C++, Bazel, Deno, Bun |
293
319
  | **Language** | TypeScript |
294
- | **Infra** | Docker, Terraform, Kubernetes |
320
+ | **Infra** | Docker, Terraform, Kubernetes, Wrangler |
295
321
 
296
322
  ## GitHub Action
297
323
 
@@ -305,7 +331,7 @@ jobs:
305
331
  runs-on: ubuntu-latest
306
332
  steps:
307
333
  - uses: actions/checkout@v4
308
- - uses: DnaFin/claudex-setup@v1.14.0
334
+ - uses: DnaFin/claudex-setup@v1.16.0
309
335
  with:
310
336
  threshold: 50
311
337
  ```
@@ -331,6 +357,14 @@ npx claudex-setup deep-review
331
357
 
332
358
  Claude reads your actual config and gives specific feedback: what's strong, what has issues, what's missing for your stack. This is an AI-assisted review, not a local heuristic audit. Your config goes to the Anthropic API only when you run this command; we do not receive it.
333
359
 
360
+ Deep-review trust boundary:
361
+
362
+ - sends only selected Claude-facing config surfaces: `CLAUDE.md`, settings, commands, agents, rules, hooks, and package scripts
363
+ - truncates large files before sending
364
+ - redacts embedded secrets before sending
365
+ - treats embedded repo text as untrusted review data, not as instructions to follow
366
+ - keeps all non-`deep-review` flows local
367
+
334
368
  ### Quality-Deep Checks
335
369
 
336
370
  The v0.4.0 quality-deep checks catch what basic audits miss:
@@ -353,7 +387,8 @@ These checks evaluate **quality**, not just existence. A well-configured project
353
387
 
354
388
  - **Zero dependencies** - nothing extra to audit
355
389
  - **Core flows run locally** - audit, setup, augment, plan, apply, governance, and benchmark run on your machine
356
- - **Deep review is opt-in** - only `deep-review` sends selected config to Anthropic for analysis
390
+ - **Deep review is opt-in** - only `deep-review` sends selected config to Anthropic or your local Claude Code session for analysis
391
+ - **Deep review sanitizes before send** - selected snippets are truncated, secret-redacted, and wrapped as untrusted review data
357
392
  - **Benchmark uses an isolated temp copy** - your live repo is not touched
358
393
  - **Anonymous insights** - opt-in, no PII, no file contents (enable with `--insights`)
359
394
  - **MIT Licensed** - use anywhere
package/bin/cli.js CHANGED
@@ -6,7 +6,7 @@ const { analyzeProject, printAnalysis, exportMarkdown } = require('../src/analyz
6
6
  const { buildProposalBundle, printProposalBundle, writePlanFile, applyProposalBundle, printApplyResult } = require('../src/plans');
7
7
  const { getGovernanceSummary, printGovernanceSummary, ensureWritableProfile, renderGovernanceMarkdown } = require('../src/governance');
8
8
  const { runBenchmark, printBenchmark, writeBenchmarkReport } = require('../src/benchmark');
9
- const { writeSnapshotArtifact } = require('../src/activity');
9
+ const { writeSnapshotArtifact, recordRecommendationOutcome, formatRecommendationOutcomeSummary, getRecommendationOutcomeSummary } = require('../src/activity');
10
10
  const { version } = require('../package.json');
11
11
 
12
12
  const args = process.argv.slice(2);
@@ -18,8 +18,9 @@ const COMMAND_ALIASES = {
18
18
  starter: 'setup',
19
19
  suggest: 'suggest-only',
20
20
  gov: 'governance',
21
+ outcome: 'feedback',
21
22
  };
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
+ const KNOWN_COMMANDS = ['audit', 'setup', 'augment', 'suggest-only', 'plan', 'apply', 'governance', 'benchmark', 'deep-review', 'interactive', 'watch', 'badge', 'insights', 'history', 'compare', 'trend', 'scan', 'feedback', 'help', 'version'];
23
24
 
24
25
  function levenshtein(a, b) {
25
26
  const matrix = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0));
@@ -62,12 +63,19 @@ function parseArgs(rawArgs) {
62
63
  let profile = 'safe-write';
63
64
  let mcpPacks = [];
64
65
  let requireChecks = [];
66
+ let feedbackKey = null;
67
+ let feedbackStatus = null;
68
+ let feedbackEffect = null;
69
+ let feedbackNotes = null;
70
+ let feedbackSource = null;
71
+ let feedbackScoreDelta = null;
65
72
  let commandSet = false;
73
+ let extraArgs = [];
66
74
 
67
75
  for (let i = 0; i < rawArgs.length; i++) {
68
76
  const arg = rawArgs[i];
69
77
 
70
- if (arg === '--threshold' || arg === '--out' || arg === '--plan' || arg === '--only' || arg === '--profile' || arg === '--mcp-pack' || arg === '--require') {
78
+ if (arg === '--threshold' || arg === '--out' || arg === '--plan' || arg === '--only' || arg === '--profile' || arg === '--mcp-pack' || arg === '--require' || arg === '--key' || arg === '--status' || arg === '--effect' || arg === '--notes' || arg === '--source' || arg === '--score-delta') {
71
79
  const value = rawArgs[i + 1];
72
80
  if (!value || value.startsWith('--')) {
73
81
  throw new Error(`${arg} requires a value`);
@@ -79,6 +87,12 @@ function parseArgs(rawArgs) {
79
87
  if (arg === '--profile') profile = value.trim();
80
88
  if (arg === '--mcp-pack') mcpPacks = value.split(',').map(item => item.trim()).filter(Boolean);
81
89
  if (arg === '--require') requireChecks = value.split(',').map(item => item.trim()).filter(Boolean);
90
+ if (arg === '--key') feedbackKey = value.trim();
91
+ if (arg === '--status') feedbackStatus = value.trim();
92
+ if (arg === '--effect') feedbackEffect = value.trim();
93
+ if (arg === '--notes') feedbackNotes = value;
94
+ if (arg === '--source') feedbackSource = value.trim();
95
+ if (arg === '--score-delta') feedbackScoreDelta = value.trim();
82
96
  i++;
83
97
  continue;
84
98
  }
@@ -118,6 +132,36 @@ function parseArgs(rawArgs) {
118
132
  continue;
119
133
  }
120
134
 
135
+ if (arg.startsWith('--key=')) {
136
+ feedbackKey = arg.split('=').slice(1).join('=').trim();
137
+ continue;
138
+ }
139
+
140
+ if (arg.startsWith('--status=')) {
141
+ feedbackStatus = arg.split('=').slice(1).join('=').trim();
142
+ continue;
143
+ }
144
+
145
+ if (arg.startsWith('--effect=')) {
146
+ feedbackEffect = arg.split('=').slice(1).join('=').trim();
147
+ continue;
148
+ }
149
+
150
+ if (arg.startsWith('--notes=')) {
151
+ feedbackNotes = arg.split('=').slice(1).join('=');
152
+ continue;
153
+ }
154
+
155
+ if (arg.startsWith('--source=')) {
156
+ feedbackSource = arg.split('=').slice(1).join('=').trim();
157
+ continue;
158
+ }
159
+
160
+ if (arg.startsWith('--score-delta=')) {
161
+ feedbackScoreDelta = arg.split('=').slice(1).join('=').trim();
162
+ continue;
163
+ }
164
+
121
165
  if (arg.startsWith('--')) {
122
166
  flags.push(arg);
123
167
  continue;
@@ -126,12 +170,14 @@ function parseArgs(rawArgs) {
126
170
  if (!commandSet) {
127
171
  command = arg;
128
172
  commandSet = true;
173
+ } else {
174
+ extraArgs.push(arg);
129
175
  }
130
176
  }
131
177
 
132
178
  const normalizedCommand = COMMAND_ALIASES[command] || command;
133
179
 
134
- return { flags, command, normalizedCommand, threshold, out, planFile, only, profile, mcpPacks, requireChecks };
180
+ return { flags, command, normalizedCommand, threshold, out, planFile, only, profile, mcpPacks, requireChecks, feedbackKey, feedbackStatus, feedbackEffect, feedbackNotes, feedbackSource, feedbackScoreDelta, extraArgs };
135
181
  }
136
182
 
137
183
  const HELP = `
@@ -155,13 +201,17 @@ const HELP = `
155
201
  npx claudex-setup compare Compare latest vs previous snapshot
156
202
  npx claudex-setup trend --out r.md Export trend report as markdown
157
203
 
204
+ Multi-repo:
205
+ npx claudex-setup scan dir1 dir2 Compare multiple repos side-by-side
206
+
158
207
  Advanced:
159
208
  npx claudex-setup governance Permission profiles, hooks, policy packs
160
209
  npx claudex-setup benchmark Before/after in isolated temp copy
161
210
  npx claudex-setup deep-review AI-powered config review (opt-in, uses API)
162
211
  npx claudex-setup interactive Step-by-step guided wizard
163
- npx claudex-setup watch Live monitoring on config changes
212
+ npx claudex-setup watch Live monitoring on config changes with cross-platform watch fallback
164
213
  npx claudex-setup badge Generate shields.io badge markdown
214
+ npx claudex-setup feedback Record recommendation outcomes or show local outcome summary
165
215
 
166
216
  Options:
167
217
  --threshold N Exit with code 1 if score is below N (useful for CI)
@@ -171,6 +221,12 @@ const HELP = `
171
221
  --only A,B Limit plan/apply to selected proposal ids or technique keys
172
222
  --profile NAME Choose permission profile (read-only, suggest-only, safe-write, power-user, internal-research)
173
223
  --mcp-pack A,B Merge named MCP packs into generated settings (e.g. context7-docs,next-devtools)
224
+ --key NAME Recommendation key for feedback logging (e.g. permissionDeny)
225
+ --status VALUE Feedback status: accepted, rejected, deferred
226
+ --effect VALUE Feedback effect: positive, neutral, negative
227
+ --notes TEXT Short notes to store with a feedback event
228
+ --source NAME Source label for feedback event (default: manual-cli)
229
+ --score-delta N Optional observed score delta tied to the outcome
174
230
  --snapshot Save a normalized snapshot artifact under .claude/claudex-setup/snapshots/
175
231
  --lite Show a short top-3 quick scan with one clear next command
176
232
  --dry-run Preview apply without writing files
@@ -197,6 +253,8 @@ const HELP = `
197
253
  npx claudex-setup apply --profile power-user --only claude-md,hooks
198
254
  npx claudex-setup governance --json
199
255
  npx claudex-setup benchmark --out benchmark.md
256
+ npx claudex-setup feedback
257
+ npx claudex-setup feedback --key permissionDeny --status accepted --effect positive --score-delta 12
200
258
  npx claudex-setup --json --threshold 60
201
259
  npx claudex-setup setup --auto
202
260
  npx claudex-setup interactive
@@ -249,7 +307,7 @@ async function main() {
249
307
  process.exit(1);
250
308
  }
251
309
 
252
- if (options.require && normalizedCommand !== 'audit' && !['audit', 'discover'].includes(command)) {
310
+ if (options.require && options.require.length > 0 && normalizedCommand !== 'audit' && !['audit', 'discover'].includes(command)) {
253
311
  console.error(`\n Warning: --require is only supported with the audit command. Ignoring for '${normalizedCommand}'.\n`);
254
312
  }
255
313
 
@@ -279,7 +337,74 @@ async function main() {
279
337
  }
280
338
 
281
339
  try {
282
- if (normalizedCommand === 'history') {
340
+ if (normalizedCommand === 'scan') {
341
+ const scanDirs = parsed.extraArgs;
342
+ if (scanDirs.length === 0) {
343
+ console.error('\n Error: scan requires at least one directory argument.');
344
+ console.error(' Usage: npx claudex-setup scan dir1 dir2 dir3\n');
345
+ process.exit(1);
346
+ }
347
+ const fs = require('fs');
348
+ const pathMod = require('path');
349
+ const rows = [];
350
+ for (const rawDir of scanDirs) {
351
+ const dir = pathMod.resolve(rawDir);
352
+ if (!fs.existsSync(dir)) {
353
+ rows.push({ name: pathMod.basename(rawDir), dir: rawDir, score: null, passed: '-', failed: '-', suggested: '-', error: 'directory not found' });
354
+ continue;
355
+ }
356
+ try {
357
+ const result = await audit({ dir, silent: true });
358
+ rows.push({
359
+ name: pathMod.basename(dir),
360
+ dir: rawDir,
361
+ score: result.score,
362
+ passed: result.passed,
363
+ failed: result.failed,
364
+ suggested: result.suggestedNextCommand || '-',
365
+ error: null,
366
+ });
367
+ } catch (err) {
368
+ rows.push({ name: pathMod.basename(dir), dir: rawDir, score: null, passed: '-', failed: '-', suggested: '-', error: err.message });
369
+ }
370
+ }
371
+
372
+ if (options.json) {
373
+ console.log(JSON.stringify(rows, null, 2));
374
+ } else {
375
+ // Find weakest
376
+ const validRows = rows.filter(r => r.score !== null);
377
+ const minScore = validRows.length > 0 ? Math.min(...validRows.map(r => r.score)) : null;
378
+ const weakest = validRows.length > 1 && validRows.filter(r => r.score > minScore).length > 0
379
+ ? validRows.find(r => r.score === minScore)
380
+ : null;
381
+
382
+ console.log('');
383
+ console.log('\x1b[1m claudex-setup multi-repo scan\x1b[0m');
384
+ console.log('\x1b[2m ═══════════════════════════════════════\x1b[0m');
385
+ console.log('');
386
+
387
+ // Table header
388
+ const nameW = Math.max(8, ...rows.map(r => r.name.length)) + 2;
389
+ const header = ` ${'Project'.padEnd(nameW)} ${'Score'.padStart(5)} ${'Pass'.padStart(4)} ${'Fail'.padStart(4)} Suggested Command`;
390
+ console.log('\x1b[1m' + header + '\x1b[0m');
391
+ console.log(' ' + '─'.repeat(header.trim().length));
392
+
393
+ for (const row of rows) {
394
+ if (row.error) {
395
+ console.log(` ${row.name.padEnd(nameW)} \x1b[31m${('ERR').padStart(5)}\x1b[0m ${String(row.passed).padStart(4)} ${String(row.failed).padStart(4)} ${row.error}`);
396
+ continue;
397
+ }
398
+ const isWeak = weakest && row.name === weakest.name && row.dir === weakest.dir;
399
+ const scoreColor = row.score >= 70 ? '\x1b[32m' : row.score >= 40 ? '\x1b[33m' : '\x1b[31m';
400
+ const prefix = isWeak ? '\x1b[31m⚠ ' : ' ';
401
+ const suffix = isWeak ? ' ← weakest\x1b[0m' : '';
402
+ console.log(`${prefix}${row.name.padEnd(nameW)} ${scoreColor}${String(row.score).padStart(5)}\x1b[0m ${String(row.passed).padStart(4)} ${String(row.failed).padStart(4)} ${row.suggested}${suffix}`);
403
+ }
404
+ console.log('');
405
+ }
406
+ process.exit(0);
407
+ } else if (normalizedCommand === 'history') {
283
408
  const { formatHistory } = require('../src/activity');
284
409
  console.log('');
285
410
  console.log(formatHistory(options.dir));
@@ -366,6 +491,41 @@ async function main() {
366
491
  console.log(' Insights request timed out. Run locally: npx claudex-setup');
367
492
  });
368
493
  return; // keep process alive for http
494
+ } else if (normalizedCommand === 'feedback') {
495
+ if (parsed.feedbackKey) {
496
+ if (!parsed.feedbackStatus) {
497
+ console.error('\n Error: feedback logging requires --status when --key is provided.\n');
498
+ process.exit(1);
499
+ }
500
+ const artifact = recordRecommendationOutcome(options.dir, {
501
+ key: parsed.feedbackKey,
502
+ status: parsed.feedbackStatus,
503
+ effect: parsed.feedbackEffect || 'neutral',
504
+ notes: parsed.feedbackNotes || '',
505
+ source: parsed.feedbackSource || 'manual-cli',
506
+ scoreDelta: parsed.feedbackScoreDelta !== null ? Number(parsed.feedbackScoreDelta) : null,
507
+ });
508
+ const summary = getRecommendationOutcomeSummary(options.dir);
509
+ if (options.json) {
510
+ console.log(JSON.stringify({ artifact, summary }, null, 2));
511
+ } else {
512
+ console.log('');
513
+ console.log(` Feedback recorded for ${parsed.feedbackKey}`);
514
+ console.log(` Artifact: ${artifact.relativePath}`);
515
+ console.log('');
516
+ console.log(formatRecommendationOutcomeSummary(options.dir));
517
+ console.log('');
518
+ }
519
+ } else {
520
+ if (options.json) {
521
+ console.log(JSON.stringify(getRecommendationOutcomeSummary(options.dir), null, 2));
522
+ } else {
523
+ console.log('');
524
+ console.log(formatRecommendationOutcomeSummary(options.dir));
525
+ console.log('');
526
+ }
527
+ }
528
+ process.exit(0);
369
529
  } else if (normalizedCommand === 'augment' || normalizedCommand === 'suggest-only') {
370
530
  const report = await analyzeProject({ ...options, mode: normalizedCommand });
371
531
  const snapshot = options.snapshot ? writeSnapshotArtifact(options.dir, normalizedCommand, report, {