Commit Graph

9 Commits

Author SHA1 Message Date
Caleb Cox aa9d8c43df feat: add remote_repo variable (#3745)
Add a `remote_repo` variable that returns the repo name from the remote
URL. Unlike `repo`, it stays consistent even if the clone was renamed.

Feel free to reject, or suggest other names for the variable. But this
change would improve my workflow. I hope you don't mind my submitting a
PR before opening an issue. Thanks for an amazing developer tool!

AI Disclosure 🤖: I used Claude Code to generate the changes, but
reviewed every line and made adjustments.

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-14 00:48:54 -07:00
Maximilian Roos c9cfc1ef16 feat(eval): add --format=json for structured template output (#3106)
Adds `--format=json` to `wt step eval`, the structured counterpart to its `-v` view.

`wt step eval` printed only the bare rendered result to stdout; the template's name, source, and rendered result were reachable solely from the `-v` human lane on stderr, with no machine-readable form. `--format=json` emits `{name, template, result}` to stdout — the structured analog of the `-v` expansion view, and consistent with `wt hook show --format=json` and the other `wt step` JSON lanes.

Text mode is unchanged (bare result to stdout). The two lanes compose: `--format=json -v` keeps JSON on stdout and the human expansion view on stderr, so `wt step eval --format=json -v … 2>/dev/null` yields clean JSON.

Implementation mirrors the sibling `wt step` commands: a `SwitchFormat` flag under the `Automation` heading, an inline `serde_json::json!` payload, and snapshot tests covering the JSON lane and the JSON + `-v` composition.

> _This was written by Claude Code on behalf of max_
2026-06-17 10:42:24 -07:00
Maximilian Roos a01a9020ca refactor(eval): inspect template variables via -v, not --dry-run (#3078)
`--dry-run` previews a mutation a command would perform. `wt step eval`
mutates nothing — it expands a template and prints the result — so its
`--dry-run` was a category error: it dumped the full variable context as
raw `key=value` text, and was the only `--dry-run` in the CLI not using
the gutter house style.

This moves variable discovery to the verbose lane. `wt step eval -v` now
lists the available template variables on stderr in the gutter style,
above the `{{ template }} → result` expansion view that `-v` already
rendered:

```console
$ wt step eval -v '{{ branch }}'
○ Available template variables
  branch        = feature/auth-oauth2
  worktree_path = /home/user/projects/myapp-feature-auth-oauth2
  …
○ Expanding eval
  {{ branch }}
  →
  feature/auth-oauth2
feature/auth-oauth2
```

The result still goes to stdout, so `$(wt step eval …)` is unchanged.
`eval` is experimental, so `--dry-run` is removed outright rather than
deprecated — it now errors loudly instead of breaking silently.

The convention is documented in `src/commands/CLAUDE.md`: `--dry-run`
previews a mutation, so a command that changes nothing carries none, and
inspection belongs to `-v`. The other nine `--dry-run` commands already
conform (`config alias dry-run` and `hook --dry-run` use `info_message`
+ the gutter), so eval was the only outlier.

> _This was written by Claude Code on behalf of max_

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 13:44:23 -07:00
Maximilian Roos ee242f3090 refactor(git): close remaining cache-staleness vectors after RefSnapshot cutover (#2530)
## Summary

Two follow-ups deferred from #2528 (the `RefSnapshot` cutover that
closed the ambient ref-keyed cache staleness class), plus reviewer
cleanups.

- **Thread `RefSnapshot` through `prepare_worktree_removal`** — adds
`snapshot: Option<&RefSnapshot>`; `step_prune` and
`validate_remove_targets` capture once and reuse across candidates
instead of re-running `for-each-ref` per candidate (N+1 → 1).
- **Drop `head_shas` cache** — the per-worktree HEAD SHA cache had no
invalidation. Any flow that moved HEAD mid-command (rebase, hook-emitted
commits) left a stale value that surfaced in `{{ commit }}` template
variables for later hooks. `WorkingTree::head_sha` now always reads
fresh; new `head_sha_tracks_head_movement` test guards against
re-introducing a cache.
- **Reviewer cleanups** — refreshed four stale comments referring to the
deleted cache, collapsed a now-redundant short-circuit in
`command_executor.rs::build_hook_context`, and replaced
`Arc::new(snapshot.clone())` in `step_prune`'s rayon worker with
`Arc::clone` (shares by refcount, no deep copy).

## Test plan

- [x] `cargo run -- hook pre-merge --yes` — full test + lint suite (3425
tests pass; clippy / doc / doctest with `-Dwarnings` clean)
- [x] New unit test `head_sha_tracks_head_movement` exercises the
freshness contract directly
- [x] Manual review of stale-comment fixes vs current behavior

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-02 11:46:55 -07:00
Maximilian Roos e8a5ab2ab8 perf(alias): reuse cached HEAD SHA on on-branch dispatch hot path (#2374)
Follow-up to #2367. That PR folded the detached-HEAD case of
`build_hook_context` into the `prewarm_info` batch; the on-branch case
still fired a standalone `git rev-parse <branch>` on every alias / hook
dispatch.

When `ctx.branch` matches the running worktree's current branch, the
SHAs resolved by `rev-parse <branch>` and `rev-parse HEAD` are identical
— and `prewarm_info` has already cached the latter. Compare `ctx.branch`
against `current_worktree().branch()` and read the cached `head_sha`
when they match. Cross-worktree contexts (e.g. `wt step for-each`
iterating over sibling worktrees, the switch pre/post-hook path) fall
through to the existing `rev-parse <branch>`.

## Measurements

`wt --yes <alias>` on-branch: 5 → 4 git subprocesses; the standalone
`rev-parse <branch>` fork is gone. Detached HEAD unchanged — still
handled by #2367's batch.

## Tests

- `test_eval_commit_matches_head_sha` — on-branch cache-hit path via `wt
step eval`.
- `test_for_each_commit_matches_per_worktree_head` — cross-worktree
fallback; seeds each fixture feature worktree with a distinct HEAD so a
buggy reuse (same SHA everywhere) fails visibly.

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

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-21 19:26:00 -07:00
Gregg Donovan 3e8799ae27 Add {{ owner }} template variable for worktree paths (#2051) 2026-04-10 13:28:18 +00:00
Maximilian Roos 5913e3d56b feat: Rust-orchestrated pipeline execution with stdin spec passing (#1893)
Background pipelines no longer compile to compound shell strings with
`__WT_TPL_*` env vars and `eval "$(wt step eval --shell-escape ...)"`
wrapping. Instead, the parent `wt` process serializes a `PipelineSpec`
to JSON and spawns `wt hook run-pipeline` as a detached background
process, piping the spec to stdin. The background runner expands
templates just-in-time and spawns shell children per step.

Key changes:

- **New `spawn_detached_exec`** in `process.rs` — spawns a binary
directly (no intermediate shell), pipes data to stdin, with proper
detachment on both Unix (`process_group(0)`) and Windows
(`CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS`). Shared log setup
extracted into `create_detach_log` helper.
- **New `pipeline_spec.rs`** — serde types for the JSON spec
(`PipelineSpec`, `PipelineStepSpec`, `PipelineCommandSpec`) with
roundtrip test.
- **New `run_pipeline.rs`** — the background orchestrator. Reads spec
from stdin, walks steps in order (serial abort-on-failure, concurrent
spawn-then-wait-for-all), expands templates with `shell_escape=true`,
pipes context JSON to each shell child's stdin. Module docstring
specifies the full execution model.
- **`wt hook run-pipeline`** — hidden subcommand (not in `--help` or
autocomplete) replacing the old top-level `_run-pipeline`.
- **Deleted**: `build_pipeline_command`, `format_cmd`, `--shell-escape`
flag, `__WT_TPL_N` env vars, `extra_env` parameter on `spawn_detached`,
7 unit tests for the old shell builder, 2 integration tests + snapshots
for `--shell-escape`.
- **Updated docs** — "How it works" sections in CLI help,
`docs/content/hook.md`, and skill reference no longer show the compound
shell command example. Test comments updated to describe the new
execution model.
- **New tests** — concurrent group execution (both commands run),
concurrent partial failure (sibling completes, later steps abort), and
shell escaping of metacharacters (spaces, quotes, `$`).

> _This was written by Claude Code on behalf of maximilian_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-02 19:04:37 -07:00
Maximilian Roos 79fa6a8b73 feat: lazy template expansion for pipeline vars (#1840)
Pipeline steps referencing `{{ vars.* }}` are expanded at execution time
rather than upfront, so vars set by step N are available to step N+1.
This enables DRY patterns like deriving a container name once and
reusing it across pipeline steps and across hooks
(post-start/post-remove).

A pipeline that sets vars in step 1 and uses them in step 2:

```toml
post-start = [
  "wt config state vars set container='{{ repo }}-{{ branch | sanitize }}-postgres'",
  { db = "docker run --name {{ vars.container }} ..." },
]

[post-remove]
db-stop = "docker stop {{ vars.container }} 2>/dev/null || true"
```

**Background pipelines** wrap lazy steps in `eval "$(wt step eval
--shell-escape "$__WT_TPL_N")"` with templates passed as env vars on the
spawned process. **Foreground mode** (`--foreground`) re-expands
templates in-process for structured error reporting.

Detection uses `minijinja::undeclared_variables` (via new shared
`template_references_var()` helper) — no string heuristics. Syntax
errors are caught at prepare time; only var resolution is deferred.
`--shell-escape` on `wt step eval` is hidden from `--help` (internal
mechanism).

Updates database examples in hook docs and tips-patterns to use the
pipeline pattern.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-31 01:13:15 -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