Commit Graph

8 Commits

Author SHA1 Message Date
Maximilian Roos 4bacab23af refactor(config): flatten section fields from Option<T> to T (#2104)
Each per-section config field on `UserConfig` and `UserProjectOverrides`
(`ListConfig`, `CommitConfig`, `MergeConfig`, `SwitchConfig`,
`StepConfig`, `aliases`) was wrapped in `Option<>` despite each section
type having all-`Option<T>` fields internally — making
`Some(MergeConfig::default())` semantically equivalent to `None`.
`ResolvedConfig::for_project` was already calling `.unwrap_or_default()`
on every accessor result.

Replace `Option<Type>` with `Type` using `#[serde(default,
skip_serializing_if = "is_default")]` (sections) or `BTreeMap::is_empty`
(aliases), where `is_default` compares against `T::default()`. This
removes redundant Option indirection across the accessor, persistence,
mutation, and resolved config layers. Net -53 lines.

Continues the config cleanup from #2093.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-11 17:48:31 -07:00
Maximilian Roos 81182a687a refactor: remove config crate, inline OverridableConfig (#2087)
Removes the `config` crate dependency entirely and replaces it with
direct TOML parsing + a manual env-var overlay. This eliminates the root
causes of three problems we were working around: no source tracking
(can't tell if error came from file or env var), `#[serde(flatten)]`
destroying error locations, and a single bad override killing the entire
deserialize.

**Step 1 — Inline OverridableConfig.** The `#[serde(flatten)]` on
`configs: OverridableConfig` in `UserConfig` (and `overrides:
OverridableConfig` in `UserProjectOverrides`) caused toml to lose field
paths on errors. Inlining the fields directly means
`toml::from_str::<UserConfig>` now gives correct line/col for all
section field errors. ~31 accessor sites simplified from
`.configs.field` to `.field`. `OverridableConfig` stays as a merge-only
type for per-project resolution.

**Step 2 — Replace ConfigError.** Every usage was
`ConfigError::Message(String)`. Replaced with a simple
`ConfigError(String)` newtype.

**Step 3 — Replace config crate.** New load flow: parse each file with
`toml::from_str` for validation, parse as `toml::Table` for merging,
build a `toml::Table` from `WORKTRUNK_*` env vars with type coercion
(bool → i64 → f64 → string), deep-merge all tables, deserialize the
result. Env-var convention unchanged (`WORKTRUNK__LIST__TIMEOUT_MS` →
`list.timeout-ms`).

Deletes `collect_worktrunk_override_vars` heuristic (exact var tracking
replaces hardcoded exclusion list), the two-pass pre-validation
workaround, and the `config` dependency from Cargo.toml.

> _This was written by Claude Code on behalf of Maximilian Roos_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-11 14:51:37 -07:00
Maximilian Roos a569ddbfce refactor: consolidate config deprecation from two layers to one (#1879)
Previously config deprecation required two synchronized layers: Layer 1
scanned raw TOML to generate `.new` migration files, while Layer 2
(`normalize_deprecated_sections()`) moved deprecated struct fields to
canonical locations after serde parsing. Every new deprecation required
changes in both layers.

Now `migrate_content()` transforms raw TOML before serde parses it, so
deprecated patterns are rewritten into canonical form at the single
boundary between file I/O and deserialization. The struct no longer
needs deprecated fields — serde only sees canonical TOML.

Key changes:
- `migrate_content()` is the single entry point for all structural TOML
migrations, called by all load paths (`load()`, `load_from_str()`,
`mutation.rs` reload)
- `check_and_migrate()` returns the migrated content alongside
deprecation info, so the user config load path doesn't re-parse the TOML
- `no-ff` → `ff` and `no-cd` → `cd` migrations added at the TOML level
(previously only handled post-deserialization)
- `[select]` migration extended to project-level sections (was top-level
only)
- Removed: `normalize_deprecated_sections()`, `SelectConfig`,
`commit_generation` fields, `no_ff_deprecated`, `no_cd_deprecated`
- `replace_deprecated_vars` intentionally excluded from
`migrate_content()` — template variable renaming is cosmetic and would
break `--var` overrides using old names
- CLAUDE.md deprecation recipe updated to document the single-layer
approach

> _This was written by Claude Code on behalf of Maximilian Roos_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-01 16:12:25 -07:00
Maximilian Roos 79fe892bfa feat: add per-project hooks with append semantics (#842)
* feat: add per-project hooks with append semantics

Per-project hooks in user config now append to global hooks rather than
replacing them. Execution order: global → per-project → project.

Also:
- Move hooks into OverridableConfig for consistency
- Rename UserConfig.overrides to UserConfig.configs
- Add schemars for deriving valid config keys from JsonSchema
- Replace manual VALID_USER_CONFIG_KEYS with schema-derived function

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

* refactor: unify config validation on JsonSchema

Both UserConfig and ProjectConfig now use JsonSchema-derived keys for
validation instead of serde HashMap catchall. This:

- Removes `unknown` field from ProjectConfig
- Removes `unknown()` method from WorktrunkConfig trait
- Simplifies `is_valid_key` to not need value parameter
- Adds JsonSchema to JSON output types for future schema generation

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

* docs: document per-project hooks in setting overrides section

- Add per-project hooks to Setting overrides (Experimental) section
- Update hook.md table to show execution order with per-project hooks
- Replace JSON context example with one showing complex conditionals
- Keep documentation DRY by linking from hook.md to config.md

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

* docs: use consistent terminology (user config, not settings)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-24 18:32:17 -08:00
Maximilian Roos 5617986528 refactor: unify overridable config into shared OverridableConfig struct (#829) 2026-01-24 00:21:10 -08:00
Maximilian Roos 0f46a33194 refactor: rename WorktrunkConfig to UserConfig (#746)
Clarifies that this struct represents user-level configuration
(~/.config/worktrunk/config.toml), distinct from ProjectConfig
which is checked into the repository.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-19 16:43:49 -08:00
Maximilian Roos 928b3017cd test: remove verbose assertion messages and doc comments (#458)
* Simplify assert messages by removing redundant descriptions

Remove verbose assertion messages that repeat the condition being tested.
Messages like "Should contain indicator" when asserting `contains("●")` are
redundant since the code is self-documenting. This reduces noise in tests
while maintaining clarity through meaningful assertion failures.

* test: remove verbose assertion messages and doc comments

Removed facile test verbosity across three categories:

1. **Doc comments** (~236 lines): Removed `/// Test X` comments that just
   restated test function names. Kept multi-line explanations that add context.

2. **Test names** (9 functions): Shortened overly verbose names by removing
   filler words while maintaining clarity:
   - test_merge_when_primary_not_on_default_but_default_has_worktree →
     test_merge_primary_not_on_default_with_default_worktree
   - test_complete_switch_option_prefix_shows_options_not_branches →
     test_complete_switch_option_prefix_shows_options

3. **Assertion messages** (19 instances): Removed messages that just restate
   the check:
   - assert!(path.exists(), "Path should exist") → assert!(path.exists())
   - assert!(x.is_file(), "Should be a file") → assert!(x.is_file())

Kept messages that add semantic meaning (e.g., "hook should have run") or
include debugging variables.

Total: 279 lines removed across 31 files. All 723 integration + 401 unit tests
passing.

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

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

* test: remove unreferenced snapshot files from renamed tests

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

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

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 16:51:44 -08:00
Maximilian Roos ba012899d9 Add [select] pager config with backward-compatible auto-detection (#449)
* feat: add [select] pager config with backward-compatible auto-detection

Adds a `[select] pager` config option to override git's pager specifically
for `wt select` diff previews. When not configured, maintains existing
behavior by auto-detecting delta/bat/batcat and appending --paging=never
to prevent hangs in non-TTY contexts.

Users can now set explicit pager commands:
```toml
[select]
pager = "delta --paging=never"
```

The config option takes precedence over auto-detection, giving users full
control while preserving backward compatibility for existing setups.

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

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

* test: add coverage for has_explicit_pager_config

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

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

* test: add config deserialization tests for [select] pager

Tests verify that the SelectConfig struct deserializes correctly from TOML,
including edge cases (empty string, optional section).

Note: The actual config-loading code path in get_diff_pager() (lines 38-43)
remains uncovered because it only executes during wt select's TUI preview
generation, which requires complex PTY test setup.

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

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 13:03:38 -08:00