README.md
markdown
| 1 | # Muse — Oh My ZSH Plugin |
| 2 | |
| 3 | Domain-aware, agent-native ZSH integration for [Muse](https://github.com/gabrielchua/muse) — version control for multidimensional state. |
| 4 | |
| 5 | Unlike the Git plugin which treats every repo identically, this plugin **knows what kind of state you are versioning** — MIDI, code, genomics, spatial data — and surfaces domain-specific information at every layer: the prompt, the completions, the workflow functions, and the agent session system. |
| 6 | |
| 7 | --- |
| 8 | |
| 9 | ## Installation |
| 10 | |
| 11 | **One-liner** (run from the muse repo root): |
| 12 | |
| 13 | ```zsh |
| 14 | bash tools/install-omzsh-plugin.sh |
| 15 | ``` |
| 16 | |
| 17 | Then add `muse` to your `plugins` array in `~/.zshrc`: |
| 18 | |
| 19 | ```zsh |
| 20 | plugins=(git muse) # add 'muse' alongside your existing plugins |
| 21 | ``` |
| 22 | |
| 23 | Reload: |
| 24 | |
| 25 | ```zsh |
| 26 | source ~/.zshrc |
| 27 | ``` |
| 28 | |
| 29 | The install script creates symlinks from `~/.oh-my-zsh/custom/plugins/muse/` back into the repo, so the plugin stays up to date as you pull new Muse releases. |
| 30 | |
| 31 | --- |
| 32 | |
| 33 | ## Prompt Setup |
| 34 | |
| 35 | ### Oh My ZSH (default theme) |
| 36 | |
| 37 | Add `$(muse_prompt_info)` to your `$PROMPT` in `~/.zshrc`: |
| 38 | |
| 39 | ```zsh |
| 40 | PROMPT='%~ $(muse_prompt_info) %# ' |
| 41 | RPROMPT='$(muse_rprompt_info)' |
| 42 | ``` |
| 43 | |
| 44 | ### Powerlevel10k |
| 45 | |
| 46 | Add `muse_vcs` to your prompt element arrays in `~/.p10k.zsh`: |
| 47 | |
| 48 | ```zsh |
| 49 | POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(… muse_vcs …) |
| 50 | # or |
| 51 | POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(… muse_vcs muse_rprompt …) |
| 52 | ``` |
| 53 | |
| 54 | The plugin provides both `prompt_muse_vcs()` (async) and `instant_prompt_muse_vcs()` (instant prompt) so your prompt is never blank during p10k's async refresh. |
| 55 | |
| 56 | ### What the prompt looks like |
| 57 | |
| 58 | | State | Prompt | |
| 59 | |---|---| |
| 60 | | Clean, midi domain | `♪ midi:main ✓` | |
| 61 | | Dirty (3 changed paths) | `♪ midi:main ✗ 3Δ` | |
| 62 | | Mid-merge (2 conflicts) | `♪ midi:main ⚡ ← feature/exp (2 conflicts)` | |
| 63 | | Detached HEAD | `♪ midi:(detached:a1b2c3d4)` | |
| 64 | | Agent session active | `♪ midi:main ✗ 3Δ [🤖 claude-4.6-sonnet]` | |
| 65 | | `MUSE_AGENT_MODE=1` | `[midi\|main\|dirty:3\|no-merge]` | |
| 66 | | `MUSE_AGENT_MODE=1` + merge | `[midi\|main\|dirty:3\|merging:feature/exp:2]` | |
| 67 | |
| 68 | The right-prompt segment shows the SemVer bump of the HEAD commit: |
| 69 | |
| 70 | | SemVer | Right prompt | |
| 71 | |---|---| |
| 72 | | major | `[MAJOR]` (red) | |
| 73 | | minor | `[MINOR]` (yellow) | |
| 74 | | patch | `[PATCH]` (green) | |
| 75 | |
| 76 | Domain icons are user-overridable: |
| 77 | |
| 78 | ```zsh |
| 79 | MUSE_DOMAIN_ICONS[midi]="♩" # change just the midi icon |
| 80 | MUSE_DOMAIN_ICONS[genomics]="🔬" # add a new domain before it exists in core |
| 81 | ``` |
| 82 | |
| 83 | --- |
| 84 | |
| 85 | ## Configuration |
| 86 | |
| 87 | Set these in `~/.zshrc` **before** `plugins=(… muse …)`: |
| 88 | |
| 89 | | Variable | Default | Effect | |
| 90 | |---|---|---| |
| 91 | | `MUSE_PROMPT_SHOW_DOMAIN` | `1` | Include domain name in prompt | |
| 92 | | `MUSE_PROMPT_SHOW_OPS` | `1` | Include dirty-path count (the `3Δ` indicator) | |
| 93 | | `MUSE_PROMPT_ICONS` | `1` | Use emoji icons; set to `0` for ASCII fallback | |
| 94 | | `MUSE_AGENT_MODE` | `0` | Machine-parseable mode (see Agent Mode below) | |
| 95 | | `MUSE_DIRTY_TIMEOUT` | `1` | Seconds before dirty check gives up (shows `?`) | |
| 96 | | `MUSE_SESSION_LOG_DIR` | `~/.muse/sessions` | Where session `.jsonl` logs are written | |
| 97 | | `MUSE_BIND_KEYS` | `1` | Bind `Ctrl+B` / `ESC-M` / `ESC-H` shortcuts | |
| 98 | | `MUSE_POST_COMMIT_CMD` | _(empty)_ | Shell command run after each `muse commit` | |
| 99 | | `MUSE_POST_CHECKOUT_CMD` | _(empty)_ | Shell command run after each `muse checkout` | |
| 100 | | `MUSE_POST_MERGE_CMD` | _(empty)_ | Shell command run after a clean `muse merge` | |
| 101 | |
| 102 | **Example** — desktop notification on commit: |
| 103 | |
| 104 | ```zsh |
| 105 | MUSE_POST_COMMIT_CMD='osascript -e "display notification \"Committed!\" with title \"Muse\""' |
| 106 | ``` |
| 107 | |
| 108 | --- |
| 109 | |
| 110 | ## Aliases |
| 111 | |
| 112 | All aliases use the `m` prefix to avoid collisions with system commands. |
| 113 | |
| 114 | ### Core VCS |
| 115 | |
| 116 | | Alias | Expands to | |
| 117 | |---|---| |
| 118 | | `mst` | `muse status` | |
| 119 | | `msts` | `muse status --short` | |
| 120 | | `mstp` | `muse status --porcelain` | |
| 121 | | `mcm` | `muse commit -m` | |
| 122 | | `mco` | `muse checkout` | |
| 123 | | `mlg` | `muse log` | |
| 124 | | `mlgo` | `muse log --oneline` | |
| 125 | | `mlgg` | `muse log --graph` | |
| 126 | | `mlggs` | `muse log --graph --oneline` | |
| 127 | | `mdf` | `muse diff` | |
| 128 | | `mdfst` | `muse diff --stat` | |
| 129 | | `mdfp` | `muse diff --patch` | |
| 130 | | `mbr` | `muse branch` | |
| 131 | | `mbrv` | `muse branch -v` | |
| 132 | | `msh` | `muse show` | |
| 133 | | `mbl` | `muse blame` | |
| 134 | | `mrl` | `muse reflog` | |
| 135 | | `mtg` | `muse tag` | |
| 136 | |
| 137 | ### Stash |
| 138 | |
| 139 | | Alias | Expands to | |
| 140 | |---|---| |
| 141 | | `msta` | `muse stash` | |
| 142 | | `mstap` | `muse stash pop` | |
| 143 | | `mstal` | `muse stash list` | |
| 144 | | `mstad` | `muse stash drop` | |
| 145 | |
| 146 | ### Remotes |
| 147 | |
| 148 | | Alias | Expands to | |
| 149 | |---|---| |
| 150 | | `mfh` | `muse fetch` | |
| 151 | | `mpull` | `muse pull` | |
| 152 | | `mpush` | `muse push` | |
| 153 | | `mrm` | `muse remote` | |
| 154 | | `mclone` | `muse clone` | |
| 155 | |
| 156 | ### Domain & plumbing shortcuts |
| 157 | |
| 158 | | Alias | Expands to | |
| 159 | |---|---| |
| 160 | | `mmidi` | `muse midi` | |
| 161 | | `mcode` | `muse code` | |
| 162 | | `mcoord` | `muse coord` | |
| 163 | | `mplumb` | `muse plumbing` | |
| 164 | | `mcfg` | `muse config` | |
| 165 | | `mhub` | `muse hub` | |
| 166 | |
| 167 | --- |
| 168 | |
| 169 | ## Workflow Functions |
| 170 | |
| 171 | ### Branch management |
| 172 | |
| 173 | ```zsh |
| 174 | muse-new-feat drums-pattern # creates and switches to feat/drums-pattern |
| 175 | muse-new-fix bad-velocity # creates and switches to fix/bad-velocity |
| 176 | muse-new-refactor engine-core # creates and switches to refactor/engine-core |
| 177 | ``` |
| 178 | |
| 179 | ### Commits |
| 180 | |
| 181 | ```zsh |
| 182 | muse-wip # commit with auto-timestamp "[WIP] 2026-03-20T14:30:00Z" |
| 183 | muse-quick-commit # interactive guided commit (domain-aware metadata prompts) |
| 184 | muse-agent-commit "msg" # commit with agent session identity auto-injected |
| 185 | ``` |
| 186 | |
| 187 | ### Sync & merge |
| 188 | |
| 189 | ```zsh |
| 190 | muse-sync # fetch + pull + status |
| 191 | muse-safe-merge feature/x # merge with conflict list + editor launch on failure |
| 192 | ``` |
| 193 | |
| 194 | ### Health & provenance |
| 195 | |
| 196 | ```zsh |
| 197 | muse-health # repo health summary (dirty, merge, stashes, remotes) |
| 198 | muse-who-last # show HEAD commit author (human or agent + model) |
| 199 | muse-agent-blame 20 # show authorship breakdown for last 20 commits |
| 200 | muse-overview # domain-specific live state overview |
| 201 | ``` |
| 202 | |
| 203 | --- |
| 204 | |
| 205 | ## Keybindings |
| 206 | |
| 207 | | Key | Action | |
| 208 | |---|---| |
| 209 | | `Ctrl+B` | Open branch picker (fzf) | |
| 210 | | `ESC-M` | Open commit browser (fzf) | |
| 211 | | `ESC-H` | Show repo health summary | |
| 212 | |
| 213 | Set `MUSE_BIND_KEYS=0` in `.zshrc` to disable all keybindings. |
| 214 | |
| 215 | --- |
| 216 | |
| 217 | ## Tab Completion |
| 218 | |
| 219 | The plugin registers `_muse` as the completion function for the `muse` command. |
| 220 | Completions are provided for: |
| 221 | |
| 222 | - All ~50 top-level commands with descriptions |
| 223 | - Branch names (`checkout`, `merge`, `branch -d`, …) |
| 224 | - Tag names (`tag create`, `tag -d`, …) |
| 225 | - Remote names (`push`, `pull`, `fetch`, `remote`, …) |
| 226 | - Short SHAs (`show`, `diff`, `reset`, `revert`, `cherry-pick`, …) |
| 227 | - Config key paths (`config get`, `config set`) |
| 228 | - All `plumbing` subcommands (12) |
| 229 | - All `midi` subcommands (25) with MIDI-specific descriptions |
| 230 | - All `code` subcommands (28) with code-analysis descriptions |
| 231 | - All `coord` subcommands (6) |
| 232 | - Common flags per command (`log`, `diff`, `commit`, `status`, …) |
| 233 | |
| 234 | Branch, tag, and remote lookups read directly from `.muse/` — no subprocess. |
| 235 | File-argument completions (`midi notes <tab>`) use `muse plumbing ls-files`. |
| 236 | |
| 237 | --- |
| 238 | |
| 239 | ## Visual Tools (fzf optional) |
| 240 | |
| 241 | Install [fzf](https://github.com/junegunn/fzf) to unlock the interactive tools: |
| 242 | |
| 243 | ```zsh |
| 244 | muse-commit-browser # fzf over commit log; ↵=checkout, ctrl-d=diff, ctrl-y=copy SHA |
| 245 | muse-branch-picker # fzf over branch list; ↵=checkout, ctrl-d=delete |
| 246 | muse-stash-browser # fzf over stash list; ↵=pop, ctrl-d=drop |
| 247 | ``` |
| 248 | |
| 249 | Install [bat](https://github.com/sharkdp/bat) or [delta](https://github.com/dandavison/delta) for syntax-highlighted diff output: |
| 250 | |
| 251 | ```zsh |
| 252 | muse-diff-preview # muse diff with bat/delta highlighting |
| 253 | muse-diff-preview feature/x # diff against a branch |
| 254 | ``` |
| 255 | |
| 256 | Graph and timeline visualisations work without fzf: |
| 257 | |
| 258 | ```zsh |
| 259 | muse-graph # colorised commit graph (domain-themed, SemVer badges) |
| 260 | muse-timeline 30 # visual vertical timeline of last 30 commits |
| 261 | ``` |
| 262 | |
| 263 | --- |
| 264 | |
| 265 | ## Agent Mode |
| 266 | |
| 267 | Muse is built for AI agents as first-class authors. The plugin reflects this throughout. |
| 268 | |
| 269 | ### Machine-readable prompt |
| 270 | |
| 271 | ```zsh |
| 272 | export MUSE_AGENT_MODE=1 |
| 273 | ``` |
| 274 | |
| 275 | Prompt becomes: `[midi|main|dirty:3|no-merge|agent:coding-assistant]` |
| 276 | |
| 277 | All workflow functions emit JSON instead of human text. `$MUSE_CONTEXT_JSON` is emitted to stderr before every prompt so an orchestrating process can read it. |
| 278 | |
| 279 | ### `$MUSE_CONTEXT_JSON` |
| 280 | |
| 281 | Always set when inside a muse repo. Updated before every prompt. Format: |
| 282 | |
| 283 | ```json |
| 284 | { |
| 285 | "schema_version": 1, |
| 286 | "domain": "midi", |
| 287 | "branch": "main", |
| 288 | "repo_root": "/path/to/project", |
| 289 | "dirty": true, |
| 290 | "dirty_count": 3, |
| 291 | "merging": false, |
| 292 | "merge_branch": null, |
| 293 | "conflict_count": 0, |
| 294 | "user_type": "human", |
| 295 | "agent_id": null, |
| 296 | "model_id": null, |
| 297 | "semver": "minor" |
| 298 | } |
| 299 | ``` |
| 300 | |
| 301 | Agents can read this without running any subcommands: |
| 302 | |
| 303 | ```zsh |
| 304 | echo $MUSE_CONTEXT_JSON | python3 -m json.tool |
| 305 | ``` |
| 306 | |
| 307 | ### `muse-context` |
| 308 | |
| 309 | Human-readable, TOML, or JSON context block designed for AI context injection: |
| 310 | |
| 311 | ```zsh |
| 312 | muse-context # human-readable block |
| 313 | muse-context --json # pretty-printed JSON |
| 314 | muse-context --toml # TOML |
| 315 | muse-context --oneline # single-line summary: "midi:main dirty:3 commit:a1b2c3d4" |
| 316 | ``` |
| 317 | |
| 318 | ### Agent session management |
| 319 | |
| 320 | ```zsh |
| 321 | # Begin a session — sets identity vars, starts JSONL audit log |
| 322 | muse-agent-session claude-4.6-sonnet coding-assistant |
| 323 | |
| 324 | # Every muse commit in this shell now auto-injects agent_id + model_id |
| 325 | muse-agent-commit "Refactor velocity normalisation" |
| 326 | # equivalent to: muse commit -m "…" --agent-id coding-assistant --model-id claude-4.6-sonnet |
| 327 | |
| 328 | # End the session |
| 329 | muse-agent-end |
| 330 | ``` |
| 331 | |
| 332 | ### Session logs |
| 333 | |
| 334 | Every `muse` command run during an agent session is logged to a `.jsonl` file: |
| 335 | |
| 336 | ```json |
| 337 | {"t":"2026-03-20T14:23:11Z","cmd":"muse commit -m refactor","cwd":"/proj","domain":"midi","branch":"feat/x","pid":1234} |
| 338 | {"t":"2026-03-20T14:23:11Z","event":"cmd_end","exit":0,"elapsed_ms":312} |
| 339 | ``` |
| 340 | |
| 341 | List and replay sessions: |
| 342 | |
| 343 | ```zsh |
| 344 | muse-sessions # list recent sessions |
| 345 | muse-sessions ~/.muse/sessions/... # replay a session log |
| 346 | ``` |
| 347 | |
| 348 | --- |
| 349 | |
| 350 | ## Dependencies |
| 351 | |
| 352 | | Tool | Required | Purpose | |
| 353 | |---|---|---| |
| 354 | | `python3` | Yes | JSON/TOML parsing (always available — Muse requires it) | |
| 355 | | `fzf` | Optional | Interactive branch picker, commit browser, stash browser | |
| 356 | | `bat` | Optional | Syntax-highlighted diff preview | |
| 357 | | `delta` | Optional | Enhanced diff pager | |
| 358 | | ZSH 5.0+ | Yes | Associative arrays, `autoload -Uz`, `EPOCHSECONDS` | |
| 359 | |
| 360 | --- |
| 361 | |
| 362 | ## Starship users |
| 363 | |
| 364 | Add a custom command to `~/.config/starship.toml`: |
| 365 | |
| 366 | ```toml |
| 367 | [custom.muse] |
| 368 | command = "muse-context --oneline 2>/dev/null" |
| 369 | when = "test -d .muse || git rev-parse --git-dir 2>/dev/null | grep -q .muse" |
| 370 | shell = ["zsh", "-c"] |
| 371 | format = "[$output]($style) " |
| 372 | style = "bold magenta" |
| 373 | ``` |
| 374 | |
| 375 | This gives you the one-line context (`midi:main dirty:3 commit:a1b2c3d4`) in any Starship prompt. |