16 Commits

Author SHA1 Message Date
Maximilian Roos 57b303fda2 fix(nix): provide lsof to the flake's test sandbox (#3410)
The nix-flake job has failed on main since #3396:
`test_remove_reap_kills_process` discovers its child via `lsof -d cwd`,
but the flake's `worktrunk-tests` check didn't provide `lsof`, so
`processes_under` fails to spawn it, returns an empty list forever, and
the test's 5s discovery poll panics with `child <pid> never discovered`.
Failing main run:
https://github.com/max-sixty/worktrunk/actions/runs/29066326142.

Three changes:

- `flake.nix`: add `pkgs.lsof` to the test check's inputs, the same
pattern as `git`/`python3`/`procps`. `ps` (procps) already works in the
sandbox, so /proc-based process inspection is available there and the
test should be genuinely exercised rather than excluded.
- `.github/workflows/nightly.yaml`: add `flake.nix`, `flake.lock`, and
`nix/**` to the trigger paths (push and PR gate). A flake change
couldn't previously trigger the nix-flake job that validates it; this PR
relies on the fixed trigger for its own validation.
- `tests/integration_tests/remove.rs`: point the discovery-timeout panic
message at lsof, so the next environment missing it doesn't need
re-diagnosing.

Validation: nix isn't available locally, so the proof is this PR's own
nightly run going green on the nix-flake job.

> _This was written by Claude Code on behalf of max_

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 23:29:40 -07:00
Maximilian Roos 958b9c188a refactor(picker): migrate to skim 4.8 (ratatui), drop vendored skim-tuikit (#3137)
Migrates the `wt switch` picker from skim 0.20.5 (tuikit backend) to
skim 4.8.0 (ratatui/crossterm), removes the `vendor/skim-tuikit/` patch
tree we carried against the old line, and makes the full test suite
green under the new backend.

## Why the vendor tree goes away

We vendored skim-tuikit for two patches: `alt-<digit>` key parsing and
an `Output::flush` `write_all` fix for dropped bytes under PTY pressure.
Both are moot in 4.8. Key parsing is native — `binds.rs` routes every
single character (digits included) through `KeyCode::Char` rather than a
hardcoded `alt-a..alt-z` arm list. The flush fix is subsumed by stdlib
`BufWriter::flush` in the ratatui backend, which loops over partial
writes correctly. So the migration carries zero vendor patches against
skim, and the build machinery that kept the vendored source alive
(Taskfile `vendor-diff`, flake `vendorSrc`/`extraDummyScript`) is
deleted too.

## The picker rewrite

skim 4.x is a different API. `WorktreeSkimItem::display()` now returns
`ratatui::text::Line` instead of an `AnsiString`. alt-r removal rides
skim's `reload(remove {})` token (the selected row's `output()`,
expanded into the reload command) instead of the old
`as_any().downcast_ref` path (which never worked across compilation
units) plus a signal file. Preview-tab switching and the progressive
list redraw are driven by `Skim::event_sender()` +
`Event::Render`/`Event::RunPreview`.

## The regression that motivated the bulk of this

skim 4.x renders on demand. There is no 100ms tuikit heartbeat
re-rendering the frame, so the progressive `wt switch` list rendered
blank while collection ran in the background — the original report. The
fix pokes `Event::Render` from the list-collect callbacks (throttled to
16ms). The same on-demand model surfaced four more regressions, all
fixed here and verified head-to-head against the 0.20 picker in a
terminal: preview-tab lag, current-row highlight, Shift-Tab back-cycle
(crossterm reports Shift-Tab under three distinct `KeyEvent` shapes, so
all three are bound), and alt-r removal (4.x `execute-silent` is
fire-and-forget and raced its reader).

## Help text and the test harness

Two things the integration suite caught that the unit tests did not (the
prior pass ran `--lib --bins` only):

- **Styled help.** skim 0.20 transitively pulled in
`clap/unstable-markdown`, which renders worktrunk's `///` doc-comments
as styled help. skim 4 with `default-features = false` drops it, so help
reverted to raw text (`[experimental]` → `\[experimental\]`). Restored
by depending on `unstable-markdown` explicitly, keeping help output
identical and the `test_help` / `test_step_alias` /
`test_docs_are_in_sync` snapshots passing unchanged.
- **PTY harness.** skim 4.x queries cursor position (`ESC[6n`) at
startup in partial-height mode and blocks in `select()` for the reply;
`portable_pty` is a bare PTY and never answered, so every
`switch_picker` test failed init with "Cursor position detection timed
out." The Unix harness now answers the query, mirroring the existing
ConPTY responder. skim 4.x also draws the list/preview separator one
column left, so the snapshot panel-split columns shifted to match. The
regenerated picker snapshots show two cosmetic changes: the match
counter no longer overlaps the preview tab header, and the HEAD column
shows the full short-SHA instead of a truncated one.

