You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Equivalent invocations of the same underlying program each need their own allowlist entry today, and maintaining them by hand drifts — exactly what bit us with the rtk-rewrite mirrors (#26, #32):
gh pr list vs rtk gh pr list
git log vs rtk git log vs chezmoi git log vs git -C * log
cargo tree vs rtk cargo tree
Desired behavior
Write a read/deny verb once per program and have every equivalent prefix form emitted automatically. Adding gh pr list should also yield rtk gh pr list; adding chezmoi git log should also yield bare git log, rtk git log, git -C * log; cargo tree ↔ rtk cargo tree.
Model: prefix-equivalence groups
Define groups keyed by the underlying program:
git group:git, git -C *, rtk git, chezmoi git
gh group:gh, rtk gh
cargo group:cargo, rtk cargo
generally, every rtk-wrapped program E → { E, rtk E }, plus extra aliases where they exist (git's -C form and chezmoi passthrough).
A build step fans each verb across its group's prefixes. Deny verbs fan out identically (e.g. branch -d/-D/--delete across the whole git group — today only git/rtk git are mirrored).
Canonical-source vs any-form (the bidirectionality ask)
Option 1 — canonical source (recommended): the source list is written in one canonical form (bare verb per group): git: [log, diff, …], gh: [pr list, …]. Prefixes come from the group definition; you never hand-write mixed forms.
Option 2 — any-form input (the literal ask): accept an entry in any prefix, normalize by stripping a known prefix to recover program+verb, then expand to the whole group. Nicer ergonomics, more logic (prefix detection/normalization). Suggest shipping Option 1 first; Option 2 is a thin layer on top later.
(b) Data + run_onchange_ script (recommended) — data in .chezmoidata (or a standalone YAML/TOML); a run_onchange_ jq/python script reads it, computes the cross-product, and writes the permissions.allow/permissions.deny arrays. Real data structures + jq make expansion and validation robust; reruns on data-hash change.
Either way: a render check that chezmoi cat ~/.claude/settings.json | jq . parses — a template/script bug must not silently break all permissions.
Critical constraint: don't clobber the live rtk hook
rtk hook claude writes its PreToolUse hook into the live~/.claude/settings.json (see the drift note in the chezmoi skill). The generator must own only the permissions.allow / permissions.deny arrays and merge rather than overwrite the file, so it never wipes the live hook block (or theme, enabledPlugins, etc.).
Relationship
This is the general engine. #32 is the first concrete instance (git + rtk-wrapped providers) and would be reimplemented on top of this. #36 (non-rtk tools) is single-prefix (bare-only group) — no fan-out needed, but it can share the same source list.
Acceptance
A single source edit (one verb under one program) produces all equivalent prefix entries on chezmoi apply.
Deny verbs fan out across the full group.
jq parses the rendered settings.json.
The live rtk hook claude block and other live keys survive a re-render.
Problem
Equivalent invocations of the same underlying program each need their own allowlist entry today, and maintaining them by hand drifts — exactly what bit us with the rtk-rewrite mirrors (#26, #32):
gh pr listvsrtk gh pr listgit logvsrtk git logvschezmoi git logvsgit -C * logcargo treevsrtk cargo treeDesired behavior
Write a read/deny verb once per program and have every equivalent prefix form emitted automatically. Adding
gh pr listshould also yieldrtk gh pr list; addingchezmoi git logshould also yield baregit log,rtk git log,git -C * log;cargo tree↔rtk cargo tree.Model: prefix-equivalence groups
Define groups keyed by the underlying program:
git,git -C *,rtk git,chezmoi gitgh,rtk ghcargo,rtk cargoE→{ E, rtk E }, plus extra aliases where they exist (git's-Cform and chezmoi passthrough).A build step fans each verb across its group's prefixes. Deny verbs fan out identically (e.g.
branch -d/-D/--deleteacross the whole git group — today onlygit/rtk gitare mirrored).Canonical-source vs any-form (the bidirectionality ask)
git: [log, diff, …],gh: [pr list, …]. Prefixes come from the group definition; you never hand-write mixed forms.Mechanism (the open question)
[data]/.chezmoidata/*.toml, consumed bysettings.json.tmpl. Native, but Go-template JSON assembly with trailing-comma handling is fiddly (DRY up git permission rules: chezmoi-template one verb list across git / git -C / rtk git / chezmoi git #32 already notes "build the list andtoJsonit").run_onchange_script (recommended) — data in.chezmoidata(or a standalone YAML/TOML); arun_onchange_jq/python script reads it, computes the cross-product, and writes thepermissions.allow/permissions.denyarrays. Real data structures +jqmake expansion and validation robust; reruns on data-hash change.chezmoi cat ~/.claude/settings.json | jq .parses — a template/script bug must not silently break all permissions.Critical constraint: don't clobber the live rtk hook
rtk hook claudewrites itsPreToolUsehook into the live~/.claude/settings.json(see the drift note in thechezmoiskill). The generator must own only thepermissions.allow/permissions.denyarrays and merge rather than overwrite the file, so it never wipes the live hook block (ortheme,enabledPlugins, etc.).Relationship
This is the general engine. #32 is the first concrete instance (git + rtk-wrapped providers) and would be reimplemented on top of this. #36 (non-rtk tools) is single-prefix (
bare-onlygroup) — no fan-out needed, but it can share the same source list.Acceptance
chezmoi apply.jqparses the renderedsettings.json.rtk hook claudeblock and other live keys survive a re-render.