mirror of
https://github.com/max-sixty/worktrunk.git
synced 2026-09-14 20:00:38 +08:00
codex/remove-codex-cloud-specific-tests
276 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
92dfb686bb |
feat(approvals): let wt config approvals add --yes record approvals without a TTY (#3819)
`wt config approvals add` refused every non-interactive run — even with `--yes`, whose hint then suggested the flag already passed — so there was no way to pre-approve a project's commands unattended. An orchestrator (tend's Codex Cloud container was the motivating case) had to hand-write `approvals.toml` from `wt config approvals list --format=json` output, a third-party reimplementation of `add` that breaks whenever the schema changes. The `wt config approvals` docs already promised "`--yes` to bypass prompts in CI" and described `stale` entries as "what `--yes` would silently re-approve"; behavior now matches them. The two `--yes` meanings stay distinct: on a command that runs project commands it grants consent for that run alone and records nothing (unchanged), while on `add` — whose product is the record — it lists what it trusts and writes it. `add` no longer routes through `approve_command_batch` (the execution gate) for this: it prompts or announces, then saves itself, which also makes a failed `approvals.toml` write fail the command instead of warning behind a `✓ saved` line and exit 0 — an orchestrator reading only the exit code would otherwise walk into the prompt it just paid to avoid. The non-interactive hint's pre-approval suggestion now carries `--yes` (`run wt config approvals add --yes`), since a hint reached in CI must name a command that runs there. Per the existing `list --format=json` docs, `add --yes` re-approves templates edited since an earlier approval without comment; the `add` help now says so and points at the `stale` field for reading them first, and the worktrunk skill's escalation rule tells agents not to reach for it on a user's behalf. > _This was written by Claude Code on behalf of max-sixty_ |
||
|
|
f3ea2fd598 |
fix(errors): report a directory holding no worktree as a directory (#3773)
Passing a path that holds no worktree used to be reported as a missing branch: ``` $ wt remove /repo/.claude/worktrees/ghost ✗ No branch named /repo/.claude/worktrees/ghost ↳ To list branches, run wt list --branches --remotes ``` The argument is plainly a path; the error calls it a branch and sends the user to a listing it could never appear in. Now: ``` $ wt remove /repo/.claude/worktrees/ghost ✗ No worktree @ /repo/.claude/worktrees/ghost ↳ The directory exists but is not a worktree; to list worktrees, run wt list ``` #3767 fixed the hook that produced the leftover directory in #3753; this fixes the message a person gets when they type such a path themselves, which judewang named there as why it was hard to diagnose. Ref #3753. Two neighbouring commands had the same cause and are also fixed. `wt switch <path>` and `wt merge <path>` used to offer `wt switch --create <path>`, which fails with `fatal: … is not a valid branch name`; and `wt config state marker set --branch <path> foo` **succeeded silently**, storing state keyed by a path string. ## How the decision is made `Repository::path_selector_error` reports a directory only when all four conditions hold. Each is here because dropping it made `wt` assert something false, reproduced against a build: | Condition | Without it | |---|---| | Git could never accept the selector as a branch name | `wt step rebase HEAD~9` reports revision syntax as a path | | A directory is there | `wt remove docs` stops meaning the branch when `docs/` sits beside it | | No worktree of this repo is registered at it | a *detached* worktree — reachable by path alone — is reported as absent, contradicting the `wt list` in the hint | | It holds no git data | a sibling repo's live checkout, or a bare repository, is called a leftover only the user can delete | That last one is the data-safety case: the new variant's hint says only the user can delete the directory, so a false positive reads as an invitation to `rm -rf` a checkout with uncommitted work. Absence of git data is *established* rather than assumed — `symlink_metadata` with only `NotFound` counting, so an unreadable clone or a dangling `.git` symlink withholds the claim instead of earning it. The first condition is git's own ref-format rules, implemented in-process and pinned against `git check-ref-format` by a test. ## Navigating the diff - `src/git/repository/worktrees.rs` — `Repository::path_selector_error` and `holds_git_data`, with the rationale for each condition recorded where it's enforced. - `src/git/repository/branch.rs` — `is_valid_branch_name`, the ref-format predicate. - `src/git/error.rs` — the new `GitError::WorktreeNotFoundAtPath` variant. - `src/git/repository/config.rs` — `target_branch_at_path` → `target_worktree_at_path`, returning the worktree whole rather than collapsing "no worktree here" with "a detached worktree here". - Call sites: `commands/remove.rs`, `commands/worktree/switch.rs`, `commands/config/state.rs`. Reporting a detached *target* made `DetachedHead`'s fixed hint wrong: `git switch` acts on the tree it runs in, so `wt merge ../B` was telling the user to switch the tree they were standing in. The variant now carries the detached worktree when that isn't the current one, and the hint becomes `git -C ../B switch <branch>` — the same reason `OperationInProgress` carries `branch`. Set at the three sites where the detached worktree isn't the current one (`require_target_branch`, `require_selected_branch`, and `wt step promote`, which raises this about the main worktree); every other site passes `None` and renders as before. 140 of the 257 added production lines are comments and docstrings — three of the four conditions look like obvious things to simplify away, so the reason each exists is written down next to it. ## Testing Unit tests at the resolver boundary cover each condition and its complement, both `directory_exists` arms, the shortcut-expansion route (`wt merge -`), detached targets, revision syntax, unresolvable `.git` entries, and bare repositories. Two integration tests drive the real binary end-to-end against `../repo.leftover` — `wt remove` and `wt switch` — covering the relative-path spelling and each command's wiring, and the `switch` snapshot pins the thing that would regress silently: that no `--create` hint is offered for a name git would reject. The ref-format predicate is verified beyond its committed table: 2,791 generated names were run against real `git check-ref-format` with zero disagreements, and two review passes did the same over their own inputs. That sweep isn't committed — it needs Python and a live `git` — so the committed table is what guards against drift. Not exercised locally: the Windows-specific behaviours are reasoned, not run — `Path::new("foo.").is_dir()` succeeding against `foo`, and drive-relative `C:foo`. CI exercises the platform but not those inputs. <details> <summary>Known gaps this would ship with</summary> - `wt switch docs/` (trailing slash, branch `docs` has a worktree, `./docs` is a source directory) answers `No worktree @ docs/`. True, unhelpful. The real fix is stripping trailing separators before resolution — a resolution change, not a message change. On Windows it reads `No worktree @ …/docs`, because `path_slash`'s `to_slash_lossy` rebuilds the string from `Path::components()` there and drops the trailing separator — still true of the path, minus the character that explains it. The doc comment on `path_selector_error` now says so rather than promising a spelling guarantee that holds on one platform. - A worktree whose registration was pruned out from under it gets a safe-but-vague message. The useful answer is a distinct `git worktree repair`-or-delete diagnosis; that's new detection, and nobody has reported hitting it. - Under `-C`, a `./`-prefixed selector renders a visible `/./` (`wt -C repo remove ./ghost` → `repo/./ghost`). Cosmetic; tidying it would discard the trailing separator the `docs/` case needs. - `require_target_branch` and `require_target_ref` remain near-copies of one ladder, and this adds a line to each. Pre-existing; extracting it is a separate refactor. - `CommandEnv::require_branch` passes `worktree: None` unconditionally, and `CommandEnv::for_selector` builds an env whose `worktree_path` is the worktree the *argument* named — so a future caller combining the two would get the unqualified hint. Nothing reaches that combination today: `merge` and `squash` are the only `require_branch` callers and both build with `for_action`, and the one `for_selector` caller (`wt step commit --branch`) never calls it. Two pre-existing bugs surfaced while reviewing this, unrelated to the diff and reproducible by branch name: `wt remove` will trash a clone that now sits at a registered worktree's path, and a recreated worktree directory leaks a raw `git rev-parse --git-dir failed (exit 128)`. </details> > _This was written by Claude Code on behalf of max-sixty_ --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f57fac365b |
Release v0.72.0 (#3759)
Release v0.72.0 — 55 commits since v0.71.0. Minor bump: `cargo semver-checks` reports 5 breaking library changes, so patch is disallowed pre-1.0. ## Headline changes - **`wt merge` / `wt step push` no longer autostash the target worktree** (#3703). Both strategies now advance the target through one `advance_target` — a compare-and-swap `update-ref`, then `read-tree -m -u` in the target worktree — so `refs/stash` is never entered and staged changes stay staged. - **Forge classification returns to brand-in-hostname** (#3673), reverting the exact-DNS-label rule 0.71.0 shipped. `github-enterprise.acme.com` and friends resolve again with no config. - **`[projects."…"]` keys match by `*` pattern and carry forge settings** (#3701), so one user-config entry covers every repository on a self-hosted host. - **A published JSON Schema for `wt list --format=json` schema 2** (#3747), plus machine-readable approval state and `branch_outcome` (#3710). Full detail in `CHANGELOG.md`. ## One fix made during the release cut The release audit surfaced a gap this release's own `advance_target` rewrite introduced, fixed here rather than deferred: **`wt merge` / `wt step push` now refuse a target worktree parked mid-operation.** The target sync is a two-tree merge, which refuses an unmerged index but *not* a stopped cherry-pick or rebase whose conflict has already been staged. A target paused between steps could therefore have the push range written into it, and the user's `--continue` would commit the synced tree as the step's result. The old fast-forward path got this check for free from `receive.denyCurrentBranch=updateInstead`, which refused any unclean target outright; both strategies now ask directly, and the refusal names the worktree holding the operation. `test_push_refuses_target_mid_operation` covers it in both shapes a stopped operation can take, and both are mutation-verified. With the gate disabled, the push succeeds and writes `feature.txt` into the mid-cherry-pick worktree. The rebase case was added in response to review feedback on this PR, and pins a second dependency. A rebase detaches HEAD, so `git worktree list --porcelain` reports the target with no branch and `worktree_for_branch` finds it only because `finalize_worktree` backfills from `rebase-merge/head-name`. That makes the rebase arm the one place this guarantee rests on a helper of ours rather than on git — the fast-forward path it replaced got the refusal from `find_shared_symref`. With the backfill disabled, `wt step push` succeeds against a worktree parked mid-rebase while the cherry-pick case still passes, so the gap was real. ## Validation - Local gate green: `cargo run -- hook pre-merge --yes` — 4570 tests, clippy, fmt, doc sync. - Cross-platform nightly green on the cut-from tip `3817df079` (run 31133551751): full nextest matrix on linux/macOS/Windows, feature-powerset, all three release triples, nix-flake, minimal-versions, unused-deps, crate-build, link-check. - Changelog verified entry-by-entry against the diffs by an independent pass; every one of the 55 commits either maps to an entry or is a documented skip. - `main` advanced during the CI wait. #3762 ships in this release and now has a changelog entry; the other two commits that landed (#3758, #3749) touch only `.github/`. - Data-loss surface reviewed by four independent finders over the cumulative diff. One further finding — a pre-0.72 `approvals.toml` key containing `*` being reinterpreted as a wildcard on upgrade — was reviewed and accepted as out of scope for this release. > _This was written by Claude Code on behalf of max-sixty_ --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
1b35950a9c |
test: simplify the integration suite (#3657)
This reduces duplicated and false-confidence integration coverage while preserving the suite's semantic and user-facing contracts. ## What changed - Replaces three overlapping list-layout suites with two representative CLI integrations, leaving exhaustive geometry at the direct layout layer. - Groups Git error render variants into labeled family snapshots and removes command-by-shell wrapper cross-products while retaining shell-specific conformance and regression cases. - Updates test-authoring guidance around boundary choice, minimal contrasts, and PTY use, and runs local and CI coverage through Nextest isolation. ## Results - Test catalog: 4,642 to 4,562 - Snapshots: 1,193 to 1,131 - Warm all-feature runtime: 84.70s to 78.17-80.35s - Full coverage: 97.32% of lines ## Testing - `cargo run -- hook pre-merge --yes` - `cargo llvm-cov nextest --features shell-integration-tests --summary-only` > _This was written by Claude Code on behalf of max_. |
||
|
|
84cbb0489e |
fix(step): refuse to rebase or merge from a worktree mid-operation (#3558)
## The bug With a rebase in progress (a conflicted stop, or a killed `git`), `wt step rebase main` reported success over a conflicted tree: ```console $ git rebase main # stops on a conflict; HEAD detached mid-replay $ wt step rebase main ○ Already up to date with main $ echo $? 0 ``` `handle_rebase` asked `is_rebased_onto` first. Mid-rebase, HEAD sits on a linear extension of the target, so that returns true and the function returns `UpToDate` before anything consults `worktree_state`. `wt merge` from the same state failed differently but no better: ```console ✗ Cannot merge: not on a branch (detached HEAD) ↳ To switch to a branch, run git switch <branch> ``` The hint names the one command that throws the rebase away instead of finishing it. ## The fix Both commit-replaying commands now check for an open operation before doing anything else. `wt merge` runs the check ahead of its branch check, so the open operation is what gets named rather than the detached HEAD it causes. ```console $ wt step rebase main ✗ Cannot rebase: a git operation is already in progress ``` The gate covers every state detection reports, not just rebase. Measured on the others beforehand: a rebase started mid-bisect ran and stacked a second operation on the first, and one started mid-merge or mid-cherry-pick died inside git's autostash (`Cannot save the current index state`, `f: needs merge`). None told the user what to do next. ## Why it prescribes no remedy An earlier draft of this branch carried a per-operation table of `--continue`/`--abort` hints. It was wrong on first contact with the long tail: `git am` borrows the `rebase-apply` directory, so an open am session was reported as a rebase and pointed at `git rebase --continue`, which git refuses outright: ```console $ git rebase --continue fatal: It looks like 'git am' is in progress. Cannot rebase. ``` Adding an `Am` arm would have treated the enumeration as the fixable part. `git status` already answers this better than the table did, in git's own words and translations, for states worktrunk has no variant for: ```console $ git status interactive rebase in progress; onto b162044 You are currently rebasing branch 'feat' on 'b162044'. (fix conflicts and then run "git rebase --continue") (use "git rebase --skip" to skip this patch) (use "git rebase --abort" to check out the original branch) ``` It names the operation, the branch, the progress, and every way out including `--skip`, which the table never offered. So the table is gone rather than extended, and the refusal is a single line: it says what is blocked and why, and leaves the rest to the command every git user reaches for next. Detection still returns a structured `InProgressOperation` rather than a display string, but now purely as detection: the only caller that reads a variant is the one classifying a rebase worktrunk itself just started. The neighbouring `RebaseConflict` error took the same treatment. It forwards git's stderr in the gutter when it has it, and otherwise substituted two hint lines of its own: ```console ↳ To continue after resolving conflicts, run git rebase --continue ↳ To abort, run git rebase --abort ``` That is the operation table again in miniature — one operation's remedies, minus `--skip` — and the one path that reaches it is the safety check for a rebase git *reported as finished*, where worktrunk knows least about what is actually open. It now prints its title, plus git's own words when git said any. ## One detector Two places probed the same files for the same answer: `Repository::worktree_state` for the gate, and `WorkingTree::is_rebasing`/`is_merging` for the `wt list` gutter symbols. They are now one method, `WorkingTree::operation_in_progress`, which `Repository::operation_in_progress` delegates to — the rename also retires a name that collided with `wt list`'s unrelated `WorktreeState`. Detection now runs in the order `git status` consults the state files, which is a change from the old `wt list` path: it checked rebase before merge. Nothing can reach the difference — no rebase backend writes `MERGE_HEAD`, checked against all three — but matching git's order is one less thing to reason about. `wt list` maps the operations without a gutter symbol (cherry-pick, revert, bisect) to `None`, so its output is unchanged. ## The queued sequencer, now closed Having one detector is what made the last gap worth closing. `git cherry-pick A B` that stops on A writes `CHERRY_PICK_HEAD`; resolving that stop with `git commit` instead of `git cherry-pick --continue` removes the file and leaves B queued: ```console $ git status On branch main Cherry-pick currently in progress. (run "git cherry-pick --continue" to continue) $ ls .git/CHERRY_PICK_HEAD ls: .git/CHERRY_PICK_HEAD: No such file or directory $ cat .git/sequencer/todo pick 1f07533 side1 pick 6b32540 side2 ``` Git reads that same file to keep calling the sequence in progress, so detection reads it too: the first instruction word, `pick` or `revert`, exactly as git's own `sequencer_get_last_command` does. The reason this waited for its own change is the risk of refusing a `wt merge` the user can't un-refuse, so the lifecycle is measured rather than assumed. The directory is gone once the sequence finishes, and gone after `--quit` or `--abort`; a single-commit cherry-pick never creates one; a rebase keeps its todo elsewhere, under `rebase-merge/`. An empty or unrecognized todo reports nothing, so the failure direction is the old behavior rather than a refusal with no way out. ## Verification The repro above, plus the merge, am, cherry-pick, revert, and bisect states, checked by hand against real repos: all five refuse, including am, which is now correct precisely because nothing here claims to know what am needs. Three integration tests drive the real binary from a genuinely stopped operation (`test_step_rebase_refuses_mid_rebase`, `test_merge_refuses_mid_rebase`, `test_merge_refuses_mid_merge`). `test_merge_refuses_mid_merge` is what holds the broadened scope: a conflicted merge keeps HEAD on the branch, so it is the case the detached-HEAD check waves through. Narrowing the gate back to rebase-only turns it red, which I verified by making that edit and re-running. The sequencer case is driven end to end from a real stopped pick — `test_operation_in_progress_reads_the_queued_sequencer` commits the resolution by hand, asserts `CHERRY_PICK_HEAD` is gone, and still expects a cherry-pick, so it can only pass through the sequencer read. It then runs `git cherry-pick --quit` and expects the state to clear, which is the false-refusal half. Running the same sequence against the built binary refuses where it previously reported `○ Already up to date with main`. Confirmed unchanged: already-up-to-date, fast-forward, true-rebase, and rebase-conflict output; a plain detached HEAD still gets the `git switch` hint, which is the right remedy when no operation is open; a sibling worktree mid-rebase does not block a clean one; `wt list`'s gutter symbols. Full gate green (4494/4494 tests, lints, fmt), plus `cargo clippy --all-targets --features shell-integration-tests`, which the gate doesn't compile. ## Merged with main `main` gained #3539, which extracted the failed-rebase classification into `classify_rebase_failure`, and #3549, which adds `span_upstream` — both inside `handle_rebase`. The conflict was semantic as well as textual: #3539's `is_rebasing` check still read the old string API (`worktree_state()?.is_some_and(|s| s.starts_with("REBASING"))`), which this branch removes, so resolving the markers alone left a type error. The resolution keeps #3539's classification and reads the structured enum instead: ```rust let state = repo.operation_in_progress()?; let is_rebasing = matches!(state, Some(InProgressOperation::Rebase)); return Err(classify_rebase_failure(e, is_rebasing, &integration_target)); ``` All five of #3539's classification tests pass alongside the gate. > _This was written by Claude Code on behalf of Maximilian_ --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
aebe278a2c | refactor(tests): drop empty-valued env entries from snapshot headers (#3063) | ||
|
|
f2a8cb696d |
refactor(styling): end bash-gutter lines at their content, not a reopened dim (#3056)
Dispatched to fix a reported ANSI dim-bleed after bash-gutter blocks: in committed snapshots, multi-command hook announcements appear to inherit an unclosed `[2m` from the preceding gutter line (e.g. `post_start_named_commands.snap`, where the gutter line ends `'Installing deps'[0m[2m`). Diagnosis: the bleed doesn't exist in real output. Fresh `cat -v` captures of `wt hook pre-merge --yes` and `wt switch --create` with two-key hook tables show every gutter line closing with a full `[0m`, and later `◎ Running …` lines rendering un-dimmed. The snapshots are misleading by construction: the cross-platform filter in `tests/common/mod.rs` deletes every line-final `[0m` before snapshotting, and the formatter reopens dim after the last highlight token before its per-line reset, so filtered snapshots end `[0m[2m` and read exactly like a dangling dim. The change removes that misleading byte pattern at the source: phase 2 of `format_bash_with_gutter_impl` strips the no-op reopened dim (and the lone dim on blank lines) before appending each line's closing reset. Rendering is identical; lines now end at their content plus one reset. Also bundled: - A CAUTION comment on the reset-stripping snapshot filter, so future readers don't diagnose SGR bleed from `.snap` bytes. - `config_show_theme` now binds the standard env redactions; regenerating its snapshot leaked a host `LLVM_PROFILE_FILE` path and tripped `test_no_host_specific_paths_in_snapshots` (pre-existing gap, invisible until regeneration since insta never compares `info:` blocks). - 108 regenerated snapshots. Verified mechanically: ANSI-stripped bodies are byte-identical; raw diffs are confined to line-end SGR sequences plus stale `env:` header refreshes (`RUST_LOG: warn` from an older harness). Possible follow-up, not done here: the line-final-reset filter itself may be vestigial (anstream pass-through suggests piped output is identical across platforms now); removing it would make snapshots byte-truthful but churns nearly every snapshot and needs Windows CI to confirm. The first Windows CI run caught a real latent bug the strip exposed: askama strips a template's final newline, so on a CRLF checkout (Windows autocrlf) the fish wrapper render ends with a lone `\r` that the formatter's pair-wise CRLF normalization missed. The `\r` reached tree-sitter and came back as a trailing token after the highlight closed, defeating the end-of-line cleanup (and historically invisible because insta trims trailing whitespace when comparing). Fixed by trimming trailing `\r` in the formatter's normalization, plus `templates/* text eol=lf` in `.gitattributes` since CRLF templates embedded at compile time would leak `\r` into the shell code Windows-built binaries emit at runtime. 🤖 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> |
||
|
|
b68e10b816 |
refactor(error): render UncommittedChanges dirty files in the gutter (#2887)
The `UncommittedChanges` error variant printed the offending `git status --porcelain` lines with a manual two-space dim indent. Visually similar to the canonical gutter, but inconsistent with `format_with_gutter()` — the helper the `writing-user-outputs` skill points at for quoting external content. `ConflictingChanges` in the same file already does the right thing; this aligns `UncommittedChanges` with it. Before: ``` ✗ Cannot remove: feature has uncommitted changes M auth.rs ?? .DS_Store ↳ Commit or stash changes first ``` After: ``` ✗ Cannot remove: feature has uncommitted changes ▌ M auth.rs ▌ ?? .DS_Store ↳ Commit or stash changes first ``` Swept the codebase for similar manual-indent-quoting patterns; this was the only site. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
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) |
||
|
|
302575894a |
fix(output): bold identifiers in skipped/squash/path messages (#2661)
## Summary Several user-facing messages interpolated branch, worktree, file, and path names as plain text. Per the project's `writing-user-outputs` skill, branches and status values should be bolded; in hints, paths use underline. ## Changes - `wt step prune` "Skipped … (younger than X)" lines now bold each branch name. The joined-list variant bolds names individually so commas stay unbold. - `wt step squash`'s "Nothing to squash; no commits ahead of `<branch>`" bolds the branch. - `wt config update`'s "Copied approved commands to `<file>`" bolds the filename. - `ConflictingChanges` hint underlines the path (hint convention). - Config-load warning: `at PATH` → `@ <bold>PATH</>` (matches the codebase `@`-not-`at` convention and bolds the identifier). - Explicit-shell uninstall warnings bold the path so they match the hint counterpart. ## Test plan - [x] `cargo test --test integration` (1634 passing) - [x] `pre-commit run --all-files` (lints + fmt clean) - [x] Snapshots updated via `cargo insta accept` — all diffs are exactly the formatting changes plus cosmetic env-var drift --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
4dd84ca189 |
feat(error): list dirty files in UncommittedChanges (#2653)
## Summary The "X has uncommitted changes" error now lists the offending files via `git status --porcelain` between the title and the hint, instead of leaving the user to re-run `git status` to find out *what's* dirty. Before: ``` ✗ Cannot remove worktree after merge: feature-auth has uncommitted changes ↳ Commit or stash changes first ``` After: ``` ✗ Cannot remove worktree after merge: feature-auth has uncommitted changes M auth.rs ?? .DS_Store ↳ Commit or stash changes first ``` ## What changed - `GitError::UncommittedChanges` carries a new `dirty_files: Vec<String>` field (raw porcelain lines). - `WorkingTree::ensure_clean` runs through a new `dirty_files()` helper that returns the porcelain lines instead of discarding them; the four call sites (`wt merge` cleanup, `wt remove`, `wt step promote` × 2) now thread the file list into the error. - `wt merge --no-commit` validator in `commands/merge.rs:155` does the same. - No new git subprocesses: `is_dirty()` was already running `git status --porcelain` and dropping the output. We just stop dropping it on the failure path. - 5 existing snapshots updated; 1 new test case added. ## Why A user hitting "feature has uncommitted changes" today has to re-run `git status` to see what's blocking. The list is information we already have. There's a side benefit: this surfaces dirty state in test snapshots when something *unexpected* dirties a worktree mid-merge — useful for diagnosing the local-only flake on `test_readme_example_complex` etc., where the failing snapshot will now carry the exact file list (likely an OS metadata file like `.DS_Store` or an fsmonitor artifact) rather than a worktrunk bug. ## Test plan - [x] `cargo test --lib` (1098 passed) - [x] `cargo test --test integration` (1633 passed; 1 new test case) - [x] `cargo run -- hook pre-merge --yes` — full lint + test suite (3531 passed, lints clean) - [x] Snapshots reviewed; new format renders correctly across merge / remove / promote / git-error-display tests. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
a9b92cff89 |
refactor(error): split short Display from styled Diagnostic (#2611)
## Summary
- Split each typed leaf error's textual surface in two: `Display` is a
single-line label suitable for embedding (`format!(\"…: {e}\")`, JSON
output, log files); `Diagnostic::render` produces the styled multi-line
block (emoji, color, gutter, follow-up hints).
- Introduces a `Diagnostic` trait implemented by `GitError`,
`WorktrunkError`, `HookErrorWithHint`, `TemplateExpandError`, and
`CommandError`. The renderer in `format_command_error` walks the anyhow
chain via `try_render_diagnostic` once instead of per-type downcast
branches.
- Replaces six anyhow-error free helpers with an `ErrorExt` extension
trait so call sites use method syntax (`err.exit_code()`,
`err.render_diagnostic()`). Domain-specific helpers
(`add_hook_skip_hint`) and leaf lookups (`CommandError::find_in`) stay
as their natural form.
- `HookErrorWithHint` now stores the typed `WorktrunkError` directly;
the runtime downcast in `render` is gone, and `Error::source` exposes
the inner so `exit_code` is a single chain walk.
- `GitError::title()` is the single source of truth for variant titles.
Render uses it for the styled header; Display strips ANSI codes via
`ansi_str::AnsiStr::ansi_strip`. ~150 lines of inline title duplication
removed.
Designed in two iterations with Codex review. The trait+registry shape
was preferred over `miette` because worktrunk's variants carry bespoke
rendering (gutter blocks of git stderr, copy-pasteable shell
suggestions, fetch-time annotations, switch-suggestion ctx) that don't
decompose into miette's `(code, severity, message, help, labels)` shape.
## Test plan
- [x] \`cargo run -- hook pre-merge --yes\` (3492 tests + clippy + doc +
doctest)
- [x] Unit tests in \`src/git/error.rs\` updated for the new contract;
new snapshot tests assert the short \`Display\` per variant
- [x] Integration tests in
\`tests/integration_tests/git_error_display.rs\` switched to
\`err.render()\`
- [x] Inline \`<bold>\` highlighting on entity names preserved in styled
rendering
- [x] JSON outputs (\`for-each\`) confirmed plain (no ANSI codes leaking
via embedded Display strings)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
c710147c8c |
fix(bench): unbreak wt-perf invalidate; consolidate env-isolation helpers (#2540)
## Summary Two bugs in `wt_perf::invalidate_caches_auto`, plus consolidation of the env-isolation helpers shared between tests and benches. ## Bugs fixed **(1) Silent unset under inherited `GIT_DIR`.** `invalidate_caches_auto` shelled to `git config --unset worktrunk.default-branch` via `worktrunk::shell_exec::Cmd`. `Cmd::apply_common_settings` re-injects normalized inherited `GIT_*` path vars resolved against the wt-perf process's startup cwd (issue #1914 — correct for `wt`-from-git-alias where parent and child target the same repo). For wt-perf, which operates on an explicit, possibly-unrelated repo path, the override redirected the unset to the wrong git dir; the resulting non-zero exit was swallowed by `let _ =`, and the cache stayed put. Reproducer: ```bash cd /tmp && GIT_DIR=.git target/release/wt-perf invalidate /tmp/wt-prof-test # previously: silently leaves worktrunk.default-branch in place # now: actually unsets it ``` **(2) Linked worktrees never cleared.** `invalidate_caches_auto` resolved `<repo_path>/.git` naively. When `repo_path` is a *linked* worktree, `.git` is a file (gitdir-pointer), not a directory, so every `fs::remove_*` call no-op'd silently — `.git/wt/cache/`, `.git/index`, `packed-refs`, commit-graph all survived. Existing bench callers all pass the main worktree, so no current bench was affected, but the function's contract was broken for linked-worktree inputs. ## Fixes - wt-perf no longer routes through `worktrunk::shell_exec::Cmd`. All `git` subprocess work uses `std::process::Command` directly via a small `git_command()` helper that delegates to `worktrunk::testing::configure_git_cmd`. The inherited-`GIT_*` normalization (which was the root of bug 1) doesn't apply to plain `Command`. - `invalidate_caches_auto` resolves the git common dir from the filesystem — reads `.git` as dir-or-pointer, strips `worktrees/<name>` for linked worktrees — so the cache root is correct regardless of which worktree of a repo is named. - The `let _ =` on the unset is gone; non-success/non-5 exits print a one-line `eprintln!` so a future regression can't hide the same way. ## Consolidation Touched four near-duplicate env-isolation helpers (`configure_cli_command`, `configure_git_cmd`, `configure_git_env`, `wt_perf::isolate_cmd`). The shared core is now in `worktrunk::testing`: - `pub fn isolate_subprocess_env(cmd, user_config: Option<&Path>)` — strips host context (`GIT_*`, `WORKTRUNK_*`, `NO_COLOR`, `SHELL`, `PSModulePath`) and sets baseline `WORKTRUNK_*_PATH`. Used by `configure_cli_command` (test-side, layers on test determinism) and every bench (no extra layer — benches want realism). - `pub fn scrub_git_path_vars(cmd)` — env_remove the five `GIT_*` repo-discovery vars. Called defensively from `configure_git_cmd`/`configure_git_env` so test code that spawns `git` outside a `wt`-parent isn't vulnerable to the same redirect. - `wt_perf::isolate_cmd` deleted. Five bench files now import `worktrunk::testing::isolate_subprocess_env` directly. - Path strings unified: `/nonexistent/wt/{config,approvals}.toml` and `/etc/xdg/worktrunk/config.toml` (preserved as the real XDG path). 47 snapshots regenerated. ## Test plan - [x] `cargo clippy --all-targets --workspace` clean - [x] 1072 + 607 lib unit tests pass - [x] 1589 integration tests pass (vs. baseline's 3 pre-existing failures, which the snapshot regen also resolved) - [x] All 5 benches compile (`cargo bench --no-run`) - [x] Bug 1 (GIT_DIR redirect): unset succeeds - [x] Bug 2 (linked worktree): cache and config both cleared from any worktree path - [x] Vanilla path: still works 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
cdb17cbd7c |
feat(switch): suggest pr:N or mr:N for numeric branch names (#2516)
## Summary When `wt switch <number>` fails because no branch by that name exists, the hint now leads with how to switch to the matching PR/MR before mentioning `--create`. Platform is detected by hostname-matching the primary remote's effective URL (so `url.insteadOf` rewrites are respected): - **GitHub** → `wt switch pr:N` - **GitLab** → `wt switch mr:N` - **Unknown / opaque host** → both suggestions Non-numeric branch names keep the original hint unchanged. ### Before ``` ✗ No branch named 2474 ↳ To create a new branch, run wt switch --create 2474; to list branches, run wt list --branches --remotes ``` ### After (GitHub) ``` ✗ No branch named 2474 ↳ To switch to PR #2474, run wt switch pr:2474; to create a new branch, run wt switch --create 2474; to list branches, run wt list --branches --remotes ``` ## Test plan - [x] Snapshot tests cover GitHub, GitLab, and unknown-platform variants - [x] Direct test for `Repository::detect_ref_type` covers all URL branches - [x] Existing `branch_not_found` snapshots unchanged (non-numeric names) - [x] `cargo run -- hook pre-merge --yes` passes (3412 tests, all lints) - [x] Manual smoke test in temp repo: GitHub remote → `pr:N`, GitLab remote → `mr:N`, no remote → both --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
64e1b4efb9 | feat(cli): move approvals subcommand from hook to config (#2282) | ||
|
|
679fe539fe |
Deprecate --no-verify in favor of --no-hooks (#1932)
`--no-hooks` describes what the flag does — skip hooks. `--no-verify` was inherited from git's naming but doesn't match worktrunk's semantics (there's no "verification" step being skipped). `--no-verify` remains as a hidden alias that emits a deprecation warning, retained for at least one release cycle per the project's deprecation policy. Changes across switch, remove, merge, step commit, and step squash: - `--no-hooks` is the canonical visible flag - `--no-verify` hidden, emits `▲ --no-verify is deprecated; use --no-hooks instead` - Error hints (`↳ To skip pre-merge hooks, re-run with --no-hooks`), info messages, help text, docs, and config examples all updated - `resolve_verify()` helper in main.rs deduplicates the deprecation logic - Backward-compatibility test verifies `--no-verify` still works > _This was written by Claude Code on behalf of @max-sixty_ --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
623a2f9221 |
Show last fetch time in branch-not-found hint (#1877)
When `wt switch` fails because a branch doesn't exist, the hint now shows when the remote was last fetched — helping users realize stale local refs may be the cause. ``` ✗ No branch named fix/bump-mypy-1.19.1 ↳ To create a new branch, run wt switch --create fix/bump-mypy-1.19.1; to list branches, run wt list --branches --remotes (last fetched 3h ago) ``` If the repo has never been fetched (no `FETCH_HEAD`), no parenthetical is shown — the hint reads the same as before. Only appears in `switch` context, not `remove` or `merge`. > _This was written by Claude Code on behalf of @max-sixty_ --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
efad9312db |
feat!: rationalize hooks — rename post-create, add post-commit, background post-merge (#1679)
Implements the hook rationalization from #1670, establishing a symmetric `pre-` (blocking) / `post-` (background) pattern for every lifecycle event. ## Changes **Rename `post-create` → `pre-start`**: The old name suggested it ran *after* creation, but it actually runs *before* `post-start` as a blocking dependency step. Both names accepted for one release cycle — config deprecation detection, migration file generation, `wt config update` support, and CLI alias all in place. `merge_with` folds old-name hooks into new-name so cross-config combinations don't silently drop hooks. **Add `post-commit` hook**: New background hook firing after successful commits (including squash commits), completing the commit lifecycle pair. Included in approval batches for `wt step commit`, `wt step squash`, and `wt merge`. **Change `post-merge` to background**: Was blocking with `Warn` strategy, now runs in background like all other `post-` hooks. `--foreground` flag available for debugging. The hook table is now a clean symmetric grid: | Event | `pre-` (blocking) | `post-` (background) | |-------|-------------------|---------------------| | start | `pre-start` | `post-start` | | switch | `pre-switch` | `post-switch` | | commit | `pre-commit` | `post-commit` | | merge | `pre-merge` | `post-merge` | | remove | `pre-remove` | `post-remove` | ## Testing 868 lib + 483 bin + 1227 integration tests pass, all 13 lint checks pass. The deprecation has 23 dedicated unit tests covering detection at all three config scopes, migration, empty table filtering, cross-config merge safety, and integration with `wt config show` / `wt config update`. Closes #1670 > _This was written by Claude Code on behalf of max-sixty_ --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
e0067f064f |
feat(help): syntax-highlight code blocks in --help (#1365)
Code blocks in `--help` were rendered as plain dimmed text — the markdown language identifier (` ```toml `, ` ```console `) was discarded. Now they use the existing gutter formatting with language-aware syntax highlighting: - **TOML** blocks get synoptic highlighting (table headers cyan, strings green, comments dimmed) - **console/bash/sh** blocks get tree-sitter-bash highlighting (commands blue, flags cyan, strings green) - **Other** languages get dimmed text with gutter Also makes TOML highlighting consistently dimmed (matching bash style) and removes the 2-space indent from help tables so block elements are flush-left. > _This was written by Claude Code on behalf of @max-sixty_ Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
dc7f5bca5c | Replace bright-black with underline for inline references in hints (#1285) | ||
|
|
cbd5e4da50 |
feat: add nushell tab completions (#1220)
## Summary
- Add `nu-complete wt` completer function to the nushell init template,
wiring it to the wrapper's rest parameter via `@"nu-complete wt"`
- The completer calls the binary with `COMPLETE=nu` and parses
tab-separated output into nushell's `{value, description}` record format
- Add `"nu"` to multi-shell completion test loops (help, version,
single-dash, deprecated flags) and a dedicated subcommand snapshot test
Thanks to @omerxx for reporting in #1215.
Closes #1215
## Limitations
Nushell's completion engine bypasses custom completers when the current
token starts with `-`, so flag completions (e.g. `wt switch --<TAB>`)
don't appear. Subcommand and value completions work correctly. This is a
nushell engine limitation (nushell/nushell#14504), not something we can
fix in our template.
## Test plan
- [x] Unit tests pass (`cargo test --lib --bins`) — snapshot updated
- [x] Integration tests pass (`cargo test --test integration`) — 1121
tests
- [x] Shell integration test (`cargo test --test integration --features
shell-integration-tests -- test_nushell_completion_subcommands`)
- [x] Lints pass (`pre-commit run --all-files`)
- [x] Manual verification in nushell 0.110.0: `wt <TAB>` shows
subcommands, `wt switch <TAB>` shows branches with descriptions
> _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>
|
||
|
|
7b7c0b12d2 |
feat: add color palette to show-theme (#1185)
## Summary - Add a "Color palette" section to `wt config shell show-theme` that prints each color/style rendered in itself - Covers base colors (red, green, yellow, blue, cyan), modifiers (bold, dim), bold+color variants (emphasis in messages), dim bright-black (inline code in hints), and dim+color variants (bash syntax highlighting) - Helps users diagnose legibility issues on their terminal theme Thanks to @jhigh2000 for reporting in #1184. Closes #1184 ## Test plan - [x] Snapshot test updated and passing - [x] All 1120 integration tests pass - [x] All lints pass > _This was written by Claude Code on behalf of @max-sixty_ --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
9d6f8c523a |
fix: show remaining entries when foreground removal fails (#1150)
## Summary - When `wt remove --foreground` fails with "Directory not empty", list top-level entries remaining in the directory (capped at 10 with "and N more") - Suggest trying `wt remove` (background) as a workaround - For non-"Directory not empty" errors (e.g. permission denied), show remaining entries without the background process hint Before: ``` ✗ Failed to remove worktree for test @ ~/workspace/worktrunk.test ┃ error: failed to delete '/path': Directory not empty ``` After: ``` ✗ Failed to remove worktree for test @ ~/workspace/worktrunk.test ┃ error: failed to delete '/path': Directory not empty ↳ Remaining in directory: .vite/, node_modules/, target/ ↳ A background process may be writing files; try wt remove (without --foreground) ``` ## Test plan - [x] Snapshot tests for: directory not empty with entries, permission error with entries, many entries (cap at 10) - [x] Existing `worktree_removal_failed` snapshot unchanged (no entries case) - [x] Integration tests triggering actual `git worktree remove` failure (both normal-branch and detached-HEAD paths) - [x] All tests pass on Linux, macOS, and Windows - [x] codecov/patch passes > _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> |
||
|
|
bba3a209b8 |
fix: align user-facing output with formatting guidelines (#1138)
## Summary - Replace `<bold>` with `<bright-black>` inside `hint_message()` calls — bold's close tag (`[22m`) resets both bold and dim, breaking hint styling - Use `format_path_for_display()` instead of raw `.display()` in user-facing messages (config warning, relocate errors, step_commands output) - Remove "you/your" pronouns from help text and rephrase typo suggestions - Reorder hint phrasing to canonical "To X, run/use Y" pattern ## Test plan - [x] All 1084 integration tests pass - [x] All lints pass (`pre-commit run --all-files`) - [x] Snapshot tests updated to reflect new wording > _This was written by Claude Code on behalf of @max-sixty_ Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
339ced434e |
fix: remove stray blank lines before hints in error output (#1072)
Remove blank line between detail lines and hint in ConflictingChanges and
NotFastForward error display. The `"\n{detail}\n"` + `"\n{hint}"` pattern
produced `\n\n` (blank line) violating "hints attach to their subject."
Also fix trailing double blanks in --help-md output (print! instead of
println! since clap output already ends with \n) and diagnostic template
(Jinja {%- whitespace control).
Add snapshot_formatting_guard tests that scan all 726 snapshots for:
- Blank line before hint marker (↳)
- Double blank lines in output sections
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
51b5f6ef5f |
feat(switch): enrich error hints with --execute context (#1064)
* feat(switch): enrich error hints with --execute and trailing args context When `wt switch --execute=<cmd> -- <args>` fails (branch not found, already exists, path exists), error hints now include the full command context so users can copy-paste the suggested fix directly. Also fixes `suggest_command()` to place flags before positional args (and before any `--` separator), preventing flags from being treated as positional args when dash-prefixed branches are involved. Co-Authored-By: Claude <noreply@anthropic.com> * fix(coverage): remove unreachable -- separator branch in apply() Clap's `#[arg(last = true)]` on execute_args means `--` always routes to execute_args, so a dash-prefixed branch can't coexist with --execute via the CLI. The suggested command never has a pre-existing `--` separator when SwitchSuggestionCtx is applied, making this branch dead code. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
3b5b609a04 |
refactor(env): rename WT_TEST_* env vars to WORKTRUNK_TEST_* (#1016)
Standardize the two remaining `WT_`-prefixed env vars to use the `WORKTRUNK_` prefix, completing the migration noted in the changelog. - WT_TEST_EPOCH → WORKTRUNK_TEST_EPOCH - WT_TEST_DELAYED_STREAM_MS → WORKTRUNK_TEST_DELAYED_STREAM_MS Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
9282805b36 |
refactor(error): display failed commands in bash-highlighted gutter (#1001)
* refactor(error): display failed commands in separate bash-highlighted gutter
Extract the failed git command from inline parenthesized text into a
separate hint line + bash syntax-highlighted gutter block. This makes
long commands much more readable and visually distinct from git output.
Before:
✗ Failed to create worktree for fix from base main
┃ fatal: cannot lock ref 'refs/heads/fix'
┃ (git worktree add /path -b fix main failed, exit code 128)
After:
✗ Failed to create worktree for fix from base main
┃ fatal: cannot lock ref 'refs/heads/fix'
↳ Failed command, exit code 128:
┃ git worktree add /path -b fix main
- Add FailedCommand struct to separate command identity from git output
- Add StreamCommandError for structured error returns from run_command_delayed_stream
- Add Repository::extract_failed_command() bridge for crate boundary
- Update WorktreeCreationFailed Display impl and both callers in switch.rs
- Update writing-user-outputs skill: hints can provide non-essential context
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: simplify StreamCommandError Display to just output
Callers use Repository::extract_failed_command() to access fields
directly, so the Display impl only needs to satisfy the Error trait
bound.
Co-Authored-By: Claude <noreply@anthropic.com>
* test: add coverage for extract_failed_command branches
Tests both the StreamCommandError path (exercises Display impl) and
the fallback path for non-StreamCommandError errors.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: fully qualify doc link for run_command_delayed_stream
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: extract worktree_creation_error helper to reduce duplication
Both call sites in switch.rs had identical 6-line closures constructing
WorktreeCreationFailed from extract_failed_command. Shared helper
reduces each to a one-liner and improves test coverage.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
0c9e0205a3 |
feat(switch): integrate interactive picker into wt switch (#894)
* feat(switch): integrate interactive picker into `wt switch` (#890) `wt switch` without arguments now opens the interactive picker (previously `wt select`). This simplifies the mental model: one command for all switching. - `wt switch` → opens interactive picker (Unix) or shows error (Windows) - `wt switch --branches/--remotes` → customizes picker - `wt select` → deprecated hidden alias with warning Removes the separate `wt select` docs page since the functionality is now documented under `wt switch`. The `[select]` config section is preserved for backward compatibility with a TODO to rename once migration is confirmed. Closes #890 Co-Authored-By: Claude <noreply@anthropic.com> * fix(test): make switch TTY test Unix-only The test expects the Unix error message ("Interactive picker requires an interactive terminal") but Windows shows a different message ("Interactive picker is not available on Windows"). Co-Authored-By: Claude <noreply@anthropic.com> * chore: trigger CI Co-Authored-By: Claude <noreply@anthropic.com> * fix(switch): require branch when using --create, --base, --execute, or --clobber These flags only make sense when switching to a specific branch, not when opening the interactive picker. Previously, running `wt switch --create` would silently ignore the flag and open the picker instead. Now clap properly enforces that these flags require a branch argument: $ wt switch --create error: the following required arguments were not provided: <BRANCH> Co-Authored-By: Claude <noreply@anthropic.com> * fix(test): update snapshot for interactive picker error message The snapshot was stale after integrating `wt select` into `wt switch`. Now `wt switch` without args shows the picker error, not clap's missing argument message. Co-Authored-By: Claude <noreply@anthropic.com> * docs(switch): move interactive picker section after shortcuts The picker is a navigation method (like shortcuts), not an afterthought. New order: Shortcuts → Interactive picker → GitHub/GitLab PRs → Troubleshooting Co-Authored-By: Claude <noreply@anthropic.com> * fix(test): update help snapshot after doc reorg Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
ee54aa6102 |
chore(deps): upgrade tree-sitter to 0.26, add show-theme wrapping examples (#906)
* chore(deps): upgrade tree-sitter to 0.26, add show-theme wrapping examples
Upgrade tree-sitter ecosystem to enable unified multi-line highlighting:
- tree-sitter: 0.25 → 0.26
- tree-sitter-bash: 0.25 → 0.25.1
- tree-sitter-highlight: 0.25 → 0.26
With tree-sitter 0.26, unified highlighting correctly identifies operators
like `&&` across line breaks, enabling future switch from line-by-line to
unified highlighting for better wrapped line handling.
Add regression tests verifying unified highlighting works for:
- Multi-line commands with `&&` at line ends
- Commands containing template syntax (`{{ }}`)
Update `wt config shell show-theme` to demonstrate the wrapping issue:
- Short command
- Long command that wraps
- Multi-line string literal
- Multi-line command
All line types currently display identically, making it hard to distinguish
wrapped continuations from new commands.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(styling): preserve syntax highlighting across multiline strings
Switch from line-by-line to unified highlighting in format_bash_with_gutter().
This preserves tree-sitter context across newlines, so strings spanning
multiple lines retain their string styling.
Also adds 3 extra spaces of indentation for wrapped continuation lines,
distinguishing terminal-forced wraps from actual newlines in the source.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(styling): normalize CRLF to LF in bash highlighting
Windows templates have CRLF line endings, which caused the style
restoration code to create extra lines. Normalize to LF at input.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(styling): preserve syntax highlighting for template-heavy content
Tree-sitter's bash grammar fails to parse content when Jinja template
delimiters `{{` and `}}` are split across lines, causing all highlight
events to be suppressed. This manifested as template-heavy hook commands
losing all syntax highlighting.
Fix by replacing template delimiters with bash-valid placeholders
(`_WT_OPEN_`/`_WT_CLOSE_`) before parsing, then restoring them after
highlighting and wrapping. Also skip function styling for placeholders
since tree-sitter misidentifies them as commands.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: remove unnecessary mut from result variable
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(styling): restore template delimiters before wrapping
Move placeholder restoration before wrapping so line width calculations
use the actual 2-char `{{`/`}}` instead of the longer placeholders.
This preserves line wrapping behavior matching main.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(styling): filter out ANSI-only lines from gutter output
When input ends with a newline, style restoration after the final
newline creates a line containing only ANSI codes. Filter these out
to avoid spurious blank gutter lines in output.
Also use static LazyLock for the ANSI regex pattern.
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor(styling): simplify template placeholder handling
- Use plain identifier placeholders (no `$`) to avoid ANSI code insertion
- Trim trailing newlines from input to avoid spurious blank gutter lines
- Remove ANSI regex filtering (no longer needed)
Template delimiters `{{`/`}}` at wrapped line start may be styled as
commands, but this is acceptable given the code simplification.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(styling): use quoted string placeholders for consistent template styling
Use `"WTO"`/`"WTC"` as placeholders instead of plain identifiers.
Tree-sitter parses quoted strings as single tokens with consistent
"string" styling (green), so `{{` and `}}` now appear green regardless
of their position in the line.
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(show-theme): add template syntax example to bash gutter demo
Shows how `{{ repo_root }}` and `{{ worktree }}` are styled (green).
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
f1c7555dee |
test: update snapshots for new test environment variables (#907)
Regenerate 599 snapshot files to reflect the updated test environment:
- Add WORKTRUNK_TEST_POWERSHELL_ENV=0 (PowerShell detection control)
- Add WORKTRUNK_TEST_CLAUDE_INSTALLED=0 (Claude Code detection control)
- Add PSModulePath="" (explicit empty value for deterministic snapshots)
- Remove GIT_EDITOR="" (no longer set in test environment)
These changes follow from the test isolation improvements in
|
||
|
|
907afad0a5 |
feat(styling): add TOML syntax highlighting for gutter output (#905)
* feat(styling): add TOML syntax highlighting for gutter output Add format_toml() with syntax highlighting for config previews: - Table headers [section] in cyan bold - String values in green - Comments dimmed Update show-theme to demonstrate all three gutter styles: - Error details (plain text) - Config (TOML highlighted) - Shell code (bash highlighted) Co-Authored-By: Claude <noreply@anthropic.com> * fix(styling): add trailing newline after format_toml in config show The format_toml change to remove trailing newlines broke config show output. Use writeln! instead of write! to restore the blank line between sections. Co-Authored-By: Claude <noreply@anthropic.com> * refactor(styling): simplify format_toml token handling Merge TokOpt::Some and TokOpt::None branches to share the unstyled path. Synoptic's TOML highlighter returns TokOpt::None for unstyled content (keys, operators, whitespace), not TokOpt::Some with unrecognized types. Add test verifying both styled and unstyled paths are exercised. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
bf3461aa4c |
Add prompt_message() formatting function (#858)
* Add prompt_message() formatting function and use it consistently - Add prompt_message() to styling::constants following the pattern of error_message(), hint_message(), etc. - Update all interactive prompt sites to use the new function: - command_approval.rs: batch approval prompt - configure_shell.rs: install/uninstall prompts, show-theme demo - Prompts now have consistent cyan styling on both symbol and text Co-Authored-By: Claude <noreply@anthropic.com> * Update approval prompt snapshots for new prompt styling Co-Authored-By: Claude <noreply@anthropic.com> * Update shell integration prompt snapshots for new prompt styling Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
483f806e83 |
fix: improve BranchAlreadyExists hint wording (#797)
Change hint from "remove --create and run" to "run without --create:" for clearer, more natural phrasing. Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
85c4fc1846 |
fix(errors): improve "not found" error messages (#774)
- Rename NoWorktreeFound → BranchNotFound (consolidate with InvalidReference) - Add ReferenceNotFound for commit-ish validation (--base accepts tags/commits) - Improve message phrasing: - BranchNotFound: "No branch named X" - ReferenceNotFound: "No branch, tag, or commit named X" - WorktreeNotFound: "Branch X has no worktree" - Add show_create_hint field to BranchNotFound to suppress "create" hint for remove operations where it doesn't make sense Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
ec64dbebce |
fix: use WT_TEST_EPOCH instead of SOURCE_DATE_EPOCH for test time control (#767)
* fix: use WT_TEST_EPOCH instead of SOURCE_DATE_EPOCH for test time control SOURCE_DATE_EPOCH is a reproducible builds standard commonly set by NixOS/direnv in development shells. When set to a past timestamp (typical for builds), the Age column in `wt list` incorrectly shows "future" for all commits. Switch to WT_TEST_EPOCH which is only set by our test harness, leaving production users unaffected. Fixes #763 Co-Authored-By: Claude <noreply@anthropic.com> * fix: restore PTY test snapshots with WT_TEST_EPOCH The previous commit deleted PTY-based test snapshots that can only be regenerated in CI (they require Linux/macOS PTY support). This commit restores those 46 snapshots from main and updates them to use WT_TEST_EPOCH instead of SOURCE_DATE_EPOCH. Co-Authored-By: Claude <noreply@anthropic.com> * fix: wrap bare URL in angle brackets for rustdoc RUSTDOCFLAGS='-Dwarnings' fails on bare URLs in doc comments. Wrap the GitHub issue link in angle brackets to make it a proper hyperlink. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
2e2f1f5676 |
refactor: use -1 sentinel for delayed stream disable (#751)
Change WT_TEST_DELAYED_STREAM_MS from magic number 999999 to -1 as a cleaner sentinel value meaning "never stream". The type changes from u64 to i64 to allow negative values. Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
dc20cc63ea |
fix: handle CRLF line endings in multiline error display (#707)
The multiline error detection in main.rs only checked for `\n`, missing errors with `\r\n` (Windows) or `\r` (old Mac) line endings. These would fall through to the single-line handler and display incorrectly. Now checks for both `\n` and `\r`, normalizes all line endings to `\n` before displaying in the gutter. Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
ad8ef2d74a |
test: update snapshots for refactored module paths and improved test isolation (#705)
- Update source paths in shell module snapshots (shell.rs → shell/mod.rs) - Add MOCK_CONFIG_DIR and SHELL env vars to test environment - Remove GIT_EDITOR env var (no longer needed) - Fix indentation in inline snapshots Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
f584cf7833 |
test: add cross-platform PTY infrastructure for Windows shell tests (#670)
* test: add cross-platform PTY infrastructure for Windows shell tests Make shell integration tests cross-platform using portable_pty's ConPTY support on Windows. Changes: - tests/common/mod.rs: Remove Unix-only gate from shell module - tests/common/shell.rs: Add Windows env vars, PowerShell -Command flag, fix PATH separator for PowerShell - tests/integration_tests/shell_wrapper.rs: Add PowerShell support to build_shell_script and exec_in_pty_interactive, add Windows-only PowerShell test cases The Windows tests are gated with #[cfg(windows)] and will run when CI has Windows runners with PowerShell available. Co-Authored-By: Claude <noreply@anthropic.com> * fix: gate remaining Unix-only shell tests with #[cfg(unix)] Three more tests use `shell_command` which is Unix-only: - test_zsh_completion_produces_correct_output - test_wrapper_help_redirect_captures_all_output - test_wrapper_help_interactive_uses_pager These tests require zsh/bash/fish which aren't available on Windows. The Windows PowerShell tests at the end of the file remain ungated. Co-Authored-By: Claude <noreply@anthropic.com> * fix: gate all Unix shell tests with #[cfg(unix)] Comprehensive update to gate all tests that use bash/zsh/fish shells with #[cfg(unix)] to prevent them from running on Windows CI. Tests gated: - All parameterized shell tests (bash/zsh/fish cases) - Bash-specific tests (completions, job control, shell integration) - Zsh-specific tests (wrapper function, job control) - Fish-specific tests (completions, multiline commands) - README example tests that use Unix shells Windows PowerShell tests at the end of the file remain active on Windows via their existing #[cfg(windows)] attributes. Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): gate Unix-only imports and statics for Windows CI When all Unix shell tests are gated with #[cfg(unix)], the imports and static variables they use become unused on Windows, causing clippy errors. Add #[cfg(unix)] to: - Imports: canonicalize, wait_for_file_content, assert_snapshot, fs, PathBuf, LazyLock, shell - Statics: TMPDIR_REGEX, TMPDIR_PLACEHOLDER_COLLAPSE_REGEX, WORKSPACE_REGEX, COMMIT_HASH_REGEX, JOB_CONTROL_REGEX - Methods: assert_no_job_control_messages, normalized - Function: generate_completions Co-Authored-By: Claude <noreply@anthropic.com> * test(windows): temporarily ignore PowerShell PTY tests PowerShell PTY tests timeout in CI (60+ seconds). The issue is likely that Get-Command finds Windows Terminal's wt.exe instead of the test binary, causing the completion setup to hang. Mark tests as ignored pending investigation. The test infrastructure and cross-platform PTY support remain in place. Co-Authored-By: Claude <noreply@anthropic.com> * fix(tests): gate assert_success method for Windows CI The assert_success() method (added in #668) is only used by Unix shell tests. Gate it with #[cfg(unix)] to fix Windows clippy dead_code error. Co-Authored-By: Claude <noreply@anthropic.com> * refactor(tests): organize shell tests by platform - Group Unix-only imports in single #[cfg(unix)] block - Move Windows tests into `mod windows_tests` with single #[cfg(windows)] - Add documentation explaining test organization by platform - Remove individual #[cfg(windows)] from each Windows test This makes it clearer which tests run on which platform and reduces the number of scattered cfg attributes. Co-Authored-By: Claude <noreply@anthropic.com> * refactor(tests): organize shell_wrapper tests by platform with module-level gating - Replace 46 individual #[cfg(unix)] annotations with single module-level gate - Restructure from nested `mod tests { mod windows_tests }` to sibling modules: - `#[cfg(unix)] mod unix_tests` - 47 bash/zsh/fish tests - `#[cfg(windows)] mod windows_tests` - 4 PowerShell tests - Rename 30 snapshot files to match new module path (__tests__ → __unix_tests__) This makes platform organization explicit at the module level rather than scattered across individual test functions. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
63def67f0d |
feat: add --force hint when removing dirty worktrees (#671)
When `wt remove` fails due to uncommitted changes, the hint now shows
the full command to bypass the check:
✗ Cannot remove worktree: feature has uncommitted changes
↳ Commit or stash changes first, or to lose uncommitted changes,
run wt remove feature --force
The hint is context-aware: it only appears for `wt remove` where
`--force` is available, not for `wt merge` contexts where there's no
force option.
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
f039f4a6ab |
Align user messages with output formatting guidelines (#631)
* Align user messages with output formatting guidelines - Use "To X, run Y" pattern for hints with runnable commands - Keep direct imperatives for action guidance (no "To proceed" filler) - Remove quoted commands, use styling instead - Add styling to error messages (bright-black for commands, bold for values) - Clarify git-wt hint: "invoke directly (with the -)" - Update guidelines to distinguish command hints from action guidance Co-Authored-By: Claude <noreply@anthropic.com> * Update PTY-based snapshot tests These tests require the shell-integration-tests feature flag to run locally, so they weren't caught in the initial commit. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
d4be5ee197 |
refactor: reorder CLI commands by usage frequency (#434)
* refactor: reorder CLI commands by usage frequency Reorder Commands enum to surface most-used commands first: switch, list, remove, merge, select, step, hook, config This matches the natural workflow (switch → work → merge) rather than the previous arbitrary ordering. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: remove trailing blank line 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: update snapshots for new command ordering 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
2937aa5830 |
Fix snapshot raw string literals
Change snapshot assertions from standard string literals to raw string literals to properly preserve backslash escape sequences in expected output. |
||
|
|
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> |
||
|
|
90f24c0ac6 |
Remove obsolete hook_show_outside_git_repo snapshot (#393)
This snapshot test was removed as part of the hook show refactoring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
3af0d98619 |
fix: improve shell integration warning for explicit path invocations (#382)
When running wt via an explicit path (cargo run, ./wt, /usr/bin/wt), shell integration can't intercept the command. Previously this showed a misleading "restart shell" message. Now shows: "ran target/debug/wt; shell integration wraps wt" - clearly explaining what was run vs what the shell wrapper intercepts. Detection uses argv[0]: PATH lookup sets it to just "wt", while explicit paths include the path separator. This catches cargo run, symlinks to dev builds, and any direct binary invocation. 🤖 Generated with [Claude Code](https://claude.ai/claude-code) Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
8a9db5f8f0 |
refactor: unify shell restart messages (#381)
* Add shell integration prompt after wt switch When shell integration is not active after `wt switch`, show a one-time interactive prompt asking users if they want to install it. This helps new users discover shell integration without requiring them to read docs. ## Features - Interactive prompt with preview option (?) to see what will be installed - Remembers user choice via `skip-shell-integration-prompt` config flag - Falls back to hint message for non-TTY or after declining ## Decision flow 1. Unsupported shell → hint with supported shells 2. $SHELL not set → hint to run install command 3. Already installed → hint to restart shell 4. Already prompted → hint to run install command 5. TTY available → show interactive prompt 6. Non-TTY → hint to run install command ## Code improvements - Consolidated shell install output into `print_shell_install_result()` - Extracted `print_skipped_shells()` helper to eliminate duplication - Improved hint styling: commands/paths use `<bright-black>` per guidelines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: install for all shells on prompt accept, skip flag only on decline - Prompt now installs for ALL shells with config files, matching `wt config shell install` behavior - Skip flag only set on explicit decline, not before prompt (Ctrl+C doesn't permanently disable prompts) - Shell detection uses starts_with for versioned binaries (zsh-5.9) - If no config files exist anywhere, show install hint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: update prompt_accept snapshot for all-shells install The prompt now installs for all shells (same as `wt config shell install`), so the snapshot includes skip messages for shells without config files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: show shell integration warnings for all switch operations Previously, `wt switch` only warned about missing shell integration when switching to existing worktrees without `--execute`. Now warnings are shown consistently: - `SwitchResult::Existing`: warns when shell integration not active - `SwitchResult::Created`: shows success, then warns if shell won't cd - `--execute`: custom message noting command runs but shell stays put Warning reasons are now proper reasons, not suggestions: - "shell requires restart" (configured but not active) - "shell integration not installed" (not configured) For created worktrees, the warning is simplified to avoid repetition: ✓ Created new worktree for feature from main @ ~/repo.feature ▲ Cannot change directory — shell integration not installed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: unify shell restart messages - All restart hints now use "Restart shell to activate" (removed fish/bash/zsh branching that showed source command for bash/zsh) - Warning reasons use statement form: "shell requires restart" and "shell integration not installed" (per em-dash guidelines) - Combined three conditions that showed same hint into single if - Removed "run:" colon per CLI output formatting guidelines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Replace repo_root with repo_path in wt.toml Update template variable references from repo_root to repo_path to match the current configuration convention. * fix: restore full "shell integration" text in restart hint The abbreviated "Restart shell to activate" was unclear about what was being activated. Restored to "Restart shell to activate shell integration". 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
058bfab195 |
feat: show shell integration warnings for all switch operations (#377)
Previously, `wt switch` only warned about missing shell integration when switching to existing worktrees without `--execute`. Now warnings are shown consistently: - `SwitchResult::Existing`: warns when shell integration not active - `SwitchResult::Created`: shows success, then warns if shell won't cd - `--execute`: custom message noting command runs but shell stays put Warning reasons are now proper reasons, not suggestions: - "shell requires restart" (configured but not active) - "shell integration not installed" (not configured) For created worktrees, the warning is simplified to avoid repetition: ✓ Created new worktree for feature from main @ ~/repo.feature ▲ Cannot change directory — shell integration not installed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com> |