Skip to content
This repository was archived by the owner on Jul 17, 2026. It is now read-only.

Commit bd4751f

Browse files
tschmclaude
andcommitted
docs(adr): add ADR-0006 — rhiza-cli as the Claude Code plugin home
Proposes relocating the Claude Code plugin (currently in rhiza-config) into rhiza-cli so engine logic lives once and slash commands wrap the co-located CLI via `uvx --from "${CLAUDE_PLUGIN_ROOT}" rhiza …`. Status: Proposed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 7d3ab7b commit bd4751f

2 files changed

Lines changed: 121 additions & 0 deletions

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# ADR-0006: Make `rhiza-cli` the home of the Claude Code plugin
2+
3+
**Status:** Proposed
4+
5+
## Context
6+
7+
Rhiza currently ships across two repositories:
8+
9+
- **`rhiza-cli`** (this repo) — the engine. A Typer application (`rhiza`) with real
10+
domain models (`models/lock.py`, `models/template.py`, the `_git/` merge engine)
11+
and eight commands: `init`, `sync`, `status`, `tree`, `validate`, `list`,
12+
`uninstall`, `summarise`. It is pip/uv-installable (`project.scripts.rhiza`) and
13+
is what `make sync` invokes inside managed repos.
14+
- **`rhiza-config`** — the Claude Code plugin. A `.claude-plugin/plugin.json` plus
15+
`commands/*.md` exposing four slash commands: `/boost`, `/quality`, `/revisit`,
16+
`/stats`.
17+
18+
These two serve different execution contexts (a CLI for humans and CI; slash
19+
commands driven interactively by an agent), but they overlap in substance. The
20+
`/stats` command was recently reworked into a bundled Python script
21+
(`rhiza-config/scripts/stats.py`) so it could run without an agent. That script
22+
re-derives, with ad-hoc regular expressions, information that `rhiza-cli` already
23+
computes properly — its "Rhiza template status" section duplicates what
24+
`rhiza status` and `rhiza tree` produce from `models/lock.py` and
25+
`models/template.py`.
26+
27+
That duplication is the crux. With the plugin in a separate repo, every command
28+
that needs engine logic faces a bad choice: **reimplement it** (drift, as
29+
`stats.py` began to) or **coordinate across two repos** (a version-matching
30+
burden). The plugin has no way to call the engine it conceptually depends on,
31+
because a plugin install and a CLI install are unrelated artifacts.
32+
33+
## Decision
34+
35+
Relocate the Claude Code plugin into `rhiza-cli`, so a single repo has two faces:
36+
the CLI (unchanged) and a Claude Code plugin whose slash commands are thin
37+
wrappers over the co-located engine.
38+
39+
**1. Plugin lives beside the engine.** Add `.claude-plugin/plugin.json` (+ a
40+
marketplace entry, plugin `name: "rhiza"`) and a `commands/` directory to
41+
`rhiza-cli`. The four slash commands move here from `rhiza-config`.
42+
43+
**2. Slash commands invoke the bundled CLI.** A plugin install copies the repo
44+
into `~/.claude/plugins/cache/…` but does **not** put `rhiza` on `PATH`. Because
45+
the plugin root then contains `pyproject.toml` + `src/`, commands run the engine
46+
from its own bundled source:
47+
48+
```bash
49+
uvx --from "${CLAUDE_PLUGIN_ROOT}" rhiza status
50+
```
51+
52+
This needs no separate `pip install` and **guarantees the slash commands and the
53+
CLI are the same version**. (Requiring `uv tool install rhiza` on `PATH` was
54+
considered and rejected as the default — see Alternatives.)
55+
56+
**3. Deterministic commands become CLI subcommands; judgement commands stay
57+
Markdown.** The governing rule:
58+
59+
| Slash command | Disposition |
60+
|---|---|
61+
| `/stats` | Fold into a new `rhiza stats` subcommand that reuses `models/lock.py`/`template.py` (not regex) and deduplicates against `status`/`tree`/`summarise`; the `.md` becomes a thin `uvx --from … rhiza stats` wrapper. |
62+
| `/boost` | Stays an agent-orchestrated `.md` (conflict resolution, quality scorecard, issue-dedup are genuine LLM work) but calls `rhiza sync`/`rhiza status` for its mechanical steps. |
63+
| `/quality` | Stays `.md` (code-quality judgement). |
64+
| `/revisit` | Stays `.md`; may lean on `rhiza summarise`. |
65+
66+
That is: deterministic/report work belongs in the engine wrapped by a thin `.md`;
67+
judgement work stays in `.md` but delegates its mechanical sub-steps to the engine.
68+
69+
**4. One version, one release.** The plugin `version` tracks the `rhiza` package
70+
version, enforced by a parity check (mirroring `rhiza-config`'s
71+
`check_version_parity.py`). `rhiza-cli`'s existing release machinery (`cliff`,
72+
`rhiza_release.yml`, bump scripts) becomes the single release path.
73+
74+
**5. `rhiza-config` is retired.** It becomes either a marketplace pointer at the
75+
`rhiza-cli` plugin or a deprecated repo with a redirect. Because the plugin
76+
`name` is `"rhiza"` in both, existing installs need a documented transition.
77+
78+
### Phased rollout (each phase non-breaking on its own)
79+
80+
1. **Skeleton (additive):** add `.claude-plugin/` + `commands/` to `rhiza-cli`,
81+
copying the four `.md` files; add manifest-validation + version-parity to CI.
82+
2. **Wire to the engine:** rewrite command bodies to `uvx --from
83+
"${CLAUDE_PLUGIN_ROOT}" rhiza …`; add `rhiza stats`; retire the duplicated
84+
`stats.py` logic.
85+
3. **Cut over:** point `rhiza-config`'s marketplace at this plugin (or deprecate
86+
it); announce the transition.
87+
4. **Cleanup:** single version/release story; delete the `rhiza-config` scripts.
88+
89+
## Consequences
90+
91+
-**Single source of truth.** Engine logic lives once; slash commands wrap it
92+
instead of reimplementing it. The `stats.py` drift risk is removed, not grown.
93+
-**`rhiza stats` gets *better*, not just relocated** — it uses the real lock
94+
and template models rather than the regex parsing the standalone script used.
95+
-**No version skew.** `uvx --from "${CLAUDE_PLUGIN_ROOT}"` runs the plugin's
96+
own copy of the CLI; slash commands and CLI cannot disagree.
97+
-**One release process** for both faces.
98+
- ⚠️ **Marketplace continuity.** The plugin `name` is shared across both repos;
99+
users who installed from `rhiza-config` need a migration path.
100+
- ⚠️ **`uvx --from` cold start.** The first invocation builds an ephemeral
101+
environment (seconds). Acceptable for interactive use; `uv tool install rhiza`
102+
can be documented as a faster opt-in.
103+
- ⚠️ **Dual audience.** `rhiza-cli`'s README/issues now serve both CLI users and
104+
plugin users; docs must address both.
105+
- ⚠️ **Two unrelated "plugin" systems coexist.** `rhiza-cli`'s `rhiza.plugins`
106+
entry-point mechanism (CLI subcommand plugins, e.g. `rhiza-tools`) is unrelated
107+
to the Claude Code plugin (just files under `.claude-plugin/` + `commands/`);
108+
documentation must keep the two from being conflated.
109+
110+
## Alternatives considered
111+
112+
- **Keep the repos separate and reimplement engine logic in `rhiza-config`
113+
scripts.** Rejected — this is the drift `stats.py` already started; it scales
114+
badly as more commands need engine data.
115+
- **Port the CLI commands *into* `rhiza-config`.** Rejected — it inverts
116+
ownership: the substantive Python (models, merge engine) already lives in
117+
`rhiza-cli`, and moving it toward the presentation layer is backwards.
118+
- **Require `uv tool install rhiza` on `PATH` instead of `uvx --from`.** Rejected
119+
as the default — it adds a setup step and reintroduces version skew between an
120+
installed CLI and the plugin. Fine as a documented fast-path opt-in.

docs/adr/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Each ADR follows this structure:
2323
| [ADR-0003](0003-lock-file-concurrency.md) | Concurrency-safe lock file I/O with fcntl and atomic rename | Accepted |
2424
| [ADR-0004](0004-keep-git-utils-as-single-module.md) | Keep `models/_git_utils.py` as a single module | Superseded by ADR-0005 |
2525
| [ADR-0005](0005-split-git-engine-into-subpackage.md) | Split the git engine into a `models/_git/` subpackage | Accepted |
26+
| [ADR-0006](0006-rhiza-cli-as-claude-plugin.md) | Make `rhiza-cli` the home of the Claude Code plugin | Proposed |
2627

2728
## Creating a New ADR
2829

0 commit comments

Comments
 (0)