Releases: rbinar/cli-dispatch
Release list
v4.27.0 — worktree runners mirror every node_modules dir from the repo top
Fixed
- Worktree runners now mirror every
node_modulesdir of the source checkout, resolved
from the repo top (issue #158). Each*-worktree-run.shused to create one link,
$REPO/node_modules → $WT/node_modules, with$REPObeing whatever--cwdwas. On an npm
workspaces monorepo with--cwd <repo>/packages/core,git worktree addstill checks out the
whole repo, so the worktree ROOT received the package-localnode_modulesand the hoisted
root install (node_modules/.bin/vitest) was never linked — the worker finished fine and
--verify 'cd packages/core && npx vitest run …'died withsh: vitest: command not found
(exit 127). The runners now list the source's ignorednode_modules/directories from
git rev-parse --show-toplevel(git ls-files --others --ignored --exclude-standard --directory, ~17 ms on a six-package monorepo) and symlink each into the worktree at the same
relative path — root and per-package alike — skipping anything that already exists there.
Cleanup removes all such links (find "$WT" -name node_modules -type l -delete; the printed
manual-cleanup hint says the same);git worktree remove --forcewas verified not to follow
the links into the source install. Regression test:worktree-node-modules.test.mjs(all
five runners;--cwdat a package, at the root, and with nonode_modulesat all).
v4.26.0 — flag trivial delegations at the end of a run
Added
- Deterministic runs now surface trivial delegation decisions immediately.
verdict.json
carries a top-level advisorytrivialboolean derived from the same shared diffstat rule as
cli-dispatch-gain: a run is trivial only when insertions plus deletions are greater than zero
and fewer than 50. Empty and missing diffstats remain non-trivial, and the signal never changes
the runner's exit code or judges the work itself. When true, both Bash and PowerShell runners
print one short inline-or-batch suggestion after the terminal summary and before the worktree
note. The gain report's existing text and counts remain unchanged.
v4.25.0 — stop claimedButMissing from flagging dotted prose
Fixed
- Worker-report cross-checks no longer mistake dotted prose for missing files. Claimed-file
extraction now requires either a slash-containing path or a bare filename with a known source,
asset, or config extension — which is also what keeps bare domains such aswww.java.comout,
since no TLD is a source extension. Extension matching consumes the complete token, preventing
long symbol names such asTasksServiceImpl.createTaskfrom being truncated into invented file
claims likeTasksServiceImpl.createTa. Measured against the 68 verdicts on one machine that
carried a usable worker report,claimedButMissingfired on 47% of runs before the change and
19% after (71 → 29 tokens); every dropped token was a symbol, module name, or domain, and no
file-shaped token was lost. Measured-file suffix matching and the conservative "nothing
contradicted" meaning of an empty result remain unchanged.
v4.24.0 — scope the [CD] statusline badge to this session
Changed
- The
[CD]statusline now shows only workers spawned by the current Claude Code session.
When stdin contains Claude Code's snake_casesession_id, the fragment requires a matching
meta.jsonparentSessionIdin addition to the existingrunningstate and 90-second
status.jsonfreshness check. It groups live workers in fixedds,ag,cx,oc,cp
order (for example,[CD](ds:1,ag:2,cx:1)), accepts both long and short backend spellings,
and excludes workers from other parent sessions plus legacy workers without parent metadata.
The group uses the existing yellow counter color;[CD]remains cyan. Stdin is still drained
fully, and callers without a non-emptysession_idretain the global▶Nbehavior unchanged.
The hot path reads onlystatus.jsonandmeta.json, never transcripts.
v4.23.0 — show the spawning Claude Code session on worker rows
Added
-
Worker rows now identify the Claude Code session that spawned them. Every backend parser
records Claude Code's inherited session id inmeta.json, preserving the original non-empty
value across resumes. The dashboard shows the parent project and short session id before the
worker cwd, while legacy workers without the field retain their previous metadata line. -
Parent-project resolution stays cheap on the SSE-refreshed worker list. The list route maps
session ids to dash-encoded~/.claude/projectsdirectories using cached directory listings
only; it never reads transcript contents. The existing transcript-based parent index remains
confined to the worker detail route.
v4.22.0 — ship the verify caveat with its remedy attached
Changed
- The injected policy now states the remedy in the same breath as the verify caveat.
Measured across two sessions that received the policy and never once used the runner
(5,799 and 2,361 Bash calls, 732 and 220 of them gate-able test/build/lint commands), the
runner was considered exactly once and rejected like this: "--verifyonly says the tests
pass, not that behavior was preserved — so I won't delegate the verification." That inverts
the rule. The caveat exists because verification stays with the orchestrator, which is a
reason to re-measure after delegating, not a reason to keep the work inline. Quoted
half, the caveat became a veto. The policy paragraph now carries the remedy attached, and a
test pins the pairing so it cannot be dropped silently. The wording change is bounded by the
existing 160-word ceiling test, which was not modified (148 words with everything enabled).
v4.21.0 — stop counting the injected policy as runner adoption
Fixed
-
/cli-dispatch:driftno longer counts its own policy text as runner adoption.
countRunnerBashToolUsessearched the whole span between two"type":"tool_use"markers
forcli-dispatch-run//cli-dispatch:run. That span can run to ~90KB and swallow the
injected policy paragraph, user messages and tool_result payloads — and since the policy
quotes the runner's command line verbatim, every ordinary Bash call in a policy-carrying
session was scored as a runner invocation. The counter now reads only the Bash tool_use's
owninput.command, bounded to its JSONL record line, and applies the same rule to the
no-marker fallback path. -
A command that merely names the runner is no longer counted as running it.
Waiting on it (pgrep -f cli-dispatch-run), reading its source (grep/sedon the
script), probing it (command -v), and writing a commit message or PR body that quotes the
documented command line were all scored as delegations. A runner invocation now requires
the needle in shell command position — the first word of the command or of a;/&&/||/
pipe/newline-separated segment, after any leadingVAR=valueassignments — with heredoc
bodies excluded. The bare,cd-prefixed, env-prefixed, absolute-path, multi-line and
/cli-dispatch:runslash forms all still count.Measured on a frozen 646-file transcript snapshot, reported invocations fell from 228 to 18
(about 15 real launches plus three--helpprobes) whileagentSpawns,inlineEditsand
policyInjectedSessionsstayed byte-identical. The drift ratio the report had been printing
was therefore understating drift by roughly an order of magnitude: 5.5 became 69.8 on the
same data. Transcript scanning stays a text scan with no per-lineJSON.parse; the report
runs in the same ~2.5s over that snapshot as before.
v4.20.0 — measure delegation drift
Added
/cli-dispatch:driftreports delegation drift between injected policy and actual runner use.
The new read-only report counts recent deterministic runner sessions, raw worker sessions,
policy-injected Claude Code transcripts, AnthropicAgentspawns, inlineEdit/Write
tool use, and Bash invocations ofcli-dispatch-runor/cli-dispatch:run. It keeps
transcript scanning cheap by filtering JSONL files by mtime before reading them and by
counting with text scans rather than JSON parsing, making the "policy arrived but behaviour
did not change" failure mode visible without dumping transcript contents.
v4.19.0 — stop inventing a missing wrapper, align the injected policy
Fixed
-
The SessionStart stale-wrapper notice no longer invents missing commands.
policy-inject.mjsstill compares~/.config/cli-dispatch/.installed-versionagainst the
newest cached plugin version, but it now probes PATH for the backend-agnostic wrapper
binaries before saying anything is missing. If all core wrappers are present, the notice
says the installed wrappers are stale and recommends/cli-dispatch:setupwithout naming
cli-dispatch-runas absent. If any core wrapper is truly absent, the notice lists the
missing binary names explicitly and says they are missing from PATH. Backend-specific
wrappers (ds-agent/cx-agent/ag-agent/oc-agent/cp-agent) are deliberately not probed,
because a user may have installed only one backend. -
The injected delegation policy now matches the global CLAUDE.md routing rule.
The first policy sentence no longer narrows runner use to "mechanical work"; it names
auditability as the binding constraint and allows exploratory work when there is a
machine-checkable verify command and behavior-changing decisions remain in the
orchestrator's brief. The/cli-dispatch:run ... --verifyguidance, zero-babysitter-token
emphasis, trivial-inline exception, escalation paragraph, and issue reminder behavior are
unchanged.
v4.18.0 — keep the runner reachable after a plugin upgrade
Fixed
-
A plugin upgrade no longer silently disables the deterministic runner (issue #150).
Upgrading refreshes the versioned plugin cache dir and nothing else — it never re-runs
install.sh— so~/.local/binkeeps whatever the last/cli-dispatch:setupinstalled.
Any binary introduced by a newer version is therefore simply absent from PATH. That is how
cli-dispatch-run,cli-dispatch-waitandcli-dispatch-gaincame to be missing on a box
running plugin 4.16.0 while the file sat right there in the cache, and/cli-dispatch:run
died withcommand not foundfor a reason nothing on screen explained. Three changes:- The SessionStart hook now compares
~/.config/cli-dispatch/.installed-versionagainst the
newest version in the plugin cache and injects a one-paragraph notice when the install is
behind. It is deliberately not gated onpolicy.json: a stale install is a broken
install, not a preference, and someone who never wrote a policy file is the likeliest
person to hit it.cli-dispatch-status.shandversion-check.shalready detected this,
but only for people who ran a command that happened to check. /cli-dispatch:runfalls back to the plugin's ownscripts/cli-dispatch-runwhen the
wrapper is missing from PATH, so the documented zero-token flow still runs. Both the
fallback and the hard-failure path now name the cause (an upgrade does not reinstall
wrappers) alongside the fix, instead of only suggesting the per-backend commands./cli-dispatch:setupresolves the plugin root at runtime through the new
resolve-plugin-root.sh(+.ps1twin) rather than invoking
${CLAUDE_PLUGIN_ROOT}/scripts/install.shdirectly.CLAUDE_PLUGIN_ROOTis whichever
version the running session loaded, which an upgrade does not change — so the old form
could run an installer several versions old, and printed a stale version number while
doing it. The reporter saw3.30.1in that step with 4.16.0 active and concluded that
3.30.1 was what they had. The resolver picks the newest of (session root, newest cache dir
carrying an installer) and keeps the session root on ties, on unparseable versions, and
when the session root has no manifest — so a local dev checkout is never swapped out.
The runner itself was never broken by any of this; it was only made unreachable, which is
worse, because the fallback is the LLM-babysitter path this plugin exists to avoid. - The SessionStart hook now compares