mirror of
https://github.com/max-sixty/worktrunk.git
synced 2026-09-14 20:00:38 +08:00
a27cbd42be
Closes #3555. `/wt-switch-create` now creates through `EnterWorktree({name})` for the common invocation, which asks the user to confirm nothing, and falls back to `wt` plus a path entry for the cases that route can't serve. The confirmation Claude Code 2.1.206 added fires on a call that passes `path` and targets a worktree outside `.claude/worktrees/`. `{name}` passes no `path`, so it never fires. With the plugin installed, `{name}` runs worktrunk's own `WorktreeCreate` hook (`wt switch --create`), so the worktree is the one `wt` would have made, and it shows up in `wt list`. ## Routes - **New branch, this repo** → `EnterWorktree({name})`. No confirmation. - **Anything else** → `wt -C <repo> switch --create … --format=json`, then `EnterWorktree({path})`, keeping the outcome handling below. Three things `{name}` cannot do, each announcing its own fallback in the error it returns, so the skill tries the cheap call and reads the result rather than pre-checking: | Case | What `{name}` returns | |---|---| | Repo argument given | no `-C` equivalent; skipped before the call | | Branch already exists | the hook exits nonzero with `✗ Branch <branch> already exists` | | Session already entered a worktree | `Already in a worktree session. Pass `path` to switch into another existing worktree` | ## The tradeoff, taken deliberately A `{name}` worktree the session never touched is removed when the session ends, branch included, through the plugin's `WorktreeRemove` hook (`wt remove`). Verified end to end: create, `/exit`, and neither the worktree nor the branch remains; one untracked file is enough to keep both. That is wanted at this scale, since a research task that wrote nothing leaves nothing to prune, but it does mean "the worktree persists" was no longer true of every route. Cleanup and the rationale now say which worktrees persist; the user docs stay out of the mechanics. ## A bug fixed along the way A user declining the path-entry confirmation returns an ordinary permission denial, which the skill's single rejection branch caught, and that branch's documented recovery is to `cd` into the worktree and work there. So a declined entry became a worktree entered anyway. Three wordings that asked the agent to classify the denial text failed context-blind probes in turn: branches labeled by who refused, "the denial reports a decision", and that plus the verbatim denial string quoted as an example. Entry outcomes now split structurally instead. The tool's own `Cannot enter` errors key the reachability test; a denial of the call stops and asks by default, with one exception: a session with no user to ask (its denial says it couldn't prompt) takes the recovery, since nothing was decided. A recovery that follows a denial invites the model to route any denial into it, so the denial branch leads with stopping. Context-blind agents route both denial directions and the four invocation shapes above through their intended routes. ## Verification All of it re-run live against Claude Code 2.1.220 in scratch repos, since the analysis in #3555 rested on properties pinned to 2.1.173/177 and could not be checked from CI. Two claims died that way: an earlier draft credited the exit removal to the harness when it is worktrunk's own hook, and asserted that a `permissions.deny` rule produces a classifiable denial, when it removes the tool from the session entirely. Also recorded in the rationale: the confirmation offers no always-allow and `permissions.allow` cannot suppress it, it fires in `default`, `acceptEdits`, and `auto` while `bypassPermissions` allows silently, and a session that cannot prompt denies without asking. > _This was written by Claude Code on behalf of Maximilian_ --------- Co-authored-by: Claude Fable 5 (1M context) <noreply@anthropic.com>