Commit Graph

1008 Commits

Author SHA1 Message Date
Maximilian Roos 7339e4b283 docs: add bare repository worktree-path example and layout guide (#1664)
## Summary

- Add bare repo `worktree-path` example (`{{ repo_path }}/../{{ branch |
sanitize }}`) to config docs alongside existing examples
- Update `repo_path` variable description to note bare repo behavior
- Expand "Bare repository layout" section in tips-patterns: explains why
bare repos suit worktree workflows and documents the `git clone --bare
<url> project/.git` setup

## Test plan

- [x] `cargo run -- hook pre-merge --yes` passes
- [x] Doc sync tests pass
(`test_command_pages_and_skill_files_are_in_sync`,
`test_config_source_generates_example_toml`)
- [x] Help snapshots updated and accepted

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-22 16:07:41 -07:00
Maximilian Roos 113a2dd940 feat: add directional template vars to all two-worktree hooks (#1655)
Phase 1 of the template variable redesign discussed in #1633. Adds
directional vars (`base`, `target`, `target_worktree_path`, `cwd`) to
all two-worktree hooks — non-breaking additions only, no existing
behavior changes.

## New variables

- **`cwd`** — execution directory, always exists on disk (all hooks).
Enables the Active model in Phase 2 where `worktree_path` becomes
identity rather than execution context.
- **`target_worktree_path`** — target worktree path, added alongside
existing `target` for merge hooks (pre-merge, post-merge) and remove
hooks (pre-remove, post-remove).
- **`base` / `base_worktree_path`** — source branch/worktree, added to
pre-switch hooks. For existing destinations, `target` /
`target_worktree_path` are also provided.

These follow the "Active" model where each two-worktree operation has a
base (source/origin) and target (destination) side:
- **Arriving ops** (switch, create): Active = destination. `base` =
where you came from.
- **Departing ops** (merge, remove): Active = source. `target` = where
it's going.

Mnemonic: *"Are you going there, or is it going away?"*

Phase 2 (separate PR) will change bare var semantics so `worktree_path`
consistently points to the Active worktree, with `cwd` as the
execution-context escape hatch.

Ref #1633

> _This was written by Claude Code on behalf of @max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-22 13:44:42 -07:00
worktrunk-bot eabf4aac27 fix: protect default branch worktree from removal in bare repos (#1620) 2026-03-19 08:08:16 -07:00
worktrunk-bot 5eb522cf42 docs: add pnpm post-create example for copy-ignored (#1581) 2026-03-19 00:00:03 -07:00
worktrunk-bot ce9ea1a6b6 refactor: consolidate .git/wt-* paths under .git/wt/ and stage removed worktrees in trash (#1583)
## Summary

- Moves the rename-based staging directory from a visible sibling path
(`project.wt-removing-<timestamp>`) into `.git/wt/trash/`, hiding it
from the user's workspace
- Consolidates all worktrunk-managed `.git/wt-*` directories under a
single `.git/wt/` parent
- Adds `Repository::wt_dir()` accessor as the single root for all
worktrunk state
- Renames `wt-relocate-tmp` to `wt/relocate-staging` for consistency
with `wt/promote-staging`
- Falls back to legacy `git worktree remove` if the trash directory
can't be created
- The `.git/` directory is always on the same filesystem as worktrees,
so the instant rename guarantee is preserved

## Path migration

| Before | After |
|--------|-------|
| `.git/wt-logs/` | `.git/wt/logs/` |
| `.git/wt-cache/summaries/` | `.git/wt/cache/summaries/` |
| `.git/wt-cache/ci-status/` | `.git/wt/cache/ci-status/` |
| `.git/wt-promote-staging/` | `.git/wt/promote-staging/` |
| `.git/wt-relocate-tmp/` | `.git/wt/relocate-staging/` |
| (new) `.git/wt/trash/` | Staging for background removal |

## Context

Users reported confusion when seeing `.wt-removing-*` directories in
their workspace after `wt remove` (#1572). By staging in
`.git/wt/trash/` instead, the directory is completely hidden — even if
the background `rm -rf` is slow or gets interrupted.

The `.git/wt-*` sibling directories were also consolidated into
`.git/wt/` for tidiness per review feedback.

## Test plan

- [x] Unit tests for `generate_removing_path` and
`build_remove_command_staged` updated and passing
- [x] All 105 remove-related integration tests passing
- [x] `test_remove_background_path_gone_immediately` — verifies instant
removal still works
- [x] `test_remove_background_fallback_on_rename_failure` — verifies
fallback when staging path is blocked
- [x] `test_remove_stale_staging_dir_from_crashed_removal` — verifies
stale dirs land inside `.git/`
- [x] All 2496 tests pass (lib + bin + integration)
- [x] Help snapshots, doc sync, and lint checks all pass

Closes #1572

🤖 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.6 <noreply@anthropic.com>
Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com>
2026-03-17 13:30:49 -07:00
worktrunk-bot 5eb7a76176 docs: clarify hook execution order (post-create before post-start) (#1573) 2026-03-16 22:01:17 -07:00
Sirio Balmelli 799aab0eb9 merge: add --no-ff flag for merge commit (semi-linear history) (#1438) 2026-03-16 18:15:23 -07:00
Maximilian Roos a7f224d263 feat: add wt step eval for template expression evaluation (#1004)
## Summary

- Add experimental `wt step eval` subcommand that evaluates a minijinja
template in the current worktree context and prints the result to stdout
- Reuses existing hook template infrastructure — all variables
(`branch`, `repo`, `worktree_path`, etc.) and filters (`hash_port`,
`sanitize`, `sanitize_db`) are available
- Output goes to stdout with no decoration or shell escaping, designed
for scripting and shell substitution

```
$ wt step eval '{{ branch | hash_port }}'
16066

$ curl http://localhost:$(wt step eval '{{ branch | hash_port }}')/health
```

This is the first step from the
[IDEAS.md](https://github.com/max-sixty/worktrunk/blob/arbitrary-data/IDEAS.md)
design doc, directly addressing the user request in #947 — "found no way
to run it on a single branch."

## Test plan

- [x] 6 integration tests: basic branch, hash_port, multiple values,
sanitize_db, conditionals, template error
- [x] Help text and completion snapshots updated
- [x] stdout guard allowlisted
- [x] Doc pages auto-synced
- [x] All lints pass
- [x] 2360 tests pass (5 pre-existing shell_wrapper failures excluded)

Ref #947

> _This was written by Claude Code on behalf of @max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: worktrunk-bot <w@worktrunk.dev>
2026-03-16 05:55:23 +00:00
Maximilian Roos 7512d8695e Add opencode, llm, and aichat to example config (#1533)
Follow-up to #1531. These three tools were only documented in
`llm-commits.md` — adding them as double-commented entries in the config
example makes them discoverable via `wt config create` and keeps all
tool commands in one place, verified by the existing sync test.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 17:41:29 -07:00
worktrunk-bot df54d28c3a docs(switch): trim upstream tracking, add missing hooks, combine PR/MR sections (#1521)
## Summary

- **Trimmed upstream tracking paragraph** — `--create` doesn't configure
upstream tracking, but that's standard `git switch -c` behavior. Removed
the prominent explanation and folded the remote-branch note into the
existing sentence.
- **Added missing hooks to creation lifecycle** — The numbered list now
includes `pre-switch` (step 1) and `post-switch` (step 6), matching the
actual execution order in `handle_switch.rs`.
- **Combined GitHub/GitLab sections** — Merged two near-identical
sections into a single "Pull requests and merge requests" section,
reducing repetition.

Closes #1518

## Test plan
- [x] `test_command_pages_and_skill_files_are_in_sync` passes (docs
auto-synced)
- [x] Unit tests pass
- [ ] CI green

🤖 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.6 <noreply@anthropic.com>
Co-authored-by: Maximilian Roos <5635139+max-sixty@users.noreply.github.com>
2026-03-14 23:31:26 +00:00
worktrunk-bot e47778d5b9 feat(switch): expose destination branch in pre-switch hooks (#1497)
## Summary

- Moves branch resolution (`plan_switch`) before `pre-switch` hook
execution, so `{{ branch }}` in pre-switch hooks is the **destination**
branch rather than the source
- Applies to both `wt switch` and the interactive picker (`wt switch`
without args)
- Adds a test verifying `{{ branch }}` expands to the destination

Discussed in #1494 — `{{ branch }}` was chosen over `{{ target }}` per
[this
comment](https://github.com/max-sixty/worktrunk/issues/1494#issuecomment-4059068313).

## Test plan

- [x] All 5 pre-switch hook tests pass
- [x] All 62 user hook tests pass
- [ ] CI passes on all platforms

🤖 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.6 <noreply@anthropic.com>
2026-03-14 16:29:35 -07:00
Maximilian Roos 5a2895cd03 Add LLM tool commands to example config as single source of truth (#1531)
The Claude/Codex recommended commands were duplicated across 4 locations
(Rust code, llm-commits.md, config.md, Taskfile) with no automated sync
— they were already drifting (missing `CLAUDECODE=` prefix, missing `-c
system_prompt=''`).

Now the double-commented entries in `config.example.toml` are the single
source of truth. `recommended_config()` parses them at runtime via
`include_str!` + `LazyLock`, and a new sync test verifies
`llm-commits.md` matches. Also fixes the existing drift in all copies.

> _This was written by Claude Code on behalf of @max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 16:08:53 -07:00
Maximilian Roos 18aab7b171 docs: add experimental badge to post-processing table (#1526)
Add the `[experimental]` → empty `<span>` mapping to the post-processing
table in docs CLAUDE.md, which was missing after the badge-to-heading
change in #1523.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 14:51:42 -07:00
Maximilian Roos 879cbd906b feat(docs): move experimental badges to headings (#1523)
Move experimental badges from the start of description paragraphs to
after the heading text in web docs.

Uses empty `<span>` elements with CSS `::after` for badge text, so the
span doesn't affect Zola's heading slug generation or page TOC entries.
This avoids the need for `{#slug}` anchor overrides and keeps sidebar
TOC entries clean (no "experimental" suffix).

Before: `## wt step relocate` / `EXPERIMENTAL Move worktrees to expected
paths.`
After: `## wt step relocate EXPERIMENTAL` / `Move worktrees to expected
paths.`

> _This was written by Claude Code on behalf of @max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 14:29:41 -07:00
worktrunk-bot b5a2bb2e9c docs(list): restore --full prerequisite for LLM summaries (#1517) 2026-03-14 16:51:29 +00:00
Maximilian Roos 73fbf5a54b docs: fix stale function name in docs CLAUDE.md (#1516)
\`colorize_ci_status_for_html\` was renamed to \`post_process_for_html\`
and moved from \`src/main.rs\` to \`src/help.rs\` in #1499. The docs
CLAUDE.md reference was stale.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 01:10:38 -07:00
Maximilian Roos 7834271528 Unify timeout model for list and switch picker (#1515)
The picker had its own hardcoded 200ms per-command timeout default plus
a `WORKTRUNK_PICKER_BUDGET_MS` env var escape hatch, while `wt list` had
a separate experimental `[list] timeout-ms` for per-task timeouts. This
consolidates into a shared config model with clear separation between
per-task and wall-clock timeouts.

**New config model:**
- `[list] task-timeout-ms` — per-task command timeout, shared by both
list and picker (default: disabled)
- `[list] timeout-ms` — wall-clock budget for list's collect phase
(default: disabled)
- `[switch.picker] timeout-ms` — wall-clock budget for picker (default:
500ms, was 200ms per-command)

The picker's old per-command timeout goes away — the wall-clock budget
is sufficient. `collect_deadline` moves from a `collect()` parameter
into `ShowConfig::Resolved` where it belongs. Removes the
`WORKTRUNK_PICKER_BUDGET_MS` env var and a dead benchmark that tested a
non-functional env var.

Note: `[list] timeout-ms` changes semantics from per-task to wall-clock
budget. The old field was marked "(Experimental)" and not shown in
user-facing docs.

> _This was written by Claude Code on behalf of max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 01:08:36 -07:00
Maximilian Roos 0760817af7 feat(docs): style experimental markers as badge pills (#1499)
Replaces plain `[experimental]` text in web docs with styled pill badges
— small uppercase labels with a subtle border that read as metadata
rather than emphasis.

Canonicalizes all experimental markers to `[experimental]` (was a mix of
`[experimental]`, `(experimental)`, `(Experimental)`). One marker format
in cli.rs, one `.replace()` in the post-processing.

Also renames `colorize_ci_status_for_html` → `post_process_for_html` (it
handles badges and URLs too), adds a module docstring documenting the
full `--help-page` pipeline, and fixes a latent double-application of
the post-processor on subdoc content.

> _This was written by Claude Code on behalf of @max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 00:12:22 -07:00
Maximilian Roos 50edfbfb36 docs(step): fix cross-filesystem fallback description (#1513)
The cross-device rename happens when the worktree is on a different
filesystem from `.git/`, not "if `.git/` is on a different filesystem."
Minor accuracy fix.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 23:48:24 -07:00
Maximilian Roos e00654b3ed docs(list): rewrite LLM summaries section (#1506)
Remove the TODO ("promote this feature more prominently once it's been
tested in the wild") since LLM summaries are now on the homepage.
Tighten the subsection — the prerequisites are already in the Columns
table above, so the text now focuses on the mechanism (reuses
`commit.generation`), how to enable (`summary = true`), and caching
behavior.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 21:20:06 -07:00
Maximilian Roos 1cfa5f6806 docs(step): add promote subdoc, improve swap description (#1505)
The promote command swaps branches *and* gitignored files between
worktrees — the old definition ("Put a branch into the main worktree")
undersold what it does. Updated to "Swap a branch into the main
worktree" with a subdefinition that mentions the gitignored file
exchange.

Added promote as a subdoc section on the step page (it was the only step
command with substantial `after_long_help` content that lacked one).
Expanded the gitignored files documentation to mention the shared
discovery mechanism with `copy-ignored`, `.worktreeinclude` filtering,
and the `rename()`-based swap approach.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 21:14:30 -07:00
Maximilian Roos a892caa39b docs(homepage): add headline features and tips link (#1501)
Add three new entries to the "Workflow automation" feature list on the
homepage: `wt list --full` (CI status & AI summaries), PR checkout (`wt
switch pr:123`), and dev server per worktree (`hash_port`). Also add a
"Full mode" section to the list docs as a link target, and link tips &
patterns from next steps.

> _This was written by Claude Code on behalf of @max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 20:48:13 -07:00
Maximilian Roos 6048546e3a docs(step): move aliases section after subcommands (#1502)
The Aliases section was sandwiched between the Operations list and See
Also — disconnected from the subcommand documentation below. Every other
step operation (commit, squash, copy-ignored, etc.) gets its own section
after the command reference, but aliases didn't.

Moved aliases after the last subdoc marker so it appears alongside the
other subcommand sections. Also added `[experimental]` prefix to match
the operations list entry, and fixed the `console → bash` code block
transform to apply before the subdoc split (it was only applied to main
content, so anything after subdoc markers missed it).

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 20:38:34 -07:00
Maximilian Roos e89049a321 docs(list): document placeholder symbols in help text (#1496)
Adds a legend for the `⋯` (loading) and `·` (skipped) placeholder
symbols to `wt list --help`, under the existing Status symbols section.

These symbols appear across all columns during progressive table
rendering but weren't documented. The `·` symbol was introduced
alongside the picker budget feature — it indicates data collection was
truncated (timed out or branch too stale), as opposed to `⋯` which means
data is still arriving.

> _This was written by Claude Code on behalf of @max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 15:39:57 -07:00
Maximilian Roos f2ea4d65f7 docs(step): link Operations index to subcommand sections (#1495)
Link subcommands from the Operations index to their detailed sections
below. Previously all items were plain text; now commit, squash, diff,
copy-ignored, for-each, prune, relocate, and alias are clickable
anchors.

Also adds `diff` as a subdoc expansion (it had `after_long_help` content
but wasn't included as a subdoc), and fixes the repetitive opener in
diff's `after_long_help` — the definition+subdefinition already listed
what's included, so the opener now just adds the `wt merge` context.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-13 15:34:50 -07:00
Jarryd Tilbrook 00eb04a042 feat(switch): add no-cd config option (#1401) 2026-03-13 07:42:40 -07:00
worktrunk-bot 905aa273df docs: add manual commit message recipes (#1469) 2026-03-13 06:06:25 -07:00
worktrunk-bot 54f34bb19c feat(switch): make --no-cd print-only in picker mode (#1445)
## Summary

Draft implementation per @max-sixty's
[suggestion](https://github.com/max-sixty/worktrunk/pull/1404#issuecomment-4041170944):
reuse `--no-cd` to make the picker print-only instead of adding a
separate `--print` flag.

- When `wt switch --no-cd` opens the interactive picker (no branch
argument), selecting a branch prints its name to stdout and exits — no
switching, no cd directive, no hooks
- When `wt switch --no-cd <branch>` is used with an explicit branch,
behavior is unchanged (switches without cd)
- `alt-r` (remove) is blocked in print mode since it's a read-only
operation

This avoids adding a new CLI flag while giving the same scripting
capability that #1404 proposed.

## Test plan

- [x] Unit tests for `resolve_print_identifier` (switch, create, remove
cases)
- [x] PTY integration test: `--no-cd` picker prints branch name, emits
no cd directive
- [x] Existing `--no-cd` tests still pass (directive suppression, hooks,
execute)
- [x] Help snapshots and doc sync updated


🤖 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.6 <noreply@anthropic.com>
2026-03-11 22:12:16 -07:00
Maximilian Roos 9b77ab2d93 fix: add --no-session-persistence to Claude commit generation (#1454)
Without this flag, `claude -p` saves the commit generation conversation
to session history. When the user later runs `claude --continue`, it
resumes that ephemeral commit session instead of their previous working
session.

> _This was written by Claude Code on behalf of @max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-11 16:20:00 -07:00
worktrunk-bot 3080eb0922 docs(remove): update example heading to mention branches (#1449)
## Summary

- Updates example heading from "Remove specific worktrees:" to "Remove
specific worktrees / branches:" to clarify that `wt remove` also works
on branches

Ref #1415

## Test plan

- [x] Snapshot test updated and passing
- [x] Doc sync test passes

🤖 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.6 <noreply@anthropic.com>
2026-03-11 22:07:11 +00:00
Rickey 26333bc87b docs: add Xcode DerivedData cleanup recipe (#1423)
## Summary

For iOS developers, this PR add a `post-remove` hook recipe to Tips &
Patterns that cleans up Xcode's DerivedData when removing a worktree.

## Context

Xcode's DerivedData directory names are hashed from the `.xcodeproj`
path, making them hard to match by name alone. Each DerivedData
directory contains an `info.plist` with a `WorkspacePath` field that
records the original project path. This recipe leverages that to find
and remove only the matching cache.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:04:47 -07:00
Maximilian Roos 1389a0b783 docs(hook): improve JSON context section (#1360)
The JSON context section had two issues:

- It described hook_type and hook_name as extras "plus" the template
variables, but they're regular template variables already in the table.
Fixed to say "all template variables".
- It showed a floating Python script without the toml hook definition
that calls it. Added the toml entry so the reader sees how JSON stdin
gets wired up.

Also trimmed the example (removed bridge text and filename comment) to
keep it concise.

> _This was written by Claude Code on behalf of @max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-09 21:12:18 -07:00
Maximilian Roos 4dfac6cde8 fix(docs): broken anchor link in step.md (#1391)
The `@/hook.md#command-approval` anchor in step.md doesn't exist,
causing
the `publish-docs` workflow to fail (`zola build` rejects broken
internal
anchors). The approval docs live under the `# Security` heading, so the
correct anchor is `#security`.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-08 19:57:52 -07:00
Maximilian Roos fe56eaa335 Remove --no-background, promote --no-progressive, carry foreground through (#1381)
Removes the deprecated `--no-background` flag (replaced by
`--foreground`) from
`remove`, `hook post-start`, `hook post-switch`, and `hook post-remove`.
Removes
all supporting deprecation infrastructure: warning message,
`is_foreground_mode`
helper, `DEPRECATED_ARGS` completion filtering.

Promotes `--no-progressive` from deprecated to hidden flag with inline
mention
in `--progressive`'s help text, using `flag_pair()` for consistent
handling.

Carries `foreground: bool` through the entire call chain instead of
negating to
`background` at call sites. Refactors `print_message` to use
`success_message()`/`progress_message()` helpers instead of manual color
construction, adding `FormattedMessage::append()` for integration
symbols that
need native styling outside the message's color span.

> _This was written by Claude Code on behalf of maximilian_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-08 17:25:56 -07:00
Maximilian Roos 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>
2026-03-08 14:07:58 -07:00
Maximilian Roos 486a8195d2 feat(hook): make hook_type and hook_name available as template variables (#1364)
These were already included in the JSON context piped to stdin but
couldn't be used as `{{ hook_type }}` or `{{ hook_name }}` in templates
— the values were inserted into `cmd_context` *after* template
expansion.

Now both code paths (`expand_commands` for execution,
`expand_command_template` for `wt hook show --expanded`) include them
before rendering. Also added to `TEMPLATE_VARS` so `--var hook_type=...`
works for testing.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-08 00:31:19 -08:00
Maximilian Roos 6d8415f58c Group hook help into navigational sections (#1359)
Promotes major sections of `wt hook --help` to H1 headings (rendered as
uppercase bold green), matching the pattern from `wt config --help`.
Subsections become H2.

Before: flat `##`/`###` headings made the long help text hard to scan.
After: five landmark sections — Hook Types, Security, Configuration,
Running Hooks Manually, Designing Effective Hooks.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-08 00:01:18 -08:00
Maximilian Roos 0638cd5a93 docs: fix home page SEO — canonical dedup and consistent tagline (#1357)
The home page (`/`) and `/worktrunk/` serve identical content (the index
template renders `worktrunk.content` directly), but each declared its
own
canonical URL. Google saw two competing pages and wasn't indexing either
as
the home page result.

- Set canonical + og:url on `/worktrunk/` to point to `/`
- Fix home page og:title/twitter:title — was just "Worktrunk", now uses
the
  full tagline from `config.extra.site_description`
- Align `worktrunk.md` description with `site_description`

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-07 23:55:05 -08:00
Maximilian Roos 10f752453b Remove redundant "(background)" from hook help table (#1356)
The Blocking column's "No (background)" was overspecified — the column
header
and footer definition already explain what "No" means.

> _This was written by Claude Code on behalf of max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-07 23:40:26 -08:00
Maximilian Roos 9988530bf8 Group picker options under separate help heading in wt switch (#1353)
Move `--branches` and `--remotes` into a "Picker Options" section in `wt
switch --help`, since they only apply to the interactive picker mode and
conflict with all direct-switch flags.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-07 23:15:00 -08:00
Maximilian Roos 55bc9188b4 feat(step): add alias command for user-defined command templates (#1348)
## Summary

- Adds `wt step <alias-name>` for running user-defined command templates
configured in `[aliases]` sections of user or project config
- Aliases support the same template variables as hooks (`{{ branch }}`,
`{{ worktree }}`, etc.) plus custom `--var KEY=VALUE` variables
- Project-config aliases require the same command approval flow as
project hooks; user-config aliases are trusted. `--dry-run` skips
approval since it's a read-only preview
- Alias names matching built-in step commands are filtered from the
"available" list in error messages (shadowed by the built-in)
- Introduces `Phase` enum (`Hook(HookType)` | `Alias`) replacing the old
`hook_type` + `phase_override` pattern in the approval system
- Renames `HookCommand` to `ApprovableCommand` since it now covers both
hooks and aliases

## Test plan

- [x] Unit tests for `AliasOptions::parse` (name-only, --dry-run, --yes,
--var, empty key rejection, positional arg rejection)
- [x] Integration tests for alias execution, dry-run (without --yes),
exit code propagation
- [x] Integration tests for shadowing (filtered from available list)
- [x] Integration tests for user/project config merging
- [x] Integration tests for approval flow (project prompts, user skips,
override skips, already-approved, --yes bypass, decline)
- [x] Unit tests for `merge_alias_maps` coverage
- [x] Sync test: `BUILTIN_STEP_COMMANDS` matches actual `StepCommand`
clap variants

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-07 21:46:41 -08:00
worktrunk-bot 2e29a275dd docs: use .localhost subdomains instead of .lvh.me for Caddy routing (#1343)
Replace `.lvh.me` with `.localhost` in all Caddy subdomain routing
examples
(docs, CLI help, tests). `.localhost` resolves to 127.0.0.1 via the OS
on
macOS and Linux with systemd-resolved — no external DNS dependency.

Also fixes a flaky PTY test: `WORKTRUNK_TEST_DELAYED_STREAM_MS` was
missing
from `STANDARD_TEST_ENV` in shell_wrapper.rs, causing `git worktree add`
output to appear non-deterministically under heavy parallel load.

Closes #1334

> _This was written by Claude Code on behalf of maximilian_

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Maximilian Roos <m@maxroos.com>
2026-03-07 17:11:15 -08:00
Maximilian Roos 3327edcb31 fix(list): add missing summary field to JSON output (#1339)
The `summary` field was wired up in the table UI but never added to
`JsonItem`, so `wt list --format=json` never included LLM summaries even
when configured. Also improves doc comments to mention all three
requirements for summary generation.

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-07 14:08:30 -08:00
worktrunk-bot 691060e665 fix(docs): use git pull instead of git fetch in pre-switch hook example (#1323)
## Summary

- Change the `pre-switch` hook example from `git fetch` to `git pull`
- `git fetch` only updates remote tracking refs without advancing the
local branch, so a new worktree created after fetch is still based on
the old local HEAD — not the latest remote

Reported by @jdb8 in #1085.

## Test plan

- [x] Doc sync test passes (docs and skill reference updated
automatically)

🤖 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.6 <noreply@anthropic.com>
2026-03-06 19:03:08 -08:00
worktrunk-bot afc3754a5a feat: add alt-d shortcut to delete worktrees from switch picker (#1253)
## Summary

Adds an `alt-d` keybinding to the `wt switch` interactive picker that
removes the highlighted worktree directly, without needing to type the
branch name or switch first.

- **Safe defaults**: no `-D` (force-delete unmerged branches), no `-f`
(force-remove dirty worktrees) — matches the safety behavior requested
in #1251
- Removes the worktree and its merged branch in foreground (immediate
feedback)
- Shows standard `wt remove` success/failure output after picker exits
- Controls line in the preview pane updated to show `alt-d: delete`

Closes #1251

## Test plan

- [x] All 554 unit tests pass (`cargo test --lib --bins`)
- [x] Clippy clean
- [x] Doc sync test passes
- [ ] Manual testing: open `wt switch`, highlight a worktree, press
`alt-d` — verify it removes the worktree and shows the success message
- [ ] Manual testing: try `alt-d` on main worktree — verify it shows
"Cannot remove the main working tree" error
- [ ] Manual testing: try `alt-d` on a worktree with uncommitted changes
— verify it shows the dirty worktree error


🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-03-06 05:52:29 +00:00
worktrunk-bot e596a07db6 docs: add empty system prompt to codex command for speed (#1300)
## Summary

- Add `-c system_prompt=''` to the Codex example command to skip the
default system prompt, reducing tokens and latency for commit message
generation

Follow-up from #1295 — @max-sixty asked about using empty system prompts
for speed.

**opencode**: Does not currently support disabling the system prompt.
The system prompt is always injected, and even the plugin system has a
safety fallback that restores the original if emptied. There are open
feature requests
([#1242](https://github.com/anomalyco/opencode/issues/1242),
[#1894](https://github.com/sst/opencode/issues/1894),
[#5005](https://github.com/anomalyco/opencode/issues/5005)) but nothing
available yet.

**Codex**: Has an undocumented `system_prompt` config key that can be
overridden via `-c system_prompt=''`. There are feature requests for
dedicated `--system-prompt` flags
([#6023](https://github.com/openai/codex/issues/6023),
[#11588](https://github.com/openai/codex/issues/11588)) but not yet
merged.

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:13:16 -08:00
worktrunk-bot b49ab2bbc0 docs: add opencode and condense other LLM commit tools (#1295)
## Summary

- Add opencode as a supported tool for LLM commit message generation
- Condense opencode, llm, and aichat into a single "Other tools" section
with just recommended commands
- Claude Code and Codex retain their own detailed sections
- Auto-sync skill reference file

Closes #911

## Feedback welcome

@viicslen @gabimoncha — does `opencode run -m anthropic/claude-haiku-4.5
--variant fast` work correctly? Happy to adjust the flags.

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 19:45:05 -08:00
Maximilian Roos 0f317c79db Add DevOpsToolbox video link to further reading 2026-03-04 11:10:51 -08:00
worktrunk-bot 070e23f6e3 fix: exclude VCS metadata directories from copy-ignored (#1250) 2026-03-03 21:07:25 -08:00
Maximilian Roos 340a83f6d0 fix: fail fast when ffmpeg lacks libass for demo GIF builds (#1237)
## Summary

- Add early `check_ffmpeg_libass()` at demo build startup — exits with
install
instructions when ffmpeg lacks libass (required for keystroke overlay
ASS
  subtitles)
- Add output verification in `record_vhs()` — catches silent failures
where VHS
  exits 0 but produces no GIF
- Document the Homebrew API-bottle vs tap-formula issue in demos
CLAUDE.md

Homebrew's API-sourced formula strips `libass`; the tap formula includes
it.
The fix is `HOMEBREW_NO_INSTALL_FROM_API=1 brew install
--build-from-source ffmpeg`.

## Test plan

- [x] Verified `check_ffmpeg_libass()` passes with correct ffmpeg
- [x] Verified full demo build succeeds with `--only wt-switch-picker`
- [x] Lints pass (`pre-commit run --all-files`)

> _This was written by Claude Code on behalf of @max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-28 20:11:49 -08:00