- collect_sessions.py: name-only repo matches (worktree/basename fallback, which can match an unrelated same-named repo) are now recorded per session (repo_match), counted in inventory.json, called out in the summary, and excludable via a new --strict-repo flag - score_aggregator.py: never-scored detection tracks session ids in a set instead of substring-matching error text, so a prefix-style id can no longer suppress another session's never-scored error - redact_secrets(): broader patterns no longer stack markers on top of an already-redacted value - session files read through the MAX_FILE_BYTES cap instead of slurped whole - README deviations 21-22 recorded; count trued to 22 in CLAUDE.md and authoring-notes; prefix-id regression + single-marker redaction verified Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017yFdbrdBnFL8Gw7DLrdg19 |
||
|---|---|---|
| .. | ||
| .claude-plugin | ||
| agents | ||
| commands | ||
| skills/skill-doctor | ||
| LICENSE | ||
| README.md | ||
skill-doctor — grade the agent setup from real sessions
A rebuild of warpdotdev/common-skills' skill-doctor
(MIT, © Denver Technologies, Inc.) as a full plugin in this library's conventions.
The idea is upstream's, and it is a good one: the only honest benchmark for an agent setup is what actually happened in its recent sessions. skill-doctor harvests the last N days of local Claude Code / Codex conversations scoped to one repo, has the agent judge each condensed transcript against two fixed rubrics (efficiency and code quality), measures which installed skills actually fired, and proposes only the skill edits the evidence justifies — rendered as one local, shareable HTML report.
See Deviations from upstream below for the authoritative list of what changed.
Pipeline
| Step | Who | Artifact |
|---|---|---|
| 1. Collect | scripts/collect_sessions.py |
inventory.json + redacted transcripts/*.md |
| 2. Score | the agent, against scorers/*.md |
session_scores.json (labels + reasons only) |
| 3. Draft | the agent, per references/skill_edit_governance.md |
proposed/<skill>/SKILL.md + suggestions.json |
| 4. Aggregate | scripts/score_aggregator.py — the gate |
validated report.json (all arithmetic lives here) |
| 5. Render | scripts/render_report.py |
self-contained report.html |
Everything lands in one mktemp -d scratch dir; nothing is written into the
user's repo and nothing is uploaded anywhere. All three tools are stdlib-only and
support --help, --output json, and --sample.
The gate (what makes this version stricter)
Upstream had the scoring agent compute its own averages and assemble
report.json by hand. Here score_aggregator.py owns that step and refuses:
- a score whose label is not in the rubric table (numbers are derived, never accepted);
- a score for a session that was never sampled (fabrication guard);
- a reason under 20 characters (theater guard);
- a suggestion that cites no sampled session id (evidence traceability);
- a suggestion with no drafted diff.
Exit 4 means nothing is written — the SKILL.md instructs the agent to fix what the gate names, never to bypass it. Zero suggestions passing the filing bar is a valid, reportable success.
Privacy contract
- Session history is read-only input; artifacts are
chmod 0700/0600. - Every transcript line passes a 12-pattern secret redactor (keys, tokens, JWTs, connection strings, env-style secrets) before it touches disk; there is no off switch, and per-label redaction counts land in the inventory and report.
- The report page itself says what was and wasn't collected.
Details and sources: skills/skill-doctor/references/session_mining_privacy.md.
Deviations from upstream
This numbered list is the authoritative record. The attribution.derivation_note
in .claude-plugin/authoring-notes.json summarizes it; if the two ever disagree, this list wins.
Structural
- Repo-native plugin layout. Upstream is a bare skill folder
(
SKILL.md,scripts/,scorers/,references/,assets/). Here it is a full plugin:skills/skill-doctor/{SKILL.md,scripts,scorers,references,assets}plusagents/cs-skill-doctor.md,commands/cs-skill-doctor.md, a manifest plus anattributionrecord in.claude-plugin/authoring-notes.json, and this README. - Preserved verbatim: both scoring rubrics (
scorers/efficiency.md,scorers/code-quality.md) and the skill-improvement method + filing bar (now §1–§2 ofreferences/skill_edit_governance.md). These are the best part of upstream and were not touched. - Warp session source dropped. Upstream reads Warp's
warp.sqlitestores via a 388-line hand-rolled protobuf decoder (warp_decoder.py). This library targets Claude Code (plus the Codex sync), and vendoring an unverifiable binary decoder conflicts with its keep-scripts-auditable convention. Warp users should run upstream.--harnessaccordingly offersauto|all|claude|codex. - Upstream's
test_collect_sessions.pynot vendored — this repo ships no test framework by design; the scripts carry--samplesmoke fixtures instead.
The gate (new, no upstream counterpart)
score_aggregator.pyadded. Upstream's Step 3 asks the scoring LLM to do the averaging and JSON assembly itself. Per this repo's "algorithm over AI" principle, all arithmetic moved into code: labels map to scores via embedded tables mirroring the rubrics, weights (0.5/0.35/0.15) and the letter-grade table are applied deterministically, andreport.jsonis emitted only after validation passes.- Anti-theater checks. Scores for unsampled sessions are rejected outright (upstream would silently average them in), every sampled session must be scored, reasons are length-checked, duplicate entries fail.
- Evidence traceability enforced. Upstream's rule that suggestions "must trace back to observed waste" was prose; here a suggestion without a sampled session citation or a diff fails the gate.
--emit-templateprints the exact scores-file skeleton for the sampled sessions, so the scoring agent cannot mis-shape the handoff.
Collector hardening
- Secret redaction, always on. Upstream writes condensed transcript excerpts
to disk unredacted. Every entry now passes
redact_secrets()(12 ordered patterns: private-key blocks, AWS/GitHub/Anthropic/OpenAI/Slack/Stripe tokens, JWTs, bearer tokens, connection strings, URL credentials, env-style secret assignments) before writing; per-label counts land ininventory.json. There is deliberately no--no-redactflag. Follows theskillopt-sleepdeviation precedent andproductivity/handoff's redaction linter. - Artifact permissions. Output dirs
chmod 0700, files0600(upstream left them at the process umask). Applied to transcripts, inventory, report JSON and HTML. - Plugin-layout skill discovery. Skill roots now also match
<root>/*/skills/*/SKILL.md, so marketplace-plugin checkouts (like this repo) are inventoried, not just bareskills/<name>/folders. - Slash-command usage detection. Claude Code
<command-name>markers in user turns are mined for skill usage before the injected-content filter drops them (upstream only detected theSkilltool and path mentions), and namespaced invocations like/cs:foomatch skillfoo. - Deterministic ordering. Session sort keys are tie-broken by path/id so
two runs over the same history sample identically;
--sampleruns the whole pipeline on fixed synthetic fixtures (one with a planted fake secret, so the redactor is exercised) without touching real history. - House CLI contract. All three tools take
--help,--output json,--sample, and return typed exit codes (0 / 2 warnings / 3 bad input / 4 validation failure) instead of upstream's mixed conventions.
Renderer replaced
- Zero JavaScript. Upstream embeds a 1,531-line prebuilt
@pierre/diffsbundle (unreadable in review — exactly what this library's no-opaque-vendored- artifacts stance avoids) plus ~200 lines of canvas share-image code. Diffs are now colored with pure CSS spans, long ones collapse behind a native<details>toggle, and sharing is print-to-PDF via@media print. - Vendor branding removed. The Warp pixel-mark SVG, "warp factories" stamp,
and the
warp.dev/factoriesrequest-access CTA (upstream hardcodes it as the report's defaultcta_urland its SKILL.md ends every response with the link) are gone. The footer states the privacy contract instead. - Theme + a11y.
prefers-color-schemedark palette,langattribute, viewport meta — upstream's page is fixed light-only. - Renderer validates its input. Missing
scores/stats/top_findings/suggestionsfields refuse with exit 3 and point to the aggregator, instead of upstream's KeyError traceback.
Post-review hardening (added during PR review)
- Repo-scoping transparency +
--strict-repo. Upstream's worktree/basename fallback silently treats any directory named like the repo as the repo — an unrelated project sharing a common name (backend,app) could leak its sessions into the run. Each session now records how it matched (repo_match: "path" | "name"), name-only matches are counted ininventory.json(sessions_matched_by_name_only) and called out in the collector's summary, and--strict-repodisables the fallback entirely. - Bounded session reads. Session files are read through a
MAX_FILE_BYTEScap instead of upstream's slurp-then-truncate, so a pathological multi-hundred-MB JSONL never lands in memory whole.
Docs & governance
- References rewritten as cited canon. Upstream ships one reference
(
skill-improvements.md, preserved here) with no sources. This plugin adds three references citing 7 sources each: the LLM-as-judge scoring canon, the session-mining privacy canon, and the expanded edit-governance doc that wires proposals intoengineering/write-a-skill's 6-item checklist. - Cross-links into this repo's ecosystem — routing table in the agent (skillopt-sleep for the automated nightly loop, write-a-skill for authoring, self-eval for grading the current session, plugin-audit for static checks).
Install
Registered in .claude-plugin/marketplace.json as skill-doctor. From this
marketplace: /plugin install skill-doctor@claude-code-skills.
Attribution
Upstream: warpdotdev/common-skills
.agents/skills/skill-doctor/ at commit f3b58c81d1cfd5d8eabf2e32edb32db2b0573923,
MIT, © Denver Technologies, Inc. See LICENSE and the attribution record in
.claude-plugin/authoring-notes.json.