Muse Porcelain Commands
Layer guide: Muse commands are organised into three tiers. This document covers Tier 2 — Core Porcelain: the high-level, human-friendly commands that build on the Tier 1 plumbing layer. Tier 3 commands (MIDI, Bitcoin, Code) live in their own reference docs.
All porcelain commands accept --format json where documented below. JSON is
printed to stdout; human text goes to stdout too; error messages always go
to stderr. Exit codes follow the same convention as the plumbing layer:
0 success · 1 user error · 3 internal error.
Quick Index
| Command | Description |
|---|---|
init |
Initialise a new Muse repository |
commit |
Record the working tree as a new version |
status |
Show working-tree drift against HEAD |
log |
Display commit history |
diff |
Compare working tree or two commits |
show |
Inspect a commit — metadata, diff, files |
branch |
List, create, or delete branches |
checkout |
Switch branches or restore a snapshot |
merge |
Three-way merge a branch into the current branch |
rebase |
Replay commits onto a new base |
reset |
Move HEAD to a prior commit |
revert |
Undo a commit by creating a new one |
cherry-pick |
Apply a single commit's changes |
shelf |
Shelve and restore uncommitted changes |
tag |
Attach and query semantic tags on commits |
blame |
Line-level attribution for any text file |
reflog |
History of HEAD and branch-ref movements |
gc |
Garbage-collect unreachable objects |
archive |
Export a snapshot as tar.gz or zip |
bisect |
Binary-search through history for a regression |
worktree |
Multiple simultaneous branch checkouts |
clean |
Remove untracked files from the working tree |
describe |
Label a commit by its nearest tag |
shortlog |
Commit summary grouped by author or agent |
verify |
Whole-repository integrity check |
snapshot |
Explicit snapshot management |
bundle |
Pack and unpack commits for offline transfer |
content-grep |
Full-text search across tracked file content |
whoami |
Show the current identity |
config |
Read and write repository configuration |
Established Core Porcelain
init — initialise a repository
Create a fresh .muse/ directory in the current folder.
muse init # initialise in current dir
muse init --domain midi # set the active domain
muse init -d code # short flag
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--domain |
-d |
midi |
Domain plugin to activate for this repo |
Exit codes: 0 success · 1 already initialised
commit — record the working tree
Snapshot the working tree and write a commit pointing to it.
muse commit -m "verse melody"
muse commit --message "Add chorus" --author "gabriel"
muse commit --allow-empty
muse commit --format json
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--message |
-m |
"" |
Commit message |
--author |
-a |
config value | Override the author name |
--allow-empty |
-e |
off | Commit even when nothing changed |
--format |
-f |
text |
text or json |
JSON output
{
"commit_id": "a3f2...c8d1",
"branch": "main",
"message": "Add verse melody",
"author": "gabriel",
"committed_at": "2026-03-21T12:00:00+00:00",
"snapshot_id": "b7e4...f912",
"sem_ver_bump": "minor"
}
sem_ver_bump is "none", "patch", "minor", or "major" depending on the
domain plugin's assessment of the change.
Exit codes: 0 committed · 1 nothing to commit (and --allow-empty not given)
status — show drift against HEAD
muse status
muse status --short
muse status --json # machine-readable
muse status -s -j
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--short |
-s |
off | Compact one-line-per-file output |
--json |
-j |
off | JSON output (equivalent to --format json) |
Text output:
On branch main
Modified:
tracks/bass.mid
Added:
tracks/lead.mid
clean when working tree matches HEAD.
JSON output
{
"branch": "main",
"clean": false,
"modified": ["tracks/bass.mid"],
"added": ["tracks/lead.mid"],
"deleted": []
}
Exit codes: 0 always (non-zero drift is shown, not signalled)
log — display commit history
muse log
muse log --limit 20
muse log --branch feat/audio
muse log --format json
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--branch |
-b |
current | Branch to walk |
--limit |
-n |
50 | Max commits to emit |
--format |
-f |
text |
text or json |
JSON output — envelope with truncated flag and commits array, newest first:
{
"truncated": false,
"commits": [
{
"commit_id": "sha256:a3f2...c8d1",
"branch": "main",
"message": "Add verse melody",
"author": "gabriel",
"agent_id": "claude-code",
"model_id": "claude-sonnet-4-6",
"committed_at": "2026-03-21T12:00:00+00:00",
"parent_commit_id": "sha256:ff01...23ab",
"parent2_commit_id": null,
"snapshot_id": "sha256:b7e4...f912",
"sem_ver_bump": "minor",
"breaking_changes": [],
"metadata": {},
"files_added": ["tracks/drums.mid"],
"files_removed": [],
"files_modified": [],
"structured_delta": {"ops": [...]}
}
]
}
All keys are always present — no dict.get guards needed. structured_delta
is a dict with an "ops" key for code commits, null otherwise.
Exit codes: 0 always
diff — compare working tree or two commits
muse diff # working tree vs HEAD
muse diff --from HEAD~3
muse diff --from v1.0 --to v2.0
muse diff --format json
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--from |
-f |
HEAD | Ref or commit to diff from |
--to |
-t |
working tree | Ref or commit to diff to |
--format |
— | text |
text or json |
JSON output
{
"from": "ff01...23ab",
"to": "a3f2...c8d1",
"added": ["tracks/lead.mid"],
"removed": ["tracks/old.mid"],
"modified": ["tracks/bass.mid"],
"total_changes": 3
}
Note: The JSON field is
"total_changes"(not"ops"or"changes").
Exit codes: 0 always
show — inspect a commit
muse read HEAD
muse read abc123
muse read --format json
muse read --stat HEAD # files changed, not full diff
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--ref |
-r |
HEAD | Commit or branch to inspect |
--stat |
-s |
off | Show file-level summary instead of raw diff |
--format |
-f |
text |
text or json |
JSON output — full diff mode
{
"commit_id": "a3f2...c8d1",
"branch": "main",
"message": "Add verse melody",
"author": "gabriel",
"committed_at": "2026-03-21T12:00:00+00:00",
"snapshot_id": "b7e4...f912",
"parent_commit_id": "ff01...23ab",
"delta": {
"added": ["tracks/lead.mid"],
"removed": [],
"modified": ["tracks/bass.mid"]
}
}
JSON output — --stat mode
{
"commit_id": "a3f2...c8d1",
"branch": "main",
"message": "Add verse melody",
"author": "gabriel",
"committed_at": "2026-03-21T12:00:00+00:00",
"snapshot_id": "b7e4...f912",
"parent_commit_id": "ff01...23ab",
"files_added": 1,
"files_removed": 0,
"files_modified": 1
}
Exit codes: 0 found · 1 commit not found
branch — list, create, or delete branches
muse branch # list all
muse branch feat/reverb # create
muse branch --delete feat/reverb
muse branch -d feat/reverb # short flag
muse branch --format json # structured list
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--delete |
-d |
off | Delete a branch |
--format |
-f |
text |
text or json |
JSON output — list
{
"current": "main",
"branches": [
{"name": "dev", "commit_id": "ff01...23ab", "is_current": false},
{"name": "main", "commit_id": "a3f2...c8d1", "is_current": true}
]
}
JSON output — create / delete
{"action": "created", "branch": "feat/reverb"}
{"action": "deleted", "branch": "feat/reverb"}
Exit codes: 0 success · 1 branch already exists (create) or not found (delete)
checkout — switch branches or restore snapshot
muse checkout main
muse checkout feat/guitar
muse checkout --create feat/new-idea # create and switch
muse checkout -c feat/new-idea # short flag
muse checkout --format json # machine-readable result
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--create |
-c |
off | Create branch then switch |
--format |
-f |
text |
text or json |
JSON output
{
"action": "switched",
"branch": "feat/guitar",
"commit_id": "a3f2...c8d1"
}
"action" is one of "switched", "created", or "already_on" (when you
check out the branch that is already active).
Exit codes: 0 success · 1 branch not found (and --create not given)
merge — three-way merge
muse merge feat/audio
muse merge --message "Merge audio feature"
muse merge --abort
muse merge --continue
muse merge --format json
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--message |
-m |
auto | Override merge commit message |
--abort |
-a |
off | Abort an in-progress merge |
--continue |
-c |
off | Resume after resolving conflicts |
--format |
-f |
text |
text or json |
JSON output — clean merge
{
"action": "merged",
"branch": "feat/audio",
"commit_id": "a3f2...c8d1",
"message": "Merge feat/audio into main"
}
JSON output — conflict
{
"action": "conflict",
"branch": "feat/audio",
"conflicts": ["tracks/bass.mid"]
}
Conflict flow:
muse merge <branch>→ conflict reported, writesMERGE_STATE.json- Resolve files manually
muse merge --continue→ commit the merge- Or
muse merge --abort→ restore original HEAD
Exit codes: 0 merged · 1 conflict or bad arguments
rebase — replay commits onto a new base
Muse rebase replays a sequence of commits onto a new base using the same
three-way merge engine as muse merge. Because commits are content-addressed,
each replayed commit gets a new ID — the originals are untouched in the store.
muse rebase main # replay current branch onto main
muse rebase --onto newbase upstream # replay onto a specific base
muse rebase --squash main # collapse all commits into one
muse rebase --squash -m "feat: all in" # squash with custom message
muse rebase --abort # restore original HEAD
muse rebase --continue # resume after conflict resolution
muse rebase --format json
Flags
| Flag | Short | Description |
|---|---|---|
--onto <ref> |
-o |
New base commit |
--squash |
-s |
Collapse all commits into one |
--message <msg> |
-m |
Message for squash commit |
--abort |
-a |
Abort and restore original HEAD |
--continue |
-c |
Resume after resolving a conflict |
--format <fmt> |
-f |
text or json |
JSON output — squash rebase
{
"action": "squash_rebase",
"onto": "main",
"new_commit_id": "a3f2...c8d1",
"commits_squashed": 4,
"branch": "feat/audio"
}
JSON output — normal rebase
{
"action": "rebase",
"onto": "main",
"branch": "feat/audio",
"commits_replayed": 4,
"new_tip": "a3f2...c8d1"
}
Conflict flow:
muse rebase main→ conflict reported, writesREBASE_STATE.jsonandMERGE_STATE.json- Resolve files manually
muse rebase --continue→ commit the resolved state and continue- Or
muse rebase --abort→ restore the original branch pointer
State file: .muse/REBASE_STATE.json — tracks remaining/completed commits
and the onto base. Cleared automatically on successful completion or --abort.
Exit codes: 0 clean · 1 conflict or bad arguments
reset — move HEAD to a prior commit
muse reset HEAD~1 # move back one commit
muse reset abc123 # move to specific commit
muse reset --hard # also reset working tree
muse reset --format json
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--hard |
-H |
off | Also update the working tree to match |
--format |
-f |
text |
text or json |
JSON output
{
"action": "reset",
"branch": "main",
"previous_commit": "a3f2...c8d1",
"new_commit": "ff01...23ab",
"hard": false
}
Exit codes: 0 success · 1 commit not found
revert — undo a commit by creating a new one
Non-destructive: the original commit remains in history. A new commit is created whose effect is the inverse of the target commit.
muse revert HEAD
muse revert abc123
muse revert --message "Undo broken change"
muse revert --format json
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--message |
-m |
auto | Override revert commit message |
--format |
-f |
text |
text or json |
JSON output
{
"action": "reverted",
"reverted_commit": "a3f2...c8d1",
"new_commit_id": "b7e4...f912",
"branch": "main",
"message": "Revert \"Add verse melody\""
}
Exit codes: 0 success · 1 commit not found or nothing to revert
cherry-pick — apply a single commit's changes
muse cherry-pick abc123
muse cherry-pick abc123 --message "Cherry: verse fix"
muse cherry-pick abc123 --format json
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--message |
-m |
auto | Override the cherry-picked commit message |
--format |
-f |
text |
text or json |
JSON output
{
"action": "cherry_picked",
"source_commit": "abc1...2345",
"new_commit_id": "a3f2...c8d1",
"branch": "main",
"message": "Cherry: verse fix"
}
Exit codes: 0 success · 1 commit not found or conflict
shelf — shelve and restore changes
muse shelf save -m "WIP: bridge section"
muse shelf list
muse shelf list --format json
muse shelf pop
muse shelf drop 0
Subcommands
| Subcommand | Description |
|---|---|
push |
Shelf current working-tree changes |
list |
List saved shelf entries |
pop |
Restore the most recent shelf entry and drop it |
apply <n> |
Restore shelf entry N without dropping it |
drop <n> |
Delete shelf entry N |
show <n> |
Show what a shelf entry contains |
Flags — push
| Flag | Short | Description |
|---|---|---|
--message |
-m |
Label for the shelf entry |
Flags — list / show
| Flag | Short | Description |
|---|---|---|
--format |
-f |
text or json |
JSON output — list
[
{
"index": 0,
"message": "WIP: bridge section",
"created_at": "2026-03-21T12:00:00+00:00",
"branch": "feat/audio"
}
]
Exit codes: 0 success · 1 shelf index out of range or nothing to shelve
tag — semantic tags on commits
muse tag v1.0.0
muse tag v1.0.0 --commit abc123
muse tag list
muse tag list --format json
muse tag delete v0.9.0
muse tag show v1.0.0 --format json
Subcommands
| Subcommand | Description |
|---|---|
<name> |
Create a tag on HEAD (or --commit) |
list |
List all tags |
show <name> |
Inspect a tag |
delete <name> |
Delete a tag |
Flags — create
| Flag | Short | Description |
|---|---|---|
--commit |
-c |
Attach tag to a specific commit ID |
--message |
-m |
Optional annotation |
--format |
-f |
text or json |
JSON output — create
{"action": "created", "name": "v1.0.0", "commit_id": "a3f2...c8d1"}
JSON output — list
[
{"name": "v1.0.0", "commit_id": "a3f2...c8d1", "created_at": "2026-03-21T12:00:00+00:00"}
]
Exit codes: 0 success · 1 tag or commit not found
blame — line-level attribution
muse blame song.mid
muse blame --format json song.mid
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--format |
-f |
text |
text or json |
--ref |
-r |
HEAD | Branch or commit to blame against |
JSON output
[
{
"line": 1,
"content": "tempo: 120",
"commit_id": "a3f2...c8d1",
"author": "gabriel",
"committed_at": "2026-03-21T12:00:00+00:00",
"message": "Add verse melody"
}
]
Exit codes: 0 success · 1 file or ref not found
reflog — HEAD and branch movement history
muse reflog
muse reflog --branch feat/audio
muse reflog --limit 50
muse reflog --format json
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--branch |
-b |
HEAD | Branch to show reflog for |
--limit |
-n |
50 | Max entries to show |
--format |
-f |
text |
text or json |
The reflog is the "undo safety net" — every ref movement is recorded so you can recover from accidental resets, force-pushes, or botched rebases.
JSON output
[
{
"index": 0,
"commit_id": "a3f2...c8d1",
"action": "commit",
"message": "Add verse melody",
"author": "gabriel",
"moved_at": "2026-03-21T12:00:00+00:00"
}
]
"index" is 0-based, with 0 being the most recent entry. "action" describes
what caused the ref movement: "commit", "merge", "rebase", "reset", "checkout", etc.
Exit codes: 0 always
gc — garbage collect
Removes objects that are not reachable from any branch or tag ref. Orphaned commits (e.g. after a reset), dangling snapshots, and unreferenced blobs are all eligible.
muse gc # remove unreachable objects
muse gc --dry-run # preview what would be removed
muse gc -n # short flag for --dry-run
muse gc --format json
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--dry-run |
-n |
off | Preview without deleting |
--format |
-f |
text |
text or json |
JSON output
{
"commits_removed": 2,
"snapshots_removed": 2,
"objects_removed": 11,
"bytes_freed": 204800,
"dry_run": false
}
Exit codes: 0 always
archive — export a snapshot
muse archive HEAD
muse archive HEAD --format zip --output release.zip
muse archive v1.0.0 --prefix project/
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--format |
-f |
tar.gz |
Archive format: tar.gz or zip |
--output |
-o |
<commit>.tar.gz |
Output file path |
--prefix |
-p |
"" |
Directory prefix inside the archive |
All archive entries use the prefix/ directory. Tar-slip / zip-slip are
prevented: entry paths are validated to stay within the prefix.
Exit codes: 0 archive written · 1 ref not found · 3 I/O error
bisect — binary-search for a regression
Muse bisect works on any domain — not just code. Use it to find which commit introduced a melody change, a tuning drift, or a data regression.
muse bisect start
muse bisect bad HEAD # mark HEAD as bad (broken)
muse bisect good v1.0.0 # mark v1.0.0 as good (working)
muse bisect bad # mark the currently-tested commit as bad
muse bisect good # mark the currently-tested commit as good
muse bisect skip # skip an untestable commit
muse bisect log # show the current session log
muse bisect reset # end the session and restore HEAD
muse bisect run pytest # automated bisect: run command, 0=good, 1=bad
Subcommands
| Subcommand | Description |
|---|---|
start |
Begin a new bisect session |
bad [<ref>] |
Mark a commit as bad; omit to mark current |
good [<ref>] |
Mark a commit as good; omit to mark current |
skip [<ref>] |
Skip a commit that cannot be tested |
log |
Print the current session state |
reset |
End the session; restore the original branch |
run <cmd> |
Automate: run command, exit 0 = good, exit 1 = bad |
Bisect narrows the search range using binary search. On each step, Muse
checks out the midpoint commit and waits for a verdict (good/bad/skip).
The search converges in O(log N) steps regardless of domain.
Exit codes: 0 session active or found · 1 bad arguments · 3 I/O error
worktree — multiple simultaneous checkouts
muse worktree add /path/to/dir feat/audio
muse worktree list
muse worktree list --format json
muse worktree remove feat/audio
muse worktree prune
Subcommands
| Subcommand | Description |
|---|---|
add <path> <branch> |
Check out branch into path |
list |
List registered worktrees |
remove <branch> |
Remove a linked worktree |
prune |
Remove entries for deleted directories |
Flags — list
| Flag | Short | Description |
|---|---|---|
--format |
-f |
text or json |
JSON output — list
[
{"path": "/home/g/muse-main", "branch": "main", "is_main": true},
{"path": "/home/g/muse-audio", "branch": "feat/audio", "is_main": false}
]
Exit codes: 0 success · 1 path or branch conflict
clean — remove untracked files
Scans the working tree against the HEAD snapshot and removes files not tracked
in any commit. --force is required to actually delete files (safety guard).
muse clean -n # dry-run: show what would be removed
muse clean -f # delete untracked files
muse clean -f -d # also delete empty directories
muse clean -f -x # also delete .museignore-excluded files
muse clean -f -d -x # everything untracked + ignored + empty dirs
Flags
| Flag | Short | Description |
|---|---|---|
--dry-run |
-n |
Preview without deleting |
--force |
-f |
Required to actually delete |
--directories |
-d |
Remove empty untracked directories |
--include-ignored |
-x |
Also remove .museignore-excluded files |
Exit codes: 0 clean or cleaned · 1 untracked exist but --force not given
describe — label by nearest tag
Walks backward from a commit and finds the nearest tag. Returns <tag>~N
where N is the hop count. N=0 gives the bare tag name.
muse describe # → v1.0.0~3
muse describe --ref feat/audio # describe the tip of a branch
muse describe --long # → v1.0.0-3-sha256:abc123456789
muse describe --require-tag # exit 1 if no tags exist
muse describe --format json # machine-readable
Flags
| Flag | Short | Description |
|---|---|---|
--ref <ref> |
-r |
Branch or commit to describe |
--long |
-l |
Always show <tag>-<dist>-g<sha> |
--require-tag |
-t |
Fail if no tag found |
--format <fmt> |
-f |
text or json |
JSON output schema:
{
"commit_id": "string (full SHA-256)",
"tag": "string | null",
"distance": 0,
"short_sha": "string (12 chars)",
"name": "string (e.g. v1.0.0~3)"
}
Exit codes: 0 description produced · 1 ref not found or --require-tag with no tags
shortlog — commit summary by author or agent
Groups commits by author or agent_id and prints a count + message list.
Especially expressive in Muse because both human and agent contributions are
tracked with full metadata.
muse shortlog # current branch
muse shortlog --all # all branches
muse shortlog --numbered # sort by commit count (most active first)
muse shortlog --email # include agent_id alongside author name
muse shortlog --limit 100 # cap commit walk at 100
muse shortlog --format json # JSON for agent consumption
Flags
| Flag | Short | Description |
|---|---|---|
--branch <br> |
-b |
Branch to summarise |
--all |
-a |
Summarise all branches |
--numbered |
-n |
Sort by commit count |
--email |
-e |
Include agent_id |
--limit <N> |
-l |
Max commits to walk |
--format <fmt> |
-f |
text or json |
JSON output schema:
[
{
"author": "string",
"count": 12,
"commits": [
{"commit_id": "...", "message": "...", "committed_at": "..."}
]
}
]
Exit codes: 0 always
verify — whole-repository integrity check
Walks every reachable commit from every branch ref and performs a three-tier check:
- Every branch ref points to an existing commit.
- Every commit's snapshot exists.
- Every object referenced by every snapshot exists, and (unless
--no-objects) its SHA-256 is recomputed to detect silent data corruption.
This is Muse's equivalent of git fsck.
muse verify # full integrity check (re-hashes all objects)
muse verify --no-objects # existence-only check (faster)
muse verify --quiet # exit code only — no output
muse verify -q && echo "healthy"
muse verify --format json | jq '.failures'
Flags
| Flag | Short | Description |
|---|---|---|
--quiet |
-q |
No output; exit 0 = clean, 1 = failure |
--no-objects |
-O |
Skip SHA-256 re-hashing |
--format <fmt> |
-f |
text or json |
JSON output schema:
{
"refs_checked": 3,
"commits_checked": 42,
"snapshots_checked": 42,
"objects_checked": 210,
"all_ok": true,
"failures": [
{
"kind": "object",
"id": "abc123...",
"error": "hash mismatch — data corruption detected"
}
]
}
Failure kinds: ref · commit · snapshot · object
Exit codes: 0 all checks passed · 1 one or more failures
snapshot — explicit snapshot management
A snapshot is Muse's fundamental unit of state: an immutable, content-addressed record mapping workspace paths to their SHA-256 object IDs.
muse snapshot exposes snapshots as a first-class operation — capture, list,
show, and export them independently of commits. Useful for mid-work checkpoints
in agent pipelines.
snapshot create
muse snapshot create
muse snapshot create -m "WIP: before refactor"
muse snapshot create --format json # prints snapshot_id
JSON output:
{
"snapshot_id": "string",
"file_count": 42,
"note": "string",
"created_at": "ISO8601"
}
snapshot list
muse snapshot list
muse snapshot list --limit 5
muse snapshot list --format json
snapshot show
muse snapshot show <snapshot_id>
muse snapshot show abc123 # prefix lookup
muse snapshot show abc123 --format text
snapshot export
muse snapshot export <snapshot_id>
muse snapshot export abc123 --format zip --output release.zip
muse snapshot export abc123 --prefix project/
Archive formats: tar.gz (default) · zip
Flags (export):
| Flag | Short | Description |
|---|---|---|
--format <fmt> |
-f |
tar.gz or zip |
--output <path> |
-o |
Output file path |
--prefix <str> |
Directory prefix inside archive |
Exit codes: 0 success · 1 snapshot not found
bundle — offline commit transfer
A bundle is a self-contained JSON file carrying commits, snapshots, and objects. Copy it over SSH, USB, or email — no network connection required.
The bundle format is identical to the plumbing MPack and is
human-inspectable.
bundle create
muse bundle create out.bundle # bundle from HEAD
muse bundle create out.bundle feat/audio # bundle a specific branch
muse bundle create out.bundle HEAD --have old-sha # delta bundle
bundle unbundle
muse bundle unbundle repo.bundle # apply and update branch refs
muse bundle unbundle repo.bundle --no-update-refs # objects only
bundle verify
muse bundle verify repo.bundle
muse bundle verify repo.bundle --quiet
muse bundle verify repo.bundle --format json
bundle list-heads
muse bundle list-heads repo.bundle
muse bundle list-heads repo.bundle --format json
Bundle value-add over plumbing: unbundle updates local branch refs from
the bundle's branch_heads map, so the receiver's repo reflects the sender's
branch state automatically.
Exit codes: 0 success · 1 file not found, corrupt, or bad args
content-grep — full-text search across tracked files
Searches every file in the HEAD snapshot for a pattern. Files are read from the content-addressed object store. Binary files and non-UTF-8 files are silently skipped.
Muse-specific: the search target is the immutable object store — you're searching a specific point in history, not the working tree.
muse content-grep --pattern "Cm7"
muse content-grep --pattern "TODO|FIXME" --files-only
muse content-grep --pattern "verse" --ignore-case
muse content-grep --pattern "tempo" --format json
muse content-grep --pattern "chord" --ref feat/harmony
muse content-grep --pattern "hit" --count
Flags
| Flag | Short | Description |
|---|---|---|
--pattern <regex> |
-p |
Python regex to search for |
--ref <ref> |
-r |
Branch or commit to search |
--ignore-case |
-i |
Case-insensitive matching |
--files-only |
-l |
Print only matching file paths |
--count |
-c |
Print match count per file |
--format <fmt> |
-f |
text or json |
JSON output schema:
[
{
"path": "song.txt",
"object_id": "abc123...",
"match_count": 3,
"matches": [
{"line_number": 4, "text": "chord: Cm7"}
]
}
]
Exit codes: 0 at least one match · 1 no matches
whoami — show the current identity
A shortcut for muse auth whoami.
muse whoami
muse whoami --json # JSON output for agent consumers
muse whoami --all # show identities for all configured hubs
Flags
| Flag | Short | Description |
|---|---|---|
--json |
-j |
JSON output |
--all |
-a |
Show all hub identities |
Text output:
hub: app.musehub.ai
type: agent
name: mozart-agent-v2
id: usr_abc123
token: set
JSON output:
{
"hub": "app.musehub.ai",
"type": "agent",
"name": "mozart-agent-v2",
"id": "usr_abc123",
"token_set": true,
"capabilities": ["push", "pull", "share"]
}
"token_set" is a boolean. "capabilities" lists the operations the current
token is authorised for on the configured hub.
Exit codes: 0 identity found · 1 no identity stored (not authenticated)
Domain Auth & Config
auth — identity management
muse auth keygen --hub app.musehub.ai
muse auth register --hub app.musehub.ai --handle alice
muse auth register --hub app.musehub.ai --handle mozart-v2 --agent
muse auth whoami
muse auth logout
config — repository configuration
Read and write repository configuration stored in .muse/config.toml.
muse config show # full config as text
muse config show --format json # machine-readable
muse config get core.author # single value
muse config set core.author "Gabriel" # write a value
Subcommands
| Subcommand | Description |
|---|---|
show |
Print the entire config |
get <key> |
Print a single key's value |
set <key> <value> |
Write a key/value pair |
Flags — show
| Flag | Short | Description |
|---|---|---|
--format |
-f |
text or json |
JSON output — show
{
"user": {
"author": "gabriel",
"email": ""
},
"hub": {
"url": "https://app.musehub.ai"
},
"remotes": {
"origin": "https://app.musehub.ai/repos/my-repo"
},
"domain": {
"my.key": "value"
}
}
Top-level sections:
user— local author identityhub— MuseHub connection (HTTPS URLs only)remotes— named remote URLsdomain— domain-specific key/value pairs (anydomain.*key is permitted)
Blocked namespaces: auth.* and remotes.* keys cannot be written via
config set — use dedicated commands (muse auth keygen/register, muse remote add).
Exit codes: 0 success · 1 key not found, blocked namespace, or bad format
hub — MuseHub connection
muse hub connect https://app.musehub.ai
muse hub status
muse hub disconnect
Composability Patterns
Muse porcelain commands are designed to compose cleanly in pipelines.
Check integrity before every push:
muse verify --quiet || { echo "repo corrupt!"; exit 1; }
muse push
Offline collaboration via bundle:
# Sender:
muse bundle create session.bundle
scp session.bundle colleague:/tmp/
# Receiver:
muse bundle verify /tmp/session.bundle --quiet
muse bundle unbundle /tmp/session.bundle
Generate a release label in CI:
VERSION=$(muse describe --format json | jq -r .name)
echo "Building $VERSION..."
muse snapshot export HEAD --output "${VERSION}.tar.gz"
Find which commits touched a melody line:
muse content-grep --pattern "tempo: 120" --format json | jq '.[].path'
Agent activity summary:
muse shortlog --all --numbered --email --format json \
| jq '.[] | select(.author | test("agent")) | {agent: .author, count: .count}'
Checkpoint before a risky refactor:
SNAP=$(muse snapshot create -m "pre-refactor" --format json | jq -r .snapshot_id)
# ... do the work ...
muse snapshot show "$SNAP" --format json | jq '.manifest | keys'
Binary-search for the broken commit (automated):
muse bisect start
muse bisect bad HEAD
muse bisect good v1.0.0
muse bisect run pytest tests/regression.py
# Muse prints the first bad commit and resets automatically.
Audit all recent changes by agents:
muse log --format json | jq '[.[] | select(.author | startswith("agent-"))]'