mirror of
https://github.com/max-sixty/worktrunk.git
synced 2026-09-14 20:00:38 +08:00
codex/remove-codex-cloud-specific-tests
63 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e1745db105 |
feat(list): abbreviate the table's SHA with git, not a fixed slice (#3676)
The Commit cell sliced `&head[..8]` while `--format=json`'s `short_sha` carried git's `%h`, so one commit read `1b9f1d96` in the table and `1b9f1d9` in JSON, and `core.abbrev` reached only the JSON. #3675 gave a detached row's Branch cell the same slice, so the disagreement showed up twice on one row. `ListItem::short_sha` becomes the only abbreviation of `head` anywhere: the Commit cell, a detached row's Branch cell, the statusline, and JSON all render it. `abbreviated_head()` is gone. ## Column widths `COMMIT_HASH_WIDTH = 8` is gone. The Commit column and the Branch column's detached budget both measure the SHAs they will render, so `core.abbrev = 12` no longer truncates mid-hash and the default 7 stops reserving a column nothing fills — the freed character goes to Message. ## Latency `collect()` folds `%h` onto the rows before layout instead of after the skeleton. The batch carrying it already gates the skeleton for `%ct` sort order, so this is a map lookup rather than new I/O, and both cells are identity columns with no placeholder — they still paint in the first frame. Measured on a 40-worktree / 400-branch fixture: git subprocess counts are identical (5 pre-skeleton, 108 for the full run). Pre-skeleton wall time is unchanged; running both binaries in each order, the sign of the difference follows run order rather than the binary (+1.5 ms with this branch second, −0.5 ms with it first), so the residual sits inside drift. ## Behavior change Where the commit-details batch fails, the Commit cell is now empty rather than a slice of a SHA git refused. Age and Message already report that failure the same way, under the same warning, and two snapshots show it. `render_text_cell` also stops styling empty text, so a blank cell no longer emits an escape pair around nothing. ## Reading the diff 160 files, but the hand-written part is +91/−79 in `src/commands/list/` plus a +71 test. The rest is generated. The docs mirrors and help snapshots are symmetric. Of the snapshot lines, content is +799/−799 — every changed line a 1-for-1 hash swap — while +1396 is insta `env:` metadata refreshing on the 128 snapshots this happens to touch. `test_list_abbreviated_sha_follows_git` pins the invariant: the table's hash equals JSON's `short_sha` at git's default and at `core.abbrev = 12`, and a longer prefix is ruled out. It fails against the old fixed slice. > _This was written by Claude Code on behalf of max-sixty_ |
||
|
|
cfd6f099bc |
fix(codex): clear activity marker on session end (#3660)
Codex now exposes `SessionEnd`, but Worktrunk’s Codex plugin only returned the activity marker to idle at turn end. This adds a main-session exit hook that clears the marker, using Codex’s three-second maximum hook timeout so cleanup has the best chance to complete without delaying shutdown. The CLI help, plugin-layout guidance, user documentation, generated mirrors, metadata test, and help snapshot now describe and verify the complete Codex lifecycle. Tested with `cargo run -- hook pre-merge --yes` (4,564 tests passed; 1 skipped). > _This was written by Claude Code on behalf of max_. |
||
|
|
a27cbd42be |
feat(skill): create the worktree by name, fall back to a path (#3636)
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> |
||
|
|
4549715af1 |
docs(claude-code): link the statusline segment details instead of restating them (#3576)
The `## Statusline` section of `claude-code.md` paraphrased three facts that the `wt list statusline` reference already owns: | Fact | `claude-code.md` said | `src/cli/list.rs` says | |---|---|---| | Pace maths | "1.4× the pace that would exactly fill that window, coloured by how much of it would be spent locked out at the cap" | "2.9× the pace that would exactly fill that window… colour deepens with severity as the forecast lockout grows" | | Link degradation | "clickable links where the terminal supports them, plain text where it doesn't" | "Both links are OSC 8, which a terminal that doesn't support them discards" | | CI fetch latency | "runs it in the background… 1–2 second CI fetch invisible" | "reach the network for a second or two, so it fits a statusline the host renders in the background" | #3568 removed the near-verbatim copies from this section but left these paraphrases behind, which is arguably the worse state: two wordings of one fact drift apart independently, and a reader can't tell whether they're the same claim or two different ones. ## What stays The cut isn't to zero. The page shows an example line, so it needs enough gloss for a reader to parse the line in front of them — a bare pointer would make the section a stub. It keeps one sentence naming what the stdin JSON contributes, with "how the links behave" folded into the pointer that was already there. The latency clause also stays, though it is the third duplicated fact. Without it the opening paragraph only restates the command name, and it is the one "why" that justifies the `settings.json` block below it. ## Side effect This leaves the `OSC 8` wording in `src/cli/list.rs` as the single home for the link behaviour. A precise term earns its place in an exhaustive `--help` reference and grated on an end-user integration page — so the jargon was really a symptom of the fact living in two places. Docs-only. Mirrors regenerated by `test_docs_are_in_sync`; `zola build` clean (14 pages, anchors resolve). > _This was written by Claude Code on behalf of Maximilian Roos_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
11a1498fa1 |
docs(list): render wt list statusline's help on the docs site (#3568)
`wt list statusline`'s `after_long_help` documents the three output formats, the Claude Code stdin JSON contract, and the pace segment, but `wt list`'s help had no `<!-- subdoc: statusline -->` placeholder, so none of it reached `docs/content/list.md` or the skill reference. It was terminal-only. Adding the placeholder pulls it in as a `wt list statusline` section under `# Subcommands`, matching how `wt step` and `wt config` expose theirs. ## What reading it on the web surfaced The help text needed edits once it rendered as a page rather than a block below an Options list. A context-blind agent was given the two rendered pages and asked to answer setup questions from them alone; three of its snags were real. **The format lists were stale and used private names.** They read `branch status ±working commits upstream ci url`, while the Columns table higher up the same page calls those `HEAD±`, `main↕`, `Remote⇅`. They also omitted `main…±`, which `format_statusline_segments` has emitted since that column became a default. The lists now use the column names and include it, and `claude-code` is stated as a delta on `table` rather than repeating it. **The formats read as a fixed layout.** The example line on the Claude Code page has nine segments against an eleven-name format string, with no explanation. Three rules were in the code and in no doc: empty cells are omitted, `claude-code` drops `branch` when `dir` already ends in `.<branch>` (`filter_redundant_branch`), and an overlong line drops whole cells worst-priority-first (`fit_to_width`). All three are now stated. **The latency caveat lived only on the Claude Code page.** A reader of the command's own docs had no way to learn it reaches the network. It moves to the reference. That exposed a contradiction with the definition, "Single-line status for shell prompts", against a caveat saying it is too slow for a synchronous prompt — so the definition becomes "Single-line status for the current worktree". This is the one user-visible string change here; it lands in `wt list --help` and the three help snapshots. ## Deduplication with the Claude Code page The pace paragraph and the OSC 8 paragraph were near-verbatim on both pages, and would have rendered twice on the site. `claude-code.md` keeps what is Claude Code-side (install, demo, the example line, and a plain note that the links degrade to unclickable text where the terminal lacks support) and links to the reference for the rest. ## Follow-ups folded in The module docstring at `src/commands/statusline.rs:4` carried `±working commits upstream`, the last occurrence in the tree of the labels this branch retired, and opened "Statusline output for shell prompts" — the framing the definition dropped. Both now match the help text. ## Not done An audit of every `after_long_help` in `src/cli/` found 46 that never reach a docs page. Most are editorial calls rather than oversights: `wt config create` alone would embed ~450 lines of example TOML into a page that already covers that ground by hand. The one that looks like a plain oversight is `wt step rebase` / `wt step push`, the only two of twelve step operations without a marker, and the only two the `## Operations` list leaves unlinked. Covering them needs their openers rewritten first, since both currently restate their definition. Left for a follow-up. Verified with `wt hook pre-merge --yes` (4499 tests) and a `zola build`, which checks internal anchors. > _This was written by Claude Code on behalf of Maximilian Roos_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f3e5c82604 |
feat(statusline): dim the dev-server URL until its port answers (#3561)
Follow-ups to #3550. The dev-server URL in the statusline dims until something answers on its port, matching the `wt list` cell it was already copying in every other respect. ``` ~/w/worktrunk.feature ↕|🤖 ↑2 ↓1 #3550 :17913 server up ~/w/worktrunk ⊂| ↑2 ↓1 #3550 :12107 server down (dim) ``` `url_active` was already collected on this path (the health check runs under the statusline's full-column plan), so the dim consumes something already paid for rather than adding a probe. ## The other half, and why it isn't here The follow-up list also carried "detect OSC 8 support and render accordingly", which is how `wt list` decides its URL cell: linked `:3000` when the terminal supports hyperlinks, the URL in full when it doesn't. I built that, and three review passes took it apart. Recording the path, because the conclusion is the interesting part: 1. The first version probed `stderr`, since a shell prompt captures stdout through command substitution, and forced links on for `--format=claude-code`. An adversarial pass showed the exception reintroduced the bug it was meant to fix: Claude Code re-emits OSC 8 to the terminal rather than drawing it, so Claude Code inside Apple Terminal got the link, and the URL collapsed to a bare `:3000` with its host inside an escape the terminal discarded. 2. Reading the environment alone fixed that and deleted the exception. Then a bug sweep measured what the plain-text fallback actually costs: unlinked, the cell grows from 5 columns to the whole URL, and the URL is the worst-priority segment, so `fit_to_width` drops it first. Against a long dev hostname the linked line kept the URL down to 39 columns and the unlinked one lost it below 82. At 80 columns in tmux the "fallback" showed nothing where the old code showed a clickable port. 3. Separating the two decisions fixed that (collapse to `:port` always, link only when the terminal renders it) but left the probe controlling nothing but escape bytes. At which point an outer-loop pass asked whether it should exist, and it shouldn't: - The [OSC 8 spec](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) guarantees a terminal implementing OSC parsing per ECMA-48 "is guaranteed not to suffer from compatibility issues … the target URI is silently ignored and the supposed-to-be-visible text is displayed, without artifacts." The named buggy set is VTE up to 0.48 (2018), Windows Terminal up to 0.9, Emacs term-mode, and screen with 700+ character URLs. The statusline has emitted unconditional OSC 8 to shell prompts since #199 in January, on that same reasoning. - `supports-hyperlinks` is an allowlist, so its dominant error is the false negative. xterm, foot, mintty, rio, contour and a configured tmux all render OSC 8 and none are named. The probe's certain cost is withdrawing a working link from those users, against a speculative and shrinking risk. - The escape-hatch argument I had for it was inert. Once the probe no longer changed any text, `FORCE_HYPERLINK=0` could not reproduce what the old hardcoded Claude Code gate did, which was print the URL in full and copyable during that regression window. So the answer to "shouldn't we detect?" is that `wt list` detects because there the answer changes what the reader gets: `estimate_url_width` reserves a column wide enough for the full URL, so an unlinked cell can print it. The statusline reserves nothing, so it has no second rendering to switch to. `format_url_cell` now says that, in place of a stale note claiming the statusline's stdout was a pipe "to its editor". ## Also here - `isolate_subprocess_env` scrubs `FORCE_HYPERLINK`. It stands alone: `wt list` probes with `on(stream)`, which is `(FORCE_HYPERLINK set || tty) && allowlist`, and tests pin `TERM=alacritty`, so a developer with `FORCE_HYPERLINK=1` exported flips the `wt list` table snapshots from full URL to linked `:port`. - The `table` and `claude-code` format lists in `wt list statusline --help` name the `url` segment, which they emit and never listed. - A `--format=claude-code` test pins where the URL segment lands among the directory, CI and model segments. ## Verification Full gate green (`cargo run -- hook pre-merge --yes`, 4486 tests). Beyond the suite, the built binary rendering a live and a dead port, and the drop threshold measured at six widths to confirm the segment behaves as before. ## Known, not addressed A `[list] url` with no port is permanently dim on both surfaces: `UrlStatusTask` only connects when a port parses, so `url_active` stays `None`, and the `== Some(true)` test reads "never checked" as "nothing listening". Pre-existing in `render.rs`, which this change doesn't touch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) > _This was written by Claude Code on behalf of max_ --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
14d4d92532 |
feat(statusline): make the CI and URL segments clickable in Claude Code (#3550)
The statusline suppressed OSC 8 hyperlinks in Claude Code mode, so its CI segment was colored but not clickable and its dev-server URL printed in full. Claude Code renders OSC 8, so both segments now link the way they already do in `wt list`. ``` ~/w/worktrunk.statusline-osc8-hyperlinks ↕|🤖 ↑2 ↓1 ^+93 -24 #3550 :17913 Opus 4.8 └link┘ └link┘ ``` ## Why it was off The gate dated from 2026-01-02, inside Claude Code's OSC 8 regression window — links worked through 2.0.76, broke around 2.1.3, and got a partial IDE-terminal-only fix in 2.1.42 ([anthropics/claude-code#26356](https://github.com/anthropics/claude-code/issues/26356)). The premise was correct when written and has since expired; that issue was auto-closed for inactivity rather than on a fix, so the tracker understates current support. ## Verification PTY-captured the raw byte stream from Claude Code 2.1.218, with `TERM_PROGRAM`/`VSCODE_*` stripped so it exercises the standalone-terminal path that was reported broken. Claude Code doesn't strip or blindly pass through — it parses OSC 8 into its frame model, assigns a hyperlink id, and re-emits canonically (normalizing an ST terminator to BEL): ``` PRE ESC]8;id=1l7bqdh;https://example.com/ST-PROBE BEL STLINKTEXT ESC]8;; BEL MID … ``` It holds in both the normal and alt-screen (`CLAUDE_CODE_NO_FLICKER=1`) render paths, with `FORCE_HYPERLINK` unset. Driving the real `wt` binary through Claude Code end to end yields both links live: ``` LINK: https://github.com/max-sixty/worktrunk/pull/3550 LINK: http://127.0.0.1:17913 ``` Degradation is graceful: a terminal or multiplexer that drops OSC 8 shows the same text, just not clickable (tmux only gained OSC 8 in 3.4; zellij and Alacritty support it). ## Shape of the change With links unconditional for the statusline, the plumbed `include_links` flag had one value, so it collapses into the segment builder. `format_url_cell` likewise takes the link decision from its caller rather than probing the terminal, matching `PrStatus::format_cell` — the statusline's stdout is a pipe, so `supports_hyperlinks` reports false there even though the consumer renders OSC 8. That left `hyperlink_stdout` with no callers, so it goes. `format_cell` keeps its `include_link` parameter: `wt list` passes the terminal probe, and the picker passes `false` because a `--prs` row never reaches the strip path. `format_url_cell` moved next to `estimate_url_width`, which budgets the column against it — the two have to agree on when a cell collapses to `:port` and were in separate files. The URL segment also gets shorter: the URL rides inside the escape sequence, so `http://127.0.0.1:17913` becomes `:17913`, returning 16 columns on a line that budgets by width. ## Safety of the truncation interaction `truncate_visible` ends its cut with `\e[0m`, which resets colour but leaves an OSC 8 link *open* — a severed link would make the rest of the terminal line clickable, and `ansi_cut` really will sever one if reached. It can't be reached, because the two cuts never meet: `fit_to_width` drops whole segments worst-priority-first and stops at one, so character truncation only ever lands on a best-priority survivor — Directory (0), Branch or Model (1) — none of which carry escapes beyond SGR. Every link-bearing segment is strictly worse (CI 5, URL 9), so each is dropped entire first. Reviewing the branch turned up that the numbered comments in `format_statusline_segments` had drifted from `COLUMN_SPECS` — CI was labelled 9 (it is 5) and the URL 8 (it is 9), with branch-diff and upstream also off — and the first version of the test had taken those stale numbers as its specification. The comments are corrected and the test now rests on the invariant above, which doesn't depend on where CI sits. It sweeps widths 1–90 over both links, asserts it spans every drop stage, and pins that the URL goes before CI. A second test pins that the hidden URL costs no visible width (`ansi_strip` drops OSC 8 for both terminators), so priority budgeting isn't inflated. ## Docs The Claude Code statusline page now says the segments are clickable — it's the feature's own page and said nothing about it. The `wt list --help` JSON field description gains the links but stays short: an earlier, longer wording shrank the help table's Field column and wrapped two dozen unrelated rows. ## One judgement call worth flagging `format_statusline_segments` also feeds plain `wt list statusline` (shell prompts) and the JSON `statusline` field. The CI link was already unconditional on both before this change; what's new is that the URL cell renders as a linked `:3000` rather than the full URL, so a consumer that strips OSC 8 sees only `:3000`. The structured `url` / `dev_server.url` field still carries the full URL, and `:3000` still answers "which port", so this reads as the right trade — but it is the one place the collapse to a constant reaches a renderer that isn't Claude Code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) > _This was written by Claude Code on behalf of max_ --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
512a94de2f | feat(plugin): ship Codex-native activity hooks (#3364) | ||
|
|
830cc850dd |
feat(list): show the main…± column by default; --full gates only off-machine columns (#3236)
Move the `main…±` branch-diff column (line diffs since the merge-base) into the default `wt list` view — it's pure local git backed by a persistent content-addressed cache, so the original blocking-walk concern no longer applies. `--full` now gates only the two off-machine columns: CI status (network) and LLM branch summaries. The interactive picker (`wt switch`) follows suit and is effectively `wt list --full`; on narrow terminals with the preview shown, CI clips past the split and alt-p reveals it. Also adds a `.typos.toml` ignore rule for truncated word fragments glued to the … ellipsis, so the narrower Message column's truncated quickstart embed doesn't get spell-"corrected" by pre-commit.ci. |
||
|
|
2f0ee196e6 |
statusline: color the rate-limit pace notice by expected-lockout severity (#3229)
The statusline pace segment (the rate-limit warning, e.g. `1.4×(10am–3pm)`) was a flat yellow whenever it showed. This grades its color by how bad the projected hit actually is, deepening dim → dim-yellow → yellow. ## Two axes, separated The segment already gated its appearance on **confidence** — `P(over)`, the Bayesian probability of crossing the cap before the window resets. But `P(over)` saturates near 1, so it can't also grade *severity*: a near-certain cross in the last hour and one halfway through the week both read ≈1. Color is now driven by a second metric, **`expected_lockout`**: the expected fraction of the window that will be spent throttled, integrated over the posterior on the consumption rate (7-point Gauss–Hermite). It keeps discriminating after `P(over)` has flat-lined, and degrades to ~0 on thin early-window bursts — so a 5× burst on Monday doesn't light up the segment. The displayed number stays the raw `u/t` pace; only the color carries the Bayesian severity. ## Per-window thresholds from duration, not magic numbers The same lockout *fraction* costs very different real time across windows: 0.30 of the 5-hour window is 90 min, 0.30 of the weekly window is ~2 days. So `colorize_pace` compares a **duration-weighted cost** `= lockout · √(window / 5h)` against one shared cutoff pair, rather than two hand-tuned per-window pairs. The 5-hour window keeps its existing calibration (weight 1); the weekly window is sensitized ~5.8×, escalating on real time forfeited. Duration drives this (known exactly, 33.6× apart) — not σ, which is only 1.17× apart and already folded into the posterior integral. ## Reviewing Start at `src/commands/statusline.rs`: `expected_lockout` (the severity metric + its module-header rationale), `lockout_cost` (the √-duration weighting), and `colorize_pace`. The `-vv` trace now logs `lockout` and the weighted `cost` for future threshold tuning. Well-covered: the metric (bounds/monotonicity), the color tiers, the cross-window weighting, and the refactor's behavioral equivalence of `p_over` are all unit-tested; the 5-hour window is unchanged (snapshots confirm). The threshold anchors (dim-yellow 0.10, yellow 0.30, exponent 0.5) are feel-calibrated and meant to be tuned from real traces. ## Note: included an unrelated CI fix Merging main surfaced a pre-existing failure in `test (linux)`: `switch_picker_alt_l_ignored_list.snap` was left stale by the #3226 merge race (it still rendered the old `Age` column while every sibling picker snapshot already shows `Remote⇅`). Main pushes don't run the full suite, so it slipped in and blocks any PR that merges with main. This PR regenerates that one snapshot — it's unrelated to the statusline change but required to get CI green. > _This was written by Claude Code on behalf of max_ --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a5622d1424 |
refactor(plugin): rework /wt-switch-create cross-repo handling around reachability (#3118)
The `/wt-switch-create` skill could create a worktree in another repo but then leave the session unable to work in it cleanly — a real session hit that and read it as a failure. This reworks the cross-repo handling around what the harness actually does, verified live and against the Claude Code 2.1.177 binary. The model: `EnterWorktree` re-roots within the repo the cwd is in, and a session reaches another repo's worktree whenever that repo is in `permissions.additionalDirectories` (via `cd`). So the skill now creates the worktree (`wt -C`), tries `EnterWorktree`, and on a cross-repo rejection `cd`-tests reachability — working in place if it sticks, or escalating (ask the user to add the repo, or a parent like `~/workspace`, to `additionalDirectories`) when it doesn't. The procedure dropped from five steps to three and no longer hands off to a separate session. `skills/wt-switch-create/rationale.md` now carries the harness-mechanics reference inline (M1 `cd` / M2 `EnterWorktree`, how they compose, the master gate), consolidated from a separate doc. `docs/content/claude-code.md` is trimmed back to a two-sentence overview — the mechanism detail belongs in the skill, not the website page. Skill- and docs-only; `test_docs_are_in_sync` and `test_plugin_layout_is_consolidated` pass. > _This was written by Claude Code on behalf of max_ --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
fd3ae5bff4 |
feat(plugin): optional branch name and single-route flow for /wt-switch-create (#3058)
The `/wt-switch-create` branch argument becomes optional (a name is
picked from the task when omitted), and the skill's workflow is rebuilt
from a guard-heavy three-route flow into one route plus one error-driven
fallback.
**The new flow**: pick a branch name if none was given, create the
worktree with `wt -C <repo> switch --create <branch> --no-cd
--format=json` in Bash (rerunning without `--create` when the user named
an existing branch), re-root the session with `EnterWorktree({path})`,
and on a graceful rejection (different repo, pinned cwd, nested session)
work in the worktree via absolute paths instead.
**Why the rewrite**: the old flow's guards encoded hypotheses about
Claude Code that turned out wrong or untested. The load-bearing
discoveries, each verified by binary inspection, official docs, or live
tests and documented in the new `skills/wt-switch-create/rationale.md`:
`EnterWorktree({path})` accepts worktrunk's sibling layout on first
entry; every rejection it can raise is side-effect-free, so
try-then-fallback replaces prediction; the previous
`EnterWorktree({name})`/hook route silently auto-removes a clean
worktree at session exit, which is wrong for durable worktrunk worktrees
(path-entered worktrees persist); and the "pinned cwd" the old guards
defended against was a misdiagnosis of the harness resetting `cd` that
leaves the session's working directories.
**Hook fix**: both `WorktreeCreate` and `WorktreeRemove` pipelines are
wrapped in `bash -c 'set -o pipefail; ...'`. Without it the trailing
`jq` exits 0 on empty input, so a `wt` failure surfaced as a
"successful" hook with an empty path. The wrapper is explicit `bash`
because hooks spawn via `/bin/sh -c`, where dash rejects `set -o
pipefail` fatally. `xargs -I{}` also fixes worktree paths containing
spaces. Tested end-to-end in both directions (success exits 0 with the
path; failure exits 1 with empty stdout).
Docs, README, and plugin CLAUDE.md are aligned with the new mechanism.
The agent-isolation use of the `WorktreeCreate` hook is unchanged.
Review trail: the design survived an evidence audit (every rationale
claim independently re-verified), a code review (bare directory-named
tokens no longer parse as the repo; mid-session moves now stash
uncommitted work across), and a level check that considered and rejected
fixing this in `wt` itself (an idempotent `--create` cannot encode who
chose the branch name, which determines the correct recovery).
> _This was written by Claude Code on behalf of max_
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
8f44e98636 |
feat(statusline): show used % instead of pace above 90% of the rate limit (#3057)
When the statusline rate-limit segment shows and the binding window is above 90% used, it now displays the used percentage instead of the pace ratio — `95%(8:30am–1:30pm)` rather than `1.1×(8:30am–1:30pm)`. Near the cap, how much is left matters more than how fast it's going; the pace form is unchanged below 90%. The show condition is untouched (P(over) ≥ 0.5 via the existing Bayesian gate), so this only changes which number renders once the segment is already visible. Strictly greater-than 90: a window at exactly 90% still shows pace. Unit test pins the switch and the boundary; the 95%- and 100%-used integration snapshots now render the percent form, and the help text and claude-code docs describe it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) > _This was written by Claude Code on behalf of max_ Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ce7c7c4861 |
fix(statusline): drop the word "pace" from the rate-limit segment (#3053)
The Claude Code statusline's rate-limit segment read `2.9×pace(Tue–Tue
5pm)`. The word "pace" made the longest segment longer without earning
the space, so the segment now renders as `2.9×(Tue–Tue 5pm)` and `wt
list statusline --help` explains the reading instead: 2.9× the pace that
would exactly fill the window, plus a one-line gesture at why the
segment doesn't show on every over-pace reading ("Likely" is a Bayesian
forecast; early-window bursts don't trigger it).
While there, the help page's structure had drifted: the `claude-code`
bullet under "Output formats" and the "Claude Code mode" section both
described the stdin context and the added segments. The segment list now
lives in the bullet (parallel with the `table` bullet) and the section
carries the input fields and the pace explanation. Ranges use en-dashes
(`0–100`), and the hard-wrapped paragraphs are unwrapped to match
`after_long_help` convention elsewhere in the CLI.
`docs/content/claude-code.md` and its skill mirror drop the word from
the example line and segment description. CHANGELOG.md keeps the old
spelling as a historical record.
> _This was written by Claude Code on behalf of max_
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
c511d3fa2b |
feat(list): show PR/MR number in the CI column (#3041)
The CI column in `wt list --full` (and the statusline) previously showed a single colored dot. It now shows the branch's open PR/MR reference — `#3035` on GitHub/Gitea/Azure DevOps, `!3035` on GitLab — colored by CI status, dimmed when stale, and hyperlinked to the PR. When no number is available (branch workflows without a PR/MR, pre-number cache entries, or a number wider than the allocated column), the cell shows a bare `#` in the same colors. Fetch errors always render `⚠`, even when a number is known — Error and Conflicts share yellow, so a yellow `#3035` would read as a conflicted PR. The branch merges main's review-state feature (#3044): review colors (magenta/cyan) and draft dimming apply to the number cells exactly as they did to the dot, and the `--help` legend shows colored `#` samples for all seven states (the interim version had dropped the colored samples from the legend entirely). ## The width problem `wt list` renders skeleton-first: column widths are fixed before any CI data arrives, and the table never resizes mid-render. The PR number's width therefore has to be known up front. The solution is a repo-level ratchet cache (`.git/wt/cache/pr-number/max.json`) holding the largest PR number any fetch has seen — PR numbers are monotonic per repo, so the value needs no invalidation. Pre-skeleton, `collect` reads that one file and sizes the column exactly; on a cold cache the estimate is 5 chars (`#9999`). A number that outgrows the estimate renders as the bare `#` for that run and sizes correctly on the next run once the ratchet records it. The ratchet is deliberately separate from the per-branch `ci-status/` entries so the width hint isn't coupled to branch-entry retention, and `detect` re-ratchets on cache hits too, so a deleted or racily regressed `max.json` heals from locally cached numbers instead of waiting out the TTL. ## Reviewer's map - `src/commands/list/ci_status/mod.rs` — `PrRef` (number + forge sigil, `PrRef::pr`/`PrRef::mr` constructors), `PrStatus.number` (serde-default so pre-existing cache entries still deserialize, rendering `#` until their 30–60s TTL expires), `format_cell` width-aware renderer with the Error guard, ratchet in `detect` (both cache-hit and fetch paths) - `src/commands/list/ci_status/cache.rs` — `MaxPrNumber` ratchet (read/ratchet/clear) - `src/commands/list/ci_status/{github,gitlab,gitea,azure}.rs` — each fetcher populates the number (`gh --json number`, `iid`, Gitea `number`, `pullRequestId`); GitLab's mr-view-failure path carries the iid/URL/review state into the error status so the `⚠` stays clickable - `src/commands/list/layout.rs`, `collect/mod.rs` — width estimate threading - `src/commands/list/render.rs`, `model/item.rs` — table cell and statusline both go through `format_cell` - `src/commands/list/json_output.rs` — `ci.number` field - `src/commands/config/state.rs` — ratchet shown by `state get`/`cache get` (table + JSON) and swept with the CI cache category, including the deprecated `ci-status clear --all` path - `src/md_help.rs`, `src/help.rs` — legend colorization rules rewritten from `●` to `#` (terminal + website) Most of the diff is snapshot churn from the column width and glyph changes plus regenerated docs mirrors. Known trade-offs: concurrent statusline ratchet writes can transiently lose an update (monotonic, re-learns on the next render, documented at the write site); one anomalously high PR number widens the column until `wt config state cache clear`; an open Azure DevOps PR still shows gray `NoCI` instead of its pipeline status — a pre-existing gap, now marked `TODO(azure-pr-pipeline)`. Testing: unit tests for `format_cell` (including the Error-with-number and oversized-number link cases)/`pr_ref_width`/ratchet/width estimates; integration coverage for all four forges with real numbers (the Gitea mocks now exercise the number path too), review-state × number composition, the GitLab mr-view-failure `⚠` and branch-pipeline success paths, cache-TTL expiry → refetch, the statusline number view, and the `wt config state` surfaces. > _This was written by Claude Code on behalf of max_ --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
51e32f4425 |
fix(statusline): space the context gauge emoji from its percent (#2944)
The context gauge in the Claude Code statusline rendered the moon-phase emoji flush against the percent (`🌕42%`). Most terminals draw emoji as double-width and bleed the glyph into the cell to its right, so the moon visually collided with the digits. This adds a trailing space — `🌕 42%`. The flush-packing convention still holds for ASCII and narrow markers (`@+1`, `↓1`, `●`); emoji are the exception. That distinction is now documented in the `writing-user-outputs` skill so future output keeps it consistent. Tests, the three inline statusline snapshots, nine rate-limit `.snap` files, and the hand-authored docs example (auto-synced to the skill reference) are all updated. > _This was written by Claude Code on behalf of max_ Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3be40ad36f |
docs: writing-prose cleanup (claude-code, hook, llm-commits, tips-patterns) (#2922)
Targeted writing-prose cleanup across four docs pages. Per-page summary: **`claude-code.md`** — dropped the duplicate skill definition (the lead paragraph already covered it) and rewrote the `## Configuration skill` opener as "With the `/worktrunk` skill, the agent can help with:" so the section stands without depending on its heading. **`hook.md`** (edits in the `Hook` command's `after_long_help` in `src/cli/mod.rs`) — four small fixes: - Dropped "As usual, post-* hooks run in the background" (already established earlier on the page). - Split the perspective+cwd run-on paragraph; the three `cwd ≠ worktree_path` cases (`pre-switch`, `post-remove`, `post-merge` with removal) are now a bulleted list. - Folded the semicolon-spliced conditional-variables enumeration into the existing template-variables table descriptions (added "switch/create only" to `base`, "when target has a worktree" to `target_worktree_path`, hook-type qualifiers to `pr_number`/`pr_url`). The follow-on guidance about undefined variables and conditionals/defaults stays. - Trimmed the redundant `sanitize` sentence from the filters paragraph (the table above already says it). The two hook-types tables (event×pre/post matrix + per-hook purpose) are intentionally kept — they're complementary, not duplicate. **`llm-commits.md`** — dropped the "How it works" stub that mostly restated the lead, and the "There are sensible defaults, but templates are fully customizable" hedge. **`tips-patterns.md`** — three trims: - The `wt step tether` recipe's middle "This matters because…" sentence (covered by tether's own docs). - The ports-deterministic line tightened to lean on the concrete example rather than restate the abstract claim. - "in real-time" filler dropped from the Monitor hook logs section. Auto-synced skill mirrors (`skills/worktrunk/reference/*.md`) carry the same edits. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e22ed71282 |
feat(statusline): add Claude Code rate-limit pace segment (#2899)
## Motivation
Claude Code exposes
`rate_limits.{five_hour,seven_day}.{used_percentage,resets_at}` on its
statusLine JSON. Without UI for it, users only learn they're over-pacing
once Claude throttles them mid-task. We want a single-glance warning
that surfaces *before* throttling, and only when continuing at the
current pace would actually blow the cap — not whenever raw `u/t > 1`,
which fires constantly on early-window bursts.
## Change
A new yellow segment `1.3×pace(10am–3pm)` appears in the
`--format=claude-code` statusline when either rate-limit window is
meaningfully likely to be hit before reset. For the weekly window it
formats as `1.9×pace(Mon–Mon 3pm)`. Only the worse-projected of the two
windows is shown; the segment is hidden entirely when both are safe.
The trigger is a Bayesian forecast on `P(final ≥ 100%)`: cumulative
consumption modelled as Brownian-with-drift, prior on the long-run rate
pulled toward "most windows finish under" (`m₀ = 0.8`), posterior
predictive Gaussian. Segment appears only when `P > 50%`. Early-window
noise (e.g., 5% used at 3% elapsed → naive `u/t = 1.67` but P(over) =
42%) stays correctly hidden.
The *displayed* number is the naive `u/t` ratio (one decimal), not the
Bayesian posterior — the user sees an honest measurement of "what you've
actually done over the elapsed window," while the smoothing stays
internal where it decides visibility. Window bounds in parentheses
locate which 5h or weekly window the pace was measured over.
Adjacent delimiter cleanup in the same file, so the new segment fits the
existing line:
- Drop the vestigial `| ` prefix on the model segment — the two-space
inter-segment separator already does the job
- Drop the space inside `🌕 42%` → `🌕42%`, matching the other prefix-char
segments (`@+1`, `↓1`, `?^|`)
- En-dash inside the parenthetical (`10am–3pm`, not `10am-3pm`) —
typographically correct for a range, visually distinct from the ASCII
`-3` used for line deletions
## Tests
Eight file snapshot tests cover hidden/well-paced, 5h binding, the `:mm`
time branch, 5h at-limit edge, 7d binding (day+time form),
both-windows-pick-worse, dirty-worktree combined state, and narrow-width
segment drop. Time and timezone are pinned via the existing
`WORKTRUNK_TEST_EPOCH` env hook plus a per-subprocess `TZ=UTC`.
23 new unit tests: erf reference-value accuracy, standard-normal CDF,
`p_over` boundaries + monotonicity + six Python-prototype calibration
points, `format_clock`, `format_window_bounds` for both window kinds,
`select_binding_window` selection logic across five scenarios,
`format_rate_limit_segment` shape, and `parse_rate_limits` JSON parsing
across four cases.
|
||
|
|
ec62580c29 |
revert(hooks): keep docs on pre-start/post-start; code accepts both (#2857)
Per @max-sixty's [direction in #2838](https://github.com/max-sixty/worktrunk/issues/2838#issuecomment-4509447593): revert the docs portion of #2840 and keep the code. Docs continue to recommend `pre-start`/`post-start`; both names work in code so anyone who already followed the briefly-changed docs (e.g. @EcksDy) isn't stranded once a release ships these aliases. ## User-visible — back to `pre-start`/`post-start` - README, docs site, skill mirrors, `dev/*.example.toml`, `plugins/worktrunk/README.md`, `flake.nix`, `.config/wt.toml` - `src/cli/mod.rs` / `src/cli/config.rs` / `src/cli/step.rs` / `src/help.rs` after_long_help and example snippets — and the auto-synced `docs/content/` and `skills/worktrunk/reference/` mirrors - `wt hook --help` canonical subcommand names; completion advertises `-start` only - `HookType` Display via strum, serde `rename`, and clap `ValueEnum` name — all `pre-start`/`post-start`. The Rust variant identifiers stay `PreCreate`/`PostCreate` (internal; we already paid for that rename in #2840, and now the eventual flip is a Display-only change) - `HooksConfig` serde canonical fields ## `*-create` still works (kept code) - `wt hook pre-create` / `post-create` — CLI alias on the canonical subcommand - `pre-create` / `post-create` in config: top-level, `[hooks.*]`, and per-project, in string, `[table]`, and `[[array-of-tables]]` form. Mechanism: serde `alias = ...` on the field, plus a silent in-memory rename in `migrate_content()` so the round-trip in `unknown_tree` doesn't flag table forms as schema-unknown. - The pre-0.32.0 `post-create` fatal-load-error machinery stays removed — the name is reclaimed, and both forms load without error. ## Smaller bits - `valid_user_config_keys()` / `valid_project_config_keys()` append `pre-create` / `post-create` so the unknown-field round-trip skips them. `test_valid_*_keys_all_deserialize` skips both aliases (they can't sit alongside the canonical without a duplicate-field error). - `DEPRECATED_SECTION_KEYS` drops the `pre-start`/`post-start` entries #2840 added — `pre-start`/`post-start` are canonical again. - `find_pre_start_from_doc` / `find_post_start_from_doc` / `find_renamed_hook_key` / `is_non_empty_item` / `migrate_start_hooks_doc` and their tests are removed; the migration direction flips via a new `migrate_create_hooks_doc` (silent, mirrors the prior shape). - Test files `e2e_shell_post_create.rs` and `post_create_commands.rs` rename back to `_post_start_` (via `git mv`, so the rename shows as a rename). ## Testing `cargo run -- hook pre-merge --yes` — 3806 tests pass; the 10 failures are all `case_4` of `shell_wrapper::unix_tests::*` (nu-shell case; `nu` isn't installed in this runner; same failures occur on `main`). Also manually verified that a fresh `wt switch --create` against a project config with `[post-create]` loads cleanly with no unknown-field warning and the hook fires as `post-start`. ## Follow-up Per @max-sixty: in a couple of weeks, once a release with both-names-work is out and users have had a chance to upgrade, the docs flip is straightforward (most of it is in `src/cli/mod.rs`'s `after_long_help` and the doc-sync test propagates). Re #2838. Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
d7e3f88422 |
feat(hooks): rename worktree-creation hooks to pre-create/post-create (#2840)
Phase 1 of the staged hook rename tracked in #2838: the worktree-creation hooks `pre-start`/`post-start` become `pre-create`/`post-create`. The old names keep working with no deprecation warning yet (Phase 2, months out, adds the warning). ## What changes - `pre-create`/`post-create` are canonical everywhere: the `HookType` enum, the `HooksConfig` serde fields, the `wt hook` CLI, completion, and all docs. - Old names keep working: `migrate_content()` rewrites `pre-start`/`post-start` config keys to `-create` before serde, and `parse_hook_type` accepts the old CLI names as silent aliases. `wt config update` rewrites them on disk; `wt config show` shows the migration diff. `wt hook <type>` execution and `wt hook show` both accept the old names; completion and `--help` advertise only the canonical names. - `detect_deprecations()` flags the old keys so `update`/`show` act on them, but `format_deprecation_warnings()` stays silent (Phase 2 adds the warning). A new empty-warnings guard in `check_and_migrate` keeps a `-start`-only config from emitting a stray hint. - The dead pre-0.32.0 `post-create` machinery is removed: the fatal `POST_CREATE_REMOVED_MSG` load error, the vestigial `HooksConfig.post_create` merge-fold, and `find_post_create_from_doc`. `post-create` is reclaimed as the canonical background creation hook. ## Semantic flip Before v0.32.0, the key `post-create` named a *blocking* hook. It now names the *background* one. Since v0.44.0, a pre-0.32.0 `post-create` config is a fatal load error on the `check_and_migrate` paths: `ProjectConfig::load` and user/system config loading, which fire on essentially every `wt` command. A repo carrying one has been unusable ever since. The one path that skips that check is `project_config_at_ref` (the base-ref read behind `wt switch --create`), which applies only structural migration. A pre-0.32.0 `post-create` surviving solely on a base ref, never checked out into a worktree, would now load as a background hook rather than folding into the blocking `pre-start`. That edge case is accepted: once `post-create` is valid again, reclaiming the name and detecting the dead key are mutually exclusive. ## Reviewing this diff 205 files, but the substance is ~36 files under `src/`. The rest is regenerated snapshots and auto-synced doc mirrors. Start with: - `src/config/deprecation.rs` — detection (`find_renamed_hook_key`), migration (`rename_hook_key`), removal of the fatal block, the empty-warnings guard, and the `DEPRECATED_SECTION_KEYS` entries that stop unknown-field detection from flagging the migrated keys. - `src/config/hooks.rs`, `src/git/mod.rs` — the serde field and enum renames. - `src/config/project.rs` — `ProjectConfig::load` deserializes `check_and_migrate`'s migrated content, so a current-worktree config using the old keys loads into the canonical fields. - `src/cli/hook.rs`, `src/commands/hook_commands.rs`, `src/completion.rs`, `src/main.rs` — the CLI alias layer; `wt hook show` accepts the old type names as hidden value-parser aliases. - `src/cli/mod.rs` — the `wt hook` docs, including the soft-deprecation note linking #2838. The ~93 modified snapshots also pick up deterministic env-block lines (`GIT_*: ""`, `LLVM_PROFILE_FILE`) that pre-existing snapshots already carry. That is stale-snapshot drift surfaced by the regeneration, not a behavior change. ## Testing Full suite green (3799 tests). New coverage: `snapshot_migrate_start_to_create` (migration preserves value shape and position), `test_deprecated_start_hook_key_runs_silently` and `test_standalone_hook_start_alias_runs_silently` (old config and CLI names run with no warning), `test_config_show_displays_start_hook_migration` (`config show` reveals the diff without an "unknown field" warning), and `test_hook_show_accepts_deprecated_start_hooks` (a current-worktree config using the old keys loads, and `wt hook show` takes both the canonical and the deprecated type arguments). Part of #2838. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
5b2a41ee73 |
feat(plugin): detect Gemini extension; document OpenCode and Gemini install (#2819)
Closing two gaps a prior plugin-implementation review surfaced: Gemini's
native install path was invisible (no `wt config show` section, no
user-facing docs), and OpenCode — which has the most substantial
implementation of the four — was absent from the plugins page despite
being the only tool that ships activity tracking via a worktrunk-written
plugin.
`wt config show` now renders a `GEMINI CLI` section gated on
`which::which("gemini")`. Installed state is detected by parsing
`~/.gemini/extensions/worktrunk/gemini-extension.json` and checking
`name == "worktrunk"` — exactly where `gemini extensions install` clones
the extension. Fail-closed on a missing home, unreadable file, or
invalid JSON, matching the existing claude/codex/opencode detection.
Test infrastructure (`gemini_installed` field on `TestRepo`,
`setup_mock_gemini_installed`, `setup_gemini_extension_installed`) and
the two new snapshot tests mirror the OpenCode equivalents
line-for-line.
`docs/content/claude-code.md` retitled "Agent Integration" and
restructured around a four-tool capability table covering configuration
skill, activity tracking, worktree isolation, and `/wt-switch-create`.
OpenCode and Gemini get install subsections; the activity-tracking
section is generalized from Claude-only to Claude+OpenCode+Gemini so the
heading matches the table. A Codex-only sentence under "Worktree
isolation" was redundant with the table and dropped.
## Verified end-to-end
After **v0.52.0** went `Latest`, `gemini extensions install
https://github.com/max-sixty/worktrunk` resolves via `/releases/latest`
→ the v0.52.0 source tarball (which now carries the root
`gemini-extension.json` from #2807) → install succeeds. `gemini
extensions list` shows `Type: github-release`, `Release tag: v0.52.0`,
with both skills (`wt-switch-create`, `worktrunk`) loaded via
`${extensionPath}/skills/`. Live `wt config show` reports `✓ Extension
installed`. The bare URL is what the docs use — `owner/repo` shorthand
returns "Install source not found" against gemini-cli, so the full URL
is the right form.
Per the `release` and detection paths in gemini-cli 0.42
(`bundle/chunk-CHERUG6W.js` lines 44170–44290 / 44640–44675), the URL
install always tries `releases/latest` first when releases exist, and
only auto-falls-back to `git clone` if there's no release data at all —
so the work landed in #2807 on `main` only became user-visible once
v0.52.0 was cut. This PR ships the detection and docs that have now been
confirmed accurate against the live release path.
Pre-merge gate green locally (3747 tests, 0 skipped; pre-commit clean;
`test_docs_are_in_sync` green so the skill reference and `llms.txt` are
in sync).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
233cd493d0 |
fix(codex): drop activity-marker hooks Codex can't drive (#2786)
The Codex plugin shipped `hooks.json` with `SessionStart`→💬, `UserPromptSubmit`→🤖, and `Stop`→💬. But codex-cli 0.130.0's `HookEventNameWire` schema (extracted from the binary) has **no `Stop`/turn-end event**: `PreToolUse`, `PermissionRequest`, `PostToolUse`, `PreCompact`, `PostCompact`, `SessionStart`, `UserPromptSubmit`. So on Codex the 🤖 marker set at `UserPromptSubmit` could never return to 💬 — a Codex worktree stuck at "working" for the entire session. That's worse than the docs claimed ("rests at 💬"). This removes the marker hooks entirely until Codex exposes a turn-end hook event. The Codex plugin now ships only the configuration skill; activity tracking joins worktree-isolation and `/wt-switch-create` as Claude-Code-only. A re-enablement comment (exact conditions + the files to restore) lives in `src/commands/config/codex.rs` and a new `CLAUDE.md` → "Codex Plugin" section, which also documents the accepted tradeoff that the `skills/` symlink exposes the Claude-only `wt-switch-create` skill to Codex (harmless — Codex can't act on it). Bundled adjacent fixes: `plugin.json` metadata drift (`description`/`longDescription` no longer claim activity hooks; `homepage`/`websiteURL` `https://worktrunk.dev/claude-code/` → `https://worktrunk.dev`), and the install/uninstall/`--help`/docs text made honest about Codex having only the configuration skill. **Reviewer navigation:** `src/commands/config/codex.rs` + `src/cli/config.rs` (runtime + `--help` text), `docs/content/claude-code.md` (skill ref + `llms.txt` auto-synced), `CLAUDE.md` (new section), `tests/integration_tests/config_show.rs` (`test_codex_plugin_metadata_is_valid_json` now asserts `hooks` absent + guards metadata regression). Snapshot env-block churn is stale-fixture convergence to the repo's current `/nonexistent/wt/` convention, not a leak. **Testing:** Full pre-merge gate green locally (3704 tests, 0 skipped; lints clean). The metadata test was strengthened; the marker behavior itself is removed, so there's nothing runtime-testable to add. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f00b787e1b |
fix(codex): relocate plugin into subdir so the marketplace resolves it (#2782)
## Problem #2780 put `.agents/plugins/marketplace.json` at the path codex-cli reads, but the plugin was still **uninstallable**. Driving the Codex `/plugins` TUI surfaced the real, complete set of constraints in codex-cli 0.130.0: 1. A plugin `source` must be the **object** form `{"source":"local","path":"./plugins/<name>"}` — a bare string `"./"` fails with `local plugin source path must not be empty`. 2. The path must be a **non-empty subdirectory**. Codex resolves it relative to the marketplace root (repo root), so a repo-root plugin (`./.codex-plugin/` at the top) can't be referenced at all — every Codex marketplace plugin lives in `./plugins/<name>/.codex-plugin/plugin.json`, exactly like the OpenAI-curated marketplace. So the plugin had to move into a subdirectory. ## Changes - Move `.codex-plugin/` → `plugins/worktrunk/.codex-plugin/` and `hooks/` → `plugins/worktrunk/hooks/` (a plugin-root sibling, matching the curated layout where `skills/` and `assets/` sit beside `.codex-plugin/`). - `plugins/worktrunk/skills` → symlink to `../../skills` so the shared `worktrunk` + `wt-switch-create` skills stay single-source (no duplication; the repo still auto-syncs `skills/worktrunk/reference/`). - Rewrite `.agents/plugins/marketplace.json` with the object `source`, `policy.installation: "AVAILABLE"`, and `category` that Codex actually parses. - `plugin.json` `hooks` → `./hooks/hooks.json` (plugin-root relative). - Update the install hint (`src/commands/config/codex.rs`), docs, auto-synced skill reference, the validity test, and the install snapshot. ## Verification Verified end-to-end against codex-cli 0.130.0: repointed a local marketplace at the worktree and drove `/plugins`. The `Worktrunk` marketplace now appears (plugin count 123 → 124), the plugin shows as **Available** with **both skills resolved** (`worktrunk:worktrunk`, `worktrunk:wt-switch-create` — the symlink works), and it installs. `test_codex_plugin_metadata_is_valid_json` now asserts the object `source`, `policy.installation`, `interface.displayName`, and the new paths. `test_docs_are_in_sync` and the install snapshot are updated; full `config_show` module (119 tests) + pre-commit pass. **Caveat (documented, not a regression):** plugin-bundled *command* hooks remain gated by Codex's `plugin_hooks` feature flag — already covered in the troubleshooting docs (`codex features enable plugin_hooks`). The pre-install plugin detail view shows "No plugin hooks", consistent with every one of the 123 curated plugins (none bundle command hooks, so this path has no working precedent to verify against in-TUI). Marker behavior with `plugin_hooks` enabled is the remaining thing to confirm in a real installed session. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
2c675e00f7 |
Add Codex support (#2512)
## What problem does this solve? Worktrunk already has a Claude Code integration, but Codex users do not have an equivalent first-class path for: - discovering Worktrunk guidance inside the agent environment - bundling hooks that can show active Codex work in `wt list` - learning the Codex-specific worktree workflow and its differences from Claude Code - installing the integration through a Worktrunk CLI command This PR adds Codex support while preserving the important semantic difference between Claude Code and Codex: Claude Code can route agent-created worktrees through `wt` lifecycle hooks, but Codex currently needs users/agents to invoke `wt switch --create` and `wt remove` directly. ## What changed? ### Codex plugin package - Adds repo-local Codex plugin metadata in `.codex-plugin/plugin.json`. - Adds a Codex marketplace entry in `.agents/plugins/marketplace.json`. - Adds `hooks/hooks.json` for bundled activity hooks: - `SessionStart` sets the marker to `💬` - `UserPromptSubmit` sets the marker to `🤖` - `Stop` sets the marker back to `💬` - Reuses the existing Worktrunk skill/reference docs as the plugin skill payload. ### CLI integration - Adds `wt config plugins codex install`. - Runs `codex plugin marketplace add max-sixty/worktrunk`. - Clearly tells the user to open `/plugins` in Codex and install Worktrunk from the marketplace. - Adds `wt config plugins codex uninstall`. - Removes the Worktrunk marketplace entry. - Intentionally leaves already-installed plugins and global Codex hook feature flags unchanged, because users may have configured hook settings for other hooks. - Adds a `CODEX` section to `wt config show` when the Codex CLI is available. - It reports Codex CLI availability. - It avoids claiming the plugin is installed, because the CLI path only detects the Codex binary. ### Documentation - Adds a new Codex integration page with installation, bundled activity hooks, worktree workflow, LLM commit setup, and a Claude Code comparison. - Updates README, overview docs, tips/patterns, FAQ, and generated skill references so Codex is listed alongside Claude Code/OpenCode where relevant. - Keeps the Claude Code docs as the place for Claude-only worktree lifecycle hook behavior. ## Why this shape? The implementation keeps Codex separate from the existing Claude plugin command instead of abstracting over both. That is intentional: Claude installs a plugin directly, while the Codex flow configures a marketplace and still requires the user to install from `/plugins`. A shared abstraction would hide those differences and make the user-facing behavior easier to misread. The docs also avoid presenting Codex as feature-identical to Claude Code. Codex gets skills and bundled activity hooks here, but not automatic worktree lifecycle routing. ## Review guide - Plugin packaging: `.codex-plugin/plugin.json`, `.agents/plugins/marketplace.json`, `hooks/hooks.json` - CLI behavior: `src/commands/config/codex.rs`, `src/cli/config.rs`, `src/commands/config/show.rs` - Test helpers and coverage: `src/testing/mod.rs`, `tests/integration_tests/config_show.rs`, `tests/integration_tests/help.rs` - Primary docs: `docs/content/codex.md` - Generated docs/snapshots: `skills/worktrunk/reference/codex.md`, config/help snapshots ## Tests - `cargo fmt --check` - `cargo check --lib --bins` - `cargo test --lib --bins` - `cargo test --test integration test_docs_are_in_sync` - `cargo test --test integration "test_help"` - `cargo test --test integration config_show` - `git diff --check HEAD` ## Local pre-merge note `cargo run -- hook pre-merge --yes` could not complete locally because this environment does not have `pre-commit` or `cargo-nextest` installed. The hook's doctest/doc portions did run and passed. --------- Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Maximilian Roos <m@maxroos.com> |
||
|
|
52555982a8 |
feat(plugin): /wt-switch-create — optional repo arg and -- task delimiter (#2751)
The `/wt-switch-create` skill now accepts `<branch> [<repo>] [-- <task>]`. The optional second token names a different repository to create the worktree in (instead of the session's current one); the skill `cd`s into that repo with a `Bash` call before invoking `EnterWorktree` (which has no repo parameter), then verifies the new worktree landed under the requested repo as a safety check. The `--` cleanly separates the task from the rest. Without one, the parser treats a path-shaped second token (absolute, `~`-relative, `./`/`../`-relative, or an existing directory) as the repo and the remaining tokens as the task — anything else after the branch is task text, so the old shorthand `/wt-switch-create my-branch fix the bug` keeps working. Examples in the skill: ``` /wt-switch-create my-feature -- fix the parser bug /wt-switch-create my-feature ~/workspace/other-repo -- fix the parser bug /wt-switch-create my-feature ``` Also tidied existing prose: the nesting + different-repo case now stops rather than silently running the task in the wrong repo, and the scope note mentions the repo arg. The follow-up commit updates the public docs page (`docs/content/claude-code.md`) so the `/wt-switch-create` signature shown at worktrunk.dev matches the new grammar; `skills/worktrunk/reference/claude-code.md` re-synced via `test_docs_are_in_sync`. No code or test changes. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5a07bc09bd | feat(plugin): ship the wt-switch-create skill in the Claude Code plugin (#2737) | ||
|
|
8abefbce82 |
refactor(docs): unify AUTO-GENERATED marker form; share constants across crate boundary (#2418)
## Summary Follow-ups from #2417 review, plus adjacent simplifications. - **Cross-crate share of marker constants.** `MARKER_OPEN_PREFIX` and `MARKER_CLOSE` now live in `src/docs.rs`. `src/help.rs` (the `--help-page` producer of help-region markers) and `tests/integration_tests/readme_sync.rs` (the consumer + producer of snapshot/section markers) both reference them — drift becomes a build error rather than a silent mismatch. - **Drop `MARKER_OPEN_HTML_PREFIX`.** The `-HTML` variant emitted visually identical wrapping; the suffix only signalled "in-place refreshable" but the `.snap` ID + terminal-shortcode body in `DOCS_SNAPSHOT_MARKER_PATTERN` already discriminate that. Standardised on a single open prefix and stripped the `-HTML` literals from `README.md`, the four standalone docs files, and the test file. - **Help-page marker uses the shared prefix.** The mirrored close (`<!-- END AUTO-GENERATED from \`wt <cmd> --help-page\` -->`) stays as-is because adjacent regions need unambiguous pairing, but the open prefix now goes through `MARKER_OPEN_PREFIX`. - **Drop `MarkerType::output_format()` / `extract_inner()`.** Both had a single trivial non-panic branch that existed only to enforce "no Snapshot markers in README" via `unreachable!()`. Replaced with one explicit assertion in `sync_readme_markers` — surfaces the invariant as an actionable error message instead of a panic. - **Collapse `format_replacement`.** `wrap_in_marker` is invoked once for both output formats; only body construction varies. - **`__WT_QUOT__` rename in `tests/integration_tests/user_hooks.rs`.** Inlined the single quotes — the `.replace('__WT_QUOT__', \"'\")` was unnecessary indirection (the outer raw-string literal already tolerates `'`, and TOML / Tera don't care about embedded `'`). Removes a name-conflation footgun with the unrelated `__WT_QUOT__` placeholder in `src/docs.rs`. Net diff: -7 lines. ## Test plan - [x] `cargo test --test integration` — full integration suite (1559 tests) pass - [x] `cargo test --test integration readme_sync` — all 13 sync tests pass; `test_readme_examples_are_in_sync` now produces a stable README on a clean run (verified by re-running multiple times — no further updates after the initial regeneration) - [x] `cargo test --test integration test_args_indexing_and_length_in_hook_template` — confirms the user_hooks single-quote inlining works through TOML and Tera - [x] Visual check via local Zola dev server: \`/worktrunk/\`, \`/llm-commits/\`, \`/claude-code/\`, \`/tips-patterns/\`, \`/merge/\`, \`/step/\`, \`/remove/\`, \`/hook/\`, \`/list/\` all render terminal blocks cleanly with no leaked marker strings (`AUTO-GENERATED-HTML`, `__WT_OPEN2__`, trailing `|||`) - [x] `cargo clippy --all-targets --all-features` — clean - [x] `cargo fmt --check` — clean 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
7ae28e1d16 |
docs: trim needless parenthetical qualifiers (#2289)
Parenthetical examples (e.g., `origin/HEAD`) read naturally, but qualifiers and conditions belong in prose. This trims three cases: - `Checks git config worktrunk.default-branch (single command)` → drop the redundant `(single command)` (the command fragment already shows it's a single command). - `queries git ls-remote (100ms–2s)` → `queries git ls-remote — typically 100ms–2s`. - Statusline doc: `fetches from the network (often ~1–2 seconds), making it suitable for async...` → em-dash clause so the timing detail doesn't interrupt the main claim. Auto-synced: `docs/content/config.md`, both `skills/worktrunk/reference/*.md` copies, and the `help_config_state_default_branch.snap` help snapshot. > _This was written by Claude Code on behalf of Maximilian_ Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
d3fda9a8c1 |
docs: clarify plugin install command (#1906)
## Summary - document `wt config plugins claude install` as the recommended plugin install path - keep the existing Claude marketplace commands as the manual equivalent - align the docs page with the shipped CLI help and plugin installer behavior ## Testing - compared the docs change against `src/cli/config.rs`, which already advertises `wt config plugins claude install` - verified the diff stays limited to `docs/content/claude-code.md` ## Notes - docs-only change --------- Co-authored-by: ming <silverchris@foxmail.com> Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
d6e84ed258 |
feat: add --format=json to wt switch and CC worktree hooks (#1959)
Adds `--format=json` to `wt switch` and WorktreeCreate/WorktreeRemove hooks to the Claude Code plugin, so agent-created worktrees go through `wt` instead of raw `git`. **`wt switch --format=json`** prints structured JSON to stdout (`action`, `branch`, `path`, etc.) without changing any behavior — hooks, `--execute`, shell integration, and cd all run normally regardless of format. A `SwitchFormat` enum (`text`/`json`) keeps the help text clean (only valid values shown). **Plugin hooks** are inline one-liners in `hooks.json`: - `WorktreeCreate` — pipes CC's JSON through `jq` to extract the branch, calls `wt switch --create --format=json`, extracts the path - `WorktreeRemove` — extracts the worktree path from CC's JSON, passes it directly to `wt remove -D --foreground` Approval prompts still run — the hooks don't pass `--yes`, so `wt`'s non-interactive detection applies normally. > _This was written by Claude Code on behalf of @max-sixty_ --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
d8292f9a8b |
fix: use terminal shortcode for installation commands (#1828)
The installation block on the Claude Code docs page used raw `{%
terminal() %}` with manual `<span>` tags, bypassing the `cmd` parameter
path — no Syntect syntax highlighting and no `$ ` prompts, unlike every
other command block on the site.
Switched to the `{{ terminal(cmd="...") }}` shortcode format via the
standard `$ ` console block convention.
> _This was written by Claude Code on behalf of @max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
b6a6744380 |
Consistent code block convention for syntax highlighting (#1777)
Shell code blocks on the docs site had inconsistent syntax highlighting.
Blocks with `$ ` prompt prefixes rendered as a single flat color because
Syntect's bash grammar treats `$` as variable expansion. This PR adds `$
` prompts to all shell commands while preserving full Syntect
highlighting by routing through terminal shortcodes.
## Approach
All shell commands in `console` blocks use `$ ` prefix.
`convert_dollar_console_to_terminal()` (new library function in
`src/docs.rs`) detects `$ ` lines and emits Zola terminal shortcodes:
- **Single or multi-command blocks** (no `{{ }}`): Uses `cmd` parameter
with `|||` delimiter. The shortcode template splits, highlights each
line individually through Syntect, and wraps commands in `<span
class="cmd">` (CSS `::before` adds `$ `). Comment lines (`#`) are
highlighted as comments without a prompt.
- **Blocks with `{{ }}` template syntax**: Falls back to body approach
with `<span class="cmd">` (accent color only, since Tera would interpret
`{{ }}` in the `cmd` parameter).
The function runs in both the `--help-page` generator (CLI source →
docs) and the doc sync test (hand-written docs → terminal shortcodes).
Hand-written docs can use plain `console` fences with `$ ` and get
auto-converted.
## Key files
- `src/docs.rs` — New library module with
`convert_dollar_console_to_terminal()` and unit tests
- `docs/templates/shortcodes/terminal.html` — Template enhanced to loop
over `|||`-delimited commands, highlighting each through Syntect.
Supports self-closing `{{ }}` syntax for bodyless blocks.
- `src/help.rs` — Uses library function, updated pipeline docs
- `tests/integration_tests/readme_sync.rs` — Sync test runs conversion
on all docs (not just CLI-generated). Updated skill transformation to
handle both body and self-closing terminal shortcodes.
- All `src/cli/*.rs` — `$ ` added to all console blocks
- All `docs/content/*.md` — Auto-converted to terminal shortcodes (zero
`bash` blocks remain)
> _This was written by Claude Code on behalf of @max-sixty_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: worktrunk-bot <w@worktrunk.dev>
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
|
||
|
|
5c1672932f |
docs: remove deprecated post-create from documentation (#1776)
Replace all `post-create` references with `pre-start` across documentation, skills, example config, and test names. The Rust deprecation handling code (migration, alias, config parsing) remains intact for users with existing configs. Also removes `post-create` from the `wt hook show` value_parser — it was listed as a valid hook type for display even though it's been deprecated. > _This was written by Claude Code on behalf of @max-sixty_ Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
1cf2c8203f |
Add page metadata, canonical URLs, and structured data to docs (#1167)
## Summary - Add per-page `<meta name="description">` to all doc pages — command pages auto-generated from CLI `about`/`long_about` via new `--help-description` flag, non-command pages manually written - Add `<link rel="canonical">` URLs and JSON-LD structured data (WebSite + SoftwareApplication) on the homepage - Add custom `sitemap.xml` template with `<lastmod>` dates and descriptive homepage `<title>` - Extract shared `extract_about_and_subtitle()` helper, eliminating duplicated subtitle logic between `handle_help_description` and `combine_command_docs` - Fix broken anchor in faq.md (`#picker-summaries` → `#branch-summaries-experimental`) ## Test plan - [x] Full test suite passes (2713 tests via `wt hook pre-merge --yes`) - [x] All lints clean (pre-commit, clippy, cargo fmt) - [x] Doc sync test confirms auto-generated descriptions match CLI help - [x] Zola build succeeds with all template changes > _This was written by Claude Code on behalf of @max-sixty_ --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
91a505603a |
feat: add Summary column to wt list --full (#1100)
## Summary - Adds an LLM-generated Summary column to `wt list --full`, showing a one-line description of each branch's changes - Extracts shared summary module (`src/summary.rs`) from picker-specific code for reuse across `wt list` and `wt switch` - Summary and Message are both flexible columns — Summary expands first (10→70 chars), then Message gets remaining space (10→100 chars) - Gated on `[list] summary = true` config (disabled by default — each branch's diff is sent to the configured LLM) plus `[commit.generation]` and `--full` - Labeled as experimental throughout docs ## Test plan - [x] 539 unit tests pass - [x] 1072 integration tests pass (including 9 new layout tests for flexible Summary/Message allocation) - [x] Pre-commit lints pass - [x] Manual: `wt list --full` with LLM configured + `summary = true` — Summary column appears - [x] Manual: `wt list --full` without `summary = true` — no Summary column - [x] Manual: `wt list` (no `--full`) — no Summary column - [x] Manual: `wt switch` picker — summary tab still works > _This was written by Claude Code on behalf of max-sixty_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
1d85de945f |
Migrate docs syntax highlighting to giallo with warm theme (#1080)
## Summary - Migrate from static CSS syntax highlighting to Zola's giallo engine with custom `worktrunk-light.json` theme - Replace hardcoded `syntax-light.css` / `syntax-dark.css` with theme-based class generation - Design a warm "sunlit workshop" palette: amber commands, gold strings, chartreuse quoted strings, rusty constants - Add CSS sibling selector to differentiate quoted from bare strings (giallo tokenizes both as `z-string`) ## Test plan - [ ] Verify syntax colors on `/switch/` (bash: commands, flags, strings, quoted strings) - [ ] Verify TOML blocks on `/config/` (section headers, keys, values) - [ ] Verify dark mode is unaffected (quoted string CSS rule scoped to `prefers-color-scheme: light`) - [ ] Check all tests pass (`cargo test`) > _This was written by Claude Code on behalf of @max-sixty_ --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
3ab31bd73f |
feat(statusline): add --format=json and --format=claude-code options (#875)
* feat(statusline): add --format=json and --format=claude-code options Add `--format=json` to output current worktree as JSON (same structure as `wt list --format=json` but single-element array). Migrate `--claude-code` to `--format=claude-code` as the canonical syntax. The old `--claude-code` flag is hidden for backwards compatibility (no deprecation warning). Also fixes nested worktree detection: previously used `starts_with()` prefix matching which would incorrectly identify the parent worktree. Now uses `git rev-parse --show-toplevel` via `repo.worktree_at().root()` with path canonicalization, matching the approach in `wt list`. Co-Authored-By: Claude <noreply@anthropic.com> * fix(statusline): require current_dir in Claude Code JSON When parsing Claude Code JSON context, treat missing `.workspace.current_dir` as invalid input (return None). The caller already falls back to `env::current_dir()` when no valid context is parsed. This is cleaner than fabricating a current_dir value - if Claude Code sends JSON, it should include the required fields. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
1f239aced7 |
docs: add Quick Start section to front page (#864)
Reapply Quick Start documentation with fix for README rendering. The terminal output blocks now include explicit `$ ` prompts before commands, which makes GitHub's console syntax highlighting work correctly (commands styled differently from output). Changes: - Add Quick Start section showing switch, list, merge workflow - Add snapshot tests for Quick Start terminal output examples - Fix strip_html() to convert .cmd spans to `$ ` prefixed commands - Sync skill reference files from docs Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
a6d39d053f |
Revert "docs: add Quick Start section to front page (#851)"
This reverts commit
|
||
|
|
69b3e47509 |
docs: add Quick Start section to front page (#851)
* docs: add Quick Start section to front page Shows the basic workflow with clear directory paths at each stage: - Create worktree with wt switch --create - Check status with wt list - Two merge paths: PR workflow (push + gh pr create + wt remove) and local merge (wt merge) - Parallel agents example with -x flag Co-Authored-By: Claude <noreply@anthropic.com> * docs: improve Quick Start with realistic examples and snapshot sync - Add snapshot-based output sync for quickstart examples (switch, list, merge) - Make examples more realistic: 53 lines of auth module changes instead of 9 - Show uncommitted changes in wt list demo (WIP state, not already committed) - Add wt step commit to PR workflow since changes are now uncommitted - Remove redundant git push from PR workflow (gh pr create auto-pushes) - Suppress worktree-path hint in quickstart tests for cleaner output - Add transform_zola_to_github() for converting HTML terminal markers to plain code blocks Co-Authored-By: Claude <noreply@anthropic.com> * Simplify code block formatting in README and tests Convert bash code blocks to console format with command prompts and output in a single block. Update regex pattern to optionally strip redundant bash preamble when converting AUTO-GENERATED-HTML terminal markers. * docs: remove redundant bash blocks before terminal examples The terminal shortcodes already include the command with $ prefix, so separate bash blocks showing just the command were redundant. Also fix broken anchor link in config.md. Co-Authored-By: Claude <noreply@anthropic.com> * feat(docs): make terminal prompt non-copyable via CSS Use CSS ::before pseudo-element to generate the $ prompt, making it structurally non-copyable. This works with both manual text selection and copy buttons. Changes: - Add .cmd::before { content: "$ "; } to generate prompt via CSS - Remove explicit <span class="prompt">$</span> from HTML output - Extract command from snapshot YAML header instead of parsing HTML - Update expand_command_placeholders for command pages (list.md, etc.) Co-Authored-By: Claude <noreply@anthropic.com> * fix(docs): show commit step in quick start merge example The merge example now shows staged changes being committed as part of the wt merge workflow, reflecting a realistic user experience where code is staged but not yet committed before merging. Co-Authored-By: Claude <noreply@anthropic.com> * style: fix cargo fmt formatting Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): use cross-platform mock LLM for quickstart_merge test The quickstart_merge test was using a shell script for the mock LLM which doesn't work on Windows. Now uses the mock-stub system which creates a cross-platform mock binary. Co-Authored-By: Claude <noreply@anthropic.com> * fix(test): use to_slash_lossy for Windows path compatibility Windows paths with backslashes trigger shell metacharacter handling, which wraps the command in `sh -c`. Bash can't parse Windows paths like `C:\Users\...`. Converting to forward slashes with to_slash_lossy() makes the path bash-compatible. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
632c6ca28a |
docs: Update stale documentation since v0.18.2 (#853)
- Update deprecated [commit-generation] config format to [commit.generation]
with single command string (per
|
||
|
|
cd8ac3e060 |
perf(tests): add pre-built fixture to speed up Windows CI (#634)
* perf(tests): add pre-built fixture to speed up Windows CI Instead of creating repos from scratch for each test, copy a pre-built fixture with worktrees and remote already configured. This should significantly reduce test time on Windows where process spawning is slow. - Add tests/fixtures/standard/ with repo, 3 worktrees, and bare remote - Update TestRepo to copy fixture instead of running git init/commit - Add fixture cleanup to doc-generating tests for clean output - Remove git sample hooks and boilerplate from fixture Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): correctly clean fixture state for isolated tests - Change `remove_fixture_worktrees()` to take `&mut self` and clear the worktrees map so `add_worktree()` can recreate branches - Add remote removal to select and approval tests to prevent `origin/main` from appearing in snapshots - Update select snapshots for fixture-based commit hashes Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): use relative paths in fixture for portability The fixture gitdir files contained absolute paths to the local development machine, causing CI failures. Updated to use relative paths which work after the _git → .git rename during fixture copy. Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): remove invalid remote from bare repo fixture The bare repository in the fixture had a remote configured with an absolute path to the local machine, which wouldn't exist on CI. Bare repositories don't need remotes configured. Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): remove origin in approval tests for consistent project_id Tests manually create approvals using a computed project_id based on repo path. With the fixture's origin remote, worktrunk computes a different project_id from the remote URL. Removing origin makes both use the same fallback (directory path). Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): improve fixture copy robustness for CI - Add platform-specific copy: `cp -r` on Unix, `robocopy` on Windows - Add verification that essential paths exist after copy - Add verification that origin.git is a valid git repository after rename - Include stderr in error messages for better debugging Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): add .gitkeep to preserve empty git directories in fixture Git doesn't track empty directories, so objects/info, objects/pack, and refs/tags were missing on CI after checkout. These directories are required for a valid git repository. Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): ensure LF line endings in fixture git files Add .gitattributes to force LF line endings for git internal files (packed-refs, HEAD, config, etc.) to prevent CRLF corruption on Windows. Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): handle file copies separately from directories on Windows robocopy only works with directories, not files. Use Rust's std::fs::copy for individual files like .gitattributes, and robocopy only for directories. Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): enable rerere in test git config for consistent output Git's rerere feature produces "Recorded preimage" messages during conflicts. Enable this in test config so output is consistent between local machines and CI. Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): restore accidentally deleted hook_clear tests The tests test_hook_clear_no_approvals and test_hook_clear_with_approvals were accidentally removed during the fixture refactoring. This restores them with the necessary remote removal so project_identifier matches "repo" (directory name) instead of the fixture's remote URL. Also adds remote removal to test_hook_show_approval_status for consistency. Co-Authored-By: Claude <noreply@anthropic.com> * chore: remove unreferenced ping_pong snapshots These snapshots used feature_a but the tests now use feature, leaving these orphaned. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
8d56331210 |
docs: document the Claude Code configuration skill (#477)
* docs: document the Claude Code configuration skill The Claude Code integration page previously focused only on activity tracking (🤖/💬 markers), burying installation and not mentioning that the plugin includes a skill. Restructure to make both features clear: 1. Configuration skill — documentation Claude Code can read 2. Activity tracking — status markers in wt list Move installation to the top where users expect it. Relates to #475 Co-Authored-By: Claude <noreply@anthropic.com> * sync skill reference with docs Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
49c15097ff |
refactor(tests): simplify snapshot testing with auto-bound settings (#396)
* refactor(tests): simplify snapshot testing with auto-bound settings - Auto-bind snapshot settings in TestRepo::new() via _snapshot_guard field - Remove manual setup_snapshot_settings().bind() wrappers from tests - Inline make_snapshot_cmd calls directly in assert_cmd_snapshot! macros - Remove unused helper functions (json_settings, bind, bind_json) - Use auto-naming from test function names (explicit names only for multi-snapshot tests) - Fix directive file guard lifetime in test_remove_internal_mode - Clean up orphaned snapshot files and let git detect renames Net reduction: -183 lines in test files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: update statusline tests for fish-abbreviated paths The auto-bound snapshot settings from TestRepo use `_REPO_` filters for full paths, but the statusline command uses fish-style path abbreviation (e.g., `/p/v/f/.../repo`). These abbreviated paths weren't matched by the existing filters. Updated `claude_code_snapshot_settings()` to: - Filter fish-abbreviated paths ending in `/repo` to `[PATH]` - Strip leading ANSI reset codes from output - Remove unused `repo` parameter 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: normalize statusline paths across platforms The statusline output varies by platform: - Linux: Raw path filtered by auto-bound settings to `_REPO_` - macOS: Fish-style abbreviation bypasses auto-bound filters Updated claude_code_snapshot_settings() to normalize both cases to a consistent `[PATH]` placeholder for cross-platform tests. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
5dac49806a |
docs: add demo GIFs to command pages (#324)
* docs: add demo GIFs to command pages Add demos to switch, list, merge, llm-commits, and claude-code pages. Position demos after the intro sentence for consistent layout. Changes: - Add wt-switch, wt-list, wt-commit, wt-statusline to DOCS_DEMOS - Update doc pages with <figure class="demo"> elements - Update CLAUDE.md with new available demos list Note: Demos involving `claude` (wt-switch, wt-statusline) need fixes to the Claude Code mock before the GIFs look correct. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: add demo markers to cli.rs for switch, list, merge commands Demo markers in after_long_help allow demos to persist through page regeneration by test_command_pages_are_in_sync. Positioned after intro sentence (matching wt-select pattern). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: add new demo pages to lychee exclude_path These files have root-relative asset paths that lychee can't resolve locally (assets are fetched at deploy time from worktrunk-assets repo). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: update help snapshots for demo markers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: exclude asset GIF links by pattern instead of by file Broadened `/assets/wt-.*\.gif` to `/assets/.*\.gif` to match all asset directories including `/assets/docs/{light,dark}/`. This is cleaner than excluding entire files from link checking. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: pin lychee to 0.20.x in CI Version 0.22.0 has a regression where exclude patterns for root-relative paths (like /assets/*.gif) don't work properly - they're treated as errors instead of being excluded. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
3471fee63b |
Update docs and help text to clarify default branch references (#275)
* Update docs and help text to clarify default branch references * Replace "main" with "default branch" in docs and help text Update references throughout documentation and help snapshots to use "default branch" instead of hardcoded "main" for clarity. Changes include: - Help text descriptions for list, merge, switch, and remove commands - Configuration file documentation and examples - JSON output field descriptions - Status symbol explanations - Example commands and shortcuts Also update environment variable from GIT_EDITOR to GIT_CONFIG_GLOBAL in test snapshots and add missing RUST_LOG variable. |
||
|
|
ad6ec2351b |
Replace emojis with single-width Unicode symbols (#255)
* Replace emojis with single-width Unicode symbols Switch from graphical emojis (🔄, ✅, ❌, etc.) to single-width Unicode symbols (◎, ✓, ✗, etc.) for message indicators: - ◎ Progress (bullseye) - ✓ Success (checkmark) - ✗ Error (x mark) - ▲ Warning (triangle) - ↳ Hint (corner arrow) - ○ Info (empty circle) - ❯ Prompt (shell prompt) Benefits: - Better terminal compatibility (emojis render inconsistently) - More serious/professional aesthetic - Consistent single-character width for alignment Also reduces gutter indentation from 3 to 2 columns to align with 1-char symbols, and adds colors to the symbols themselves (red for errors, green for success, etc.). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Update shell integration test snapshots 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Update documentation and comments to use new symbols Update help text examples, doc comments, and .claude rules files to reflect the new single-width Unicode symbols. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Update .claude rules to use symbol terminology Rename emoji→symbol consistently in documentation guidelines. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Use cstr! for pre-colored symbols Symbols now have their semantic colors embedded using color_print's cstr! macro, which creates colored &'static str constants. This ensures symbols are always consistently colored without needing to wrap each usage site. - PROGRESS_SYMBOL: cyan ◎ - SUCCESS_SYMBOL: green ✓ - ERROR_SYMBOL: red ✗ - WARNING_SYMBOL: yellow ▲ - HINT_SYMBOL: dim ↳ - INFO_SYMBOL: dim ○ - PROMPT_SYMBOL: cyan ❯ Message formatting functions now apply color only to the text content, since symbols are pre-colored. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Update shell integration test snapshots for pre-colored symbols 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Add filter for shell-escaped paths in test snapshots When shell_escape quotes paths, the filters replace the path portion but leave the quotes. Add filters to normalize '[REPO]' to [REPO]. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Filter syntax-highlighted [REPO] placeholders Shell-escaped paths get green syntax highlighting. Add filter to normalize colored [REPO] placeholders back to plain [REPO]. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix snapshot filters for shell-escaped path formatting CI (Ubuntu/Windows) produces different ANSI formatting around shell-escaped paths than local macOS. Add filters to normalize the dim formatting codes around [REPO] placeholders. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Update snapshots for single-width Unicode symbols 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Update PTY test snapshots for new symbols 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Update snapshots for new symbols after merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
58f00ef8b3 |
Remove bold styling from current branch in wt list (#254)
The current worktree is already distinguished by the @ gutter symbol and its position at the top of the list, so bolding adds no additional information. This simplifies the styling code by removing the position_style function entirely and only applying dimming for removable worktrees. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
bb86da253b |
Show paths relative to main worktree in wt list (#251)
Previously, paths were shown relative to a computed common prefix, which could degenerate to `/` when worktrees were in unrelated locations, resulting in confusing output like `./Users/max/project`. Now paths are computed relative to the main worktree: - Main worktree: `.` - Children: `./subdir` - Siblings: `../project.feature` Uses `pathdiff` crate for cross-platform relative path computation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
7f0c927240 |
Unify git config state storage under worktrunk.state.<branch>.* (#234)
Changes from separate namespaces to unified format: - CI cache: worktrunk.cache.<branch>.ci-status → worktrunk.state.<branch>.ci-status - Markers: worktrunk.marker.<escaped> → worktrunk.state.<branch>.marker Benefits: - Numeric branch names (10704) now work - git subsections have no naming restrictions - No escaping needed for slashes, underscores, hyphens - Unified namespace for all branch-keyed state - Removed ~260 lines of escape/unescape code and tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com> |