@uxcontinuum/ccaudit 1.0.2 → 1.0.3

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.
Files changed (2) hide show
  1. package/index.js +94 -18
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -362,17 +362,19 @@ function grade(stats, setup) {
362
362
  // 2. Project hygiene (human sessions only).
363
363
  // Three signals: custom titles (strongest), slugs (informal auto-titles),
364
364
  // and CWD diversity (project-scoped work via tmux or shell). Any of these
365
- // counts as organizational hygiene.
365
+ // counts as organizational hygiene. Long prompts are a tell only when CWD
366
+ // count is low (single project + walls of text = unscoped sprawl).
366
367
  if (human.sessions) {
367
368
  let hScore = 50;
368
369
  hScore += Math.round(human.titledPct * 0.35); // formal title bonus
369
- hScore += Math.round(human.sluggedPct * 0.15); // slug bonus (smaller)
370
- // CWD diversity: launching Claude from named project dirs is good hygiene
371
- if (human.uniqueCwds >= 3) hScore += 8;
372
- if (human.uniqueCwds >= 10) hScore += 7;
373
- if (human.uniqueCwds >= 25) hScore += 5;
374
- if (human.avgPromptLen > 0 && human.avgPromptLen < 80) hScore -= 8;
375
- if (human.avgPromptLen > 1500) hScore -= 6;
370
+ hScore += Math.round(human.sluggedPct * 0.15); // slug bonus
371
+ // CWD diversity: launching from named project dirs is real hygiene.
372
+ if (human.uniqueCwds >= 3) hScore += 10;
373
+ if (human.uniqueCwds >= 10) hScore += 10;
374
+ if (human.uniqueCwds >= 25) hScore += 6;
375
+ if (human.avgPromptLen > 0 && human.avgPromptLen < 80) hScore -= 6;
376
+ // Walls of text are only a problem when work is unscoped.
377
+ if (human.avgPromptLen > 2500 && human.uniqueCwds < 5) hScore -= 8;
376
378
  hScore = Math.max(0, Math.min(100, hScore));
377
379
  const titleNote = human.titledPct > 0
378
380
  ? `${human.titledPct}% titled`
@@ -387,21 +389,39 @@ function grade(stats, setup) {
387
389
  });
388
390
  }
389
391
 
390
- // 3. Tool balance (human sessions only).
392
+ // 3. Tool balance (human sessions only). Adaptive: don't punish Bash
393
+ // dominance if absolute Edit volume is high, because that's "busy operator"
394
+ // not "bash hammer." Only penalize when Edit absolute volume is also low.
391
395
  if (human.sessions && human.totalTools > 0) {
392
396
  let bScore = 75;
393
- if (human.bashPct > 65) bScore -= 18; // bash hammer
394
- if (human.readPct + human.grepPct + human.editPct < 15) bScore -= 12;
395
- if (human.editPct > 10 && human.editPct < 55) bScore += 8; // healthy editing
396
- if (human.agentPct > 2) bScore += 7; // delegates work
397
+ const editAbs = Math.round(human.editPct * human.totalTools / 100);
398
+ const readAbs = Math.round(human.readPct * human.totalTools / 100);
399
+
400
+ // Bash dominance penalty scales with how thin the rest of the toolkit is.
401
+ if (human.bashPct > 65) {
402
+ if (editAbs > 500) bScore -= 6; // big absolute Edit volume — busy operator
403
+ else if (editAbs > 100) bScore -= 12;
404
+ else bScore -= 18; // truly a bash hammer
405
+ } else if (human.bashPct > 50) {
406
+ bScore -= 4;
407
+ }
408
+
409
+ if (human.readPct + human.grepPct + human.editPct < 15) bScore -= 10;
410
+ if (human.editPct > 10 && human.editPct < 55) bScore += 8;
411
+ if (human.agentPct > 2) bScore += 7;
412
+ if (human.agentPct > 5) bScore += 5;
413
+
397
414
  bScore = Math.max(0, Math.min(100, bScore));
415
+
416
+ const fix = human.bashPct > 65 && editAbs < 200
417
+ ? 'You are running things, not editing things. Use Edit/Write more.'
418
+ : null;
419
+
398
420
  dims.push({
399
421
  name: 'Tool balance (human)',
400
422
  score: bScore,
401
- detail: `Bash ${human.bashPct}%, Edit+Write ${human.editPct}%, Read ${human.readPct}%, Grep+Glob ${human.grepPct}%, Agent/Task ${human.agentPct}%.`,
402
- fix: human.bashPct > 65
403
- ? 'You are running things, not editing things. Use Edit/Write more.'
404
- : null,
423
+ detail: `Bash ${human.bashPct}%, Edit+Write ${human.editPct}% (${editAbs} calls), Read ${human.readPct}%, Grep+Glob ${human.grepPct}%, Agent/Task ${human.agentPct}%.`,
424
+ fix,
405
425
  });
406
426
  }
