gabriel / muse public
porcelain.md markdown
781 lines 19.5 KB
1ba7f7b1 feat(porcelain): implement 9 gap-fill porcelain commands with full test… Gabriel Cardona <gabriel@tellurstori.com> 2d ago
1 # Muse Porcelain Commands
2
3 > **Layer guide:** Muse commands are organised into three tiers.
4 > This document covers **Tier 2 — Core Porcelain**: the high-level,
5 > human-friendly commands that build on the Tier 1 plumbing layer.
6 > Tier 3 commands (MIDI, Bitcoin, Code) live in their own reference docs.
7
8 ---
9
10 ## Quick Index
11
12 | Command | Description |
13 |---------|-------------|
14 | [`init`](#init) | Initialise a new Muse repository |
15 | [`commit`](#commit) | Record the working tree as a new version |
16 | [`status`](#status) | Show working-tree drift against HEAD |
17 | [`log`](#log) | Display commit history |
18 | [`diff`](#diff) | Compare working tree or two commits |
19 | [`show`](#show) | Inspect a commit — metadata, diff, files |
20 | [`branch`](#branch) | List, create, or delete branches |
21 | [`checkout`](#checkout) | Switch branches or restore a snapshot |
22 | [`merge`](#merge) | Three-way merge a branch into the current branch |
23 | [`rebase`](#rebase) | Replay commits onto a new base |
24 | [`reset`](#reset) | Move HEAD to a prior commit |
25 | [`revert`](#revert) | Undo a commit by creating a new one |
26 | [`cherry-pick`](#cherry-pick) | Apply a single commit's changes |
27 | [`stash`](#stash) | Shelve and restore uncommitted changes |
28 | [`tag`](#tag) | Attach and query semantic tags on commits |
29 | [`blame`](#blame) | Line-level attribution for any text file |
30 | [`reflog`](#reflog) | History of HEAD and branch-ref movements |
31 | [`rerere`](#rerere) | Reuse recorded conflict resolutions |
32 | [`gc`](#gc) | Garbage-collect unreachable objects |
33 | [`archive`](#archive) | Export a snapshot as tar.gz or zip |
34 | [`bisect`](#bisect) | Binary-search through history for a regression |
35 | [`worktree`](#worktree) | Multiple simultaneous branch checkouts |
36 | [`clean`](#clean) | Remove untracked files from the working tree |
37 | [`describe`](#describe) | Label a commit by its nearest tag |
38 | [`shortlog`](#shortlog) | Commit summary grouped by author or agent |
39 | [`verify`](#verify) | Whole-repository integrity check |
40 | [`snapshot`](#snapshot) | Explicit snapshot management |
41 | [`bundle`](#bundle) | Pack and unpack commits for offline transfer |
42 | [`content-grep`](#content-grep) | Full-text search across tracked file content |
43 | [`whoami`](#whoami) | Show the current identity |
44
45 ---
46
47 ## Established Core Porcelain
48
49 ### `init` — initialise a repository
50
51 Create a fresh `.muse/` directory in the current folder.
52
53 ```
54 muse init # initialise in current dir
55 muse init --domain midi # set the active domain
56 muse init -d code # short flag
57 ```
58
59 **Exit codes:** `0` success · `1` already initialised
60
61 ---
62
63 ### `commit` — record the working tree
64
65 Snapshot the working tree and write a commit pointing to it.
66
67 ```
68 muse commit -m "verse melody"
69 muse commit --message "Add chorus"
70 muse commit --allow-empty
71 ```
72
73 **Exit codes:** `0` committed · `1` nothing to commit
74
75 ---
76
77 ### `status` — show drift against HEAD
78
79 ```
80 muse status
81 muse status --short
82 muse status --json # machine-readable
83 muse status -s -j
84 ```
85
86 **Output:**
87 - `modified`: files changed since HEAD
88 - `added`: new files not in HEAD
89 - `deleted`: files present in HEAD but not on disk
90 - `clean` when working tree matches HEAD
91
92 **Exit codes:** `0` always (non-zero drift is shown, not signalled)
93
94 ---
95
96 ### `log` — display commit history
97
98 ```
99 muse log
100 muse log --limit 20
101 muse log --branch feat/audio
102 muse log --format json
103 ```
104
105 ---
106
107 ### `diff` — compare working tree or two commits
108
109 ```
110 muse diff # working tree vs HEAD
111 muse diff --from HEAD~3
112 muse diff --from v1.0 --to v2.0
113 muse diff --format json
114 ```
115
116 ---
117
118 ### `show` — inspect a commit
119
120 ```
121 muse show HEAD
122 muse show abc123
123 muse show --format json
124 ```
125
126 ---
127
128 ### `branch` — list, create, or delete branches
129
130 ```
131 muse branch # list all
132 muse branch feat/reverb # create
133 muse branch --delete feat/reverb
134 muse branch -d feat/reverb # short flag
135 ```
136
137 ---
138
139 ### `checkout` — switch branches or restore snapshot
140
141 ```
142 muse checkout main
143 muse checkout feat/guitar
144 muse checkout --create feat/new-idea # create and switch
145 muse checkout -c feat/new-idea # short flag
146 ```
147
148 ---
149
150 ### `merge` — three-way merge
151
152 ```
153 muse merge feat/audio
154 muse merge --message "Merge audio feature"
155 muse merge --abort
156 muse merge --continue
157 ```
158
159 **Conflict flow:**
160 1. `muse merge <branch>` → conflict reported, writes `MERGE_STATE.json`
161 2. Resolve files manually
162 3. `muse merge --continue` → commit the merge
163 4. Or `muse merge --abort` → restore original HEAD
164
165 ---
166
167 ### `reset` — move HEAD to a prior commit
168
169 ```
170 muse reset HEAD~1 # move back one commit
171 muse reset abc123 # move to specific commit
172 muse reset --hard # also reset working tree
173 ```
174
175 ---
176
177 ### `revert` — undo a commit by creating a new one
178
179 Non-destructive: the original commit remains in history.
180
181 ```
182 muse revert HEAD
183 muse revert abc123
184 muse revert --message "Undo broken change"
185 ```
186
187 ---
188
189 ### `cherry-pick` — apply a single commit's changes
190
191 ```
192 muse cherry-pick abc123
193 muse cherry-pick abc123 --message "Cherry: verse fix"
194 ```
195
196 ---
197
198 ### `stash` — shelve and restore changes
199
200 ```
201 muse stash push -m "WIP: bridge section"
202 muse stash list
203 muse stash pop
204 muse stash drop 0
205 ```
206
207 ---
208
209 ### `tag` — semantic tags on commits
210
211 ```
212 muse tag v1.0.0
213 muse tag v1.0.0 --commit abc123
214 muse tag list
215 muse tag delete v0.9.0
216 ```
217
218 ---
219
220 ### `blame` — line-level attribution
221
222 ```
223 muse blame song.mid
224 muse blame --format json song.mid
225 ```
226
227 ---
228
229 ### `reflog` — HEAD and branch movement history
230
231 ```
232 muse reflog
233 muse reflog --branch feat/audio
234 muse reflog --limit 50
235 muse reflog --format json
236 ```
237
238 The reflog is the "undo safety net" — every ref movement is recorded so
239 you can recover from accidental resets, force-pushes, or botched rebases.
240
241 ---
242
243 ### `rerere` — reuse recorded resolutions
244
245 ```
246 muse rerere list # show cached resolutions
247 muse rerere apply # auto-apply cached fixes to current conflicts
248 muse rerere forget abc123 # remove a cached resolution
249 muse rerere status # show which conflicts have cached resolutions
250 ```
251
252 **How it works:** After a successful merge, Muse records the resolution in
253 `.muse/rerere/`. On future conflicts with the same "conflict fingerprint",
254 `rerere apply` replays the resolution automatically.
255
256 ---
257
258 ### `gc` — garbage collect
259
260 ```
261 muse gc # remove unreachable objects
262 muse gc --dry-run # preview what would be removed
263 muse gc -n # short flag for --dry-run
264 ```
265
266 ---
267
268 ### `archive` — export a snapshot
269
270 ```
271 muse archive HEAD
272 muse archive HEAD --format zip --output release.zip
273 muse archive v1.0.0 --prefix project/
274 ```
275
276 **Formats:** `tar.gz` (default), `zip`
277
278 ---
279
280 ### `bisect` — binary-search for a regression
281
282 ```
283 muse bisect start
284 muse bisect good v1.0.0
285 muse bisect bad HEAD
286 muse bisect run pytest tests/
287 muse bisect reset
288 ```
289
290 Muse bisect works on any domain — not just code. Use it to find which
291 commit introduced a melody change, a tuning drift, or a data regression.
292
293 ---
294
295 ### `worktree` — multiple simultaneous checkouts
296
297 ```
298 muse worktree add /path/to/dir feat/audio
299 muse worktree list
300 muse worktree remove feat/audio
301 muse worktree prune
302 ```
303
304 ---
305
306 ## New Gap-Fill Porcelain (v2.x)
307
308 The following commands were added to close the gap between Muse's
309 feature set and Git's porcelain layer, plus Muse-specific additions
310 that exploit domain-agnostic version control.
311
312 ---
313
314 ### `rebase` — replay commits onto a new base
315
316 Muse rebase replays a sequence of commits onto a new base using the same
317 three-way merge engine as `muse merge`. Because commits are content-addressed,
318 each replayed commit gets a **new ID** — the originals are untouched in the store.
319
320 ```
321 muse rebase main # replay current branch onto main
322 muse rebase --onto newbase upstream # replay onto a specific base
323 muse rebase --squash main # collapse all commits into one
324 muse rebase --squash -m "feat: all in" # squash with custom message
325 muse rebase --abort # restore original HEAD
326 muse rebase --continue # resume after conflict resolution
327 ```
328
329 **Flags:**
330
331 | Flag | Short | Description |
332 |------|-------|-------------|
333 | `--onto <ref>` | `-o` | New base commit |
334 | `--squash` | `-s` | Collapse all commits into one |
335 | `--message <msg>` | `-m` | Message for squash commit |
336 | `--abort` | `-a` | Abort and restore original HEAD |
337 | `--continue` | `-c` | Resume after resolving a conflict |
338
339 **Conflict flow:**
340 1. `muse rebase main` → conflict reported, writes `REBASE_STATE.json` and `MERGE_STATE.json`
341 2. Resolve files manually
342 3. `muse rebase --continue` → commit the resolved state and continue
343 4. Or `muse rebase --abort` → restore the original branch pointer
344
345 **State file:** `.muse/REBASE_STATE.json` — tracks remaining/completed commits
346 and the `onto` base. Cleared automatically on successful completion or `--abort`.
347
348 **Exit codes:** `0` clean · `1` conflict or bad arguments
349
350 ---
351
352 ### `clean` — remove untracked files
353
354 Scans the working tree against the HEAD snapshot and removes files not tracked
355 in any commit. `--force` is required to actually delete files (safety guard).
356
357 ```
358 muse clean -n # dry-run: show what would be removed
359 muse clean -f # delete untracked files
360 muse clean -f -d # also delete empty directories
361 muse clean -f -x # also delete .museignore-excluded files
362 muse clean -f -d -x # everything untracked + ignored + empty dirs
363 ```
364
365 **Flags:**
366
367 | Flag | Short | Description |
368 |------|-------|-------------|
369 | `--dry-run` | `-n` | Preview without deleting |
370 | `--force` | `-f` | Required to actually delete |
371 | `--directories` | `-d` | Remove empty untracked directories |
372 | `--include-ignored` | `-x` | Also remove .museignore-excluded files |
373
374 **Exit codes:** `0` clean or cleaned · `1` untracked exist but `--force` not given
375
376 ---
377
378 ### `describe` — label by nearest tag
379
380 Walks backward from a commit and finds the nearest tag. Returns `<tag>~N`
381 where N is the hop count. N=0 gives the bare tag name.
382
383 ```
384 muse describe # → v1.0.0~3
385 muse describe --ref feat/audio # describe the tip of a branch
386 muse describe --long # → v1.0.0-3-gabc123456789
387 muse describe --require-tag # exit 1 if no tags exist
388 muse describe --format json # machine-readable
389 ```
390
391 **Flags:**
392
393 | Flag | Short | Description |
394 |------|-------|-------------|
395 | `--ref <ref>` | `-r` | Branch or commit to describe |
396 | `--long` | `-l` | Always show `<tag>-<dist>-g<sha>` |
397 | `--require-tag` | `-t` | Fail if no tag found |
398 | `--format <fmt>` | `-f` | `text` or `json` |
399
400 **JSON output schema:**
401 ```json
402 {
403 "commit_id": "string (full SHA-256)",
404 "tag": "string | null",
405 "distance": 0,
406 "short_sha": "string (12 chars)",
407 "name": "string (e.g. v1.0.0~3)"
408 }
409 ```
410
411 **Exit codes:** `0` description produced · `1` ref not found or `--require-tag` with no tags
412
413 ---
414
415 ### `shortlog` — commit summary by author or agent
416
417 Groups commits by `author` or `agent_id` and prints a count + message list.
418 Especially expressive in Muse because both human and agent contributions are
419 tracked with full metadata.
420
421 ```
422 muse shortlog # current branch
423 muse shortlog --all # all branches
424 muse shortlog --numbered # sort by commit count (most active first)
425 muse shortlog --email # include agent_id alongside author name
426 muse shortlog --limit 100 # cap commit walk at 100
427 muse shortlog --format json # JSON for agent consumption
428 ```
429
430 **Flags:**
431
432 | Flag | Short | Description |
433 |------|-------|-------------|
434 | `--branch <br>` | `-b` | Branch to summarise |
435 | `--all` | `-a` | Summarise all branches |
436 | `--numbered` | `-n` | Sort by commit count |
437 | `--email` | `-e` | Include agent_id |
438 | `--limit <N>` | `-l` | Max commits to walk |
439 | `--format <fmt>` | `-f` | `text` or `json` |
440
441 **JSON output schema:**
442 ```json
443 [
444 {
445 "author": "string",
446 "count": 12,
447 "commits": [
448 { "commit_id": "...", "message": "...", "committed_at": "..." }
449 ]
450 }
451 ]
452 ```
453
454 **Exit codes:** `0` always
455
456 ---
457
458 ### `verify` — whole-repository integrity check
459
460 Walks every reachable commit from every branch ref and performs a three-tier check:
461
462 1. Every branch ref points to an existing commit.
463 2. Every commit's snapshot exists.
464 3. Every object referenced by every snapshot exists, and (unless `--no-objects`)
465 its SHA-256 is recomputed to detect silent data corruption.
466
467 This is Muse's equivalent of `git fsck`.
468
469 ```
470 muse verify # full integrity check (re-hashes all objects)
471 muse verify --no-objects # existence-only check (faster)
472 muse verify --quiet # exit code only — no output
473 muse verify -q && echo "healthy"
474 muse verify --format json | jq '.failures'
475 ```
476
477 **Flags:**
478
479 | Flag | Short | Description |
480 |------|-------|-------------|
481 | `--quiet` | `-q` | No output; exit 0 = clean, 1 = failure |
482 | `--no-objects` | `-O` | Skip SHA-256 re-hashing |
483 | `--format <fmt>` | `-f` | `text` or `json` |
484
485 **JSON output schema:**
486 ```json
487 {
488 "refs_checked": 3,
489 "commits_checked": 42,
490 "snapshots_checked": 42,
491 "objects_checked": 210,
492 "all_ok": true,
493 "failures": [
494 {
495 "kind": "object",
496 "id": "abc123...",
497 "error": "hash mismatch — data corruption detected"
498 }
499 ]
500 }
501 ```
502
503 **Failure kinds:** `ref` · `commit` · `snapshot` · `object`
504
505 **Exit codes:** `0` all checks passed · `1` one or more failures
506
507 ---
508
509 ### `snapshot` — explicit snapshot management
510
511 A snapshot is Muse's fundamental unit of state: an immutable, content-addressed
512 record mapping workspace paths to their SHA-256 object IDs.
513
514 `muse snapshot` exposes snapshots as a first-class operation — capture, list,
515 show, and export them independently of commits. Useful for mid-work checkpoints
516 in agent pipelines.
517
518 #### `snapshot create`
519
520 ```
521 muse snapshot create
522 muse snapshot create -m "WIP: before refactor"
523 muse snapshot create --format json # prints snapshot_id
524 ```
525
526 **JSON output:**
527 ```json
528 {
529 "snapshot_id": "string",
530 "file_count": 42,
531 "note": "string",
532 "created_at": "ISO8601"
533 }
534 ```
535
536 #### `snapshot list`
537
538 ```
539 muse snapshot list
540 muse snapshot list --limit 5
541 muse snapshot list --format json
542 ```
543
544 #### `snapshot show`
545
546 ```
547 muse snapshot show <snapshot_id>
548 muse snapshot show abc123 # prefix lookup
549 muse snapshot show abc123 --format text
550 ```
551
552 #### `snapshot export`
553
554 ```
555 muse snapshot export <snapshot_id>
556 muse snapshot export abc123 --format zip --output release.zip
557 muse snapshot export abc123 --prefix project/
558 ```
559
560 **Archive formats:** `tar.gz` (default) · `zip`
561
562 **Flags (export):**
563
564 | Flag | Short | Description |
565 |------|-------|-------------|
566 | `--format <fmt>` | `-f` | `tar.gz` or `zip` |
567 | `--output <path>` | `-o` | Output file path |
568 | `--prefix <str>` | | Directory prefix inside archive |
569
570 **Exit codes:** `0` success · `1` snapshot not found
571
572 ---
573
574 ### `bundle` — offline commit transfer
575
576 A bundle is a self-contained JSON file carrying commits, snapshots, and objects.
577 Copy it over SSH, USB, or email — no network connection required.
578
579 The bundle format is identical to the plumbing `PackBundle` JSON and is
580 human-inspectable.
581
582 #### `bundle create`
583
584 ```
585 muse bundle create out.bundle # bundle from HEAD
586 muse bundle create out.bundle feat/audio # bundle a specific branch
587 muse bundle create out.bundle HEAD --have old-sha # delta bundle
588 ```
589
590 #### `bundle unbundle`
591
592 ```
593 muse bundle unbundle repo.bundle # apply and update branch refs
594 muse bundle unbundle repo.bundle --no-update-refs # objects only
595 ```
596
597 #### `bundle verify`
598
599 ```
600 muse bundle verify repo.bundle
601 muse bundle verify repo.bundle --quiet
602 muse bundle verify repo.bundle --format json
603 ```
604
605 #### `bundle list-heads`
606
607 ```
608 muse bundle list-heads repo.bundle
609 muse bundle list-heads repo.bundle --format json
610 ```
611
612 **Bundle value-add over plumbing:** `unbundle` updates local branch refs from
613 the bundle's `branch_heads` map, so the receiver's repo reflects the sender's
614 branch state automatically.
615
616 **Exit codes:** `0` success · `1` file not found, corrupt, or bad args
617
618 ---
619
620 ### `content-grep` — full-text search across tracked files
621
622 Searches every file in the HEAD snapshot for a pattern. Files are read from
623 the content-addressed object store. Binary files and non-UTF-8 files are
624 silently skipped.
625
626 Muse-specific: the search target is the **immutable object store** — you're
627 searching a specific point in history, not the working tree.
628
629 ```
630 muse content-grep --pattern "Cm7"
631 muse content-grep --pattern "TODO|FIXME" --files-only
632 muse content-grep --pattern "verse" --ignore-case
633 muse content-grep --pattern "tempo" --format json
634 muse content-grep --pattern "chord" --ref feat/harmony
635 muse content-grep --pattern "hit" --count
636 ```
637
638 **Flags:**
639
640 | Flag | Short | Description |
641 |------|-------|-------------|
642 | `--pattern <regex>` | `-p` | Python regex to search for |
643 | `--ref <ref>` | `-r` | Branch or commit to search |
644 | `--ignore-case` | `-i` | Case-insensitive matching |
645 | `--files-only` | `-l` | Print only matching file paths |
646 | `--count` | `-c` | Print match count per file |
647 | `--format <fmt>` | `-f` | `text` or `json` |
648
649 **JSON output schema:**
650 ```json
651 [
652 {
653 "path": "song.txt",
654 "object_id": "abc123...",
655 "match_count": 3,
656 "matches": [
657 { "line_number": 4, "text": "chord: Cm7" }
658 ]
659 }
660 ]
661 ```
662
663 **Exit codes:** `0` at least one match · `1` no matches
664
665 ---
666
667 ### `whoami` — show the current identity
668
669 A shortcut for `muse auth whoami`.
670
671 ```
672 muse whoami
673 muse whoami --json # JSON output for agent consumers
674 muse whoami --all # show identities for all configured hubs
675 ```
676
677 **Flags:**
678
679 | Flag | Short | Description |
680 |------|-------|-------------|
681 | `--json` | `-j` | JSON output |
682 | `--all` | `-a` | Show all hub identities |
683
684 **Text output:**
685 ```
686 hub: app.musehub.ai
687 type: agent
688 name: mozart-agent-v2
689 id: usr_abc123
690 token: set
691 ```
692
693 **JSON output:**
694 ```json
695 {
696 "hub": "app.musehub.ai",
697 "type": "agent",
698 "name": "mozart-agent-v2",
699 "id": "usr_abc123",
700 "token_set": "true"
701 }
702 ```
703
704 **Exit codes:** `0` identity found · `1` no identity stored (not authenticated)
705
706 ---
707
708 ## Domain Auth & Config
709
710 ### `auth` — identity management
711
712 ```
713 muse auth login --hub app.musehub.ai
714 muse auth login --agent --agent-id mozart-v2 --model gpt-4.5
715 muse auth whoami
716 muse auth logout
717 ```
718
719 ### `config` — repository configuration
720
721 ```
722 muse config show
723 muse config get core.author
724 muse config set core.author "Gabriel"
725 ```
726
727 ### `hub` — MuseHub connection
728
729 ```
730 muse hub connect https://app.musehub.ai
731 muse hub status
732 muse hub disconnect
733 ```
734
735 ---
736
737 ## Composability Patterns
738
739 Muse porcelain commands are designed to compose cleanly in pipelines.
740
741 **Check integrity before every push:**
742 ```bash
743 muse verify --quiet || { echo "repo corrupt!"; exit 1; }
744 muse push
745 ```
746
747 **Offline collaboration via bundle:**
748 ```bash
749 # Sender:
750 muse bundle create session.bundle
751 scp session.bundle colleague:/tmp/
752
753 # Receiver:
754 muse bundle verify /tmp/session.bundle --quiet
755 muse bundle unbundle /tmp/session.bundle
756 ```
757
758 **Generate a release label in CI:**
759 ```bash
760 VERSION=$(muse describe --format json | jq -r .name)
761 echo "Building $VERSION..."
762 muse snapshot export HEAD --output "${VERSION}.tar.gz"
763 ```
764
765 **Find which commits touched a melody line:**
766 ```bash
767 muse content-grep --pattern "tempo: 120" --format json | jq '.[].path'
768 ```
769
770 **Agent activity summary:**
771 ```bash
772 muse shortlog --all --numbered --email --format json \
773 | jq '.[] | select(.author | test("agent")) | {agent: .author, count: .count}'
774 ```
775
776 **Checkpoint before a risky refactor:**
777 ```bash
778 SNAP=$(muse snapshot create -m "pre-refactor" --format json | jq -r .snapshot_id)
779 # ... do the work ...
780 muse snapshot show "$SNAP" --format json | jq '.manifest | keys' # check what was there
781 ```