Releases: Samuel0101010/wisp-agentdiff
v1.4.1 — accept v2.1+ Agent tool_use name in transcript parser
Bugfix. Closes the label-correlation loop end-to-end on Claude Code v2.1+.
The bug
v1.3's transcript correlator parsed the session JSONL via transcript_path from the WorktreeCreate payload and filtered tool_uses with name === "Task" to find subagent dispatches. Claude Code v2.1.x renamed that tool_use to name === "Agent" in its on-disk transcript schema, so the filter rejected every entry. pending-tasks stayed empty, displayLabel was never written, and the TUI fell back to the hex worktree slug.
The v1.4 TUI render-side fix (prefer displayLabel over name) was working correctly — there was just nothing to render because of this upstream parse miss.
Evidence from a real v2.1.144 session
In the user's plugin-test session JSONL:
- 0×
"name":"Task" - 8×
"name":"Agent"(e.g.{"type":"tool_use","name":"Agent","id":"toolu_...","input":{"subagent_type":"wisp-self-test",...}})
payloadKeys from the WorktreeCreate hook log confirmed transcript_path IS delivered — the data was always there, our filter was wrong.
The fix
One line in src/wrap/transcript-correlator.ts:
if (p.type !== "tool_use" || (p.name !== "Task" && p.name !== "Agent")) continue;Accept both names so legacy and v2.1+ transcripts both work.
Tests
82 → 83 (+1). New case asserts the v2.1+ "Agent" shape with a plugin-namespaced subagent_type (matching what the user's actual session writes). Existing "ignores tool_uses for tools other than Task" test renamed to "...other than Task/Agent".
Upgrade (cache-busting required)
/plugin marketplace remove wisp-agentdiff
/plugin marketplace add Samuel0101010/wisp-agentdiff
Then Update now in /plugin (or uninstall + install with --scope if it was installed in user scope).
After that, dispatch Task(subagent_type: "wisp-agentdiff:wisp-self-test", prompt: "go") and run /review-agents — the new tab should finally show wisp-self-test (or wisp-agentdiff:wisp-self-test), not the hex slug.
v1.4.0 — outer-repo resolver + TUI displayLabel render
Two real-world bugs surfaced during the v1.3.0 end-to-end verification, both fixed here.
Fixed
Bug 1 — Nested wisp-agentdiff worktrees leaked state into inner repos
When Claude Code dispatched a worktree-isolated subagent while the parent's cwd was already inside an existing .claude/worktrees/wisp-agentdiff/agent-<hex>/ worktree, the WorktreeCreate hook resolved repo-root via the inner worktree's git-toplevel. The new worktree was created nested inside the old one and state landed in an inner state file invisible to the outer /review-agents invocation.
Fix: new src/wrap/resolve-repo-root.ts walks out of nested wisp-agentdiff worktree patterns (up to 10 levels) and returns the outermost non-wisp git root. All three entry points (pre-spawn-hook, post-spawn-hook, prune) now route through it. The debug log emits a [v1.4] retargeting signal on every rewrite so you can see in .claude/wisp-agentdiff/debug.log when it engages.
Bug 2 — TUI showed hex-id agent.name even when displayLabel was populated
The v1.3 transcript correlator wrote displayLabel = "wisp-self-test" into the state file, but the TUI rendered agent.name everywhere, so tab headers always showed agent-a4925f3· rather than the subagent_type.
Fix: centralized agentLabel() helper in src/tui/lib/agent-label.ts — returns displayLabel when non-empty after trim, else falls back to name. Underlying name still used for React keys, branch lookups, and merge operations.
Tests
69 → 82 (+13). New: resolve-repo-root.test.ts (6 cases incl. double-nesting and realpath round-trip), pre-spawn-hook-nested.test.ts (2 integration cases that build a real nested-repo scenario and assert state lands at outer + the retargeting debug-log signal is emitted), tui-label-preference.test.ts (5 helper cases).
All gates green on Linux / macOS / Windows × Node 20 + 22.
Upgrade
/plugin uninstall wisp-agentdiff
/plugin marketplace remove wisp-agentdiff
/plugin marketplace add Samuel0101010/wisp-agentdiff
/plugin install wisp-agentdiff@wisp-agentdiff
(The middle two steps are required — Claude Code caches the marketplace manifest and won't see the new version without a refresh.)
v1.3.0 — real subagent_type labels + worktree garbage collection
Two roadmap items from v1.2.1 land together — both built on real diagnostic evidence from a fresh /plugin install, not assumption.
What's new
Real displayLabel from the session transcript
The TUI now labels each agent by its actual `subagent_type` (`wisp-self-test`, `Explore`, …) instead of Claude Code's opaque hex worktree id.
The previously-shipped PreToolUse:Task path (v1.2.0) never actually fired on Claude Code v2. v1.3 sidesteps this by reading the `transcript_path` Claude Code DOES pass into the WorktreeCreate payload. New module `src/wrap/transcript-correlator.ts` streams the JSONL, finds `tool_use {name:"Task"}` blocks, and enqueues each into the existing pending-tasks store keyed by `tool_use_id`. WorktreeCreate dequeues FIFO. The PreToolUse path stays in the code as a fallback for any future Claude Code build that starts firing the hook.
`wisp-agentdiff prune`
Cleans up orphaned worktrees + `wisp-agentdiff/agent-*` branches that accumulate across sessions because Claude Code rarely fires `WorktreeRemove`.
```text
wisp-agentdiff prune --dry-run # preview the plan
wisp-agentdiff prune --older-than-hours 24 # only entries older than a day
wisp-agentdiff prune --all # ignore age cutoff
```
Handles three categories: state-orphan (state entry without worktree on disk), fs-orphan (worktree on disk without state entry), aged-out (state + worktree older than threshold). Exits 0 always; errors are per-item not fatal.
Tests
69 passing (was 53, +16 new). Lint + typecheck + build clean. CI matrix Linux / macOS / Windows × Node 20 + 22.
Known limitation carried forward to v1.4
- Agent status still stays `running` indefinitely — Claude Code persists subagent worktrees until session-end and almost never fires `WorktreeRemove`. v1.2.1's live-diff fallback at review-time sidesteps this so it's cosmetic, not blocking.
Upgrade
```text
/plugin uninstall wisp-agentdiff
/plugin marketplace remove wisp-agentdiff
/plugin marketplace add Samuel0101010/wisp-agentdiff
/plugin install wisp-agentdiff@wisp-agentdiff
```
Then `Task(subagent_type: "wisp-agentdiff:wisp-self-test", prompt: "go")` → `/review-agents` should now show a tab labelled `wisp-self-test` with a real one-line diff. Run `wisp-agentdiff prune --dry-run` to inspect the worktree clutter accumulated across earlier sessions.
v1.2.1 — live worktree diff at review time
End-to-end diagnostic against Claude Code v2 surfaced two of v1.2.0's architectural assumptions as wrong. This patch sidesteps both so the captured diff actually shows up in the TUI.
Diagnostics from the user's real debug.log
PreToolUse: Taskdoes not fire on Claude Code v2. Zeropre-tool-use.queuedevents appeared after /plugin install of v1.2.0, despite the matcher and command being valid. The pending-tasks buffer stayed empty, so the v1.2.0 `displayLabel` was never set in practice.WorktreeRemovedoes not fire on subagent completion. Claude Code persists subagent worktrees until session-end or explicit `--remove-worktree`. All three captured agents in the user's session stayed at `status: "running"` and the cached diff JSON was never written.
Fix
`buildAgentReport` now falls back to a live `git diff ` against the agent's still-existing worktree when the cached diff is missing or empty. Read-only — no commits, no mutation. Captures committed AND uncommitted changes. A new `diffSource` field reports `"stored"` / `"live"` / `"missing"` for diagnostics.
Net effect: the canary's diff (and any real subagent's edits) now show up in the TUI regardless of whether WorktreeRemove ever fires.
Carried forward to v1.3
- `transcript_path`-based subagent_type extraction (replaces the unfired PreToolUse:Task scaffolding, which stays in the codebase as a fallback for the day Claude Code starts firing the hook).
- `wisp-agentdiff prune` to garbage-collect the worktrees + `wisp-agentdiff/agent-*` branches left behind by unfired WorktreeRemove.
Upgrade
```text
/plugin uninstall wisp-agentdiff
/plugin marketplace remove wisp-agentdiff
/plugin marketplace add Samuel0101010/wisp-agentdiff
/plugin install wisp-agentdiff@wisp-agentdiff
```
Then dispatch the canary; `/review-agents` should now show a real one-line diff (`+ wisp-agentdiff hook verification — `) on the new agent's tab. The tab label remains the hex worktree id until v1.3.
Tests
53 passing (+1 new for the live-diff fallback). Lint, typecheck, build all green.
v1.2.0 — subagent_type labels in the review TUI
The review TUI now shows real subagent_type labels (`wisp-self-test`, `Explore`, etc.) instead of Claude Code's opaque hex worktree id.
How it works
Three new hook moving parts paired with the existing WorktreeCreate handler:
- PreToolUse: Task fires before any subagent spawn. The hook records `{subagentType, description?, queuedAt}` into a FIFO pending-tasks buffer at `.claude/wisp-agentdiff/pending-tasks.json` (60 s TTL).
- WorktreeCreate dequeues the oldest non-stale pending entry and stamps the subagent_type onto the new `AgentRecord` as `displayLabel`.
- The TUI tab bar renders `displayLabel ?? name` — fallback to the worktree slug for direct `claude --worktree …` invocations that never go through Task.
What changed
New files
- `src/wrap/pending-tasks.ts` — FIFO store with TTL pruning and corruption-safe load.
- `src/wrap/pre-tool-use-hook.ts` — `handlePreToolUse`; never throws (PreToolUse must not block tool execution).
Modified
- `src/wrap/state.ts` — optional `displayLabel` on `AgentRecord`.
- `src/wrap/pre-spawn-hook.ts` — calls `dequeueOldestUnstale` and stamps the label; emits `worktree-create.label-correlated` in the debug log.
- `src/index.ts` — new `hook pre-tool-use` subcommand.
- `hooks/hooks.json` — `PreToolUse` block with `matcher: "Task"`.
- `src/tui/tab-bar.tsx` — single-line: `displayLabel ?? name`.
Tests
- `tests/pending-tasks.test.ts` (7), `tests/pre-tool-use.test.ts` (5), `tests/correlation.test.ts` (2), `tests/tui.test.tsx` (one new case).
- 52 tests total (was 38), all green on Linux / macOS / Windows × Node 20 / 22.
Known limitations carrying forward
- Subagents that don't commit inside their worktree still produce empty diffs (the bundled `wisp-self-test` canary commits itself as a belt-and-braces). v1.3 will add a working-tree snapshot fallback.
Upgrade
```text
/plugin uninstall wisp-agentdiff
/plugin marketplace remove wisp-agentdiff
/plugin marketplace add Samuel0101010/wisp-agentdiff
/plugin install wisp-agentdiff@wisp-agentdiff
```
Then `Task(subagent_type: "wisp-agentdiff:wisp-self-test", prompt: "go")` and `/review-agents` — the captured agent now appears in the tab bar as `wisp-self-test`.
v1.1.3 — canary commits its own diff + hook debug log
End-to-end verification in v1.1.2 confirmed the worktree-hook loop works, but surfaced two UX issues for the bundled canary path. This release fixes them.
Fixes
Canary now commits its own diff so the review TUI is non-empty
`wisp-self-test` now runs `git add wisp-self-test.txt && git commit -q -m "wisp-self-test: canary verification"` inside the worktree before reporting. The review TUI computes the diff between the worktree branch and the base ref — an uncommitted file produces a `+0 -0` diff and looks like the plugin is broken. Committing inside the worktree makes the verification path deterministic regardless of Claude Code's worktree-teardown timing.
Real subagents that mutate the working tree without committing will still produce empty diffs in v1.1.x. A working-tree snapshot fallback is planned for v1.2 (see `docs/roadmap.md`).
Always-on hook diagnostic log
`.claude/wisp-agentdiff/debug.log` now captures every `WorktreeCreate` and `WorktreeRemove` invocation with agent id, name, path existence, commit sha (if any), file count, diff bytes — so the next "empty diff" or "no subagents recorded" report can be diagnosed without re-running the failing session. Soft-rotated at 64 KiB. Logging never propagates failures up — hooks stay side-effect-free even when logging fails.
Known limitations carrying into v1.2
- Agent label in the TUI is Claude Code's worktree id (e.g. `agent-abe343b9...`) not the subagent_type. Fixing this requires a `PreToolUse: Task` correlation layer that pairs the type with the next `WorktreeCreate`.
- Subagents that don't commit inside their worktree produce empty diffs. v1.2 will add a working-tree snapshot fallback.
Upgrade
```text
/plugin uninstall wisp-agentdiff
/plugin marketplace remove wisp-agentdiff
/plugin marketplace add Samuel0101010/wisp-agentdiff
/plugin install wisp-agentdiff@wisp-agentdiff
```
Then `wisp-agentdiff:wisp-self-test` to verify, then `/review-agents` to see the captured diff.
v1.1.2 — self-test canary + doctor subcommand
Closes the verification gap. v1.1.1 made the plugin installable; v1.1.2 makes it self-verifiable without depending on the user having their own `isolation: worktree` subagent definitions.
What's new
-
Bundled canary subagent `agents/wisp-self-test.md`. Has `isolation: worktree` in its frontmatter, makes a single one-line file edit inside the worktree, then exits. Dispatch with:
```
Task(subagent_type: "wisp-self-test", description: "verify wisp-agentdiff hooks fire", prompt: "go")
```
After it finishes, `/review-agents` should show one agent labelled `wisp-self-test` with that single diff. If it doesn't, the WorktreeCreate hook isn't firing. -
`wisp-agentdiff doctor` subcommand. Renders an OK / WARN / FAIL checklist for: workspace is git, binary present, plugin manifest reachable (with version), state file present (and how recently mtime'd, agent count), skill registered in `~/.claude`. Exits non-zero on FAIL, 0 with explanatory hint on WARN-only.
```
node "${CLAUDE_PLUGIN_ROOT}/dist/index.js" doctor --repo .
``` -
README adds a Verify the install section walking from empty-state → doctor → canary dispatch → `/review-agents`.
-
SKILL description triggers on "test wisp-agentdiff", "verify wisp-agentdiff", "is wisp-agentdiff working" in addition to the original review-related phrases.
Bug-fix follow-up reminder
After upgrading bust the manifest cache: `/plugin uninstall wisp-agentdiff` → `/plugin marketplace remove wisp-agentdiff` → `/plugin marketplace add Samuel0101010/wisp-agentdiff` → `/plugin install wisp-agentdiff@wisp-agentdiff`.
CLI behaviour for the original review / install / hook / demo subcommands is unchanged.
v1.1.1 — fix: ship dist/ in the plugin clone
Bug fix for the v1.1.0 plugin shipment.
v1.1.0 published the plugin metadata correctly but the plugin clone arrived without a built CLI — `dist/` was gitignored. Hooks fell back to `npx -y wisp-agentdiff@latest`, which Claude Code's auto-mode classifier blocks as an external package fetch. So `/review-agents` and the worktree hooks could never run on a freshly installed plugin.
Fix
- `dist/` is now committed to the repo. Sourcemap inside it uses only relative `../src/...` paths — no absolute paths or PII leak.
- `hooks/hooks.json` invokes `node "${CLAUDE_PLUGIN_ROOT}/dist/index.js" hook worktree-{create,remove}` — plugin-local, network-free, classifier-clean.
- `commands/review-agents.md` prefers `node "${CLAUDE_PLUGIN_ROOT}/dist/index.js" review --repo .` and falls back to a globally-installed `wisp-agentdiff` only for npm-path users.
- `allowed-tools` for the slash command widened to `Bash(node *), Bash(wisp-agentdiff *)`.
CLI behaviour byte-identical to 1.1.0; this is structural-fix-only.
Action for plugin users
Bust the local manifest cache and reinstall:
```text
/plugin uninstall wisp-agentdiff
/plugin marketplace remove wisp-agentdiff
/plugin marketplace add Samuel0101010/wisp-agentdiff
/plugin install wisp-agentdiff@wisp-agentdiff
```
v1.1.0 — Claude Code plugin installable
Now installable as a Claude Code plugin in addition to the existing npx wisp-agentdiff install path.
/plugin marketplace add Samuel0101010/wisp-agentdiff
/plugin install wisp-agentdiff@wisp-agentdiff
Claude Code clones the repo, registers the wisp-agentdiff skill + /review-agents slash command, and wires the native WorktreeCreate / WorktreeRemove hooks automatically — no more manual settings.json edit.
What changed since v1.0.0
Plugin support
.claude-plugin/plugin.json— plugin manifest.claude-plugin/marketplace.json— marketplace listing with{source:"github", repo:"..."}formskills/wisp-agentdiff/SKILL.md— moved fromtemplates/to plugin-standard pathcommands/review-agents.md— moved fromtemplates/;allowed-toolsmatcher fixed to space formhooks/hooks.json— three-layer matcher-envelope shape Claude Code's loader requiressrc/install.tsnow reads from the new paths (single source of truth for both flows)
Documentation
- Three TUI screenshots in the README, now aligned to a uniform 68-cell width (the renderer asserts at build time)
- About-panel topics, description, and homepage URL set on the public repo
Relatedsection in the README cross-links towisp-orchestrator- README install block now documents both the plugin path and the npm path
CLI behavior — unchanged
- All hook handlers, the review TUI, the merge approver, and the conflict detector are byte-identical to v1.0.0
wisp-agentdiff@1.0.0on npm continues to work for users on the npm install path- The plugin's hooks invoke
npx -y wisp-agentdiff@latest— so plugin-installed users get whichever npm version is@latestat the moment their first worktree fires
Tests
- 37 tests still pass on the Linux / macOS / Windows × Node 20 + 22 CI matrix
Hard-won schema rules (memorialised in global Claude Code rules)
Plugin schema notes that took multiple /plugin install rounds to nail:
plugin.jsonrepositoryMUST be a STRING (npm-style{type,url}object is rejected)marketplace.jsonplugins[].sourceMUST be an OBJECT{source:"github",repo:"..."}(bare path string is rejected)hooks/hooks.jsonMUST be three-layer: top-levelhookswrapper, event maps to an array, each entry is a{matcher, hooks:[{type,command}]}envelopeWorktreeCreate+WorktreeRemoveARE real hook events (subagent validators with stale event lists will tell you they aren't)/plugin marketplace adddefaults to SSH — users without a GitHub SSH key needgit config --global "url.https://github.com/.insteadOf" "git@github.com:"once
npm
wisp-agentdiff@1.1.0 is not yet on npm — wisp-agentdiff@1.0.0 is the latest published. Run npm publish from a clean checkout when ready to ship the 1.1.0 tarball.
v1.0.0 — Per-agent diffs for Claude Code parallel subagents
Live on npm: npx wisp-agentdiff install — https://www.npmjs.com/package/wisp-agentdiff
wisp-agentdiff consumes Claude Code's native WorktreeCreate / WorktreeRemove hooks, captures the diff + JSONL transcript of every subagent worktree, and opens a tabbed Ink TUI where each subagent gets its own pane with single-key approve / revert / merge.
Highlights
- Per-agent review — one tab per subagent, with diff, token + tool-call telemetry, and decision glyph.
- Cross-agent conflict detection — if two approved agents touched the same file, the merge step refuses until one side is reverted.
- Native worktree integration — hooks into Claude Code's
WorktreeCreate/WorktreeRemove, no shadow process. - Single-key flow —
aapprove,rrevert,n/pnext/prev,cconflict view,mmerge approved,j/kscroll,qquit.
Install
\``bash
npx wisp-agentdiff install
````
Paste the printed hook snippet once into ~/.claude/settings.json and every subagent Claude Code spawns with `isolation: worktree` in its frontmatter is captured automatically.
What's inside
37 tests, CI matrix Linux / macOS / Windows × Node 20 + 22, MIT, TypeScript. Three module groups: `wrap/` (worktree manager + pre/post-spawn hook handlers), `collect/` (unified-diff parser + JSONL transcript reader + per-agent report aggregation), `tui/` (Ink components + pure-reducer hotkey core), `merge/` (cross-agent conflict detector + sequential `git merge --no-ff` approver with abort-on-conflict).
See `docs/architecture.md` for the full pipeline, `docs/roadmap.md` for what's next.