Commit Graph

81 Commits

Author SHA1 Message Date
Worktrunk Bot 246c6bd919 fix(list): keep [list] columns out of the --format json plan (#3812)
Closes the `[list] columns` half of #3787, per the call in [this
comment](https://github.com/max-sixty/worktrunk/issues/3787#issuecomment-5273942067):
JSON always emits the same shape, and `list.columns` only affects the
actual columns.

Before, `--format json` planned `all_columns` (source `Default`)
*unioned* with the selection's forced-on columns, so the selection
reached JSON in one direction only — it couldn't narrow the emitted
fields, but a listed `ci` did force the forge fetch on without `--full`.
That made a presentation setting decide whether a machine-readable call
talks to GitHub, which is the thing the Neovim plugin in #3787 had to
pin `--config-set 'list.columns=[…]'` against. Now the JSON branch plans
`all_columns` alone; `--full` is the only switch for the gated data, and
it's the one a caller controls.

The table and the `wt switch` picker are untouched — a listed `ci` still
renders the CI column without `--full`, and the picker still unions the
selection in so its table matches `wt list`'s.

Only `ci` and `summary` are affected: every other column is ungated, so
`full_plan()` already covered them, and custom columns require no
background task.

**For the release note — this changes schema 1 too.** A caller with
`[list] columns = […, "ci"]` and no `--full` used to get the `ci` object
in schema-1 JSON and now won't; schema 1 has no `collected` envelope to
say why. The schema-1 `ci` row already documented `` `--full` only ``,
so the docs get *more* accurate, but the observable output changes for
anyone who was relying on the forcing path. Schema 2 reports the same
narrowing through `collected.ci`.

Docs updated in `after_long_help` (the `[list] columns` section plus the
schema-2 `pr`, `summary`, and `checks` rows — `summary` now names
`--full` alongside `[list] summary = true`, and `checks` names the
`--full` gate it shares with `pr`), with the generated mirrors,
`dev/config.example.toml`, and the `--help` snapshots regenerated. The
`CLAUDE.md` network inventory and the `collect` planning comment now
record the exemption too.

<details><summary>Test</summary>

`test_list_json_columns_selection_does_not_force_ci` in
`tests/integration_tests/list_config.rs` asserts schema 2's
`collected.ci` across three configs: unset (false), `columns =
["branch", "ci"]` without `--full` (false — the regression this fixes),
and the same with `--full` (true). `collected` records what the plan
requested rather than what a fetch returned, so the test needs no forge
and no `gh` on PATH. It sits next to
`test_list_json_ignores_columns_selection`, which owns the narrowing
direction, and `test_list_config_listed_column_overrides_full_gate`,
which owns the table's forcing behaviour and still passes unchanged.

Ran locally: full `cargo test --test integration` and `cargo test --lib
--bins`, plus `cargo clippy --all-targets` and `cargo fmt --check`. One
unrelated failure,
`test_copy_ignored_preserves_file_executable_permissions`, is a umask
artifact of this sandbox (expects `0644`, the runner's `umask 002`
produces `0664`); it touches no code in this diff.

The docs-row follow-up in df5c238 re-ran `cargo test --test integration
-- test_help test_docs_are_in_sync` (48 passed) and `cargo fmt --check`.

</details>

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-15 08:00:04 -07:00
Maximilian Roos 92dfb686bb feat(approvals): let wt config approvals add --yes record approvals without a TTY (#3819)
`wt config approvals add` refused every non-interactive run — even with
`--yes`, whose hint then suggested the flag already passed — so there
was no way to pre-approve a project's commands unattended. An
orchestrator (tend's Codex Cloud container was the motivating case) had
to hand-write `approvals.toml` from `wt config approvals list
--format=json` output, a third-party reimplementation of `add` that
breaks whenever the schema changes. The `wt config approvals` docs
already promised "`--yes` to bypass prompts in CI" and described `stale`
entries as "what `--yes` would silently re-approve"; behavior now
matches them.

The two `--yes` meanings stay distinct: on a command that runs project
commands it grants consent for that run alone and records nothing
(unchanged), while on `add` — whose product is the record — it lists
what it trusts and writes it. `add` no longer routes through
`approve_command_batch` (the execution gate) for this: it prompts or
announces, then saves itself, which also makes a failed `approvals.toml`
write fail the command instead of warning behind a `✓ saved` line and
exit 0 — an orchestrator reading only the exit code would otherwise walk
into the prompt it just paid to avoid.

The non-interactive hint's pre-approval suggestion now carries `--yes`
(`run wt config approvals add --yes`), since a hint reached in CI must
name a command that runs there. Per the existing `list --format=json`
docs, `add --yes` re-approves templates edited since an earlier approval
without comment; the `add` help now says so and points at the `stale`
field for reading them first, and the worktrunk skill's escalation rule
tells agents not to reach for it on a user's behalf.

> _This was written by Claude Code on behalf of max-sixty_
2026-08-14 02:39:29 -07:00
Maximilian Roos 7f8ac8e5d7 feat(list): publish a JSON Schema for the schema-2 envelope (#3747)
`wt list --format=json` schema 2 now has a published, machine-readable
contract at
[worktrunk.dev/schema/list-v2.json](https://worktrunk.dev/schema/list-v2.json).

The schema was already derived — `test_schema_generates` built one,
asserted it compiled, and threw it away, with a comment saying "until
the schema export ships." This ships it: `wt list --print-schema` prints
the document (a developer entry point alongside `--help-page`,
intercepted before clap), and a new step in `test_docs_are_in_sync`
commits it to `docs/static/schema/list-v2.json`, the same
generate-and-commit pattern as `llms.txt`. It shells out rather than
calling `schema_for!` because `JsonEnvelope` lives in the bin-only
`crate::commands` tree.

Two things had to be fixed for the document to be usable.

**The contract.** `schema_for!` generates under schemars' *deserialize*
contract, which marks a `skip_serializing_if` field required — nothing
supplies it on the way in. The first document I generated therefore
required `default_branch`, `upstream`, `pr`, `checks`, `summary` and
`vars` on every item, all of which the absence rule routinely omits, so
it rejected the output it documents. Generating under `for_serialize()`
fixes it.

**The vocabularies.** Four fields — `checks.status`, `display.state`,
`default_branch.integration.reason` and `worktree.operation` — were
`&'static str`, so the schema described them as bare strings. They are
now `JsonCheckStatus`, `JsonMainState`, `JsonIntegrationReason` and
`JsonOperation`, each converted from its domain enum by an exhaustive
match, so a new `CiStatus`, `MainState`, `IntegrationReason` or
`InProgressOperation` variant is a compile error rather than a value
silently missing from the published vocabulary. **The emitted JSON is
unchanged**; the existing envelope snapshot passes untouched.

<details>
<summary>Before and after, for one item</summary>

```json
// before — rejects its own output, and loses the vocabulary
"required": ["default_branch", "upstream", "pr", "checks", "summary", "vars", "display"],
"status": { "type": "string" }

// after
"required": ["branch", "head", "display"],
"status": { "enum": ["passed", "running", "failed"] }
```

</details>

## Testing

`test_schema_accepts_envelopes` validates a battery — every `CiStatus`
over both sources, every `MainState`, a populated worktree row, an
integrated row with an upstream and a dev server, plus the absent and
null arms of the absence rule — against the same document
`--print-schema` emits.

Validating proves nothing about a type the battery never instantiates,
so the test also pins every non-`Nullable_` type in the document to a
path that must carry a non-null value. A new `Json*` type fails until
the battery reaches it, and a row that stops populating one fails too —
the check reports the type names rather than leaving the gap to a
reader. This needed a `jsonschema` dev-dependency: schemars only
generates, and derives the document from the types without ever seeing
an envelope, so nothing otherwise tied the two together.

The test was confirmed to fail on the bug it exists for. Reverting to
`for_deserialize()` makes it report `pr`, `checks`, `summary`, `vars`
and `display.columns` as wrongly required.

The dependency is dev-only: `reqwest`, `rustls` and `async-trait` stay
unselected so no HTTP stack comes along, and `cargo tree --package
worktrunk --edges normal -i jsonschema` finds no path to it.

One direction it deliberately does not cover: a *loosening*. If a field
reverted to `&'static str` the schema would say `type: string` and
anything would validate. That direction is held by the compiler instead,
via the exhaustive matches.

## Notes for review

- `schema_document()` lives in `json_v2.rs` beside the types, not in
`help.rs`, so `--print-schema` and the test compile the same document
rather than two constructions that could drift on the contract setting.
- The lychee exclusion for `worktrunk.dev/schema/` follows the entry
directly above it: a generated link that 404s until the site deploys.

> _This was written by Claude Code on behalf of max-sixty_
2026-08-05 22:33:23 -07:00
Maximilian Roos 568b6de85f docs: consolidate duplicated explanations and trim slop (#3494)
Sweep of the docs for repetition and filler, from an audit of the
hand-authored pages, the command pages' source in `src/cli/mod.rs`, and
the plugin skill. Net −574 lines; every cut either had a surviving
canonical home or restated an adjacent sentence.

**One home per mechanism** (other mentions now link to it):

- `template-append` — the LLM commits guide owns the rendering
mechanism; the user- and project-config sections keep the key, an
example, and what is unique to them (the project approval gate and the
only-`template-append`-from-project scoping). Was explained in full in
three places.
- `-vv` diagnostic files — `wt config state logs` owns the four file
descriptions; the FAQ summarizes in one sentence and links.
- fsmonitor/trash cleanup — the FAQ's two overlapping `wt remove`
bullets merge into one that distinguishes own-daemon teardown from the
orphan sweep; troubleshooting.md's restatement compresses to a pointer,
keeping its unique wedged-daemon-on-live-worktree guidance.
- copy-ignored built-in excludes — the `wt step copy-ignored` page owns
the directory list (previously enumerated 4×); the config sections state
the rule and link.
- hook-types table — `wt hook` owns it; the extending guide replaces its
verbatim copy with a sentence.
- LLM tool commands — unchanged, deliberately: the apparent hand-synced
duplication between llm-commits.md and the config example is already
machine-pinned by `test_llm_docs_commands_match_config_example`, so it
cannot drift.

**Cut-over debt**: the deprecated `wt config state ci-status` section
shrinks to a deprecation pointer — its status table, fetch order, and
caching notes all duplicated the `wt list` CI-status section.

**Structure**: `wt step copy-ignored`'s "Features" list dissolves into
the sections that owned its facts (excludes → "What gets copied",
reflink → "Performance"); the four trailing "Note: This command is
experimental…" lines go (the `[experimental]` badge already appears
twice per section); shell-integration.md described the directive-file
mechanism twice and now describes it once.

**SKILL.md** drops from 339 to 181 lines: the permission-model section
duplicated the config-types bullets, the hook-type mapping appeared
twice, and the "Determining Which Config to Use" / "Validation Before
Adding Commands" scaffolding enumerated judgments an agent makes on its
own. The approvals-escalation and agent-handoff sections are untouched.

**Deliberately not addressed**: `wt list`'s schema 1 JSON tables (still
the default schema; the wholesale deletion lands when the default flips)
and corpus-wide em-dash density (a house-style decision, not a per-page
fix).

Generated mirrors (docs command pages, skill references, plugins mirror,
`dev/*.example.toml`, help snapshots) regenerated via
`test_docs_are_in_sync` and `cargo insta`.

> _This was written by Claude Code on behalf of max_

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 13:44:44 -07:00
Worktrunk Bot 29fa13dc95 docs(code-signing): add SignPath Foundation code-signing policy page (#3366)
Addresses the first, gating step of option #1 (SignPath Foundation) from
#3355 — the fix for the `Trojan:Win32/Wacatac.B!ml` false positive on
the unsigned Windows binary.

## What this PR does

Adds a **code signing policy** page at
[worktrunk.dev/code-signing/](https://worktrunk.dev/code-signing/). A
published policy page is a hard eligibility requirement for the
[SignPath Foundation](https://signpath.org/terms.html) free OSS
code-signing program, so it's the natural thing to land first — the
application can't proceed without it. The page documents:

- **Certificate provenance** — the required attribution ("Free code
signing provided by SignPath.io, certificate by SignPath Foundation")
and the HSM-held key.
- **What's signed** — `git-wt.exe` in the Windows release archive /
winget; nothing else.
- **The build+sign pipeline** — tag-triggered, cargo-dist on GitHub
runners, SignPath action for signing, all reproducible from public
source.
- **Project roles** (Author / Reviewer / Approver), **per-release manual
approval**, and the **no-telemetry** privacy stance SignPath asks
projects to state.

The generated skill mirror
(`skills/worktrunk/reference/code-signing.md`) and
`docs/static/llms.txt` entry are produced by `test_docs_are_in_sync` —
not hand-edited.

> **Roster check:** the roles table lists @max-sixty as
Author/Reviewer/Approver. Adjust if anyone else should hold signing
authority before this is submitted to SignPath.

## What this PR deliberately leaves out

The `release.yaml` signing job and the SignPath account are a
**follow-up**, for two reasons:

1. **The concrete slugs don't exist yet.** The SignPath action needs
`organization-id`, `project-slug`, and `signing-policy-slug` — all
issued *after* the project is registered and approved.
2. **cargo-dist doesn't sign natively, and the wiring is
checksum-sensitive.** cargo-dist generates a per-artifact `.sha256` in
the *local build job*, alongside the `.zip`. Signing the binary
afterward means the signed `.zip` no longer matches its checksum, so a
correct job has to sign → re-zip → **regenerate the `.sha256`** →
overwrite the artifact before the `host` job uploads it. I don't want to
ship that against a live release pipeline without being able to run it
end-to-end (it only runs on a version-tag push), so it belongs in a
follow-up gated behind the real SignPath config.

I've laid out the exact maintainer steps and the proposed job shape in a
comment on #3355.

## Verification

- `cargo test --test integration test_docs_are_in_sync` — green
(regenerates + validates the skill mirror, llms.txt, and Zola link
transformation).
- zola build + lychee link check run in CI (`check-docs`); neither tool
is installed in the tend sandbox.

Closes nothing on its own — #3355 stays open until signing is live.

## Follow-up commits on this branch

- **`docs/static/code-signing.md` companion symlink** →
`skills/worktrunk/reference/code-signing.md`, matching every existing
page, so the `worktrunk.dev/code-signing.md` entry in `llms.txt`
resolves once deployed. Also swaps a dead
`opensource.axo.dev/cargo-dist` link for the repo's
`axodotdev.github.io/cargo-dist` convention.
- **`.config/lychee.toml` exclusion** for the self-referential
`worktrunk.dev/<page>.md` links in `llms.txt`. Those point at the live
site, so a brand-new page's entry always 404s in the link check until
the site is deployed — a bootstrap gap every new-docs-page PR hits.
Their existence is already guaranteed by `test_docs_are_in_sync`, and
prose/template links use the trailing-slash form (`worktrunk.dev/faq/`),
so the exclusion is scoped to the `llms.txt` index entries only.

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: worktrunk-bot <worktrunk-bot@users.noreply.github.com>
2026-07-09 12:13:47 -07:00
Worktrunk Bot 9147c0e46d docs(skill): frame hook approvals as user consent, stop advocating --yes (#3146) 2026-06-20 13:40:36 -07:00
Maximilian Roos 22768d425f feat(hooks)!: table-form pre-* hooks run concurrently — remove the serial path (#3052)
## Summary

Lands the cutover announced in v0.37.0 (#2135): a multi-entry table hook
now runs its commands concurrently for every hook type. `[pre-merge]`
with several keys behaves like `[post-start]` with several keys and like
a single `[[pre-merge]]` block — one execution semantic for `Concurrent`
steps.

The serial behavior was not implemented where one might expect. The
executor's serial branch (`ForegroundStep.concurrent` /
`SourcedStep.is_pipeline` / `CommandConfig::is_pipeline()`) was mostly
dead: the deprecation shipped with a load-time TOML migration that
rewrote multi-entry pre-* tables into serial pipeline form in memory on
every config load, so affected configs never reached the executor as
`Concurrent` steps. Removing that migration (now a `DEPRECATION_RULES`
row, after #3045) is the actual behavior change; the executor flags come
out with it.

## Why no replacement runtime warning

Affected configs have printed a warning on every `wt` invocation for
twenty minor releases (v0.37.0, April 12 → v0.57.0), with `wt config
update` offering a one-command migration that preserved serial behavior
explicitly ("migrate now to keep the current serial behavior once the
table form is repurposed"). A post-cutover warning has no coherent
shape: table form is now legitimate concurrent config, so it would nag
users who want exactly that behavior with no way to silence it, and the
`wt config update` rewrite would no longer be behavior-preserving.

## Parse normalization

A one-entry top-level table previously parsed as a `Concurrent` group of
one; with the serial branch gone it would have picked up `name │` output
prefixes. One-entry maps now parse as `Single(named)` everywhere,
matching one-entry maps inside pipeline lists (removing the documented
dict-at-top vs dict-in-list asymmetry), and a new `Serialize` arm keeps
one-step named configs round-tripping as named tables. Single-entry
table hooks render exactly as before.

## Behavior changes

1. **Multi-entry table-form pre-* hooks: serial → concurrent.** The
announced change.
2. **`wt hook <post-type>` foreground runs of multi-entry table hooks:
serial → concurrent.** Previously an undocumented inconsistency — the
same config already ran concurrently via the background path.
3. **Single-entry table aliases (`[aliases.x]` with one key):
prefixed-stderr → stdout passthrough.** Now matches the string and
`[[aliases.x]]` spellings, and makes `wt <alias> | …` work for this
spelling too.

(2) and (3) were never deprecation-warned; all three belong in the
release notes.

## Tests

- `test_pre_merge_deprecated_table_runs_serially` → rewritten as
`test_pre_merge_table_form_runs_concurrently`; the
single-`[[pre-merge]]`-block test is deleted (both forms now parse
identically, so it duplicated the rewritten test).
- Fixtures that genuinely need serial ordering (`>`/`>>` chains, the
signal-abort "second must not run" assertion) converted to pipeline
form.
- `test_user_hooks_preserve_toml_order` (#737) and
`test_post_start_named_commands` keep table form and pin ordering via
`WORKTRUNK_TEST_SERIAL_CONCURRENT=1`, so insertion order is still
asserted through the real concurrent input ordering.
- New: one-entry-table parse/round-trip unit test;
`test_alias_single_entry_table_writes_to_stdout` pinning change (3).
- Regenerated snapshots drop a stale `RUST_LOG: warn` env line (the
harness stopped setting it in #2901).

## Known follow-up

Concurrent-group announcements expose a pre-existing ANSI dim-bleed:
`format_bash_with_gutter` output ends without closing the dim attribute,
so back-to-back `◎ Running …` lines render dim (visible today on
pipeline-form hooks and `--execute` verbose). Now that this rendering is
the table-form default it's worth fixing, but the fix lives in the
gutter formatter and churns every bash-gutter snapshot — separate PR.

> _This was written by Claude Code on behalf of max_

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 21:31:07 -07:00
Maximilian Roos 5da0d2c3e4 docs: alias template-expansion timing; fix narrow help-test env redaction (#3006)
## Docs: alias template-expansion timing

The main addition documents a gotcha that's easy to hit and hard to
diagnose: an alias body renders its `{{ … }}` once at dispatch, in the
invoking worktree, so a per-worktree variable like `{{ branch }}` is
baked to a single value *before* a nested `wt step for-each` / `wt
switch --execute` iterates — printing the same value in every worktree.
The fix is `{% raw %}…{% endraw %}` deferral (plus a quoted `sh -c '…'`
for `for-each`, since the deferred `{{ branch }}` contains spaces).

- `extending.md` — rewrote "Deferring expansion to a nested `wt`
command" around the `for-each` symptom; improved the `up` rebase recipe.
- `faq.md`, `troubleshooting.md` — symptom-first entries with `wt config
alias dry-run` as the diagnostic.
- `hook.md` / `config.md` / `step.md` — distinguish repo-level
(constant) vs per-worktree (active) variables; note `{{ default_branch
}}` needs no deferral; cross-link the `{{ default_branch }}` variable vs
the `wt config state default-branch` shell command.

## Factual corrections

- `integration_reason` JSON values are hyphenated (`trees-match`,
`no-added-changes`, `merge-adds-nothing`) — the docs had underscores.
Verified against `src/commands/list/model/state.rs`.
- `SKILL.md`: 7 → 10 hook types (5 events × pre/post), added an
aliases/multi-worktree task section, fixed stale anchor links.

## Test fix: narrow help-test env redaction

`test_help_list_narrow_terminal` built its own `insta::Settings` but
skipped `add_standard_env_redactions` (every other help snapshot routes
through `snapshot_help`, which calls it). Its snapshot env block
therefore leaked host-specific paths (`LLVM_PROFILE_FILE` = the
machine's temp dir, plus the `WORKTRUNK_*` paths), which churn whenever
the snapshot is regenerated on a different machine. Adding the one call
mirrors `snapshot_help` and makes the snapshot reproducible.

Worth noting (and a candidate follow-up): this gap was masked under
`cargo test` (libtest) because the `repo` fixture's `mem::forget`'d
`bind_to_scope()` guard leaks redaction settings across the shared
process's reused threads. Under nextest (process-per-test, what the
pre-merge hook uses) there's no leak, so a test missing its own
redactions is exposed. A few other help tests (`test_help_md`,
`test_version`, `test_nested_subcommand_suggestion`) have the same gap
and could be consolidated through one settings helper — left out of this
PR to keep it focused.

> _This was written by Claude Code on behalf of max_

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 00:00:52 -07:00
Worktrunk Bot ec62580c29 revert(hooks): keep docs on pre-start/post-start; code accepts both (#2857)
Per @max-sixty's [direction in
#2838](https://github.com/max-sixty/worktrunk/issues/2838#issuecomment-4509447593):
revert the docs portion of #2840 and keep the code. Docs continue to
recommend `pre-start`/`post-start`; both names work in code so anyone
who already followed the briefly-changed docs (e.g. @EcksDy) isn't
stranded once a release ships these aliases.

## User-visible — back to `pre-start`/`post-start`

- README, docs site, skill mirrors, `dev/*.example.toml`,
`plugins/worktrunk/README.md`, `flake.nix`, `.config/wt.toml`
- `src/cli/mod.rs` / `src/cli/config.rs` / `src/cli/step.rs` /
`src/help.rs` after_long_help and example snippets — and the auto-synced
`docs/content/` and `skills/worktrunk/reference/` mirrors
- `wt hook --help` canonical subcommand names; completion advertises
`-start` only
- `HookType` Display via strum, serde `rename`, and clap `ValueEnum`
name — all `pre-start`/`post-start`. The Rust variant identifiers stay
`PreCreate`/`PostCreate` (internal; we already paid for that rename in
#2840, and now the eventual flip is a Display-only change)
- `HooksConfig` serde canonical fields

## `*-create` still works (kept code)

- `wt hook pre-create` / `post-create` — CLI alias on the canonical
subcommand
- `pre-create` / `post-create` in config: top-level, `[hooks.*]`, and
per-project, in string, `[table]`, and `[[array-of-tables]]` form.
Mechanism: serde `alias = ...` on the field, plus a silent in-memory
rename in `migrate_content()` so the round-trip in `unknown_tree`
doesn't flag table forms as schema-unknown.
- The pre-0.32.0 `post-create` fatal-load-error machinery stays removed
— the name is reclaimed, and both forms load without error.

## Smaller bits

- `valid_user_config_keys()` / `valid_project_config_keys()` append
`pre-create` / `post-create` so the unknown-field round-trip skips them.
`test_valid_*_keys_all_deserialize` skips both aliases (they can't sit
alongside the canonical without a duplicate-field error).
- `DEPRECATED_SECTION_KEYS` drops the `pre-start`/`post-start` entries
#2840 added — `pre-start`/`post-start` are canonical again.
- `find_pre_start_from_doc` / `find_post_start_from_doc` /
`find_renamed_hook_key` / `is_non_empty_item` /
`migrate_start_hooks_doc` and their tests are removed; the migration
direction flips via a new `migrate_create_hooks_doc` (silent, mirrors
the prior shape).
- Test files `e2e_shell_post_create.rs` and `post_create_commands.rs`
rename back to `_post_start_` (via `git mv`, so the rename shows as a
rename).

## Testing

`cargo run -- hook pre-merge --yes` — 3806 tests pass; the 10 failures
are all `case_4` of `shell_wrapper::unix_tests::*` (nu-shell case; `nu`
isn't installed in this runner; same failures occur on `main`).

Also manually verified that a fresh `wt switch --create` against a
project config with `[post-create]` loads cleanly with no unknown-field
warning and the hook fires as `post-start`.

## Follow-up

Per @max-sixty: in a couple of weeks, once a release with
both-names-work is out and users have had a chance to upgrade, the docs
flip is straightforward (most of it is in `src/cli/mod.rs`'s
`after_long_help` and the doc-sync test propagates).

Re #2838.

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 16:03:03 +00:00
Maximilian Roos d7e3f88422 feat(hooks): rename worktree-creation hooks to pre-create/post-create (#2840)
Phase 1 of the staged hook rename tracked in #2838: the worktree-creation hooks `pre-start`/`post-start` become `pre-create`/`post-create`. The old names keep working with no deprecation warning yet (Phase 2, months out, adds the warning).

## What changes

- `pre-create`/`post-create` are canonical everywhere: the `HookType` enum, the `HooksConfig` serde fields, the `wt hook` CLI, completion, and all docs.
- Old names keep working: `migrate_content()` rewrites `pre-start`/`post-start` config keys to `-create` before serde, and `parse_hook_type` accepts the old CLI names as silent aliases. `wt config update` rewrites them on disk; `wt config show` shows the migration diff. `wt hook <type>` execution and `wt hook show` both accept the old names; completion and `--help` advertise only the canonical names.
- `detect_deprecations()` flags the old keys so `update`/`show` act on them, but `format_deprecation_warnings()` stays silent (Phase 2 adds the warning). A new empty-warnings guard in `check_and_migrate` keeps a `-start`-only config from emitting a stray hint.
- The dead pre-0.32.0 `post-create` machinery is removed: the fatal `POST_CREATE_REMOVED_MSG` load error, the vestigial `HooksConfig.post_create` merge-fold, and `find_post_create_from_doc`. `post-create` is reclaimed as the canonical background creation hook.

## Semantic flip

Before v0.32.0, the key `post-create` named a *blocking* hook. It now names the *background* one.

Since v0.44.0, a pre-0.32.0 `post-create` config is a fatal load error on the `check_and_migrate` paths: `ProjectConfig::load` and user/system config loading, which fire on essentially every `wt` command. A repo carrying one has been unusable ever since. The one path that skips that check is `project_config_at_ref` (the base-ref read behind `wt switch --create`), which applies only structural migration. A pre-0.32.0 `post-create` surviving solely on a base ref, never checked out into a worktree, would now load as a background hook rather than folding into the blocking `pre-start`. That edge case is accepted: once `post-create` is valid again, reclaiming the name and detecting the dead key are mutually exclusive.

## Reviewing this diff

205 files, but the substance is ~36 files under `src/`. The rest is regenerated snapshots and auto-synced doc mirrors. Start with:

- `src/config/deprecation.rs` — detection (`find_renamed_hook_key`), migration (`rename_hook_key`), removal of the fatal block, the empty-warnings guard, and the `DEPRECATED_SECTION_KEYS` entries that stop unknown-field detection from flagging the migrated keys.
- `src/config/hooks.rs`, `src/git/mod.rs` — the serde field and enum renames.
- `src/config/project.rs` — `ProjectConfig::load` deserializes `check_and_migrate`'s migrated content, so a current-worktree config using the old keys loads into the canonical fields.
- `src/cli/hook.rs`, `src/commands/hook_commands.rs`, `src/completion.rs`, `src/main.rs` — the CLI alias layer; `wt hook show` accepts the old type names as hidden value-parser aliases.
- `src/cli/mod.rs` — the `wt hook` docs, including the soft-deprecation note linking #2838.

The ~93 modified snapshots also pick up deterministic env-block lines (`GIT_*: ""`, `LLVM_PROFILE_FILE`) that pre-existing snapshots already carry. That is stale-snapshot drift surfaced by the regeneration, not a behavior change.

## Testing

Full suite green (3799 tests). New coverage: `snapshot_migrate_start_to_create` (migration preserves value shape and position), `test_deprecated_start_hook_key_runs_silently` and `test_standalone_hook_start_alias_runs_silently` (old config and CLI names run with no warning), `test_config_show_displays_start_hook_migration` (`config show` reveals the diff without an "unknown field" warning), and `test_hook_show_accepts_deprecated_start_hooks` (a current-worktree config using the old keys loads, and `wt hook show` takes both the canonical and the deprecated type arguments).

Part of #2838.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-20 19:31:50 -07:00
Maximilian Roos 5b2a41ee73 feat(plugin): detect Gemini extension; document OpenCode and Gemini install (#2819)
Closing two gaps a prior plugin-implementation review surfaced: Gemini's
native install path was invisible (no `wt config show` section, no
user-facing docs), and OpenCode — which has the most substantial
implementation of the four — was absent from the plugins page despite
being the only tool that ships activity tracking via a worktrunk-written
plugin.

`wt config show` now renders a `GEMINI CLI` section gated on
`which::which("gemini")`. Installed state is detected by parsing
`~/.gemini/extensions/worktrunk/gemini-extension.json` and checking
`name == "worktrunk"` — exactly where `gemini extensions install` clones
the extension. Fail-closed on a missing home, unreadable file, or
invalid JSON, matching the existing claude/codex/opencode detection.
Test infrastructure (`gemini_installed` field on `TestRepo`,
`setup_mock_gemini_installed`, `setup_gemini_extension_installed`) and
the two new snapshot tests mirror the OpenCode equivalents
line-for-line.

`docs/content/claude-code.md` retitled "Agent Integration" and
restructured around a four-tool capability table covering configuration
skill, activity tracking, worktree isolation, and `/wt-switch-create`.
OpenCode and Gemini get install subsections; the activity-tracking
section is generalized from Claude-only to Claude+OpenCode+Gemini so the
heading matches the table. A Codex-only sentence under "Worktree
isolation" was redundant with the table and dropped.

## Verified end-to-end

After **v0.52.0** went `Latest`, `gemini extensions install
https://github.com/max-sixty/worktrunk` resolves via `/releases/latest`
→ the v0.52.0 source tarball (which now carries the root
`gemini-extension.json` from #2807) → install succeeds. `gemini
extensions list` shows `Type: github-release`, `Release tag: v0.52.0`,
with both skills (`wt-switch-create`, `worktrunk`) loaded via
`${extensionPath}/skills/`. Live `wt config show` reports `✓ Extension
installed`. The bare URL is what the docs use — `owner/repo` shorthand
returns "Install source not found" against gemini-cli, so the full URL
is the right form.

Per the `release` and detection paths in gemini-cli 0.42
(`bundle/chunk-CHERUG6W.js` lines 44170–44290 / 44640–44675), the URL
install always tries `releases/latest` first when releases exist, and
only auto-falls-back to `git clone` if there's no release data at all —
so the work landed in #2807 on `main` only became user-visible once
v0.52.0 was cut. This PR ships the detection and docs that have now been
confirmed accurate against the live release path.

Pre-merge gate green locally (3747 tests, 0 skipped; pre-commit clean;
`test_docs_are_in_sync` green so the skill reference and `llms.txt` are
in sync).

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 18:33:05 -07:00
Maximilian Roos 233cd493d0 fix(codex): drop activity-marker hooks Codex can't drive (#2786)
The Codex plugin shipped `hooks.json` with `SessionStart`→💬,
`UserPromptSubmit`→🤖, and `Stop`→💬. But codex-cli 0.130.0's
`HookEventNameWire` schema (extracted from the binary) has **no
`Stop`/turn-end event**: `PreToolUse`, `PermissionRequest`,
`PostToolUse`, `PreCompact`, `PostCompact`, `SessionStart`,
`UserPromptSubmit`. So on Codex the 🤖 marker set at `UserPromptSubmit`
could never return to 💬 — a Codex worktree stuck at "working" for the
entire session. That's worse than the docs claimed ("rests at 💬").

This removes the marker hooks entirely until Codex exposes a turn-end
hook event. The Codex plugin now ships only the configuration skill;
activity tracking joins worktree-isolation and `/wt-switch-create` as
Claude-Code-only. A re-enablement comment (exact conditions + the files
to restore) lives in `src/commands/config/codex.rs` and a new
`CLAUDE.md` → "Codex Plugin" section, which also documents the accepted
tradeoff that the `skills/` symlink exposes the Claude-only
`wt-switch-create` skill to Codex (harmless — Codex can't act on it).

Bundled adjacent fixes: `plugin.json` metadata drift
(`description`/`longDescription` no longer claim activity hooks;
`homepage`/`websiteURL` `https://worktrunk.dev/claude-code/` →
`https://worktrunk.dev`), and the install/uninstall/`--help`/docs text
made honest about Codex having only the configuration skill.

**Reviewer navigation:** `src/commands/config/codex.rs` +
`src/cli/config.rs` (runtime + `--help` text),
`docs/content/claude-code.md` (skill ref + `llms.txt` auto-synced),
`CLAUDE.md` (new section), `tests/integration_tests/config_show.rs`
(`test_codex_plugin_metadata_is_valid_json` now asserts `hooks` absent +
guards metadata regression). Snapshot env-block churn is stale-fixture
convergence to the repo's current `/nonexistent/wt/` convention, not a
leak.

**Testing:** Full pre-merge gate green locally (3704 tests, 0 skipped;
lints clean). The metadata test was strengthened; the marker behavior
itself is removed, so there's nothing runtime-testable to add.

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 15:29:54 -07:00
Douglas Soares de Andrade 2c675e00f7 Add Codex support (#2512)
## What problem does this solve?

Worktrunk already has a Claude Code integration, but Codex users do not
have an equivalent first-class path for:

- discovering Worktrunk guidance inside the agent environment
- bundling hooks that can show active Codex work in `wt list`
- learning the Codex-specific worktree workflow and its differences from
Claude Code
- installing the integration through a Worktrunk CLI command

This PR adds Codex support while preserving the important semantic
difference between Claude Code and Codex: Claude Code can route
agent-created worktrees through `wt` lifecycle hooks, but Codex
currently needs users/agents to invoke `wt switch --create` and `wt
remove` directly.

## What changed?

### Codex plugin package

- Adds repo-local Codex plugin metadata in `.codex-plugin/plugin.json`.
- Adds a Codex marketplace entry in `.agents/plugins/marketplace.json`.
- Adds `hooks/hooks.json` for bundled activity hooks:
  - `SessionStart` sets the marker to `💬`
  - `UserPromptSubmit` sets the marker to `🤖`
  - `Stop` sets the marker back to `💬`
- Reuses the existing Worktrunk skill/reference docs as the plugin skill
payload.

### CLI integration

- Adds `wt config plugins codex install`.
  - Runs `codex plugin marketplace add max-sixty/worktrunk`.
- Clearly tells the user to open `/plugins` in Codex and install
Worktrunk from the marketplace.
- Adds `wt config plugins codex uninstall`.
  - Removes the Worktrunk marketplace entry.
- Intentionally leaves already-installed plugins and global Codex hook
feature flags unchanged, because users may have configured hook settings
for other hooks.
- Adds a `CODEX` section to `wt config show` when the Codex CLI is
available.
  - It reports Codex CLI availability.
- It avoids claiming the plugin is installed, because the CLI path only
detects the Codex binary.

### Documentation

- Adds a new Codex integration page with installation, bundled activity
hooks, worktree workflow, LLM commit setup, and a Claude Code
comparison.
- Updates README, overview docs, tips/patterns, FAQ, and generated skill
references so Codex is listed alongside Claude Code/OpenCode where
relevant.
- Keeps the Claude Code docs as the place for Claude-only worktree
lifecycle hook behavior.

## Why this shape?

The implementation keeps Codex separate from the existing Claude plugin
command instead of abstracting over both. That is intentional: Claude
installs a plugin directly, while the Codex flow configures a
marketplace and still requires the user to install from `/plugins`. A
shared abstraction would hide those differences and make the user-facing
behavior easier to misread.

The docs also avoid presenting Codex as feature-identical to Claude
Code. Codex gets skills and bundled activity hooks here, but not
automatic worktree lifecycle routing.

## Review guide

- Plugin packaging: `.codex-plugin/plugin.json`,
`.agents/plugins/marketplace.json`, `hooks/hooks.json`
- CLI behavior: `src/commands/config/codex.rs`, `src/cli/config.rs`,
`src/commands/config/show.rs`
- Test helpers and coverage: `src/testing/mod.rs`,
`tests/integration_tests/config_show.rs`,
`tests/integration_tests/help.rs`
- Primary docs: `docs/content/codex.md`
- Generated docs/snapshots: `skills/worktrunk/reference/codex.md`,
config/help snapshots

## Tests

- `cargo fmt --check`
- `cargo check --lib --bins`
- `cargo test --lib --bins`
- `cargo test --test integration test_docs_are_in_sync`
- `cargo test --test integration "test_help"`
- `cargo test --test integration config_show`
- `git diff --check HEAD`

## Local pre-merge note

`cargo run -- hook pre-merge --yes` could not complete locally because
this environment does not have `pre-commit` or `cargo-nextest`
installed. The hook's doctest/doc portions did run and passed.

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Maximilian Roos <m@maxroos.com>
2026-05-16 20:30:32 -07:00
Maximilian Roos 761c293483 chore(plugin): drop off-spec version key, align skill frontmatter (#2747)
Tidies the plugin skills' YAML frontmatter against the [Agent
Skills](https://agentskills.io/specification) spec, and cleans up a
release-skill step the change makes stale.

- `skills/worktrunk/SKILL.md`: drop the top-level `version: 0.49.0` key.
The spec only defines `name`, `description`, `license`, `compatibility`,
`metadata`, `allowed-tools` at the top level (version, if kept, belongs
under `metadata`), and Claude Code's own field set doesn't include
`version` either. Also drop the
`[package.metadata.release].pre-release-replacements` entry in
`Cargo.toml` that existed solely to bump that line on release.
- `skills/wt-switch-create/SKILL.md`: add `license` and `compatibility`
so both plugin skills carry the same spec-valid metadata.
- `.claude/skills/release/SKILL.md`: step 7 no longer applies
`pre-release-replacements`, and `index.json`'s `SKILL.md` digest no
longer goes stale on release (`cargo release` doesn't touch `SKILL.md`
anymore) — drop the parenthetical and the now-no-op digest-resync
sub-step.
- Both `compatibility` lines now say "the `wt` CLI" rather than
"worktrunk CLI".
- `docs/static/.well-known/agent-skills/index.json` digest regenerated
by `test_docs_are_in_sync`.

Follow-up to #2737 / #2745. No `.rs` changes.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:48:37 -07:00
Maximilian Roos 7a61d38348 Release v0.49.0 (#2660)
Bump worktrunk to **0.49.0** — minor bump (semver-checks reports a
breaking change in `GitError::UncommittedChanges`, which gained a
`dirty_files: Vec<String>` field).

## Highlights

**Improved**
- New `codename(n)` template filter — deterministic friendly worktree
names from a ~1.26M-combo pool (`{{ branch | codename(2) }}` →
`malleable-opah`).
[#2641](https://github.com/max-sixty/worktrunk/pull/2641), thanks
@endigma
- Picker preview disk cache for Log / BranchDiff / UpstreamDiff, with
background refresh for stale Log decorations.
[#2628](https://github.com/max-sixty/worktrunk/pull/2628),
[#2646](https://github.com/max-sixty/worktrunk/pull/2646)
- Picker shares one `default_branch_sha` lookup across BranchDiff items
instead of forking N rev-parse subprocesses.
[#2658](https://github.com/max-sixty/worktrunk/pull/2658)
- "X has uncommitted changes" errors now list the dirty files inline.
[#2653](https://github.com/max-sixty/worktrunk/pull/2653)

**Fixed**
- `wt switch` integrates with `cd` aliases like zoxide (uses `builtin
cd`). [#2644](https://github.com/max-sixty/worktrunk/pull/2644), thanks
@xkumiyu for reporting
- Empty hook tables no longer panic during `wt switch --create`.
[#2635](https://github.com/max-sixty/worktrunk/pull/2635), thanks @topit
for reporting
- `Repository::current_worktree()` and five sibling helpers no longer
silently leak the process CWD when called via `Repository::at(p)`.
[#2652](https://github.com/max-sixty/worktrunk/pull/2652)
- `WorkingTree::is_linked` tolerates non-git CWDs (e.g. Nix sandbox).
[#2625](https://github.com/max-sixty/worktrunk/pull/2625), thanks
@DArtagan for reporting
- Picker no longer overlays `collect` warnings on the active TUI; clears
its frame on exit in inline mode.
[#2627](https://github.com/max-sixty/worktrunk/pull/2627),
[#2626](https://github.com/max-sixty/worktrunk/pull/2626)
- Failed `git log` no longer poisons the picker's preview disk cache.
[#2651](https://github.com/max-sixty/worktrunk/pull/2651)
- `wt step prune` says "removing branch" for branch-only candidates.
[#2619](https://github.com/max-sixty/worktrunk/pull/2619)
- Claude Code Windows integration uses a `wt.sh` wrapper to avoid
colliding with Windows Terminal's `wt.exe`.
[#1754](https://github.com/max-sixty/worktrunk/pull/1754), thanks
@lucaspimentel

**Documentation**
- cmux workspace integration recipe.
[#1907](https://github.com/max-sixty/worktrunk/pull/1907), thanks
@alvistar

See
[CHANGELOG.md](https://github.com/max-sixty/worktrunk/blob/release/CHANGELOG.md)
for the full list including internal CI / test-isolation changes.

## Test plan

- [x] `cargo run -- hook pre-merge --yes` (tests + lints clean)
- [x] `cargo semver-checks check-release -p worktrunk` (one breaking
change confirmed → minor bump)
- [x] CHANGELOG entries verified by subagent against actual diffs

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-10 00:16:53 -07:00
Maximilian Roos f897aa7a9c Release v0.48.0 (#2618)
## Summary
Release v0.48.0. See
[CHANGELOG.md](https://github.com/max-sixty/worktrunk/blob/release/CHANGELOG.md)
for the full notes.

Highlights:
- `--format=json` extends to seven step + hook commands (#2560)
- `wt step commit` / `wt step squash` gain `--dry-run` (#2557)
- New `dirname` / `basename` template filters (#2592, #2605)
- New `[remove] delete-branch` config option (#2589)
- `wt-perf timeline` subcommand (#2558)
- Faster `wt list` on dirty worktrees (#2602) and faster alias dispatch
(#2556, #2573)
- Short-SHA display honors `core.abbrev` (#2576)
- Cleaner `wt config show` shell-integration section for new users

## Test plan
- [x] `cargo run -- hook pre-merge --yes` (3497 tests, lints clean)
- [x] `cargo semver-checks check-release -p worktrunk` consulted; minor
bump confirmed
- [ ] CI green
2026-05-04 23:32:56 -07:00
Maximilian Roos 33db25618a docs(skill): document parallel sub-Agent worktree pattern (#2600)
## Summary

Adds a subsection under *Advanced: Agent Handoffs* in
`skills/worktrunk/SKILL.md` for the case where a single Claude Code
session spawns multiple sub-Agents to work autonomously in their own
worktrees — no terminal multiplexer, no human in the other pane. The
existing handoffs section only covers tmux/Zellij user-facing handoffs,
so future Claude sessions had no guidance pointing them at the canonical
pattern, and `Agent { isolation: "worktree" }` was the obvious-looking
but wrong default.

## What it says

- **Canonical recipe**: pre-create each worktree from the parent with
`wt switch --create <branch> --no-cd --no-hooks`, then call `Agent`
*without* `isolation: "worktree"` and name the path in the prompt.
- **Footgun call-out**: don't use `Agent { isolation: "worktree" }` with
this plugin. Claude Code passes its internal agent ID as `name` to the
`WorktreeCreate` hook, so `wt` creates the worktree as
`worktrunk.agent-<id>` on a throwaway branch. If the sub-Agent then
creates a feature branch on top, you end up with non-canonical paths,
orphan branches, and post-start hooks fired against the wrong branch.

The literal API name appears in the warning so a future Claude
pattern-matches the situation when it's about to call that exact tool.

## Test plan

- [x] `cargo test --test integration test_docs_are_in_sync` — passes
(regenerated `docs/static/.well-known/agent-skills/index.json` digest)
- [x] `cargo run -- hook pre-merge --yes` — 3491 tests pass, lints clean

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:58:02 -07:00
Maximilian Roos 4b6316ac29 Release v0.47.0 (#2552)
Cuts v0.47.0. Highlights:

- **Integration detection fix** across `wt list`, `wt remove`, `wt
merge`, `wt step prune` for the local/upstream-diverged case (#2507,
#2513, #2515).
- **Faster shell-command dispatch** (~10ms per invocation, 1.49× on
trivial aliases) via event-driven signal forwarding (#2537, #2538), plus
parallel config load (#2543) and merged cold-start prewarm rev-parse
(#2541).
- **`wt switch <number>` suggests `pr:N` / `mr:N`** instead of just
`--create` (#2516).
- **Library API breaking changes**: `RefSnapshot` cutover (#2528, #2530)
and the `for-each` direct-exec change (#2465). Full list in
`CHANGELOG.md`.

See `CHANGELOG.md` for the complete entry.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-03 10:49:00 -07:00
Maximilian Roos 093f6b3fcf Release v0.46.1 2026-04-30 10:21:28 -07:00
Maximilian Roos 01a8258b6e Release v0.46.0 2026-04-29 16:40:05 -07:00
Maximilian Roos 99a381ba41 Release v0.45.2 2026-04-28 09:31:51 -07:00
Maximilian Roos 4ee813c15f Release v0.45.1 2026-04-27 18:14:39 -07:00
Maximilian Roos d567656207 Release v0.45.0 2026-04-27 08:04:52 -07:00
Maximilian Roos 89dceed12e docs(site): add llms.txt + .md companions (#2404)
## Summary

Implements the [llms.txt spec](https://llmstxt.org/) for the docs site:

- **`worktrunk.dev/llms.txt`** — curated index listing every doc page,
grouped by sidebar section (Commands, Reference) and ordered by weight.
Generated from `docs/content/*.md` front-matter.
- **`worktrunk.dev/<page>.md`** — clean-markdown versions of each doc
page, served as symlinks from `docs/static/*.md` into
`skills/worktrunk/reference/*.md`. Zola follows the symlinks at build
time, so there's no content duplication — just 13 symlink entries in
git, alongside the existing
`docs/static/.well-known/agent-skills/worktrunk` symlink.

## What changed

- 13 symlinks at `docs/static/*.md` →
`../../skills/worktrunk/reference/*.md`
- `docs/static/llms.txt` — generated output (checked in, same as
`skills/worktrunk/reference/` and `.well-known/agent-skills/index.json`)
- `sync_llms_txt()` + `extract_intro_prose()` +
`docs_content_page_names()` helpers in
`tests/integration_tests/readme_sync.rs`
- Step 4 added to `test_command_pages_and_skill_files_are_in_sync` — CI
catches drift if front-matter changes and `llms.txt` isn't regenerated

The new directory-walk helper (`docs_content_page_names`) is shared with
`sync_skill_files`, which had the same walk inlined.

## Test plan

- [x] `cargo test --test integration readme_sync` — 13/13 pass,
idempotent on re-run
- [x] `cargo fmt` + `cargo clippy --all-targets --all-features -- -D
warnings` — clean
- [x] `pre-commit run --files tests/integration_tests/readme_sync.rs` —
pass
- [x] `zola build` produces `public/merge.md` etc. as real 4814-byte
files (not symlinks) and `public/llms.txt`
- [ ] CI green

> _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>
2026-04-23 17:33:21 -07:00
Maximilian Roos 1649bb528b Release v0.44.0 2026-04-22 08:48:29 -07:00
Maximilian Roos 4874aeda94 Release v0.43.0 2026-04-21 14:39:42 -07:00
Maximilian Roos a24e9577a2 Release v0.42.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:37:25 -07:00
Maximilian Roos 387630850d docs(skill): add non-interactive hook approval guidance (#2343)
Agents using the canonical \`worktrunk\` skill hit the hook-approval
prompt error when running \`wt merge\` (or any command that runs project
hooks) in a non-interactive session. The error already names both escape
hatches — \`wt config approvals add\` and \`--yes\` — but without
context, an agent may not know which to pick, or may silently default to
\`--yes\` when the user would rather pre-approve once.

New section in `skills/worktrunk/SKILL.md` covers:
- The error signature
- `wt config approvals add` — interactive, persists to
`~/.config/worktrunk/approvals.toml` until the template changes or the
project moves
- `--yes` — single-invocation bypass for CI/CD
- Directs agents to escalate rather than auto-`--yes`, since
pre-approval is a trust decision

Verified `wt config approvals add --help` and `wt config approvals
--help` match what the doc describes.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-20 13:27:02 -07:00
Maximilian Roos 6ba5611415 Release v0.41.0 2026-04-20 11:37:18 -07:00
Maximilian Roos d715753cf0 Release v0.40.0 2026-04-19 14:55:42 -07:00
Maximilian Roos 58795e5334 docs(skill): expand worktrunk description with lexical triggers (#2301)
The prior description relied on quoted example phrases ("configuring
hooks", "automating tasks") that rarely match real user phrasing, and
never mentioned `wt` — the CLI name users actually type. I recently
skipped loading this skill while editing
`~/.config/worktrunk/config.toml` to add a post-merge push hook, which
prompted the update.

New description drops the quoted phrases and uses direct trigger
criteria: both config file paths, explicit hook event names
(`post-merge`, `post-start`, `pre-commit`, `pre-merge`, `post-switch`),
and task verbs (adding, modifying, debugging).

> _This was written by Claude Code on behalf of Maximilian_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-18 16:02:33 -07:00
Maximilian Roos ae62b2fa35 release: land v0.39.0 commit on main (#2281)
The v0.39.0 tag and GitHub release were published, but the release
commit itself never landed on main. This PR is remedial: it cherry-picks
the tagged commit `c1fad73f3` onto `origin/main` so the release bump and
CHANGELOG entry become part of the default branch.

`c1fad73f3` is the canonical release — it's what the `v0.39.0` tag and
GitHub release point to. Cherry-pick applied cleanly on top of
`06073d5b6`; the resulting commit touches only `Cargo.toml`,
`Cargo.lock`, `CHANGELOG.md`,
`docs/static/.well-known/agent-skills/index.json`, and
`skills/worktrunk/SKILL.md`.

Note: local `main` currently has two stale duplicate "Release v0.39.0"
commits (`5958147b7`, `e8063d74a`) that were never pushed. Those will be
cleaned up separately — this PR is solely about landing the tagged
`c1fad73f3`.

> _This was written by Claude Code on behalf of Maximilian_
2026-04-17 23:58:11 -07:00
Maximilian Roos 46e06b4330 Release v0.38.0 2026-04-16 08:18:07 -07:00
Maximilian Roos 956cfdea00 Release v0.37.1 2026-04-14 21:53:06 -07:00
Maximilian Roos f23023ff0a fix(test): decouple post_hook_display_path tests from global state
Without shell integration, post_hook_display_path behaves like pre_hook_display_path.
Tests now use an explicit-arg variant (post_hook_display_path_with) so they're
independent of process-wide OUTPUT_STATE, which may be pre-initialized to
shell-integration-active when tests run under `wt` (inheriting WORKTRUNK_DIRECTIVE_*
env vars). Added test for shell-integration-active case to complete coverage.
2026-04-13 22:27:22 -07:00
Worktrunk Bot c0059a533d docs(skill): support OpenCode in agent handoffs section (#2108) 2026-04-12 01:05:51 +00:00
Maximilian Roos c68d0075af Release v0.36.0 2026-04-10 20:11:57 -07:00
Maximilian Roos ebf421f5c5 Release v0.35.3 2026-04-09 09:02:29 -07:00
Maximilian Roos bb383f8c2f Release v0.35.2 2026-04-08 15:50:27 -07:00
Maximilian Roos d5314f3ed6 Release v0.35.1
Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-08 12:39:58 -07:00
Maximilian Roos e428c2ac9e Release v0.35.0
Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-07 20:12:20 -07:00
Maximilian Roos 44531f50b7 Release v0.34.2 2026-04-06 11:48:00 -07:00
Maximilian Roos 9e612a11e6 Release v0.34.1
Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-03 21:58:54 -07:00
Maximilian Roos 2ab8fe2a94 Release v0.34.0
Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-03 07:18:18 -07:00
Maximilian Roos 5c1672932f docs: remove deprecated post-create from documentation (#1776)
Replace all `post-create` references with `pre-start` across
documentation, skills, example config, and test names. The Rust
deprecation handling code (migration, alias, config parsing) remains
intact for users with existing configs.

Also removes `post-create` from the `wt hook show` value_parser — it was
listed as a valid hook type for display even though it's been
deprecated.

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

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-27 17:38:57 -07:00
Maximilian Roos 270f7a4b17 Release v0.33.0
Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-26 12:35:31 -07:00
worktrunk-bot 7d663ea97d feat: add .well-known/agent-skills/ for web-based skill discovery (#1751) 2026-03-26 08:03:36 -07:00
Maximilian Roos 1d85de945f Migrate docs syntax highlighting to giallo with warm theme (#1080)
## Summary

- Migrate from static CSS syntax highlighting to Zola's giallo engine
with custom `worktrunk-light.json` theme
- Replace hardcoded `syntax-light.css` / `syntax-dark.css` with
theme-based class generation
- Design a warm "sunlit workshop" palette: amber commands, gold strings,
chartreuse quoted strings, rusty constants
- Add CSS sibling selector to differentiate quoted from bare strings
(giallo tokenizes both as `z-string`)

## Test plan

- [ ] Verify syntax colors on `/switch/` (bash: commands, flags,
strings, quoted strings)
- [ ] Verify TOML blocks on `/config/` (section headers, keys, values)
- [ ] Verify dark mode is unaffected (quoted string CSS rule scoped to
`prefers-color-scheme: light`)
- [ ] Check all tests pass (`cargo test`)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-17 11:32:21 -08:00
Maximilian Roos 15108dc742 rename demo from wt-select to wt-switch-picker (#953)
The `wt select` command was moved to `wt switch` (with `wt select`
kept as a deprecated alias). The demo tape content already uses
`wt switch`, but all naming — tape filename, output name, GIF
filenames, doc references — still said wt-select. This aligns
naming with the current command structure.

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-08 09:55:10 -08:00
Maximilian Roos be8959eec8 docs: add higher-resolution favicons for Google Search (#627)
Google recommends favicons of at least 48x48 pixels for proper display
in search results. The previous 32x32 favicon was below this threshold.

Changes:
- Add favicon-48.png (48x48) as primary favicon
- Add apple-touch-icon.png (180x180) for iOS home screen
- Update base.html to include sizes attributes for browser selection

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-14 11:02:09 -08:00