#compdef muse # Zsh completion for the Muse CLI. # # Installation (pick one): # 1. Symlink into a directory already on $fpath: # ln -sf /path/to/muse/completions/_muse ~/.zsh/completions/_muse # 2. Oh-My-Zsh users — copy or symlink into the custom completions dir: # ln -sf /path/to/muse/completions/_muse \ # ~/.oh-my-zsh/completions/_muse # 3. Add the completions directory directly to $fpath in ~/.zshrc: # fpath=(/path/to/muse/completions $fpath) # autoload -Uz compinit && compinit # # After installing, reload completions: # exec zsh OR autoload -Uz compinit && compinit -u # --------------------------------------------------------------------------- # Helpers — read repo state directly from the filesystem; no subprocess. # --------------------------------------------------------------------------- # Walk up from $PWD and return the .muse directory path, or "" if not found. _muse_dot_muse() { local dir="$PWD" while [[ "$dir" != "/" ]]; do if [[ -f "$dir/.muse/repo.json" ]]; then print -r -- "$dir/.muse" return 0 fi dir="${dir:h}" done return 1 } # Emit branch names by listing .muse/refs/heads/ — zero subprocesses. _muse_branches() { local dot_muse dot_muse="$(_muse_dot_muse)" || return local heads_dir="$dot_muse/refs/heads" [[ -d "$heads_dir" ]] || return local -a branches # ${heads_dir}/**/*(N:t) recurses and strips directory prefix. branches=(${(f)"$(find "$heads_dir" -type f 2>/dev/null | sed "s|$heads_dir/||")"}) (( ${#branches} )) && _describe 'branches' branches } # Emit remote names from .muse/config.toml — zero subprocesses. _muse_remotes() { local dot_muse dot_muse="$(_muse_dot_muse)" || { _values 'remote' 'origin' 'local'; return; } local cfg="$dot_muse/config.toml" local -a remotes if [[ -f "$cfg" ]]; then # Extract remote names from [remotes.] section headers. remotes=(${(f)"$(grep -E '^\[remotes\.' "$cfg" 2>/dev/null \ | sed -E 's/\[remotes\.([^]]+)\]/\1/')"}) fi if (( ${#remotes} )); then _describe 'remotes' remotes else _values 'remote' 'origin' 'local' fi } # --------------------------------------------------------------------------- # Command lists (kept in sync with muse/cli/app.py) # --------------------------------------------------------------------------- local -a _muse_top_cmds=( 'plumbing:Tier 1 machine-readable plumbing commands' 'init:Initialise a new Muse repository' 'status:Show working-tree drift against HEAD' 'log:Display commit history' 'commit:Record the current state as a new version' 'diff:Compare working tree against HEAD or two commits' 'show:Inspect a commit' 'branch:List, create, or delete branches' 'checkout:Switch branches or restore from a commit' 'merge:Three-way merge a branch into HEAD' 'reset:Move HEAD to a prior commit' 'revert:Undo a prior commit' 'cherry-pick:Apply a specific commit on top of HEAD' 'stash:Shelve uncommitted changes' 'tag:Attach and query semantic tags' 'push:Upload commits and objects to a remote' 'pull:Fetch from a remote and merge' 'fetch:Download commits from a remote' 'clone:Create a local copy of a remote repository' 'remote:Manage remote connections' 'auth:Identity management' 'hub:MuseHub fabric connection' 'config:Local repository configuration' 'domains:Domain plugin dashboard' 'attributes:Display .museattributes rules' 'annotate:Attach CRDT annotations to a commit' 'blame:Show per-line commit provenance' 'reflog:History of HEAD and branch-ref movements' 'rerere:Reuse recorded conflict resolutions' 'gc:Remove unreachable objects' 'archive:Export a historical snapshot' 'bisect:Binary search commit history' 'worktree:Manage multiple branch checkouts' 'workspace:Compose multiple Muse repositories' 'rebase:Replay commits onto a new base' 'clean:Remove untracked files' 'describe:Label a commit by its nearest tag' 'shortlog:Summarise history by author or agent' 'verify:Check repository integrity' 'snapshot:Explicit snapshot management' 'bundle:Pack commits into a portable bundle' 'content-grep:Search tracked file content' 'whoami:Show current identity' 'check:Run domain invariant checks' 'cat:Print a single tracked symbol' 'midi:MIDI domain semantic commands' 'code:Code domain semantic commands' 'coord:Multi-agent coordination commands' ) local -a _muse_plumbing_cmds=( 'hash-object:SHA-256 a file and optionally store it' 'cat-object:Emit raw bytes of a stored object' 'rev-parse:Resolve branch or HEAD to a commit ID' 'ls-files:List tracked files and object IDs' 'read-commit:Emit full commit JSON' 'read-snapshot:Emit full snapshot JSON' 'commit-tree:Create a commit from an explicit snapshot ID' 'update-ref:Move a branch HEAD to a specific commit' 'commit-graph:Emit the commit DAG as JSON' 'pack-objects:Build a PackBundle to stdout' 'unpack-objects:Read a PackBundle from stdin' 'ls-remote:List remote branch heads' 'merge-base:Find the lowest common ancestor of two commits' 'snapshot-diff:Diff two snapshot manifests' 'domain-info:Inspect the active domain plugin' 'show-ref:List all refs and their commit IDs' 'check-ignore:Test paths against .museignore rules' 'check-attr:Query merge-strategy attributes for paths' 'verify-object:Re-hash stored objects to detect corruption' 'symbolic-ref:Read or write HEAD symbolic reference' 'for-each-ref:Iterate all refs with rich commit metadata' 'name-rev:Map commit IDs to descriptive names' 'check-ref-format:Validate branch or ref names' 'verify-pack:Verify the integrity of a PackBundle' ) local -a _muse_midi_cmds=( 'notes:List notes in a MIDI track' 'note-log:Show note-level history' 'note-blame:Show per-note commit provenance' 'harmony:Analyse harmonic content' 'piano-roll:Render a piano-roll view' 'note-hotspots:Find most-changed note regions' 'velocity-profile:Plot velocity distribution' 'transpose:Shift pitches by semitones' 'mix:Merge MIDI tracks' 'query:Query note events' 'check:Run MIDI domain checks' 'rhythm:Analyse rhythmic patterns' 'scale:Detect active scales' 'contour:Analyse melodic contour' 'density:Compute note density over time' 'tension:Compute harmonic tension' 'cadence:Detect cadence points' 'motif:Find recurring motifs' 'voice-leading:Analyse voice-leading quality' 'instrumentation:List instrumentation per track' 'tempo:Display tempo map' 'compare:Compare two MIDI commits' 'quantize:Snap notes to a rhythmic grid' 'humanize:Add expressive timing variation' 'invert:Invert intervals around an axis' 'retrograde:Reverse note sequence' 'arpeggiate:Spread chords into arpeggios' 'normalize:Normalise velocity levels' 'shard:Split a MIDI file into shards' 'agent-map:Show which agents modified which notes' 'find-phrase:Search for a melodic phrase' ) local -a _muse_code_cmds=( # Navigation & reading 'cat:Print a symbol'\''s source' 'symbols:List symbols in the snapshot' 'grep:Search symbol names and bodies' 'find-symbol:Search across ALL commits and branches for a symbol' 'deps:Show the import graph or call graph for a file or symbol' 'languages:List language composition of the repository' # History & provenance 'symbol-log:Track a symbol through the entire commit history' 'blame:Show which commit last touched a symbol' 'lineage:Trace a symbol'\''s full provenance chain' 'detect-refactor:Detect renames, extractions, and other refactors' 'compare:Deep semantic comparison between two snapshots' 'age:How much original implementation remains (evolutionary distance)' 'narrative:Plain-English story of a symbol'\''s life' # Analysis 'impact:Transitive blast-radius of changing a symbol' 'dead:Find unreachable dead-code candidates' 'coverage:Which methods of a class are actually called' 'hotspots:Symbols that change most often (churn leaderboard)' 'stable:Symbols unchanged the longest' 'coupling:Files that always change together (hidden coupling)' 'entangle:Symbol pairs that change together but have no import link' 'gravity:Structural weight — how much depends on each symbol' 'blast-risk:Pre-release risk leaderboard: impact × churn × test-gap' 'velocity:Symbol-growth rate by module' 'predict:Predict which symbols will change next' 'contract:Infer behavioral contract from call sites and tests' # API & public surface 'api-surface:Show public API surface and changes between commits' 'codemap:Semantic topology map of the repository' 'clones:Find exact and near-duplicate symbols' 'semantic-test-coverage:Static symbol-level test coverage (no test runner)' # Surgical modification 'patch:Replace exactly one symbol'\''s source' 'rename:Rename a symbol across definition, imports, and call sites' 'checkout-symbol:Restore a symbol to a prior version' 'semantic-cherry-pick:Cherry-pick specific named symbols from a commit' # Query DSL 'query:Query the symbol graph with a predicate DSL' 'query-history:Search commit history for symbols matching a predicate' 'code-query:Run a structured code query' # Quality gates 'breakage:Detect structural breakage in the working tree vs HEAD' 'invariants:Check architectural invariants from .muse/invariants.toml' 'code-check:Run code-domain invariant checks' 'index:Manage the optional local symbol index' # Staging 'add:Stage files for commit' 'reset:Unstage files' ) local -a _muse_coord_cmds=( 'reserve:Reserve a symbol for exclusive editing' 'intent:Declare editing intent' 'forecast:Forecast merge conflicts' 'plan-merge:Plan a coordinated merge' 'shard:Partition work across agents' 'reconcile:Reconcile diverged agent branches' ) # --------------------------------------------------------------------------- # Main dispatcher # --------------------------------------------------------------------------- _muse() { local curcontext="$curcontext" state line typeset -A opt_args _arguments -C \ '(-h --help)'{-h,--help}'[Show help]' \ '(-V --version)'{-V,--version}'[Show version]' \ '1: :->command' \ '*:: :->args' \ && return 0 case $state in command) _describe 'muse commands' _muse_top_cmds ;; args) case $words[1] in # --- sub-namespaces ----------------------------------------- plumbing) _arguments '1: :->sub' '*:: :->plumbing_args' case $state in sub) _describe 'plumbing commands' _muse_plumbing_cmds ;; esac ;; midi) _arguments '1: :->sub' case $state in sub) _describe 'midi commands' _muse_midi_cmds ;; esac ;; code) _arguments -C '1: :->sub' '*:: :->code_args' case $state in sub) _describe 'code commands' _muse_code_cmds ;; code_args) case $words[1] in add|reset) _files ;; find-symbol) _arguments \ '--name[Symbol name or prefix]:name' \ '--kind[Symbol kind]:kind:(function class method variable)' \ '--hash[Content hash prefix (≥4 chars)]:hash' \ '--file[Restrict to file]:file:_files' \ '--branch[Search within branch]:branch:_muse_branches' \ '--since[Ignore commits before DATE]:date' \ '--until[Ignore commits after DATE]:date' \ '--limit[Maximum results]:n' \ '--first[Show only first appearance]' \ '--last[Show only last appearance]' \ '--all-branches[Check HEAD of every branch]' \ '--count[Print count only]' \ '--json[JSON output]' ;; impact) _arguments \ '1:address' \ '(-c --commit)'{-c,--commit}'[Commit ref]:ref:_muse_branches' \ '--depth[Maximum traversal depth]:n' \ '--forward[Show callees instead of callers]' \ '--compare[Diff blast radius vs ref]:ref:_muse_branches' \ '--file[Restrict callers to file]:file:_files' \ '--count[Print count only]' \ '--json[JSON output]' ;; dead) _arguments \ '(-c --commit)'{-c,--commit}'[Commit ref]:ref:_muse_branches' \ '--file[Restrict to file]:file:_files' \ '--lang[Restrict to language]:lang:(python javascript typescript)' \ '--workers[Parallel workers]:n' \ '--max-file-bytes[Max file size]:bytes' \ '--allowlist[Path to allowlist JSON]:file:_files' \ '--delete[Delete dead symbols from disk]' \ '--compare[Diff dead candidates vs ref]:ref:_muse_branches' \ '--count[Print count only]' \ '--save-allowlist[Write candidates to file]:file:_files' \ '--json[JSON output]' ;; coverage) _arguments \ '1:class address' \ '(-c --commit)'{-c,--commit}'[Commit ref]:ref:_muse_branches' \ '--compare[Diff coverage vs ref]:ref:_muse_branches' \ '--exclude-dunder[Exclude __dunder__ methods]' \ '--exclude-private[Exclude _private methods]' \ '--min-callers[Minimum callers to count as covered]:n' \ '--exclude-self[Exclude calls from same file]' \ '--count[Print count only]' \ '--json[JSON output]' ;; lineage) _arguments \ '1:address' \ '(-c --commit)'{-c,--commit}'[Walk commits reachable from ref]:ref:_muse_branches' \ '(-b --branch)'{-b,--branch}'[Walk this branch only]:branch:_muse_branches' \ '--since[Ignore commits before DATE]:date' \ '--until[Ignore commits after DATE]:date' \ '--filter[Show only events of this kind]:kind:(created modified deleted renamed_from moved_from copied_from)' \ '--stability[Print stability score]' \ '--count[Print event count only]' \ '--json[JSON output]' ;; api-surface) _arguments \ '(-c --commit)'{-c,--commit}'[Commit ref]:ref:_muse_branches' \ '--diff[Show changes since ref]:ref:_muse_branches' \ '--kind[Filter by kind]:kind:(function class method variable)' \ '--file[Restrict to file]:file:_files' \ '--count[Print count only]' \ '--json[JSON output]' ;; codemap) _arguments \ '(-c --commit)'{-c,--commit}'[Commit ref]:ref:_muse_branches' \ '--top[Show top N modules]:n' \ '--json[JSON output]' ;; clones) _arguments \ '(-c --commit)'{-c,--commit}'[Commit ref]:ref:_muse_branches' \ '--tier[Detection tier]:tier:(exact near both)' \ '--json[JSON output]' ;; deps) _arguments \ '1:address or file' \ '(-c --commit)'{-c,--commit}'[Commit ref]:ref:_muse_branches' \ '--forward[Show dependencies (callees)]' \ '--reverse[Show dependents (callers)]' \ '--json[JSON output]' ;; query) _arguments \ '1:predicate' \ '--all-commits[Search all commits]' \ '--json[JSON output]' ;; query-history) _arguments \ '1:predicate' \ '--from[Start ref]:ref:_muse_branches' \ '--to[End ref]:ref:_muse_branches' \ '--json[JSON output]' ;; detect-refactor) _arguments '--json[JSON output]' ;; checkout-symbol) _arguments \ '1:address' \ '(-c --commit)'{-c,--commit}'[Source commit ref]:ref:_muse_branches' \ '--dry-run[Show changes without applying]' ;; semantic-cherry-pick) _arguments \ '*:address' \ '--from[Source commit ref]:ref:_muse_branches' \ '--dry-run[Show changes without applying]' \ '--json[JSON output]' ;; breakage) _arguments \ '(-l --language)'{-l,--language}'[Restrict to language]:lang:(Python JavaScript TypeScript)' \ '(-c --commit)'{-c,--commit}'[Check against this ref instead of HEAD]:ref:_muse_branches' \ '(-p --path)'{-p,--path}'[Only check files matching glob]:pattern:_files' \ '--strict[Treat warnings as errors]' \ '--json[JSON output]' ;; invariants) _arguments \ '(-c --commit)'{-c,--commit}'[Check a historical snapshot]:ref:_muse_branches' \ '--json[JSON output]' ;; hotspots|stable|coupling|symbol-log|blame|compare) _arguments \ '(-c --commit)'{-c,--commit}'[Commit ref]:ref:_muse_branches' \ '--json[JSON output]' ;; age) _arguments \ '--top[Show top N symbols]:n' \ '--sort[Sort by field]:field:(age churn rewrite)' \ '--kind[Filter by kind]:kind:(function class method variable)' \ '--file[Restrict to file]:file:_files' \ '--since[Ignore commits before DATE]:date' \ '--max-commits[Maximum commits to scan]:n' \ '--explain[Show per-symbol explanation]' \ '--json[JSON output]' ;; blast-risk) _arguments \ '--top[Show top N symbols]:n' \ '--since[Ignore commits before DATE]:date' \ '--max-commits[Maximum commits to scan]:n' \ '--kind[Filter by kind]:kind:(function class method variable)' \ '--file[Restrict to file]:file:_files' \ '--min-risk[Minimum risk score threshold]:score' \ '--explain[Show per-symbol explanation]' \ '--json[JSON output]' ;; contract) _arguments \ '1:address' \ '--max-commits[Maximum commits to scan]:n' \ '--json[JSON output]' ;; entangle) _arguments \ '--top[Show top N pairs]:n' \ '--min-co-changes[Minimum co-change count]:n' \ '--min-rate[Minimum co-change rate (0–1)]:rate' \ '--symbol[Restrict to pairs involving symbol]:address' \ '--since[Ignore commits before DATE]:date' \ '--max-commits[Maximum commits to scan]:n' \ '--include-same-file[Include same-file pairs]' \ '--json[JSON output]' ;; gravity) _arguments \ '--explain[Show per-symbol dependents]' \ '--top[Show top N symbols]:n' \ '--sort[Sort by field]:field:(gravity name)' \ '--depth[Traversal depth]:n' \ '--kind[Filter by kind]:kind:(function class method variable)' \ '--file[Restrict to file]:file:_files' \ '--min-gravity[Minimum gravity threshold]:n' \ '--include-tests[Include test files in dependents]' \ '--json[JSON output]' ;; narrative) _arguments \ '1:address' \ '--format[Output format]:fmt:(text markdown)' \ '--show-source[Include source diffs]' \ '--since[Ignore commits before DATE]:date' \ '--max-commits[Maximum commits to scan]:n' \ '--json[JSON output]' ;; predict) _arguments \ '--horizon[Prediction window in commits]:n' \ '--max-commits[Commits to train on]:n' \ '--top[Show top N predictions]:n' \ '--min-confidence[Minimum confidence (0–1)]:score' \ '--explain[Show prediction rationale]' \ '--kind[Filter by kind]:kind:(function class method variable)' \ '--file[Restrict to file]:file:_files' \ '--module-depth[Module granularity]:n' \ '--json[JSON output]' ;; rename) _arguments \ '1:address' \ '2:new name' \ '--scope[Rename scope]:scope:(repo file)' \ '--max-files[Maximum files to update]:n' \ '(-y --yes)'{-y,--yes}'[Skip confirmation]' \ '--dry-run[Show changes without applying]' \ '--force[Rename even with uncommitted changes]' \ '--json[JSON output]' ;; semantic-test-coverage) _arguments \ '--file[Restrict to file]:file:_files' \ '--kind[Filter by kind]:kind:(function class method variable)' \ '--transitive[Follow transitive calls]' \ '--depth[Transitive depth]:n' \ '--uncovered-only[Show only uncovered symbols]' \ '--show-tests[List covering tests per symbol]' \ '--min-coverage[Minimum coverage ratio (0–1)]:ratio' \ '--json[JSON output]' ;; velocity) _arguments \ '--window[Time window (commits)]:n' \ '--top[Show top N modules]:n' \ '--predict[Include growth prediction]' \ '--since[Ignore commits before DATE]:date' \ '--max-commits[Maximum commits to scan]:n' \ '--json[JSON output]' ;; grep) _arguments \ '1:pattern' \ '--json[JSON output]' ;; cat) _arguments '1:address' ;; patch) _arguments \ '1:address' \ '2:file:_files' ;; esac ;; esac ;; coord) _arguments '1: :->sub' case $state in sub) _describe 'coord commands' _muse_coord_cmds ;; esac ;; # --- branch-aware commands ----------------------------------- checkout) _arguments \ '(-b --branch)'{-b,--branch}'[Create new branch]:branch:_muse_branches' \ '(-f --force)'{-f,--force}'[Force checkout]' \ '1: :_muse_branches' ;; merge) _arguments \ '--no-commit[Do not auto-commit after merge]' \ '--squash[Squash into a single commit]' \ '--strategy[Merge strategy]:strategy:(ours theirs union)' \ '1: :_muse_branches' ;; branch) _arguments \ '(-d --delete)'{-d,--delete}'[Delete a branch]:branch:_muse_branches' \ '(-D --force-delete)'{-D,--force-delete}'[Force-delete a branch]:branch:_muse_branches' \ '(-m --move)'{-m,--move}'[Rename a branch]:branch:_muse_branches' \ '1:: :_muse_branches' ;; cherry-pick) _arguments \ '(-n --no-commit)'{-n,--no-commit}'[Stage without committing]' \ '1: :_muse_branches' ;; rebase) _arguments \ '--onto[New base branch]:branch:_muse_branches' \ '1: :_muse_branches' ;; # --- remote-aware commands ----------------------------------- push) _arguments \ '(-b --branch)'{-b,--branch}'[Branch to push]:branch:_muse_branches' \ '(-u --set-upstream)'{-u,--set-upstream}'[Set upstream tracking]' \ '--force[Force push even if remote diverged]' \ '1:: :_muse_remotes' \ '2:: :_muse_branches' ;; pull) _arguments \ '(-b --branch)'{-b,--branch}'[Branch to pull into]:branch:_muse_branches' \ '--rebase[Rebase instead of merge]' \ '--no-commit[Do not auto-commit after merge]' \ '1:: :_muse_remotes' \ '2:: :_muse_branches' ;; fetch) _arguments \ '(-b --branch)'{-b,--branch}'[Specific branch to fetch]:branch:_muse_branches' \ '--all[Fetch from all configured remotes]' \ '1:: :_muse_remotes' ;; clone) _arguments \ '(-b --branch)'{-b,--branch}'[Branch to check out after clone]:branch' \ '--name[Override repo directory name]:name' \ '1: :_urls' ;; # --- simple flag commands ------------------------------------ commit) _arguments \ '(-m --message)'{-m,--message}'[Commit message]:message' \ '--allow-empty[Allow a commit with no changes]' \ '--sign[Sign the commit]' \ '(-f --format)'{-f,--format}'[Output format]:fmt:(text json)' ;; status) _arguments \ '(-s --short)'{-s,--short}'[Condensed output]' \ '--porcelain[Machine-readable output]' \ '(-b --branch)'{-b,--branch}'[Show branch only]' \ '(-f --format)'{-f,--format}'[Output format]:fmt:(text json)' ;; log) _arguments \ '(-n --max-count)'{-n,--max-count}'[Limit number of commits]:count' \ '--oneline[Compact one-line output]' \ '--graph[Show commit graph]' \ '(-f --format)'{-f,--format}'[Output format]:fmt:(text json)' ;; diff) _arguments \ '--stat[Show diffstat summary]' \ '(-f --format)'{-f,--format}'[Output format]:fmt:(text json)' \ '1:: :_muse_branches' \ '2:: :_muse_branches' ;; show) _arguments \ '(-f --format)'{-f,--format}'[Output format]:fmt:(text json)' \ '1:: :_muse_branches' ;; reset) _arguments \ '--hard[Discard working tree changes]' \ '--soft[Keep working tree changes staged]' \ '1:: :_muse_branches' ;; tag) _arguments \ '(-d --delete)'{-d,--delete}'[Delete a tag]' \ '(-l --list)'{-l,--list}'[List tags]' \ '(-f --format)'{-f,--format}'[Output format]:fmt:(text json)' ;; stash) _arguments \ '1:: :(push pop list drop show apply)' ;; remote) _arguments \ '1:: :(add remove list rename set-url show)' ;; auth) _arguments \ '1:: :(login logout whoami)' ;; hub) _arguments \ '1:: :(connect disconnect status ping)' ;; config) _arguments \ '1:: :(show get set edit)' ;; esac ;; esac } _muse "$@"