fix(config): restore #20's rig config get|set <dot.path> CLI (dropped by #34's wizard rebase)#40
Conversation
…ed by #34's wizard rebase) PR #34 (the `rig setup` wizard, merged as f054910) rebased over main's PR #20 (`rig config get|set`, 635eaf5) and kept the wizard's schema-key config CLI while deleting #20's dot-path CLI (cmd_config + helpers + parser + tests/test_config_getset.py). Only one `config` command can register, so the user-facing single-key editor regressed from the dot-path design to a schema-key one. This restores #20's dot-path CLI as the user-facing command, coexisting with the wizard. `rig config get|set` (restored, #20's design): - get <dot.path> [--global] [--json]: reads ONE nested key from the single target file (./rig.yaml, or ~/.config/rig/config.yaml with --global) — NOT the cascade. Missing file / absent path exits non-zero; a mapping/list subtree prints as YAML; --json emits the JSON value; diagnostics go to stderr. - set <dot.path> <value> [--global] [--no-apply]: conservative coercion (true/false/int/float/null; leading-zero, 1e3, nan/inf, underscored, Unicode digits stay strings), creates intermediate mappings, two pre-apply gates (schema.validate, then a catalog-backed plan build), rolls the file back on any failure, reconciles via the same engine as `rig apply`; --no-apply writes + prints the plan only. A repo-local set refuses when ./rig.yaml is absent. Setting the removed `scope` key is refused. The dot-path engine (split_path/get_path/set_path/coerce_scalar/read_yaml_file) was never deleted — #34 left it in riglib/config.py — so this restores only the CLI front-end (cmd_config + _cmd_config_get/_cmd_config_set + _config_target/ _read_target_yaml + the argparse wiring) plus tests/test_config_getset.py. The shared helpers (_load_plan/_print_plan/_print_results/_validate_layer_in_isolation/ _fmt_scalar) already existed identically in main. The wizard stays as `rig setup`. Its schema-key engine (owning-layer routing, effective_value, coerce, version-seeding) is internal to the wizard (setup_wizard.py/schema.py) and is not the `config get|set` command. The wizard reads/writes config via its own helpers, so dropping main's schema-based cmd_config breaks nothing in it. Removed the now-orphaned _fmt_config_value (its sole caller was the replaced cmd_config; #20 uses the identical _fmt_scalar). schema.option_for_key is now unreferenced in production but kept (public registry API, still tested, orphaned-by-this-replacement; dead-code rule: investigate, don't delete). Tests: restored tests/test_config_getset.py (59 tests). Removed the schema-key `config get|set` CLI tests from tests/test_setup_wizard.py (their CLI surface is replaced; the schema engine they exercised stays covered by the surviving test_coerce_*/test_effective_value_*/test_writable_layer_* and the wizard- interactive tests). Docs reconciled (README, AGENTS.md, docs/config-schema.md); the wizard's NON_INTERACTIVE_USAGE updated to the dot-path + reconcile framing. Full suite 800 passed / 8 skipped; smoke OK; gitleaks clean. Live-verified: get/--json/subtree/--global/--no-apply, coercion edge cases, no-file + scope guards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 948bc70682
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| else: | ||
| state.write(target) | ||
| plan, _loaded, _env = _load_plan(args.cwd, config=None) | ||
| except Exception as exc: # noqa: BLE001 |
There was a problem hiding this comment.
Preserve structured plan-build errors
When a dot-path edit reaches the second gate but plan.build rejects it with a structured RigError—for example rig config set ci.items.typo.enabled true or a removed mcp.items.review entry—this broad catch swallows UnknownItemError, prints only a generic class/name, and always returns exit 2. rig apply lets the same error reach errors.guard, which renders the what/why/fix block and exits 4, so this new config set path loses the stable CLI semantics and actionable fix text even though it rolls the file back.
Useful? React with 👍 / 👎.
What & why
PR #34 (the
rig setupwizard, merged asf054910) rebased over main's PR #20 (rig config get|set,635eaf5) and kept the wizard's schema-key config CLI while deleting #20's dot-path CLI (cmd_config+ helpers + the argparse parser + the wholetests/test_config_getset.py). Only oneconfigcommand can register, so the user-facing single-key editor silently regressed from the dot-path design to a schema-key one. This restores #20's dot-path CLI as the user-facingrig config get|set, coexisting with the wizard.rig config get|set(restored — #20's design)get <dot.path> [--global] [--json]— reads ONE nested key from the single target file (./rig.yaml, or~/.config/rig/config.yamlwith--global) — not the cascade. Missing file / absent path exits non-zero; a mapping/list subtree prints as YAML;--jsonemits the JSON value; diagnostics go to stderr soget --json | jqkeeps a clean stdout.set <dot.path> <value> [--global] [--no-apply]— conservative coercion (true/false/int/float/null; leading-zero,1e3,nan/inf, underscored, and Unicode digits stay strings; quote-wrap forces a literal string), creates intermediate mappings, guards the write with two pre-apply gates (schema.validate, then a catalog-backed plan build over the cascade), rolls the file back on any failure, and reconciles via the same engine asrig applyon success.--no-applywrites + prints the plan only. A repo-localsetrefuses when./rig.yamlis absent. Setting the removedscopekey is refused.Reconciliation (what was kept / removed, and why)
split_path/get_path/set_path/coerce_scalar/read_yaml_file) was never deleted — feat(setup): rig setup interactive config wizard + rig config get|set #34 left it inriglib/config.pyas an orphaned util — so this PR restores only the CLI front-end (cmd_config,_cmd_config_get,_cmd_config_set,_config_target,_read_target_yaml, the parser) +tests/test_config_getset.py. The shared helpers (_load_plan/_print_plan/_print_results/_validate_layer_in_isolation/_fmt_scalar) already existed identically in main.rig setup. Its schema-key engine (owning-layer routing,effective_value,coerce, version-seeding) is internal to the wizard (setup_wizard.py/schema.py) — it is NOT theconfig get|setcommand. The wizard reads/writes config via its own helpers, so dropping main's schema-basedcmd_configbreaks nothing._fmt_config_value(its sole caller was the replacedcmd_config; feat(cli): addrig config get|set <dot.path>— read/edit one key, then reconcile #20 uses the identical_fmt_scalar).schema.option_for_keyis now unreferenced in production but kept — it is a public registry API, still tested, orphaned-by-this-deliberate-replacement (dead-code rule: investigate, don't delete).config get|setCLI tests fromtests/test_setup_wizard.py(their CLI surface is replaced). The schema engine they exercised stays covered by the survivingtest_coerce_*/test_effective_value_*/test_writable_layer_*and the wizard-interactive tests.NON_INTERACTIVE_USAGEupdated to the dot-path + reconcile framing.Verification
tests/smoke.shOK; gitleaks clean.get/--json/ subtree-as-YAML /--global/--no-apply, the conservative-coercion edge cases (09/1e3/nan/1_000/Unicode-digit all stay strings), and the no-file +scopeguards.review --staged(multi-model) + an in-harness multi-angle review.Deferred follow-ups (pre-existing #20 behavior, not regressions of this restore — see linked issues)
rig config set --globalfrom a repo with no./rig.yamlis not gated by the no-committed-source guard (only the non-global path is), so a--globalset there reconciles built-in default repo actions. This is feat(cli): addrig config get|set <dot.path>— read/edit one key, then reconcile #20's behavior; the docs over-promise the guard for--global.except Exceptioncollapses structuredRigErrorexit codes (e.g. unknown-item4) to a flat2during asetreconcile.These are #20's design choices, deliberately left faithful to the restore rather than redesigned in a restoration PR.
🤖 Generated with Claude Code