cgcardona / muse public
cli-tiers.md markdown
351 lines 12.0 KB
189a2e45 feat: three-tier CLI architecture — plumbing, core porcelain, semantic … Gabriel Cardona <cgcardona@gmail.com> 1d ago
1 # Muse CLI — Three-Tier Architecture Reference
2
3 Muse CLI is organized into three formally separated tiers. Each tier has a
4 distinct contract, audience, and stability guarantee.
5
6 ```
7 ┌──────────────────────────────────────────────────────────────────┐
8 │ Tier 3 — Semantic Porcelain │
9 │ muse midi … muse code … muse coord … │
10 │ Domain-specific multidimensional commands │
11 ├──────────────────────────────────────────────────────────────────┤
12 │ Tier 2 — Core Porcelain │
13 │ muse init / commit / status / log / diff / show … │
14 │ Human and agent VCS operations (domain-agnostic) │
15 ├──────────────────────────────────────────────────────────────────┤
16 │ Tier 1 — Plumbing │
17 │ muse plumbing hash-object / cat-object / rev-parse … │
18 │ Machine-readable, JSON-outputting, pipeable primitives │
19 └──────────────────────────────────────────────────────────────────┘
20 ```
21
22 ---
23
24 ## Tier 1 — Plumbing
25
26 **Namespace:** `muse plumbing <command>`
27
28 ### Contract
29
30 Every Tier 1 command:
31
32 - **Outputs JSON by default** — machine-stable schema, versioned, agent-parseable.
33 - **Accepts `--format text`** — human-readable fallback where meaningful.
34 - **Never prompts** — strictly non-interactive; safe for agent pipelines.
35 - **Exit codes** — `0` success, `1` user error (bad args, missing ref), `3` internal error.
36 - **Pipeable** — reads from stdin (`unpack-objects`) or writes to stdout (`pack-objects`, `cat-object`).
37 - **Stable API** — output schemas do not break across Muse versions.
38
39 Tier 1 commands are the atoms from which Tier 2 porcelain is composed. They
40 expose the raw engine directly, enabling MuseHub, agent orchestrators, CI
41 pipelines, and shell scripts to interact with the store without going through
42 the higher-level VCS logic.
43
44 ### Commands
45
46 | Command | Description |
47 |---------|-------------|
48 | `muse plumbing hash-object [--write] <file>` | SHA-256 a file; optionally store it in `.muse/objects/` |
49 | `muse plumbing cat-object [--format raw\|info] <object_id>` | Emit raw bytes of a stored blob to stdout |
50 | `muse plumbing rev-parse [--format json\|text] <ref>` | Resolve branch / HEAD / SHA prefix → full commit ID |
51 | `muse plumbing ls-files [--commit <id>] [--format json\|text]` | List all tracked files and their object IDs |
52 | `muse plumbing read-commit <id>` | Emit full commit metadata as JSON |
53 | `muse plumbing read-snapshot <id>` | Emit full snapshot manifest and metadata as JSON |
54 | `muse plumbing commit-tree --snapshot <id> [--parent <id>]… [--message <msg>]` | Create a commit from an explicit snapshot ID |
55 | `muse plumbing update-ref [--delete\|--no-verify] <branch> [<commit_id>]` | Move or delete a branch HEAD |
56 | `muse plumbing commit-graph [--tip <id>] [--stop-at <id>] [--max N]` | Emit the commit DAG as a JSON node list |
57 | `muse plumbing pack-objects [--have <id>]… <want_id>…` | Build a `PackBundle` JSON and write to stdout |
58 | `muse plumbing unpack-objects` | Read `PackBundle` JSON from stdin, write to local store |
59 | `muse plumbing ls-remote [--json] <remote-or-url>` | List remote branch heads without modifying local state |
60
61 ### JSON Output Schemas
62
63 #### `hash-object`
64
65 ```json
66 {
67 "object_id": "<sha256-hex-64>",
68 "stored": false
69 }
70 ```
71
72 #### `cat-object --format info`
73
74 ```json
75 {
76 "object_id": "<sha256-hex-64>",
77 "present": true,
78 "size_bytes": 1234
79 }
80 ```
81
82 With `--format raw` (default): raw bytes written to stdout.
83
84 #### `rev-parse`
85
86 ```json
87 {
88 "ref": "main",
89 "commit_id": "<sha256-hex-64>"
90 }
91 ```
92
93 Error (exit 1):
94 ```json
95 {
96 "ref": "nonexistent",
97 "commit_id": null,
98 "error": "not found"
99 }
100 ```
101
102 #### `ls-files`
103
104 ```json
105 {
106 "commit_id": "<sha256>",
107 "snapshot_id": "<sha256>",
108 "file_count": 3,
109 "files": [
110 {"path": "tracks/drums.mid", "object_id": "<sha256>"},
111 {"path": "tracks/bass.mid", "object_id": "<sha256>"}
112 ]
113 }
114 ```
115
116 #### `read-commit`
117
118 Full `CommitRecord` JSON — see `store.py` for the complete schema.
119 Key fields:
120
121 ```json
122 {
123 "commit_id": "<sha256>",
124 "repo_id": "<uuid>",
125 "branch": "main",
126 "snapshot_id": "<sha256>",
127 "message": "Add verse melody",
128 "committed_at": "2026-03-18T12:00:00+00:00",
129 "parent_commit_id": "<sha256> | null",
130 "parent2_commit_id": null,
131 "author": "gabriel",
132 "agent_id": "",
133 "sem_ver_bump": "none"
134 }
135 ```
136
137 #### `read-snapshot`
138
139 ```json
140 {
141 "snapshot_id": "<sha256>",
142 "created_at": "2026-03-18T12:00:00+00:00",
143 "file_count": 3,
144 "manifest": {
145 "tracks/drums.mid": "<sha256>",
146 "tracks/bass.mid": "<sha256>"
147 }
148 }
149 ```
150
151 #### `commit-tree`
152
153 ```json
154 {"commit_id": "<sha256>"}
155 ```
156
157 #### `update-ref`
158
159 ```json
160 {
161 "branch": "main",
162 "commit_id": "<sha256>",
163 "previous": "<sha256> | null"
164 }
165 ```
166
167 Delete (`--delete`):
168 ```json
169 {"branch": "todelete", "deleted": true}
170 ```
171
172 #### `commit-graph`
173
174 ```json
175 {
176 "tip": "<sha256>",
177 "count": 42,
178 "truncated": false,
179 "commits": [
180 {
181 "commit_id": "<sha256>",
182 "parent_commit_id": "<sha256> | null",
183 "parent2_commit_id": null,
184 "message": "Add verse melody",
185 "branch": "main",
186 "committed_at": "2026-03-18T12:00:00+00:00",
187 "snapshot_id": "<sha256>",
188 "author": "gabriel"
189 }
190 ]
191 }
192 ```
193
194 #### `pack-objects` / `unpack-objects`
195
196 `pack-objects` writes a `PackBundle` JSON to stdout:
197
198 ```json
199 {
200 "commits": [{ ...CommitDict... }],
201 "snapshots": [{ ...SnapshotDict... }],
202 "objects": [{"object_id": "<sha256>", "content_b64": "<base64>"}],
203 "branch_heads": {"main": "<sha256>"}
204 }
205 ```
206
207 `unpack-objects` reads a `PackBundle` from stdin and outputs:
208
209 ```json
210 {
211 "commits_written": 12,
212 "snapshots_written": 12,
213 "objects_written": 47,
214 "objects_skipped": 3
215 }
216 ```
217
218 #### `ls-remote --json`
219
220 ```json
221 {
222 "repo_id": "<uuid>",
223 "domain": "midi",
224 "default_branch": "main",
225 "branches": {
226 "main": "<sha256>",
227 "dev": "<sha256>"
228 }
229 }
230 ```
231
232 ---
233
234 ## Tier 2 — Core Porcelain
235
236 **Namespace:** top-level `muse <command>`
237
238 These are the human and agent VCS commands — the interface most users interact
239 with. They compose Tier 1 plumbing primitives into user-friendly workflows.
240
241 | Command | Description |
242 |---------|-------------|
243 | `muse init` | Initialise a new Muse repository |
244 | `muse commit` | Record the current working tree as a new version |
245 | `muse status` | Show working-tree drift against HEAD |
246 | `muse log` | Display commit history |
247 | `muse diff` | Compare working tree against HEAD, or two commits |
248 | `muse show` | Inspect a commit: metadata, diff, files |
249 | `muse branch` | List, create, or delete branches |
250 | `muse checkout` | Switch branches or restore working tree |
251 | `muse merge` | Three-way merge a branch into the current branch |
252 | `muse reset` | Move HEAD to a prior commit |
253 | `muse revert` | Create a commit that undoes a prior commit |
254 | `muse cherry-pick` | Apply a specific commit's changes on top of HEAD |
255 | `muse stash` | Shelve and restore uncommitted changes |
256 | `muse tag` | Attach and query semantic tags on commits |
257 | `muse domains` | Domain plugin dashboard |
258 | `muse attributes` | Display `.museattributes` merge-strategy rules |
259 | `muse remote` | Manage remote connections (add/remove/list/set-url) |
260 | `muse clone` | Create a local copy of a remote Muse repository |
261 | `muse fetch` | Download commits/snapshots/objects from a remote |
262 | `muse pull` | Fetch from a remote and merge into current branch |
263 | `muse push` | Upload local commits/snapshots/objects to a remote |
264 | `muse check` | Domain-agnostic invariant check |
265 | `muse annotate` | CRDT-backed commit annotations |
266
267 ---
268
269 ## Tier 3 — Semantic Porcelain
270
271 **Namespaces:** `muse midi …`, `muse code …`, `muse coord …`
272
273 Domain-specific commands that interpret multidimensional state. These are
274 impossible to implement in Git — they require awareness of the domain's
275 semantic model (note events, symbol graphs, agent coordination).
276
277 ### `muse midi …` — MIDI Domain
278
279 | Command | Description |
280 |---------|-------------|
281 | `muse midi notes` | List every note in a MIDI track as musical notation |
282 | `muse midi note-log` | Note-level commit history |
283 | `muse midi note-blame` | Per-bar attribution |
284 | `muse midi harmony` | Chord analysis and key detection |
285 | `muse midi piano-roll` | ASCII piano roll visualization |
286 | `muse midi hotspots` | Bar-level churn leaderboard |
287 | `muse midi velocity-profile` | Dynamic range and velocity histogram |
288 | `muse midi transpose` | Transpose all notes by N semitones |
289 | `muse midi mix` | Combine two MIDI tracks into one |
290 | `muse midi query` | MIDI DSL predicate query over commit history |
291 | `muse midi check` | Enforce MIDI invariant rules |
292
293 ### `muse code …` — Code Domain
294
295 | Command | Description |
296 |---------|-------------|
297 | `muse code symbols` | List every semantic symbol in a snapshot |
298 | `muse code symbol-log` | Track a symbol through commit history |
299 | `muse code detect-refactor` | Detect renames, moves, extractions |
300 | `muse code grep` | Search the symbol graph by name/kind/language |
301 | `muse code blame` | Which commit last touched a specific symbol? |
302 | `muse code hotspots` | Symbol churn leaderboard |
303 | `muse code stable` | Symbol stability leaderboard |
304 | `muse code coupling` | File co-change analysis |
305 | `muse code compare` | Deep semantic comparison between snapshots |
306 | `muse code languages` | Language and symbol-type breakdown |
307 | `muse code patch` | Surgical semantic patch on a single symbol |
308 | `muse code query` | Symbol graph predicate DSL |
309 | `muse code query-history` | Temporal symbol search across a commit range |
310 | `muse code deps` | Import graph + call-graph |
311 | `muse code find-symbol` | Cross-commit, cross-branch symbol search |
312 | `muse code impact` | Transitive blast-radius |
313 | `muse code dead` | Dead code candidates |
314 | `muse code coverage` | Class interface call-coverage |
315 | `muse code lineage` | Full provenance chain of a symbol |
316 | `muse code api-surface` | Public API surface at a commit |
317 | `muse code codemap` | Semantic topology |
318 | `muse code clones` | Exact and near-duplicate symbols |
319 | `muse code checkout-symbol` | Restore a historical version of one symbol |
320 | `muse code semantic-cherry-pick` | Cherry-pick named symbols from a commit |
321 | `muse code index` | Manage local indexes |
322 | `muse code breakage` | Detect symbol-level structural breakage |
323 | `muse code invariants` | Enforce architectural rules |
324 | `muse code check` | Semantic invariant enforcement |
325 | `muse code code-query` | Predicate query over code commit history |
326
327 ### `muse coord …` — Multi-Agent Coordination
328
329 | Command | Description |
330 |---------|-------------|
331 | `muse coord reserve` | Advisory symbol reservation |
332 | `muse coord intent` | Declare a specific operation before executing it |
333 | `muse coord forecast` | Predict merge conflicts |
334 | `muse coord plan-merge` | Dry-run semantic merge plan |
335 | `muse coord shard` | Partition the codebase into N work zones |
336 | `muse coord reconcile` | Recommend merge ordering and integration strategy |
337
338 ---
339
340 ## Extending with New Domains
341
342 To add a new domain (e.g. `muse genomics …`):
343
344 1. Create `muse/plugins/genomics/plugin.py` implementing `MuseDomainPlugin`.
345 2. Create `muse/cli/commands/genomics_*.py` command modules.
346 3. Add a `genomics_cli = typer.Typer(name="genomics", …)` in `muse/cli/app.py`.
347 4. Register commands under `genomics_cli` and add `cli.add_typer(genomics_cli, name="genomics")`.
348 5. Write tests under `tests/` and docs under `docs/reference/`.
349
350 The core engine (`muse/core/`) is **never modified** for a new domain.
351