mirror of
https://github.com/max-sixty/worktrunk.git
synced 2026-09-14 20:00:38 +08:00
codex/remove-codex-cloud-specific-tests
70 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b17a1364f1 |
docs(step): render wt step rebase & wt step push on the docs site (#3578)
Follow-up from #3568, which named these two as the one plain oversight
in its audit of `after_long_help` bodies that never reach a docs page.
`wt step rebase` and `wt step push` were the only two of twelve step
operations with no `<!-- subdoc: -->` marker, so their help was
terminal-only, and the only two bullets in `## Operations` left
unlinked. Both markers are added between `squash` and `diff`, per the
ordering invariant in `src/cli/step.rs`.
Both openers restated their `about` line, which reads as immediate
self-repetition on the web where `combine_command_docs()` concatenates
the two. Both bodies are rewritten.
## What reviewing the text against the code turned up
The old rebase body said "Conflicts abort immediately; use `git rebase
--abort` to recover", which is self-contradictory and wrong. Nothing
aborts: the worktree is left mid-rebase with git's conflict markers, and
git's output names `--continue`, `--skip`, and `--abort`. `wt merge`'s
pipeline step 3 carried the same sentence.
Four review passes then falsified nine more claims, every one reproduced
against a scratch repo before editing:
- **`--no-ff` runs no `git push` at all.** It builds the merge with
`commit-tree` + `update-ref` and syncs the worktree with `read-tree`; a
`-vv` trace shows zero `git push` invocations. The opener attributed the
whole command to `git push` with `--no-ff` as an example four lines
below. Its worktree sync is also best-effort, so the ref can move while
the worktree stays behind.
- **The Outcomes table was falsified by the upstream-swap topology.**
With local `main` diverged and behind `origin/main`, `wt step rebase
main` prints `Already up to date with origin/main` — `main` is not an
ancestor of the branch, and the result names a ref never typed.
- **"Nothing here reaches a remote" was false on a fresh clone.** With
no target argument and no cached `worktrunk.default-branch`,
`default_branch()` falls through to `git ls-remote`. The claim is now
"no commits leave the repository", which holds unconditionally.
- **The generated Arguments table contradicted the prose.** `[TARGET]
Target branch` sat two paragraphs below "the target is any commit-ish";
a blind reader nearly concluded rebase needs a branch name.
- **The table implied mutually exclusive rows.** `is_rebased_onto` is
checked first, and a branch at the target's tip satisfies both of the
first two.
- Plus: an orphan branch matched no row; "git's own output names the
ways out" is false under `advice.mergeConflict false`; "refused rather
than forced" raised the force question without closing it.
Adjacent, in files this change already touched: `wt merge`'s step 3
contradicted the new table and step 5 restated `wt step push`'s rule
with no link, so half the pipeline deferred and half repeated. Step 2
promised a backup ref unconditionally, but `create_safety_backup` runs
only when working-tree changes were staged — a clean-tree squash
rewrites commits and writes no `refs/wt-backup/` ref. That is a
data-safety claim, so it is corrected rather than left.
`docs/CLAUDE.md`'s subdoc "Use cases" cited `wt config create`, which
has no marker and no section.
## The code changes
**An annotated-tag target was never peeled.** Adding a tag example to
the rebase help is what made it worth running, and it did not work.
`is_rebased_onto` compared `git merge-base`, which peels an annotated
tag to its commit, against a bare `rev-parse`, which returns the tag
object's SHA. Those never match, so an annotated-tag target always
looked like it needed a rebase:
```
annotated v1.2.0 → "outcome": "rebased" (HEAD unchanged)
lightweight v1.2.0-lw → "outcome": "up_to_date" (same graph, same commit)
```
Fixed at the root by peeling with `^{commit}`, rather than documenting
the quirk. The test pairs the two tag types on one commit so the control
is a single factor away; it fails without the fix.
The reviewer caught that this test had gone missing, and it was right
about the consequence — without it, removing the `^{commit}` peel passes
CI. The cause was a merge resolution on this branch: `checkout --theirs`
on `tests/integration_tests/merge.rs` took main's whole file, dropping
`test_step_rebase_annotated_tag_is_peeled` along with the duplicate
squash test it was meant to drop. Restored, and re-verified in both
directions.
**`wt step push` ran out of a half-finished operation.** Mid-rebase, the
detached HEAD is a linear extension of the target, so the fast-forward
check passed and `wt step push main` printed `✓ Pushed to main (1
commit)` — moving the target branch onto a half-replayed history while
the worktree kept its conflict markers and the rebase stayed open. It
now runs the `ensure_no_operation_in_progress` gate `wt step rebase` and
`wt merge` have used since
|
||
|
|
9ecd7945e0 |
fix(merge): measure squash/rebase against the target's upstream, not a stale local ref (#3549)
Fixes #3519. ## The bug `wt merge` resolves its target to the **local** default-branch ref and squashes/rebases the span `merge-base(local-target, HEAD)..HEAD`. When the primary checkout's local `main` is behind `origin/main` **and** the branch descends from the newer `origin/main` tip (e.g. created with `--base origin/main`), that merge-base falls back at the stale local tip, so the span sweeps in every commit already on `origin/main`. The squash folds them into a single commit and the local fast-forward lands it — corrupting the primary checkout's `main` and, if that ref is later pushed, duplicating already-upstream content under new SHAs. Reproduced deterministically before fixing: a 1-commit feature off `origin/main`, with local `main` two commits behind, squashed "3 commits" into a new SHA on local `main` — matching the report. The same mis-measured span reaches every rewrite entry point: a standalone `wt step squash` folds the upstream commits into the *branch* (the reporter's 1-commit PR becoming 13), and `wt step rebase` replays them onto the stale ref when it fires. ## The fix: measure against the upstream; let the merge carry One local-only predicate (no fetch — worktrunk is local-first), `Repository::span_upstream`: the target's upstream, returned exactly when the branch's history extends past the local target ref into it — i.e. `merge-base(HEAD, upstream)` is not an ancestor of the local target. The rewrite pipeline responds by *measuring* there instead of guessing from the stale ref: - **`wt step squash`** (and the squash inside `wt merge`) computes its span, commit count, message, and `reset --soft` base against the upstream — only the branch's own commits fold, and the target ref is never touched. The `--dry-run`/`--show-prompt` previews use the same base. - **`wt step rebase`** (and the rebase inside `wt merge`) rebases *onto* the upstream, so a non-linear branch replays only its own commits instead of duplicating upstream ones onto the stale ref. In the plain stale topology it no-ops, as before. - **`wt merge`** announces the situation up front, then completes: the fast-forward carries local `main` through the already-fetched upstream commits by their real SHAs to the result — the same graph a fresh local `main` would have produced: ``` ○ Local main is 2 commits behind origin/main; the merge fast-forwards through them ◎ Squashing 2 commits into a single commit (2 files, +2)... ✓ Squashed @ 3f2a91c ✓ Merged to main (3 commits, 3 files, +3) ``` The one refusal left is forced by topology rather than chosen by policy: a target that has **diverged** from its upstream (its own commits *and* behind, with the branch based past it) can never fast-forward in any mode — reconciling the target's own commits is the user's call, so the merge stops before any rewrite or approval prompt: ``` ✗ Local main has diverged from origin/main — can't fast-forward to a branch based on origin/main ↳ Reconcile main with origin/main (rebase or merge its local commits), or specify a different target ``` This is the reporter's suggested direction ("use `origin/<default>` as the merge/rebase target"), minus the network: the already-fetched remote-tracking ref is the measurement base, and `wt merge` still never touches the wire. It also extends the rule the rest of worktrunk already applies — `IntegrationTargets` / the preview panes' comparison base treat the upstream as the truth when the local default lags it; the merge pipeline was the one mutating surface that didn't. Quiet on everything that must keep working: a legitimately-diverged local target with the branch forked from the shared base (the supported offline workflow — existing regression test unchanged), targets with no upstream, orphan branches, and explicit non-branch targets like `wt step squash origin/main`. ## Tests - `test_merge_carries_stale_local_main_through_upstream` — the #3519 topology end-to-end: merge succeeds, the carry is announced, the squash folds only the branch's own commits and sits directly on the `origin/main` tip, and `origin/main` is an ancestor of the merged `main` (real SHAs, no duplication). - `test_step_squash_measures_against_upstream_when_local_main_stale` — the standalone squash folds only the branch's own commits and never moves the target ref (this scenario previously corrupted `main` through a squash-then-merge sequence). - `test_step_rebase_targets_upstream_when_local_main_stale` — a branch that merged `origin/main` in rebases onto the upstream: linearized, real upstream SHAs retained, target ref untouched. - `test_merge_refuses_diverged_target_when_branch_based_on_upstream` — the forced refusal: nothing mutated, branch survives. - `test_merge_removes_branch_when_local_main_diverged_from_upstream` (legitimate divergence, branch from shared base) still passes — no regression. Docs: the merge help page (primary source in `src/cli/mod.rs`) describes the upstream-aware measurement, the carry, and the diverged refusal; generated mirrors and help snapshots regenerated. > _This was written by Claude Code on behalf of Maximilian_ --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
79be81a4bb | fix(merge): Accept merge-shaped fast-forward histories (#3509) | ||
|
|
3d0b99e072 |
Add WORKTRUNK_VERBOSE env var equivalent to -v/-vv (#3166)
Shell tab-completion runs the `wt` binary as its own subprocess (the shell sets `COMPLETE=<shell>`), and that path returns from `parse_cli` before `main` ever reaches `logging::init`. So when a tab-completion is slow, there's no flag that turns on logging for it — `-v`/`-vv` never run, and `RUST_LOG` only sets a level, not the `-vv` file sinks. There was no way to profile a slow completion. This adds `WORKTRUNK_VERBOSE=0|1|2` as the env-var equivalent of the `-v`/`-vv` flag count. It's read everywhere — including the completion path, which no flag can reach — and combined with the flag via `max`, so the env sets a baseline the flag can raise but never lower. Completion behaves *identically* to a flagged command at the same level: at level 2 it writes the same `trace.log`/`subprocess.log`/`diagnostic.md` under `.git/wt/logs/`, so a slow tab-completion can be profiled with: ```console $ WORKTRUNK_VERBOSE=2 COMPLETE=fish wt -- wt switch '' ``` then reading `trace.log`. (Set it inline like that, or as a one-off, rather than `export`-ing it — an exported value makes *every* TAB run as `-vv`, printing the "Writing to…" banner above your prompt and re-truncating the shared trace files on each keystroke. That's just normal `-vv` shared-sink behavior, but it's noisy interactively.) The one place completion deliberately diverges: it strips `WORKTRUNK_VERBOSE` from the environment of any forwarded `wt-*` custom-subcommand child, so the child doesn't re-run `logging::init` and clobber the trace files the parent completion just wrote (its stderr is discarded anyway). ### Testing Integration tests cover: `WORKTRUNK_VERBOSE=2` opens the trace files like `-vv` while `=1` does not; flag `-vv` combined with env `0` still writes (the `max`); and completion at level 2 writes `[wt-trace]`/`$ git` records to `trace.log` while candidates still go to stdout. A unit test pins the lossy parse (empty/garbage/out-of-range → `0`, never an error) so a stray value can't corrupt the completion candidate list. Docs (faq, config, the env-var table) and help snapshots are synced. > _This was written by Claude Code on behalf of max_ Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a17fc22904 |
Add --config-set for inline TOML config overrides (#3138)
A global, repeatable `--config-set <toml>` flag that overrides any user-config key for a single invocation, layered above config files and `WORKTRUNK_*` env vars. The value is a real TOML fragment, so arrays and tables work natively — no bespoke `key=value` grammar. ## Behavior - **Precedence**: config files → `WORKTRUNK_*` env vars → `--config-set` (highest). - **Merge semantics**: a later override replaces an earlier one for the same key; scalars and arrays replace the lower-layer value; nested tables deep-merge, so `--config-set list.full=true` leaves sibling `list.*` keys untouched. - **Global**: works in any position (`wt --config-set … list` or `wt list --config-set …`), like `-v` / `-y` / `--config`. - **Graceful degradation**: a malformed, ill-typed, or invalid override drops the whole `--config-set` layer with an attributed warning (`▲ Ignoring --config-set overrides: …`) and preserves the lower layers — consistent with how the existing `WORKTRUNK_*` env overlay degrades. ## Why This is the foundation for a follow-up that parameterizes `wt list`'s column set without baking it into config: e.g. an alias `list-fast = "wt --config-set list.columns=[...] list"` renders a smaller view while plain `wt list` stays full. Doing it as a generic config-override lever (rather than a `list`-specific flag) keeps one canonical path and composes with aliases and any future config key. ## Implementation `Cli.config_override` (`--config-set`, global, `Vec<String>`) is stashed in a process-global `OnceLock` (mirroring `set_config_path`) and applied in `UserConfig::load_with_warnings` via `apply_cli_overrides` as the top layer. New `LoadError::CliOverride` variant carries the raw values for attribution; the warning is rendered in `emit_user_config_warnings`. ## Testing 8 unit tests on `apply_cli_overrides` (sets, deep-merge-preserves-siblings, last-wins, array-replace, malformed, type-mismatch, validation-failure, empty) and 3 integration tests (overrides-the-file, malformed-warns-attributed, works-after-subcommand). Full suite green: lib + 1828 integration, clippy clean, docs in sync. > _This was written by Claude Code on behalf of max_ --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
41d5ef7349 |
docs(cli): render --format help as terse inline possible-values (#3108)
Every `--format` flag — `wt step commit`/`squash`/`rebase`/`push`/`for-each`/`copy-ignored`, `wt remove`, `wt switch`, `wt merge`, `wt config show` — rendered a verbose `Possible values: - text: … - json: …` block in long help and the generated doc pages. clap auto-generates that block from the per-variant doc comments on the shared `SwitchFormat` enum. Dropping those variant doc comments makes clap render the terser inline `[possible values: text, json]` instead, slimming every `--format` flag's long help in one place. The variant names are self-describing, so the descriptions added nothing. The sibling `OutputFormat` enum gets the same trim for `Table`/`Json`, but `claude-code` keeps its description — "reads context from stdin" conveys real, non-obvious behavior the bare name doesn't. That one surfaces in `wt list statusline --help`. Doc mirrors (`docs/content/`, `skills/worktrunk/reference/`) and help snapshots are regenerated. > _This was written by Claude Code on behalf of max_ Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
21e0b27e49 |
refactor(verbose): rename output.log → subprocess.log; -vv keeps Info on stderr (#2913)
## Motivation The `-v` / `-vv` UX had three small issues that compounded: 1. **`output.log` is misnamed.** It holds the *uncapped raw stdout/stderr of every subprocess `wt` spawns* — multi-MB possible (`git log -p`, patch-id pipelines, etc.). "output" reads as "stuff `wt` printed" — the small thing — when it's actually the big thing. Easy to misread. 2. **`-vv` went fully dark on stderr.** PR #2892 moved the noisy debug pipeline to files at `-vv`; in the process, the stderr layer was disabled entirely. Users running `-vv` to see hook output (info-level, which `-v` shows on stderr) suddenly couldn't. 3. **`-v` help text was a 150-char one-liner** packed into a parenthetical, and the surrounding docs leaned on a "stderr stays readable / `log::*` pipeline" framing that was Rust-jargon-flavored and implied stderr-quiet at `-vv` — which is no longer true after change #2. ## Change - **Rename `output.log` → `subprocess.log`.** Filename now matches content. `OUTPUT` static → `SUBPROCESS`, plus the related `OutputMakeWriter` / `OutputFileFormat` / `build_output_layer` symbol renames. - **`-vv` keeps the Info baseline on stderr.** `build_stderr_layer` no longer returns `None` at `-vv`; debug-level records still route to file layers only, so the terminal stays readable while info-level status (hook output, template variables, the `Tracing to ...` pointer) shows the same as at `-v`. - **`-v` help text rewritten** to describe both levels cleanly without a wall of detail. - **`docs/content/faq.md` gets a "What does -v / -vv do?" section** with a three-level table. - **Docs cleanup**: drop "stderr stays readable" / `log::*` jargon / "but not subprocess.log" negative framing from user-facing prose. ## Notes for review - The only `log::info!` site in the codebase is `commands/picker/mod.rs:389` (a single picker error message), so making `-vv` show info-level on stderr doesn't add meaningful noise. - `test_vv_log_pipeline_silent_on_stderr` is renamed to `test_vv_debug_pipeline_silent_on_stderr` — its assertions only check debug-level records stay out of stderr (they do); the old name implied the whole `log::*` pipeline was silent, which was never quite true (direct `eprintln!` always showed) and is less true now (info-level routes to stderr). - 67 of the 69 changed files are snapshot updates (help text and one diagnostic snapshot) and auto-synced doc/skill mirrors. `git diff --stat -- 'tests/snapshots/*' 'docs/content/*' 'skills/worktrunk/reference/*' | tail -1` separates them. - CHANGELOG: not touched. The historical entry that introduced `output.log` (`#2201`) stays accurate for its release; this rename gets a new line in the next release. ## Tests 3870 tests pass. Re-snapshotted all `test_help_*` snapshots, three `step_alias` snapshots that quote the global help, and the diagnostic file format snapshot. > _This was written by Claude Code on behalf of max-sixty_ Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3848a9da7a | chore: bump the patch group with 3 updates (#2792) | ||
|
|
a77c92e25b | docs(help): point banner at the actual cli source path (#2665) | ||
|
|
4cbc8ca5fd |
refactor(docs): drop mirrored help-page close; align convert_console_blocks_in_docs error channel (#2422)
## Summary Three follow-ups from #2419 review. - **Bare close everywhere.** With inner snapshot wrappers gone from command pages (#2419), no `AUTO-GENERATED` markers nest inside the help-page region anywhere in `docs/content/*.md`. The mirrored close (`<!-- END AUTO-GENERATED from \`wt cmd --help-page\` -->`) was the only remaining variant of the close form; collapsed to bare `MARKER_CLOSE`. - `src/help.rs::PageMode::emit_footer()` no longer takes `subcommand`. - The help-page regex in `readme_sync.rs` matches via non-greedy `.*?` to bare `MARKER_CLOSE`, with a comment pointing at the new invariant test. - `AUTO_GENERATED_MARKER_PATTERN` strip regex built from the constants. Added **`test_no_nested_auto_generated_markers`** — walks `docs/content/*.md` and fails if any `AUTO-GENERATED` open ever appears inside an already-open region. This is the explicit invariant that bare-close pairing depends on; if a future change tries to re-introduce nesting (e.g., restore an inner snapshot wrapper around terminal shortcodes), the test catches it before the subtle "regex chops region at first inner close" failure mode lands. - **Aligned error channel.** `convert_console_blocks_in_docs` now returns `(Vec<String>, Vec<String>)` like its sibling sync steps, with per-file error capture for `read_dir`, dir entries, and `read_to_string`. The caller passes errors through the same `tag()` aggregation as everything else, so a transient I/O failure on one file no longer aborts the whole pipeline silently. Also fixes the matching clippy warning (`is_some_and(|e| e == \"md\")` → `is_none_or(|e| e != \"md\")`). - **Docs alignment.** `docs/CLAUDE.md` updated in two places where the prose still documented the mirrored close as the canonical form. Visual check via curl across 12 dev-server pages confirmed no leakage of any prior marker form. Adversarial review (subagent /popper-style) caught the stale prose; otherwise no regressions found. Net diff: +113/-49 (the growth is the new invariant test and explicit per-file error handling; structural simplification shows as -49). ## Test plan - [x] `cargo test --test integration readme_sync` — 12 sync tests pass (was 11; +1 for the new invariant test) - [x] `cargo test --test integration` — full integration suite (1558 tests) pass - [x] `cargo test --test integration test_no_nested_auto_generated_markers` — new guard test passes; manually verified it fires on synthetic nesting - [x] Single-pass convergence verified: `git checkout docs/ && cargo test --test integration test_docs_are_in_sync && git diff --stat` produces an empty diff after the first run - [x] Visual check via local Zola dev server: 12 docs pages (\`/worktrunk/\`, \`/llm-commits/\`, \`/claude-code/\`, \`/tips-patterns/\`, \`/merge/\`, \`/step/\`, \`/remove/\`, \`/hook/\`, \`/list/\`, \`/switch/\`, \`/config/\`, \`/faq/\`) — only HTML comments contain marker text, no rendered leakage - [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> |
||
|
|
66a5becc25 |
refactor(tests): consolidate three sync tests; drop dead inner snapshot wrappers (#2419)
## Summary Two follow-ups from #2418 review. - **Test brittleness fix.** Three previously-separate sync tests had data dependencies that nextest could interleave: `test_readme_examples_are_in_sync` reads `docs/content/*.md`, which `test_docs_quickstart_examples_are_in_sync` generates from snapshots. Under parallelism, README sync could see stale docs and produce content that required a second run to converge. Collapsed both into the existing `test_command_pages_and_skill_files_are_in_sync` pipeline, renamed to `test_docs_are_in_sync`. Steps run sequentially in dependency order; single pass converges from a clean working tree. Each step's errors and updated-file list are tagged with the pipeline stage (`[command pages]`, `[standalone docs]`, `[README]`, etc.) so a failure tells a developer which stage broke without reading the test source. README failure also now reports `(N of M section(s) updated)`. - **Dead inner snapshot wrappers.** `expand_command_placeholders` wrapped each terminal shortcode in command pages with `<!-- ⚠️ AUTO-GENERATED from <snap> --> ... <!-- END AUTO-GENERATED -->` markers. Command pages regenerate wholesale from `--help-page` each sync, so the inner wrapper served no in-place-refresh purpose — it was dead weight nested inside the outer help-page region's markers. Stripped from `expand_command_placeholders`'s HTML branch; net -32 lines across six command-page docs files. The outer help-page region's mirrored close stays — it's load-bearing whenever any nested `AUTO-GENERATED` marker appears in the body. Comment in `src/help.rs:emit_footer` updated to explain the role. (My follow-up note that the mirrored close was redundant turned out to be a misanalysis — non-greedy `.*?` only safely pairs the open with the right close when there are no nested closes; once the inner snapshot wrappers were gone, the mirror became technically unnecessary, but keeping it survives any future reintroduction of nesting at no cost.) Renamed the test in `CLAUDE.md` (4 sites) and `docs/CLAUDE.md` (2 sites) so the documented `cargo test` invocations resolve. Net diff: −54 lines. ## Test plan - [x] `cargo test --test integration readme_sync` — 11 sync tests pass (was 13; minus the two collapsed) - [x] `cargo test --test integration` — full integration suite (1557 tests) pass - [x] **Single-pass convergence verified**: `git checkout docs/ README.md && cargo test --test integration test_docs_are_in_sync && git diff --stat` produces an empty diff after the first run - [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 - [x] Adversarial review of the consolidation (subagent /popper-style): all 4 actionable findings (stale doc references, lost README count, missing stage tags, etc.) addressed - [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> |
||
|
|
5f126d21fe |
docs(pages): add static command output to sections dominated by GIFs (#2405)
## Summary Adds static command-output blocks to the docs pages dominated by GIFs (addresses #2403). The blocks are **driven from insta snapshots** so they stay in lockstep with what `wt` actually prints — one source flows to all three surfaces: - terminal `wt <cmd> --help` (plain text, gutter-formatted) - `docs/content/*.md` (colorized `{% terminal(cmd="...") %}` shortcode) - `skills/worktrunk/reference/*.md` (plain `$ cmd\noutput\n` block) ## What changed - **Six scripted snapshot tests** produce realistic output (`cargo nextest run`, `flyctl scale count 0`, LLM-generated commit messages): - `test_docs_merge_pre_merge_hook` — `wt merge` with pre-merge hook - `test_docs_step_commit_llm` — `wt step commit` with LLM - `test_docs_step_squash_llm` — three-commit squash with LLM - `test_docs_merge_squash_llm` — `wt merge` (squash + LLM + merge) for `llm-commits.md` - `test_docs_remove_pre_remove_hook` — `wt remove` with pre-remove hook - `test_docs_hook_pre_merge` — `wt hook pre-merge` direct invocation - **Sync pipeline extension** in `tests/integration_tests/readme_sync.rs`: - New write-back pass `sync_cli_mod_example_bodies` fills the ```console``` body in each `<!-- wt <cmd> (docs-example) -->` placeholder in `src/cli/mod.rs` from the registered snapshot. Runs before `--help-page` reads the file. - `COMMAND_PLACEHOLDER_PATTERN` extended to match three forms (```bash```, `{{ terminal() }}` self-closing, `{% terminal %} body {% end %}`). This **fixes a pre-existing bug** in `docs/content/list.md` where the HTML-mode expansion was silently broken. - Stripped trailing `|||` corruption that arises when blank lines in snapshot bodies are interpreted as empty commands by `convert_dollar_console_to_terminal`. - **Docs page migration**: - `src/cli/mod.rs` — replaced four hand-written ```console``` blocks (merge, step, remove, hook) with `<!-- wt <cmd> (docs-example) -->` markers. - `docs/content/llm-commits.md` — replaced three hand-crafted HTML blocks with `<!-- ⚠️ AUTO-GENERATED-HTML from X.snap -->` markers. - **Refactor follow-up** in a separate commit: - `BADGE_EXPERIMENTAL_HTML`, `SUBDOC_MARKER_PREFIX`, `DEMO_MARKER_PREFIX` hoisted to `worktrunk::docs` so producer and consumer stay in lockstep. - `normalize_clap_help_fences()` consolidates the `text→` + `console→bash` replacement pair shared by `--help-md` and `help_reference_inner`. - **CLAUDE.md note** documenting the `.gitattributes` `linguist-generated=false` exemption requirement when adding skill-only files (carryover from #2409). ## Test plan - [x] `cargo test --test integration readme_sync` — all 13 tests pass, idempotent - [x] `cargo test --test integration test_help` — help snapshots updated - [x] `cargo run -- {merge,remove,step,hook} --help` — clean gutter formatting, no visible HTML comments - [x] `cargo run -- hook pre-merge --yes` — full project gate green (3355 tests) - [ ] Visual check on dev site (maintainer — terminal shortcodes now render with colors via ANSI→HTML; dark/light variants both expected to look like the GIFs they replace) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- 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> |
||
|
|
0253260503 |
Extend -v variable dump to aliases + help-table drift test (#2324)
Follow-ups from #2316. ## What's in here 1. **Alias `-v` variable dump.** Added `format_alias_variables(ctx)` alongside the existing `format_hook_variables(hook_type, ctx)`, with a private `format_variables_table` helper sharing the alignment + `(unset)` logic. Wired into `run_alias` before the announcement, symmetric with the foreground hook path. 2. **Help-table drift test.** `test_template_variables_table_matches_constants` parses the `## Template variables` table out of `src/cli/mod.rs`, extracts `(kind, var_name)` pairs, and asserts presence + group placement against `ACTIVE_VARS` / `REPO_VARS` / `EXEC_BASE_VARS` / `ALIAS_ARGS_KEY` / union of `vars_available_in(Hook(*))`. Uses public API only — no leak of the private `hook_extras` helper. Adding a var to the constants without updating the table (or vice versa) fails the test. Descriptions stay free-form. 3. **Shorter `-v` help text.** `Verbose output (-v: info logs + hook/alias template variable & output; ...)`. ## Example ``` \$ wt -v greet world ○ template variables: branch = feature worktree_path = _REPO_.feature worktree_name = repo.feature … args = ["world"] repo = repo … cwd = _REPO_.feature ◎ Running alias greet ○ Expanding greet echo hello {{ args }} → echo hello world hello world ``` > _This was written by Claude Code on behalf of @max-sixty_ --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
eec3b24883 |
Show resolved template variables under -v for hooks (#2316)
Closes #2309. When a hook fires under `-v`, `wt` now prints a `template variables:` block listing every variable in scope for that hook type and the value it resolved to for this specific invocation. Vars that are in scope but not populated render as `(unset)` — which is exactly how `target_worktree_path` surfaces during `wt switch -`, the thing the issue reporter hand-rolled an echo-hook to discover. The block prints *before* the `◎ Running …` announce line so it describes what the hook is about to see, not what already ran. Works in both paths: the foreground path (`announce_command`, one block per command) and the background path (`announce_and_spawn_background_hooks`, one block per distinct hook type in the pipeline batch). ## Key files - `src/config/expansion.rs` — `format_hook_variables(hook_type, ctx)` emits the aligned `name = value` block ordered per the docs table (active → operation → repo → exec). `BASE_VARS` is split into `ACTIVE_VARS` / `REPO_VARS` / `EXEC_BASE_VARS` with a `base_vars()` helper, so `vars_available_in` and the printer share one source of truth. - `src/commands/command_executor.rs` — foreground wiring in `announce_command`, gated on `verbosity() >= 1`. - `src/commands/hooks.rs` — background wiring in `announce_and_spawn_background_hooks`; prints one table per distinct hook type (since within a pipeline only `hook_name` varies). - `src/cli/mod.rs` — updates the global `-v` help text and adds a pointer under `## Template variables` in the hook help. ## Testing - Unit: `test_format_hook_variables_groups_and_unset` snapshots a pre-switch context with `target_worktree_path` omitted so `(unset)` is covered; `test_format_hook_variables_scope_filters_operation` confirms pre-commit's narrower operation scope. - Integration: `test_hook_verbose_prints_variable_table` covers the foreground path; the existing `test_post_start_verbose_shows_per_hook_output` now also exercises the background path. No new CLI surface, no new config keys — only behavior added behind the existing `-v` flag. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1a4eea3c03 |
feat(cli): promote --yes to a global flag (#2279)
## Summary `-y, --yes` is now a top-level global clap flag. It moves off every subcommand's clap args and lives once on `Cli`, so `wt -y <anything>`, `wt <anything> --yes`, and `wt --yes <anything>` all skip any approval or confirmation prompt for that invocation. ## Why Follow-up to the top-level alias dispatch (#2266). The long-term plan is to unify approval-bypass behavior — `-y` should be a true global rather than duplicated across switch, remove, merge, commit, squash, prune, all ten hook subcommands, shell install/uninstall, plugin install/uninstall, and config update. A single canonical flag removes ~50 lines of duplicated clap definitions and one source of drift. ## Call-site survey Approval and confirmation prompt sources, as surfaced by `rg -l 'approve_|requires_approval|confirm_'`: - `approve_hooks` / `approve_hooks_filtered` — reads `ctx.yes`, already threaded via `CommandContext::new(..., yes)`. No change needed; the global now feeds those call sites from `handle_*_command` in `main.rs`. - `approve_command_batch` (merge, config `hook approvals add`) — takes `yes: bool`. Receives the global. - `approve_alias_commands` — takes `yes: bool`. Receives `global_yes || opts.yes` (see "Alias compat" below). - `handle_configure_shell` / `handle_unconfigure_shell` / `handle_config_update` / `handle_claude_install[_statusline]` / `handle_claude_uninstall` / `handle_opencode_install` / `handle_opencode_uninstall` — take `yes: bool` for confirmation-prompt bypass. All receive the global. ## Alias compat `AliasOptions` is hand-rolled (not clap) so it doesn't conflict with the clap global. Its post-alias `--yes` parsing is intentionally preserved here — `run_alias` does `let skip_approval = global_yes || opts.yes;` so both `wt -y deploy` and `wt deploy --yes` work unchanged. Removing the post-alias form is a separate cleanup, tracked by the user's long-term simplification plan. ## Navigating the diff - `src/cli/mod.rs` — new `yes: bool` on `Cli` with `global = true`, `short = 'y'`, `help_heading = "Global Options"`, `display_order = 103` (slots after `-v`). Removed the per-command `yes` field from `SwitchArgs`, `RemoveArgs`, `MergeArgs`. - `src/cli/step.rs` — removed `yes` from `CommitArgs`, `SquashArgs`, and `StepCommand::Prune`. Also dropped `help_heading = "Automation"` from the four step subcommands where the group was left with a single flag (`commit`, `squash`, `for-each`, `prune`); those flags now render under default Options instead of a single-item group. - `src/cli/hook.rs` — removed `yes` from all ten hook subcommands (`pre-switch`, `post-switch`, `pre-start`, `post-start`, `pre-commit`, `post-commit`, `pre-merge`, `post-merge`, `pre-remove`, `post-remove`). `--yes` stays in `KNOWN_HOOK_LONG_FLAGS` so the shorthand rewriter still recognizes it as a real flag rather than a template variable. - `src/cli/config.rs` — removed `yes` from `ConfigShellCommand::Install/Uninstall`, `ConfigCommand::Update` (and its now-redundant `conflicts_with = "yes"` on `--print`), `ConfigPluginsOpencodeCommand::Install/Uninstall`, `ConfigPluginsClaudeCommand::Install/Uninstall/InstallStatusline`. - `src/main.rs` — `dispatch_command` takes `yes: bool`; each `handle_*_command` accepts and threads it. `Cli` destructure adds `yes`. - `src/commands/alias.rs` — `try_alias`, `step_alias`, `run_alias` take `global_yes: bool`. `run_alias` OR's with `opts.yes` before calling `approve_alias_commands`. - `src/commands/custom.rs` — `handle_custom_command` accepts and passes the global to `try_alias`. - `docs/content/` + `skills/worktrunk/reference/` — auto-generated from `--help-page`; the global appears under "Global Options" on every subcommand. - `tests/integration_tests/approval_ui.rs` — six new tests: `test_global_yes_before_subcommand`, `test_global_yes_for_hook`, `test_global_yes_for_alias`, `test_post_alias_yes_still_works`, `test_global_yes_for_step_alias`, `test_global_yes_on_command_without_approval`. ## Notes - Does not remove `AliasOptions::yes` — deferred cleanup per the task brief. - Snapshot churn (~27 help files) is the expected fallout of adding a global flag; each subcommand's `--help` now shows `-y, --yes` under Global Options. ## Test plan - [x] 3242 tests pass (`cargo run -- hook pre-merge --yes`) - [x] Lints clean (clippy, cargo fmt, pre-commit) - [x] Doc sync test passes after regeneration - [x] New approval_ui tests cover before-subcommand, after-subcommand, alias, post-alias, step-alias, and no-approval positions > _This was written by Claude Code on behalf of Maximilian_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com> |
||
|
|
d4c80ca970 |
docs(hook): use concurrent form in multi-key hook examples (#2248)
The `wt hook` page and tips/merge examples paired two `[[pre-*]]` blocks with one command each, which runs them serially. For independent commands like `cargo fmt --check` + `cargo clippy`, that taught the wrong lesson. Collapsed each pair into a single `[[pre-*]]` block with both keys (runs concurrently). The multi-entry `[pre-*]` table form is deprecated (`src/config/deprecation.rs`), so the single-element array form is the non-deprecated way to express concurrent commands. Affected: `src/cli/mod.rs` (merge local-CI, hook progressive-validation, hook-type-examples), `docs/content/tips-patterns.md` (local CI gate). Generated docs, skill mirrors, and help snapshots synced. > _This was written by Claude Code on behalf of @max-sixty_ Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
3e1b351ac1 |
feat(log): split -vv output into trace.log + output.log, drop -vvv (#2201)
## Motivation At `-vv`, raw subprocess bodies were bounded (200 lines / 64 KB) on both stderr and `verbose.log`, with full output only available by rerunning at `-vvv`. Large captures (notably `git log -p` piped into `patch-id` during `wt list`) would routinely flood stderr with elision markers and force a second run just to see what was elided. ## Change The verbosity map is now `-v` (info) and `-vv` (debug); any `-v` count above 2 collapses to `-vv`. Captured subprocess stdout/stderr fan out through two log targets in `src/shell_exec.rs`: - `SUBPROCESS_TERMINAL_TARGET` → bounded preview on stderr, mirrored to `.git/wt/logs/trace.log` (new, replaces `verbose.log`) - `SUBPROCESS_FULL_TARGET` → uncapped body to `.git/wt/logs/output.log` (new), never stderr `src/log_files.rs` (renamed from `src/verbose_log.rs`) owns both file sinks behind a `LogSink` type and a `route(target)` helper that is the single source of truth for sink selection. `src/main.rs`'s env_logger format closure matches on the `Route` enum and emits once per sink. Diagnostic reports embed `trace.log` and reference `output.log` by path — multi-MB raw bodies would swamp a bug report. ## Fallback path When `RUST_LOG=debug` is set without `-vv`, neither sink is active. `FULL` records drop and the `TERMINAL` preview reaches stderr as before — preserving the bounded-stderr guarantee. The elision marker phrases its hint based on whether `output.log` was opened, so users in the fallback path see `rerun with -vv for full output` rather than a pointer to a file that doesn't exist. ## Key files - `src/log_files.rs` — new module; `LogSink`, `TRACE`, `OUTPUT`, `route`. - `src/shell_exec.rs` — two `pub const` targets, `log_output` emits on both, elision hint switches on `OUTPUT_LOG_AVAILABLE`. - `src/main.rs` — verbosity map + format closure. - `src/diagnostic.rs` — template splits inlined `trace.log` from referenced `output.log`. - `src/commands/config/state.rs` — diagnostic file recognition for the new names. ## Testing - `test_vv_splits_full_and_bounded_output` — `[wt-trace]` in `trace.log`, raw stdout in `output.log`, no trace records in `output.log`. - `test_vv_bounded_on_stderr_full_in_output_log` — 250-ref packed-refs trip the elision cap; asserts the marker on stderr + `trace.log`, full content in `output.log` without elision. - `test_rust_log_debug_fallback_without_vv` — no log files created at `-v 0 + RUST_LOG=debug`; bounded preview reaches stderr. > _This was written by Claude Code on behalf of max-sixty_ --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
2a6389a092 |
Centralise [wt-trace] emitter and fix -vv log verbosity (#2146)
## Summary
Three changes to worktrunk's logging conventions, all motivated by `wt
list -vv` dumping raw `git diff-tree -p` bodies into the log stream at
debug level:
**1. `src/trace/emit.rs` owns the `[wt-trace]` grammar.** Previously the
grammar was emitted via ad-hoc `log::debug!("[wt-trace] ...")` format
strings in `shell_exec.rs`, with `src/trace/parse.rs` silently defining
it by how it parsed. `trace_instant`, `log_command_result`, the
`TRACE_EPOCH` static, and `thread_id_number` moved into the new emitter
module so `parse.rs` and the producer share one source. Wire format
byte-identical; `wt-perf` parsing unchanged.
**2. Level discipline.** `-v` → Info, `-vv` → Debug, `-vvv` → Trace.
Previously `-v` didn't touch the `log` crate at all and `-vvv` didn't
exist. The LLM prompt dump (`src/llm.rs`) and captured subprocess
stdout/stderr (`log_output` in `shell_exec.rs`) moved to `log::trace!`,
so `-vv` stops spilling thousand-line diff bodies and full LLM prompts.
**3. Bounded `log_output`.** At Debug, each stream caps at 200 lines /
64 KB with `… (N more lines, M bytes elided — use -vvv for full
output)`. At Trace, uncapped.
## Reviewer navigation
- **New file**: `src/trace/emit.rs` — the single-source emitter.
`command_completed`, `command_errored`, `instant` plus `trace_epoch` /
`now_us` / `thread_id` helpers.
- **Delete/delegate**: `src/shell_exec.rs` loses its duplicate
`TRACE_EPOCH` + `trace_epoch` + `thread_id_number`, and the four
`log::debug!("[wt-trace] …")` branches collapse into two calls into
`trace::emit`. `log_output` gains `log_stream_full` (Trace) and
`log_stream_bounded` (Debug) helpers.
- **Level map**: `src/main.rs:init_logging` has the new match on
`verbose_level`.
- **Help + test snapshots**: the `--verbose` help text change in
`src/cli/mod.rs:249` drives the large auto-synced snapshot / docs /
skill-reference diff.
- **Test rename**: `tests/integration_tests/diagnostic.rs` —
`test_v_does_not_enable_logging` → `test_v_does_not_write_log_files`
(the old name became inaccurate now that `-v` enables Info logging on
stderr).
## Testing
- 969 library unit tests pass.
- Integration tests for `diagnostic`, `step_alias`, `test_help` pass (82
tests).
- Lints clean.
> _This was written by Claude Code on behalf of max-sixty_
---------
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
7e12435b33 |
docs(hook): teach pipelines as [[hook]] blocks; add TOML notes to config::commands (#2149)
Cut over hook pipeline docs from inline-list form (`hook = [{name =
"..."}, ...]`) to `[[hook]]` array-of-tables blocks. The deserializer
already accepts this shape — no code changes were needed; the change is
purely teaching. This mirrors what landed for aliases in #2144.
The summary in `Pipeline Ordering` now foregrounds the TOML shape
progression instead of a labeled taxonomy:
- `post-start = "npm install"` — one command
- `[post-start]` — one section of concurrent commands
- `[[post-start]]` — one of multiple sections, run in order
The bracket count itself tracks the structural escalation (value →
section → array-of-sections).
Also adds a module-level doccomment to `src/config/commands.rs`
describing the three primitive TOML shapes the hook deserializer accepts
(string / dict / list) and the asymmetry between dict-at-top (always
`Concurrent`) vs dict-in-list (1-entry → `Single(named)`). This is the
analysis that motivated the cutover and is retained so future readers
can orient on the deserialization rules without re-deriving them.
The Databases example in `hook.md` and `tips-patterns.md` got its first
pipeline step named (`set-vars`) since `[[post-start]]` blocks can't
hold anonymous bare strings the way inline lists could.
> _This was written by Claude Code on behalf of Maximilian_
---------
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
37fb27bc97 |
Deprecate table form for pre-* hooks (#2135)
## Summary
Multi-entry table form for pre-* hooks currently runs serially, while
the same form for post-* hooks runs concurrently. The parser produces
`HookStep::Concurrent` either way, but the foreground executor flattens
steps and runs them serially. To unify the semantics — table form will
run concurrently for all hook types in a future version — this
deprecates the current form for pre-* hooks and auto-migrates it to
pipeline form, which is explicitly serial.
## Implementation
Follows the existing deprecation recipe in `src/config/deprecation.rs`:
- **Detection** and **migration** for top-level hooks (user/project
config) and per-project overrides (`[projects."id".pre-*]`).
- **Warning**: matches the terse `{old} → {new}` pattern of existing
deprecations (`[merge] no-ff → ff`, `post-create → pre-start`, etc.).
- **Auto-migration** at load time: table form rewrites to pipeline of
inline tables so current behavior (serial) is preserved until users run
`wt config update`.
## Docs
Replaces the transitional "concurrent for post-*, sequential for pre-*"
framing with a neutral three-form description (string / table /
pipeline), plus a note recommending pipeline form for pre-* hooks to
avoid the upcoming behavior change.
## Tests
- `snapshot_migrate_pre_hook_table_form` — TOML migration diff
- `test_config_show_displays_pre_hook_table_form_deprecation` — full
user-facing `wt config show` output, covering the "Project config" label
and multi-hook list form
- Unit tests for detection/migration of top-level and per-project
variants
- Existing integration test fixtures migrated to canonical pipeline form
> _This was written by Claude Code on behalf of max-sixty_
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
85c3dadf17 |
feat: add --format=json to config state subcommands (#1969)
Add `--format=json` to 10 commands across two commits.
## Config state subcommands (commit 1)
- `logs get` — `{command_log: [...], hook_output: [...]}` with
file/size/modified_at per entry
- `ci-status get` — `{status, source, stale, url}` (richer than the text
mode's bare status string)
- `marker get` — `{branch, marker, set_at}` or `null`
- `vars list` — `{key: value, ...}` object
- `hints get` — `["hint-name", ...]` array
Also extracts a shared `log_entry_to_json` helper, deduplicating the
DirEntry→JSON conversion.
## Top-level commands (commit 2)
- `config show` — serialized user/project/system config with paths and
existence flags
- `step prune --dry-run` — array of candidates with branch, path, kind,
reason, target
- `remove` — array of removed worktrees with branch, path, deletion
status
- `merge` — summary with branch, target, committed, squashed, rebased,
removed
- `step for-each` — per-worktree results with branch, path, exit_code,
success
For action commands (remove, merge, for-each), progress goes to stderr
as usual; JSON summary goes to stdout at the end — same pattern as `wt
switch --format=json`.
> _This was written by Claude Code on behalf of @max-sixty_
---------
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
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> |
||
|
|
33b87f0b95 |
Widen help text wrapping from 80 to 100 chars for web docs (#1786)
The web docs content area fits ~101 monospace characters on desktop. The previous 80-char wrap caused unnecessary line breaks in option descriptions — e.g., enum variant descriptions wrapping mid-phrase like "Stage everything: untracked files + unstaged tracked / changes". Widens the two `help_reference()` call sites in `src/help.rs` from `Some(80)` to `Some(100)`. Also fixes a pre-existing issue where clap's line wrapping would break bold (`<b>`) spans across lines — `ensure_line_resets` now tracks active SGR styles and re-opens them on continuation lines. > _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>
|
||
|
|
683327a431 | docs: improve merge command help text clarity (#1783) | ||
|
|
acff4cd03b |
Replace parentheticals with prose in after_long_help docs (#1764)
Rewrites parenthetical asides in `after_long_help` text across switch, list, merge, step, hook, and config docs. Qualifiers and conditions become em-dashes or semicolons; examples and analogies stay in parens. Changes: `src/cli/mod.rs` only (docs, skills, and snapshots are auto-synced). > _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> |
||
|
|
eabf4aac27 | fix: protect default branch worktree from removal in bare repos (#1620) | ||
|
|
799aab0eb9 | merge: add --no-ff flag for merge commit (semi-linear history) (#1438) | ||
|
|
2bca89a886 |
Group help options into Picker and Automation headings (#1355)
Group `--branches`/`--remotes` under "Picker Options" and `--yes`/`--no-verify` under "Automation" in `wt switch`, `wt remove`, and `wt merge` help output, using clap's `help_heading`. > _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> |
||
|
|
f173e2a10a |
fix(docs): add blank line after demo figures for markdown separation (#861)
The `expand_demo_placeholders` function now adds a trailing newline after `</figure>` elements, ensuring proper paragraph separation in web docs. Terminal `--help` output is unaffected since HTML comments are stripped. Also removes the inconsistent extra blank line from `wt select`'s demo placeholder in cli.rs. Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
eaadf1314b |
Fix extra linebreak after demo comments in help output (#795)
Demo HTML comments like `<!-- demo: file.gif -->` had blank lines on both sides in the source. When the comment was stripped for terminal display, both blanks remained, creating a double blank line. Fix by removing the trailing blank after demo comments in cli/mod.rs. Add a check in snapshot_help() to catch this pattern in all help tests. Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
fb011c37dd |
feat: reduce background hook output verbosity (#740)
* feat: reduce background hook output verbosity (#690) Background hooks (post-start, post-switch) now show a single-line summary by default instead of verbose per-hook output with command details: ◎ Running post-start hooks @ repo.feature: user:bg, project Use `-v` to see detailed per-hook output with expanded commands. Co-Authored-By: Claude <noreply@anthropic.com> * fix: update comment to match actual output format Co-Authored-By: Claude <noreply@anthropic.com> * test: add coverage for verbose background hook output Add test_post_start_verbose_shows_per_hook_output to verify that -v shows detailed per-hook output with command in gutter format. Co-Authored-By: Claude <noreply@anthropic.com> * docs: document -v flag for background hook verbosity Adds a note in the hook types section explaining that background hooks show a single-line summary by default, with -v for expanded details. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
f64c7863b6 |
refactor: require -vv for debug logging, reserve -v for future use (#702)
Change verbosity threshold from -v to -vv for enabling debug logging and diagnostic file generation. This frees up -v for other purposes. Behavior change: - -v: no effect (reserved for future use) - -vv: debug logging + verbose.log + diagnostic.md (unchanged) Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
d70860afbb |
docs: improve command documentation structure (#643)
* feat: include command definition at top of doc pages The first `///` doc comment line (the "about" / definition) now appears at the top of each command's documentation page, before the subtitle and after_long_help content. Previously, this definition only appeared in the Command Reference section at the bottom. Now the page structure is: 1. Definition (short about) 2. Subtitle (long about, if present) 3. Conceptual documentation (after_long_help) 4. Command reference Co-Authored-By: Claude <noreply@anthropic.com> * docs: improve command documentation structure - Add definition to top of web doc pages (was missing) - Combine definition + subdefinition into single lead paragraph on web - Remove duplication between definition and after_long_help openers - Fix em-dash spacing (spaced per style guide) - Fix pronoun clarity ("Creates one" vs "Creates it") - Use indicative mood instead of second person ("For finished feature branches" not "Use when you're done") - Add documentation guidelines to docs/CLAUDE.md Commands updated: switch, list, select, remove, merge, step, hook, config Co-Authored-By: Claude <noreply@anthropic.com> * fix: update switch help snapshot for pronoun change Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
cfc8a671a3 |
feat(docs): include long_about subtitles in web docs (#624)
The --help-page generator now extracts the "subtitle" from long_about (the expanded description beyond the short about) and prepends it to web docs. This ensures command pages show both the subtitle and the conceptual intro from after_long_help. Also fixes wt switch to have its demo after the intro text, matching the pattern of other commands (list, merge, select). Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
dfc4ddea6b |
Clarify merge command description and target branch behavior
Update documentation and help text to better explain that merge operates on the current branch merging into a target (defaulting to main), unlike `git merge`. This clarifies the distinction from standard git behavior. |
||
|
|
6665c128d4 |
feat(diagnostic): add -vv flag for diagnostic report generation (#472)
* feat(diagnostic): add -vv flag for diagnostic report generation Add verbosity levels to the CLI: - `-v` enables debug logging (existing behavior) - `-vv` also writes a diagnostic report to .git/wt-logs/diagnostic.md The diagnostic report includes: - Command that was run and result - Environment (wt version, OS, git version, shell integration) - Worktree list - User and project config contents - Verbose log (if available) When gh CLI is installed, shows a hint with the full `gh issue create` command for easy bug reporting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(test): make version check more robust in diagnostic test The version string can be either "v0.9.5" (from git describe) or "0.9.5" (from cargo), so check for "wt " instead of "wt v". 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(test): handle Windows path separators in diagnostic snapshot The project config path uses backslashes on Windows (.config\wt.toml) but forward slashes on Unix (.config/wt.toml). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test(diagnostic): add unit tests for config formatting functions Add tests for: - format_config_section: file not found, empty file, content, truncation - strip_ansi_codes: ANSI code removal - truncate_log: small and large content 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
92abd99656 |
Format help text command references with backticks
Wrap command references in backticks (e.g., `wt merge`) in help text and documentation to render them as code. Updates both source documentation files and CLI help strings, with corresponding snapshot updates. |
||
|
|
6e1f5c0fc1 |
Clarify hook execution order and types
Update documentation to distinguish between user hooks and project hooks, and clarify their execution sequence. User hooks run before project hooks and don't require approval. Replace "project commands" with generic "hooks" terminology in merge pipeline steps for accuracy. |
||
|
|
7e06824bd1 |
refactor: update merge command help text
Change the merge command description from "Run from a feature worktree to merge into the default branch" to "Merge the current branch into the default branch" for clarity and accuracy. |
||
|
|
06536029a2 |
Update merge help text to use generic terminology
Replace "merge steps" with "operations" in the wt step reference to more accurately describe the command's purpose. |
||
|
|
974b356070 |
docs: improve help text for select, squash, and merge (#413)
* refactor: clarify Push command documentation Update Push command help text to better describe its behavior. Changed "Push changes to local target branch" to "Fast-forward target to current branch" and expanded the description to clarify it updates the local target branch (e.g., main) to include current commits, equivalent to `git push . HEAD:main`. * docs: improve clarity of CLI help text descriptions - Main about: "Git worktree management" → "Git worktree management for parallel AI agent workflows" - wt list: "List worktrees and optionally branches" → "List worktrees and their status" - wt config state: "Get, set, or clear stored state" → "Manage internal data and cache" - wt list --full: "Show CI, merge-base diffstat, and working tree conflict check" → "Include CI status and diff analysis (slower)" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: update snapshots and docs for help text changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: improve help text for select, squash, and merge - wt select: Replace keybinding instructions with purpose description "Browse and switch worktrees with live preview." - wt step squash: Clarify "since target" to "since branching" "Squash commits since branching" - wt merge subtitle: Condense 5-item list to concise summary "Squash & rebase, fast-forward target, remove the worktree." Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
455b43bbd7 |
Remove 2nd person pronouns from user-facing messages (#332)
Replace "you/your" with direct references following CLI output formatting guidelines. Changes: - "your config" → "the config" - "your shell" → "the shell" - "You can hash" → "Hash" - "when you know" → "to a known" - "unless you pass" → "unless ... is passed" - "Commands you provide" → "Explicitly provided commands" - "when you approve" → "when approving" 🤖 Generated with [Claude Code](https://claude.com/claude-code) 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> |
||
|
|
e0c41ca8e4 |
Reorganize demo assets into mode/theme subdirectories
Restructure demo asset output to use mode (docs/social) and theme (light/dark) subdirectories. Updates all asset references in documentation, build scripts, and publish tooling to match the new layout: - docs/light/ and docs/dark/ for documentation site demos (1600x900) - social/light/ for social media demos (1200x700) Simplifies asset management and allows docs/social builds to coexist without overwriting each other. Updates build script to organize GIFs by theme after recording, fetch-assets to preserve directory structure, and all markdown references to use the new paths. Renames twitter build target to social for broader applicability. |
||
|
|
1a9ce24f36 |
Separate --force into --yes (prompts) and --force (removal) (#311)
* Add --force flag to wt remove for worktrees with untracked files The --force flag now also passes --force to git worktree remove, allowing removal of worktrees containing untracked files like build artifacts (.vite/, node_modules/, etc). - Extended existing --force flag semantics (was: skip approval prompts) - Added -f short form for convenience - wt merge now always forces worktree removal (build artifacts unneeded post-merge) Fixes #301 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Separate --force into --yes (prompts) and --force (removal) The --force flag on wt remove had two meanings: skipping approval prompts AND forcing worktree removal with untracked files. This was confusing and potentially dangerous. Now: - --yes/-y: Skip approval prompts (all commands with prompts) - --force/-f: Force worktree removal with untracked files (wt remove only) This follows CLI conventions (apt, npm, etc.) where --yes skips prompts and --force overrides safety checks. Breaking change acceptable per project guidelines (early release mode). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix unit test for NotInteractive error message Update test to check for --yes instead of --force. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
aae3f0808c |
Clarify wt merge runs from feature worktree
Add introductory text explaining that `wt merge` executes from the feature worktree and merges into the target branch, similar to GitHub's merge button. Update example descriptions for consistency. |
||
|
|
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. |