407
427
 
@@ -539,4 +559,60 @@ const stats = aggregate(sessions);
539
559
  const setup = inspectClaudeDir(CLAUDE_DIRS[0]);
540
560
  const graded = grade(stats, setup);
541
561
 
542
- renderCard(stats, setup, graded);
562
+ if (hasFlag('--json')) {
563
+ // Programmatic output. Stable shape for downstream tools and the future
564
+ // public-benchmark backend. No personal content (prompts, slugs, CWDs).
565
+ const payload = {
566
+ schema: 'ccaudit/1',
567
+ generated_at: new Date().toISOString(),
568
+ window_days: days,
569
+ overall: { score: graded.overall, letter: graded.letter },
570
+ dimensions: graded.dims.map(d => ({
571
+ name: d.name,
572
+ score: d.score,
573
+ letter: letterFor(d.score),
574
+ detail: d.detail,
575
+ fix: d.fix,
576
+ })),
577
+ setup: {
578
+ total_hooks: setup.totalHooks,
579
+ hooks_by_event: setup.hooksByEvent,
580
+ auto_memory_enabled: setup.autoMemoryEnabled,
581
+ mcp_servers: setup.mcpServers,
582
+ hook_files: setup.hookFiles.length,
583
+ has_claude_md: setup.hasClaudeMd,
584
+ claude_md_bytes: setup.claudeMdBytes,
585
+ skills_installed: setup.skillCount,
586
+ settings_parse_error: setup.settingsParseError,
587
+ },
588
+ human: stats.human ? {
589
+ sessions: stats.human.sessions,
590
+ prompts: stats.human.prompts,
591
+ titled_pct: stats.human.titledPct,
592
+ slugged_pct: stats.human.sluggedPct,
593
+ unique_cwds: stats.human.uniqueCwds,
594
+ avg_prompt_len: stats.human.avgPromptLen,
595
+ just_count: stats.human.justCount,
596
+ please_count: stats.human.pleaseCount,
597
+ total_tools: stats.human.totalTools,
598
+ tool_distribution: {
599
+ bash_pct: stats.human.bashPct,
600
+ edit_write_pct: stats.human.editPct,
601
+ read_pct: stats.human.readPct,
602
+ grep_glob_pct: stats.human.grepPct,
603
+ agent_task_pct: stats.human.agentPct,
604
+ },
605
+ output_tokens: stats.human.outputTokens,
606
+ input_tokens: stats.human.inputTokens,
607
+ } : null,
608
+ agent: stats.agent ? {
609
+ sessions: stats.agent.sessions,
610
+ prompts: stats.agent.prompts,
611
+ output_tokens: stats.agent.outputTokens,
612
+ input_tokens: stats.agent.inputTokens,
613
+ } : null,
614
+ };
615
+ process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
616
+ } else {
617
+ renderCard(stats, setup, graded);
618
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxcontinuum/ccaudit",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "A diagnostic for your Claude Code setup. Reads ~/.claude/ locally, grades you across hook coverage, project hygiene, tool balance, prompt tells, and pipeline ops. Zero install: npx @uxcontinuum/ccaudit",
5
5
  "main": "index.js",
6
6
  "bin": {