## Minimal-versions CI

The skim 4.8 dep tree pins tighter floors than our manifests declared,
so the nightly `minimal-versions` job needed updating. It now runs `-Z
direct-minimal-versions` — minimize only our own direct deps and let
transitive crates resolve normally — and the manifests raise each
under-specified floor to the minimum the workspace builds against
(largely mirroring skim 4.8's own requirements). Full `-Z
minimal-versions` would instead drag in skim's transitive TUI/image
stack (ansi-to-tui, ratatui's `instability` macro, color-eyre,
ratatui-image → image/avif), whose crates under-declare their floors and
don't compile at the picked versions; direct minimization confines the
check to floors we own, so no transitive pins are needed. Normal
resolution (the committed `Cargo.lock`) is unchanged. Full floor list
and the `signal-hook` libc-pin detail are in the `ci(min-versions)`
commit message.

## Reviewing

Start at `src/commands/picker/mod.rs` (the `run_skim` entry point, the
action keybinds, and `parse_reload_remove_token`), then
`progressive_handler.rs` (the render pokes) and `items.rs` (the
`Line`-based `display()`). Test-harness changes are in
`tests/common/pty.rs` (the `ESC[6n` responder) and
`tests/integration_tests/switch_picker.rs` (the panel-split columns).
The rest is the dependency swap, the vendor and build-machinery
deletions, and regenerated snapshots.

All 3980 tests pass (`cargo run -- hook pre-merge --yes`), including the
`shell-integration-tests` PTY suite that exercises the picker end-to-end
across the list, previews, scroll, create/remove, and accept flows. No
automated test drives a real terminal; the interactive surface was also
checked by hand against the 0.20 picker.

> _This was written by Claude Code on behalf of max_

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 03:41:20 -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
Worktrunk Bot f877aeade2 fix(nix): include gemini-extension.json in flake source filter (#2834) 2026-05-20 07:03:44 -07:00
Maximilian Roos 1fe346be42 ci(nightly): re-enable nix-flake job & fix sandbox failures (#2648)
## Summary

Restore the `nix-flake` nightly job (disabled in #2647) and fix the
sandbox-specific test failures it surfaces. With this merged, the
nightly cron run is back to gating against packaging-environment bugs.

## What's in here

- **Build-mode snapshot redaction**: 49 snapshots hardcoded
  `target/debug/wt` in "Invoked as:" and diagnostic output, which broke
  under crane's release builds. Collapse both modes to
  `target/[BUILD_MODE]/wt` in the shared insta filter.
- **Nix sandbox env**: add `pkgs.python3` and `pkgs.procps` to
  `worktrunk-tests`' native build inputs (needed by argv-quoting,
  post-start, and pgid-invariant tests). Replace
  `#!/usr/bin/env python3` with the absolute python path resolved at
  script-write time, since `/usr/bin/env` doesn't exist on NixOS.
- **PTY filter ordering**: extend `add_pty_binary_path_filters`'
  alternation to match the `[BUILD_MODE]` placeholder so PTY snapshots
  still collapse to `[BIN]` after the prelude rewrite runs first
  (caught by worktrunk-bot review).
- **One inherited-CWD test fix**: `test_config_init_already_exists`
  branched on whether the inherited CWD had a project config — pin
  it to a no-config tempdir so the snapshot is deterministic across
  cargo and nix.

## Test plan

- [x] `nix-flake` job passes in CI (1633 / 1633, was 54 failing)
- [x] All required `ci` checks pass
- [x] Auto-reviewer approved

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-08 16:22:53 -07:00
Maximilian Roos 793307aa3d ci(nightly): add nix flake check job (#2630)
## Summary

- Adds a `nix-flake` job to `.github/workflows/nightly.yaml` that runs
`nix flake check`.
- Adds a `worktrunk-tests` flake check that runs `cargo test` (default
features) against `pkgs.lib.cleanSource ./.`, which keeps `tests/`
fixtures and `src/` `.snap` files visible to the test build. The
production package src filter stays narrow.
- Wires the new job into the existing `create-issue-on-nightly-failure`
flow so failures open the standard nightly-failure issue.

## Why

Surfaces packaging-environment bugs before nixpkgs maintainers hit them.
The canonical example is #2624 — a unit test that depends on the process
CWD being inside a git repo, which fails in the nix build sandbox where
source is extracted from a tarball without `.git`.

`shell-integration-tests` is intentionally not enabled — it requires
zsh/fish/nushell + PTY, and nextest's InputHandler conflicts with
interactive shells in CI sandboxes (see CLAUDE.md → "Shell/PTY
Integration Tests"). Worth a follow-up if/when the basic check is
stable.

Cold runs are slow (~10–15 min build + test). Nightly cadence absorbs
the latency. If we want to cut that we can add a binary cache (cachix or
FlakeHub) in a follow-up.

## Test plan

- [ ] First scheduled run (or `workflow_dispatch`) succeeds.
- [ ] If `worktrunk-tests` fails, the failure surfaces against the
existing nightly-failure issue.
- [ ] Locally: `nix flake check` passes on a machine with nix installed
(not validated — no nix on this dev host).

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-06 22:07:42 -07:00
Worktrunk Bot 1cff0968bd chore(nix): read Rust channel from rust-toolchain.toml (#2188)
## Summary

Implements the first two proposals from [#2185
(comment)](https://github.com/max-sixty/worktrunk/pull/2185#issuecomment-4234599092):

- **`flake.nix` reads the Rust channel from `rust-toolchain.toml`** —
one source of truth for rustup and Nix. The weekly MSRV bump now
propagates to the Nix toolchain automatically, turning silent drift into
a loud "channel not in locked rust-overlay" error if the lock also falls
behind.
- **Weekly maintenance playbook now runs `nix flake update`** — covers
the residual case where a newly pinned channel is newer than the locked
`rust-overlay` revision. The `flake.nix` row in the files table is gone
since there is nothing to edit there anymore.

Extensions (`rust-src`, `rust-analyzer`) stay in `flake.nix` rather than
moving into `rust-toolchain.toml` — putting them in the toolchain file
would force rustup users and CI to download them too, and they are only
useful inside the Nix dev shell.

Skipped proposal #3 (adding a `nix build` CI job) — that's a bigger
change and worth a separate discussion.

## Test plan

- [ ] `nix build .#worktrunk` succeeds (could not verify in CI runner —
no `nix` installed)
- [ ] `nix develop -c cargo build` succeeds
- [ ] `nix flake check` passes

Co-authored-by: worktrunk-bot <worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 23:22:35 -07:00
Nick Dichev b1cea9cae5 fix: use a crane dummy source for the vendored patch to skim (#2265) 2026-04-16 18:36:24 -07:00
Worktrunk Bot 2c602bbfd6 chore: bump MSRV from 1.89 to 1.93 (#2125) 2026-04-12 06:25:48 -07:00
Marius Kimmina c80c8ba874 fix: allow dev directory in flake.nix (#1924)
Fixes #1923

Signed-off-by: Marius Kimmina <marius.kimmina@proton.me>
2026-04-06 11:25:49 -07:00
onelock fe395cb467 fix(nix): evaluation warning system renamed to stdenv.hostPlatform.system (#1336)
I am using the home-manager module and this warning is a bit annoying.
 
```bash
evaluation warning: 'system' has been renamed to/replaced by 'stdenv.hostPlatform.system'
```

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 14:29:36 -08:00
jan Lemata 966651385a feat: add Nix Home Manager module (#1287) 2026-03-05 11:47:27 -08:00
Maximilian Roos ec64dbebce fix: use WT_TEST_EPOCH instead of SOURCE_DATE_EPOCH for test time control (#767)
* fix: use WT_TEST_EPOCH instead of SOURCE_DATE_EPOCH for test time control

SOURCE_DATE_EPOCH is a reproducible builds standard commonly set by NixOS/direnv
in development shells. When set to a past timestamp (typical for builds), the
Age column in `wt list` incorrectly shows "future" for all commits.

Switch to WT_TEST_EPOCH which is only set by our test harness, leaving
production users unaffected.

Fixes #763

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: restore PTY test snapshots with WT_TEST_EPOCH

The previous commit deleted PTY-based test snapshots that can only be
regenerated in CI (they require Linux/macOS PTY support). This commit
restores those 46 snapshots from main and updates them to use
WT_TEST_EPOCH instead of SOURCE_DATE_EPOCH.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: wrap bare URL in angle brackets for rustdoc

RUSTDOCFLAGS='-Dwarnings' fails on bare URLs in doc comments. Wrap the
GitHub issue link in angle brackets to make it a proper hyperlink.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-20 11:38:22 -08:00
Mattias Sjödin ee29d4a93d fix: remove apple_sdk framework (#525) 2026-01-11 07:23:26 -08:00
marktoda bee58c6681 feat: add Nix flake for packaging (#502)
* feat: add Nix flake for packaging

Use crane for efficient Rust builds with dependency caching.
Provides packages, dev shell, and CI checks (clippy, fmt, tests).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: skip tests in flake build

* fix: anchor Nix result patterns to repo root

Use `/result` instead of `result` to avoid accidentally ignoring
legitimate `result` files/directories in subdirectories.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Maximilian Roos <m@maxroos.com>
2026-01-09 22:51:30 +00:00