Skip to content

feat: add wt config state vars for per-branch custom variables#1006

Draft
max-sixty wants to merge 24 commits intomainfrom
kv-state
Draft

feat: add wt config state vars for per-branch custom variables#1006
max-sixty wants to merge 24 commits intomainfrom
kv-state

Conversation

@max-sixty
Copy link
Copy Markdown
Owner

@max-sixty max-sixty commented Feb 13, 2026

Adds wt config state vars set/get/list/clear commands for storing custom variables per branch in git config (worktrunk.state.{branch}.vars.{key}). Variables are available in all template contexts via {{ vars.key }} syntax (hooks, wt step eval), with JSON dot access ({{ vars.config.port }}) and default filters ({{ vars.env | default('dev') }}).

Renamed from kv to vars — the original name described the implementation (key-value storage) rather than the purpose. vars reads naturally in both CLI (wt config state vars set ticket ENG-1234) and templates ({{ vars.ticket }}).

The database-per-worktree example now uses vars to store the connection string during post-start, replacing the .env.local heredoc pattern. The URL is accessible outside hooks via $(wt config state vars get db-url).

Includes vars data in wt list --format=json output and wt config state get display. Builds on #1004 (wt step eval). Part of #947.

Test plan

  • Unit tests for vars template injection (empty, with data, no branch, JSON dot access, shell escaping)
  • Integration tests for vars CLI commands (set, get, list, clear, clear --all, --branch flag)
  • Integration tests for vars in JSON output (present with data, absent when empty)
  • All 493 unit + 1277 integration tests pass

This was written by Claude Code on behalf of max-sixty

max-sixty and others added 3 commits February 13, 2026 21:18
Adds `wt config state kv set/get/list/clear` commands for storing
arbitrary key-value data per branch in git config. KV data is also
available in templates via `{{ kv.key }}` syntax, working in hooks
and `wt step eval`.

Part of #947

Co-Authored-By: Claude <noreply@anthropic.com>
Adds a `kv` object field to JSON output containing per-branch
key-value data. Absent when no kv data is set for the branch.

Co-Authored-By: Claude <noreply@anthropic.com>
- Skip kv lookup in expand_template when template doesn't reference kv
- Remove empty println!() for missing kv keys (match git config behavior)
- Inline get_kv_entries wrapper (no added value over repo.kv_entries())
- Extract parse_all_kv helper to deduplicate show_json/show_table parsing

Co-Authored-By: Claude <noreply@anthropic.com>
@max-sixty max-sixty changed the base branch from arbitrary-data to main February 14, 2026 05:19
max-sixty and others added 18 commits February 15, 2026 21:59
Co-Authored-By: Claude <noreply@anthropic.com>
`[experimental]` was interpreted as an intra-doc link by rustdoc,
causing `cargo doc -Dwarnings` to fail.

Co-Authored-By: Claude <noreply@anthropic.com>
- Add KV data to comprehensive state get tests (table and JSON)
- Add tests for kv clear nonexistent key, clear --all when empty
- Add tests for kv list/clear with --branch flag
- Escape [experimental] in rustdoc to fix broken intra-doc link

Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts:
#	src/commands/config/state.rs
#	src/commands/mod.rs
#	src/main.rs
Kv data has been accessible in hook templates as {{ kv.<key> }} since
the feature was implemented, but this wasn't documented anywhere. Add
template access documentation to:

- Hook template variables table (cli/mod.rs → hook.md)
- Kv command help text (cli/config.rs → config.md)

Co-Authored-By: Claude <noreply@anthropic.com>
JSON object and array values stored via `wt config state kv set` are
now automatically parsed at template injection time, enabling dot
access like `{{ kv.config.port }}` when the value is
`{"port": 3000}`. Plain strings stay as-is (backwards compatible).

Also updates hook and config docs to document JSON dot access.

Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts:
#	src/main.rs
#	tests/snapshots/integration__integration_tests__help__help_list_long.snap
#	tests/snapshots/integration__integration_tests__help__help_list_narrow_80.snap
Conflicts resolved:
- src/main.rs: took main's extracted handler functions, added kv imports
  and StateCommand::Kv arm to handle_state_command
- src/config/expansion.rs: added hook_type/hook_name to TEMPLATE_VARS
- src/commands/list/json_output.rs: kept both summary and kv fields,
  adapted summary tests to pass Repository parameter
- docs/content/hook.md, skills/worktrunk/reference/hook.md,
  src/cli/mod.rs: merged kv docs with hook_type/hook_name docs and
  heading level changes
- Snapshot tests regenerated for kv field in JSON help output

Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts:
#	src/commands/list/json_output.rs
#	src/config/expansion.rs
Co-Authored-By: Claude <noreply@anthropic.com>
…tives

- handle_kv_get: use config_value() to properly distinguish "key not
  found" from actual git config errors instead of swallowing all errors
- to_json_items: add Repository::all_kv_entries() to batch-fetch all
  kv data in one git call, replacing N per-branch subprocess spawns
- expand_template: tighten kv guard from contains("kv") to
  contains("kv.") to avoid false-positive git spawns from branch names
  or URLs containing "kv" (e.g. "mkv.internal")
- clear_all_kv: propagate errors via Result instead of silently
  ignoring them, matching clear_all_hints pattern

Co-Authored-By: Claude <noreply@anthropic.com>
# Conflicts:
#	docs/content/hook.md
#	skills/worktrunk/reference/hook.md
#	src/cli/mod.rs
#	src/config/expansion.rs
#	src/main.rs
#	tests/snapshots/integration__integration_tests__help__help_list_long.snap
Update test code to use Cmd::run() instead of removed .status()/.output()
methods, accept updated help snapshot with kv field, and sync docs.

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove raw_kv_output/parse_all_kv from state.rs — callers now use
  Repository::all_kv_entries() (single source of truth for kv parsing)
- clear_all_kv uses all_kv_entries() instead of raw output parsing
- from_list_item takes &mut HashMap, uses .remove() instead of .cloned()
- Statusline JSON uses kv_entries(&branch) for single-branch lookup
  instead of all_kv_entries() querying all branches

Co-Authored-By: Claude <noreply@anthropic.com>
The `kv` name described the implementation (key-value storage), not the
purpose. `vars` reads naturally in both the CLI (`wt config state vars set`)
and templates (`{{ vars.ticket }}`), and matches what users are doing:
defining custom variables for their branches.

Renames across CLI, storage, templates, handlers, tests, docs, and snapshots.
Git config key changes from `.kv.` to `.vars.`.

Co-Authored-By: Claude <noreply@anthropic.com>
@max-sixty max-sixty changed the title feat: add wt config state kv for arbitrary per-branch data feat: add wt config state vars for per-branch custom variables Mar 30, 2026
max-sixty and others added 3 commits March 29, 2026 20:08
The database-per-worktree example now stores the connection string in
vars during post-start, making it accessible outside hooks via
$(wt config state vars get db-url). Removes the .env.local heredoc
generation pattern in favor of the simpler vars approach.

Co-Authored-By: Claude <noreply@anthropic.com>
- Inject empty vars map in validate_template so {{ vars.key | default(...) }}
  doesn't error during template validation
- Use best-effort error handling in clear_all_vars (let _ =) consistent
  with marker clearing
- Add $ prompt prefix to all vars console examples for Syntect highlighting
- Add "custom variables" to FAQ git config table

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant