app.py
python
sha256:24d9cb81ac1dd071e5d98862c740ad101180588361b4f96a790af79a99aed901
docs: architectural plan for unified-store snapshot-content…
Sonnet 5
10 days ago
| 1 | """Muse CLI — entry point for the ``muse`` console script. |
| 2 | |
| 3 | Two-tier command architecture |
| 4 | ------------------------------- |
| 5 | |
| 6 | **Tier 1 — Core + Low-level** (top-level ``muse …``) |
| 7 | All VCS commands live here — both human-facing and the machine-readable, |
| 8 | JSON-outputting primitives that scripts and agents use. Agents reach |
| 9 | every command in one token. |
| 10 | |
| 11 | **Tier 2 — Semantic** (``muse midi …``, ``muse code …``, ``muse coord …``) |
| 12 | Domain-specific commands that interpret multidimensional state. Each |
| 13 | sub-namespace is served by the corresponding ``muse/plugins/`` plugin. |
| 14 | |
| 15 | Top-level commands (alphabetical):: |
| 16 | |
| 17 | agent annotate archive attributes auth |
| 18 | bisect blame branch bundle |
| 19 | cat cat-object check check-attr |
| 20 | check-ignore check-ref-format checkout cherry-pick |
| 21 | clean clone code commit |
| 22 | commit-graph commit-tree config conflicts |
| 23 | content-grep coord describe diff |
| 24 | domain domain-info domains fetch for-each-ref |
| 25 | gc hash-object hub init |
| 26 | log ls-files ls-remote merge |
| 27 | merge-base mist midi name-rev pack-objects |
| 28 | migrate path pull push rebase read-commit |
| 29 | read-snapshot reflog release remote |
| 30 | reset resolve revert rev-parse |
| 31 | rm |
| 32 | shortlog read show-ref sign |
| 33 | snapshot |
| 34 | snapshot-diff shelf status symbolic-ref |
| 35 | tag unpack-objects update-ref verify |
| 36 | verify-object verify-pack workspace |
| 37 | worktree |
| 38 | |
| 39 | Identity & hub fabric:: |
| 40 | |
| 41 | auth keygen muse auth keygen [--hub HUB] |
| 42 | auth register muse auth register [--hub HUB] [--handle NAME] [--agent] |
| 43 | auth whoami muse auth whoami [--json] |
| 44 | auth logout muse auth logout [--hub HUB] |
| 45 | hub connect muse hub connect <url> |
| 46 | hub status muse hub status [--json] |
| 47 | hub disconnect muse hub disconnect |
| 48 | hub ping muse hub ping |
| 49 | config show muse config show [--json] |
| 50 | config get muse config get <key> |
| 51 | config set muse config set <key> <value> |
| 52 | config edit muse config edit |
| 53 | |
| 54 | MIDI domain commands (``muse midi …``, alphabetical) — disabled pending rollout:: |
| 55 | |
| 56 | agent-map arpeggiate cadence check |
| 57 | compare contour density find-phrase |
| 58 | harmony humanize instrumentation invert |
| 59 | mix motif normalize note-blame |
| 60 | note-hotspots note-log notes piano-roll |
| 61 | quantize query retrograde rhythm |
| 62 | scale shard tempo tension |
| 63 | transpose velocity-profile voice-leading |
| 64 | |
| 65 | Code domain commands (``muse code …``, alphabetical):: |
| 66 | |
| 67 | add age api-surface blast-risk |
| 68 | blame breakage cat checkout-symbol |
| 69 | clones code-check code-query codemap |
| 70 | compare contract coupling coverage |
| 71 | dead deps detect-refactor docs |
| 72 | entangle find-symbol gravity grep |
| 73 | hotspots impact index invariants |
| 74 | languages lineage narrative patch |
| 75 | predict query query-history rename |
| 76 | reset semantic-cherry-pick semantic-test-coverage stable |
| 77 | symbol-log symbols test type |
| 78 | velocity |
| 79 | |
| 80 | Coordination commands (``muse coord …``, alphabetical):: |
| 81 | |
| 82 | cancel-task claim complete dag |
| 83 | enqueue fail-task forecast gc |
| 84 | heartbeat intent list plan-merge |
| 85 | reconcile release reserve shard |
| 86 | sync tasks watch |
| 87 | """ |
| 88 | |
| 89 | import argparse |
| 90 | import difflib |
| 91 | import os |
| 92 | import signal |
| 93 | import sys |
| 94 | |
| 95 | from muse.cli.domain_command_registry import register_namespace |
| 96 | |
| 97 | # Restore the default SIGPIPE handler so that piping muse output to commands |
| 98 | # that read partial output (head, grep, jq, etc.) exits cleanly instead of |
| 99 | # raising BrokenPipeError and printing a Python traceback. This matters most |
| 100 | # for agents that routinely pipe muse JSON output to filters. |
| 101 | if hasattr(signal, "SIGPIPE"): |
| 102 | signal.signal(signal.SIGPIPE, signal.SIG_DFL) |
| 103 | |
| 104 | from muse.cli.commands import ( |
| 105 | age as age_cmd, |
| 106 | agent as agent_cmd, |
| 107 | agent_config, |
| 108 | # agent_map, # midi — disabled until midi rollout |
| 109 | annotate, |
| 110 | api_surface, |
| 111 | apply, |
| 112 | apply_patch, |
| 113 | archive, |
| 114 | # arpeggiate, # midi — disabled until midi rollout |
| 115 | attributes, |
| 116 | auth, |
| 117 | trust, |
| 118 | bisect, |
| 119 | blame, |
| 120 | blast_risk, |
| 121 | branch, |
| 122 | breakage, |
| 123 | bridge, |
| 124 | bundle, |
| 125 | # cadence, # midi — disabled until midi rollout |
| 126 | cat, |
| 127 | core_cat, |
| 128 | check, |
| 129 | checkout, |
| 130 | checkout_symbol, |
| 131 | cherry_pick, |
| 132 | clean, |
| 133 | clone, |
| 134 | clones, |
| 135 | code_check, |
| 136 | code_query, |
| 137 | code_stage, |
| 138 | codemap, |
| 139 | commit, |
| 140 | compare, |
| 141 | config_cmd, |
| 142 | conflicts, |
| 143 | content_grep, |
| 144 | contract, |
| 145 | # contour, # midi — disabled until midi rollout |
| 146 | coord_gc, |
| 147 | coord_sync, |
| 148 | core_blame, |
| 149 | coupling, |
| 150 | coverage, |
| 151 | dag as dag_cmd, |
| 152 | dead, |
| 153 | # density, # midi — disabled until midi rollout |
| 154 | deps, |
| 155 | describe, |
| 156 | detect_refactor, |
| 157 | diff, |
| 158 | docs_cmd, |
| 159 | domain_cmd, |
| 160 | domains, |
| 161 | entangle, |
| 162 | fetch, |
| 163 | # find_phrase, # midi — disabled until midi rollout |
| 164 | find_symbol, |
| 165 | forecast, |
| 166 | gc, |
| 167 | gravity, |
| 168 | grep, |
| 169 | harmony, |
| 170 | # midi_harmony, # midi — disabled until midi rollout |
| 171 | heartbeat_coord, |
| 172 | hotspots, |
| 173 | hub, |
| 174 | # humanize, # midi — disabled until midi rollout |
| 175 | impact, |
| 176 | index_rebuild, |
| 177 | init, |
| 178 | # instrumentation, # midi — disabled until midi rollout |
| 179 | intent, |
| 180 | invariants, |
| 181 | # invert, # midi — disabled until midi rollout |
| 182 | languages, |
| 183 | lineage, |
| 184 | list_coord, |
| 185 | log, |
| 186 | maintenance, |
| 187 | merge, |
| 188 | merge_base, |
| 189 | migrate, |
| 190 | migrate_cmd, |
| 191 | mist as mist_cmd, |
| 192 | mv, |
| 193 | # midi_check, # midi — disabled until midi rollout |
| 194 | # midi_compare, # midi — disabled until midi rollout |
| 195 | # midi_query, # midi — disabled until midi rollout |
| 196 | # midi_shard, # midi — disabled until midi rollout |
| 197 | # mix, # midi — disabled until midi rollout |
| 198 | # motif_detect, # midi — disabled until midi rollout |
| 199 | narrative, |
| 200 | # note_blame, # midi — disabled until midi rollout |
| 201 | # note_hotspots, # midi — disabled until midi rollout |
| 202 | # note_log, # midi — disabled until midi rollout |
| 203 | # notes, # midi — disabled until midi rollout |
| 204 | patch, |
| 205 | path_cmd, |
| 206 | # piano_roll, # midi — disabled until midi rollout |
| 207 | plan_merge, |
| 208 | predict, |
| 209 | prune, |
| 210 | pull, |
| 211 | push, |
| 212 | # quantize, # midi — disabled until midi rollout |
| 213 | query, |
| 214 | query_history, |
| 215 | range_diff, |
| 216 | rebase, |
| 217 | reconcile, |
| 218 | reflog, |
| 219 | release as release_cmd, |
| 220 | release_coord, |
| 221 | remote, |
| 222 | rename, |
| 223 | reserve, |
| 224 | reset, |
| 225 | resolve as resolve_cmd, |
| 226 | restore, |
| 227 | rm, |
| 228 | # retrograde, # midi — disabled until midi rollout |
| 229 | revert, |
| 230 | # rhythm, # midi — disabled until midi rollout |
| 231 | # scale_detect, # midi — disabled until midi rollout |
| 232 | semantic_cherry_pick, |
| 233 | semantic_test_coverage, |
| 234 | shard, |
| 235 | shortlog, |
| 236 | read, |
| 237 | sign, |
| 238 | snapshot_cmd, |
| 239 | shelf, |
| 240 | social as social_cmd, |
| 241 | stable, |
| 242 | status, |
| 243 | switch, |
| 244 | symbol_log, |
| 245 | symlog as symlog_cmd, |
| 246 | symbols, |
| 247 | label, |
| 248 | tag, |
| 249 | task_queue as task_queue_cmd, |
| 250 | # tempo, # midi — disabled until midi rollout |
| 251 | # tension, # midi — disabled until midi rollout |
| 252 | test_cmd, |
| 253 | # transpose, # midi — disabled until midi rollout |
| 254 | type_cmd, |
| 255 | velocity, |
| 256 | # velocity_normalize, # midi — disabled until midi rollout |
| 257 | # velocity_profile, # midi — disabled until midi rollout |
| 258 | verify, |
| 259 | # voice_leading, # midi — disabled until midi rollout |
| 260 | watch_coord, |
| 261 | |
| 262 | workspace, |
| 263 | worktree, |
| 264 | ) |
| 265 | |
| 266 | from muse.cli.commands import ( |
| 267 | apply_patch, |
| 268 | cat_object, |
| 269 | check_attr, |
| 270 | count_objects, |
| 271 | check_ignore, |
| 272 | check_ref_format, |
| 273 | commit_graph, |
| 274 | commit_tree, |
| 275 | domain_info, |
| 276 | for_each_ref, |
| 277 | format_patch, |
| 278 | hash_object, |
| 279 | ls_files, |
| 280 | ls_remote, |
| 281 | ls_tree, |
| 282 | merge_base, |
| 283 | merge_tree, |
| 284 | name_rev, |
| 285 | pack_objects, |
| 286 | patch_id, |
| 287 | read_commit, |
| 288 | read_snapshot, |
| 289 | rev_list, |
| 290 | rev_parse, |
| 291 | show_ref, |
| 292 | snapshot_diff, |
| 293 | sparse_checkout, |
| 294 | symbolic_ref, |
| 295 | unpack_objects, |
| 296 | update_ref, |
| 297 | verify_commit, |
| 298 | verify_object, |
| 299 | verify_pack, |
| 300 | verify_tag, |
| 301 | ) |
| 302 | |
| 303 | def _no_command(parser: argparse.ArgumentParser) -> None: |
| 304 | """Print help when no subcommand is provided.""" |
| 305 | parser.print_help() |
| 306 | raise SystemExit(0) |
| 307 | |
| 308 | class _MuseArgumentParser(argparse.ArgumentParser): |
| 309 | """ArgumentParser subclass that adds 'did you mean?' suggestions on error. |
| 310 | |
| 311 | When the user mistypes a subcommand (e.g. ``muse comit``), argparse |
| 312 | normally prints only "argument COMMAND: invalid choice: 'comit'". This |
| 313 | subclass intercepts that error, checks for close matches among the |
| 314 | registered subcommand names, and appends a suggestion line so agents and |
| 315 | humans can self-correct without consulting the full ``--help`` output. |
| 316 | |
| 317 | Example output:: |
| 318 | |
| 319 | muse: error: argument COMMAND: invalid choice: 'comit' |
| 320 | Did you mean: commit? |
| 321 | """ |
| 322 | |
| 323 | def error(self, message: str) -> None: |
| 324 | # Extract the unknown token from messages like: |
| 325 | # "argument COMMAND: invalid choice: 'comit' (choose from ...)" |
| 326 | unknown: str | None = None |
| 327 | if "invalid choice:" in message: |
| 328 | try: |
| 329 | start = message.index("'") + 1 |
| 330 | end = message.index("'", start) |
| 331 | unknown = message[start:end] |
| 332 | except ValueError: |
| 333 | pass |
| 334 | |
| 335 | if unknown is not None: |
| 336 | choices: list[str] = [] |
| 337 | for action in self._actions: |
| 338 | if isinstance(action, argparse._SubParsersAction): |
| 339 | choices.extend(action.choices.keys()) |
| 340 | suggestions = difflib.get_close_matches(unknown, choices, n=3, cutoff=0.6) |
| 341 | if suggestions: |
| 342 | message = f"{message}\n Did you mean: {', '.join(suggestions)}?" |
| 343 | |
| 344 | super().error(message) |
| 345 | |
| 346 | def main(argv: list[str] | None = None) -> None: |
| 347 | """Parse arguments and dispatch to the appropriate command handler. |
| 348 | |
| 349 | Exit codes |
| 350 | ---------- |
| 351 | 0 Success — command completed normally. |
| 352 | 1 Runtime error — e.g. ``-C DIR`` directory does not exist. |
| 353 | 2 Argument error — unknown command, missing required argument, or a |
| 354 | namespace command (``code``/``midi``/``coord``) was invoked without a |
| 355 | subcommand. argparse prints the error before this function raises. |
| 356 | |
| 357 | Flag processing order |
| 358 | --------------------- |
| 359 | 1. ``--version`` / ``-V`` — print version and exit 0, no further parsing. |
| 360 | 2. ``-C DIR`` — ``os.chdir(DIR)`` before dispatch; exit 1 on |
| 361 | ``OSError`` (non-existent or permission-denied). |
| 362 | 3. Subcommand dispatch — ``args.func(args)`` called once. |
| 363 | |
| 364 | No-subcommand behaviour |
| 365 | ----------------------- |
| 366 | ``muse`` with no subcommand prints the help text and exits 0. |
| 367 | ``muse code`` / ``muse midi`` / ``muse coord`` with no subcommand exits 2 |
| 368 | (argparse enforces ``subs.required = True``). |
| 369 | |
| 370 | Calling from tests |
| 371 | ------------------ |
| 372 | Pass an explicit ``argv`` list to avoid reading ``sys.argv``:: |
| 373 | |
| 374 | from tests.cli_test_helper import CliRunner |
| 375 | result = CliRunner().invoke(None, ["--version"]) |
| 376 | assert "muse" in result.output |
| 377 | """ |
| 378 | parser = _MuseArgumentParser( |
| 379 | prog="muse", |
| 380 | description="Muse — domain-agnostic version control for multidimensional state.", |
| 381 | formatter_class=argparse.RawDescriptionHelpFormatter, |
| 382 | ) |
| 383 | parser.add_argument( |
| 384 | "--version", "-V", "-v", |
| 385 | action="store_true", |
| 386 | dest="show_version", |
| 387 | help="Print 'muse <version>' to stdout and exit 0. " |
| 388 | "Agents: parse with output.split()[1] for the bare version string.", |
| 389 | ) |
| 390 | parser.add_argument( |
| 391 | "-C", |
| 392 | metavar="DIR", |
| 393 | dest="chdir", |
| 394 | default=None, |
| 395 | help="Change to DIR before running the command (DIR must exist).", |
| 396 | ) |
| 397 | |
| 398 | subparsers = parser.add_subparsers(dest="command", metavar="COMMAND") |
| 399 | |
| 400 | # ``muse help`` — ergonomic alias for ``muse --help``. |
| 401 | # Agents and humans reach for ``help`` by reflex; honour it. |
| 402 | subparsers.add_parser("help", help="Show this help message and exit.") |
| 403 | |
| 404 | # ------------------------------------------------------------------ |
| 405 | # All top-level commands — alphabetical order. |
| 406 | # All top-level commands are interleaved alphabetically |
| 407 | # so that ``muse --help`` presents one sorted list. |
| 408 | # ------------------------------------------------------------------ |
| 409 | agent_cmd.register(subparsers) |
| 410 | agent_config.register(subparsers) |
| 411 | annotate.register(subparsers) |
| 412 | apply.register(subparsers) # "apply" |
| 413 | apply_patch.register(subparsers) # "apply-patch" |
| 414 | archive.register(subparsers) |
| 415 | attributes.register(subparsers) |
| 416 | auth.register(subparsers) |
| 417 | bisect.register(subparsers) |
| 418 | core_blame.register(subparsers) # "blame" |
| 419 | branch.register(subparsers) |
| 420 | bridge.register(subparsers) |
| 421 | bundle.register(subparsers) |
| 422 | core_cat.register(subparsers) # "cat" — file-level |
| 423 | cat_object.register(subparsers) # "cat-object" |
| 424 | check.register(subparsers) |
| 425 | check_attr.register(subparsers) # "check-attr" |
| 426 | check_ignore.register(subparsers) # "check-ignore" |
| 427 | check_ref_format.register(subparsers) # "check-ref-format" |
| 428 | checkout.register(subparsers) |
| 429 | cherry_pick.register(subparsers) # "cherry-pick" |
| 430 | clean.register(subparsers) |
| 431 | clone.register(subparsers) |
| 432 | |
| 433 | # ── code namespace (muse code …) ────────────────────────────────── |
| 434 | code_parser = subparsers.add_parser( |
| 435 | "code", |
| 436 | help="[Tier 3] Code domain semantic commands — symbol graph, call graph, and provenance.", |
| 437 | description="Code domain semantic commands.", |
| 438 | ) |
| 439 | code_subs = code_parser.add_subparsers(dest="code_command", metavar="CODE_COMMAND") |
| 440 | code_subs.required = True |
| 441 | |
| 442 | code_stage.register_add(code_subs) # "add" |
| 443 | age_cmd.register(code_subs) # "age" |
| 444 | api_surface.register(code_subs) # "api-surface" |
| 445 | blast_risk.register(code_subs) # "blast-risk" |
| 446 | blame.register(code_subs) # "blame" |
| 447 | breakage.register(code_subs) # "breakage" |
| 448 | cat.register(code_subs) # "cat" |
| 449 | checkout_symbol.register(code_subs) # "checkout-symbol" |
| 450 | clones.register(code_subs) # "clones" |
| 451 | code_check.register(code_subs) # "code-check" |
| 452 | code_query.register(code_subs) # "code-query" |
| 453 | codemap.register(code_subs) # "codemap" |
| 454 | compare.register(code_subs) # "compare" |
| 455 | contract.register(code_subs) # "contract" |
| 456 | coupling.register(code_subs) # "coupling" |
| 457 | coverage.register(code_subs) # "coverage" |
| 458 | dead.register(subparsers) # "dead" — register wraps in code namespace |
| 459 | deps.register(code_subs) # "deps" |
| 460 | detect_refactor.register(code_subs) # "detect-refactor" |
| 461 | docs_cmd.register(code_subs) # "docs" |
| 462 | entangle.register(code_subs) # "entangle" |
| 463 | find_symbol.register(code_subs) # "find-symbol" |
| 464 | gravity.register(code_subs) # "gravity" |
| 465 | grep.register(code_subs) # "grep" |
| 466 | hotspots.register(code_subs) # "hotspots" |
| 467 | impact.register(code_subs) # "impact" |
| 468 | index_rebuild.register(code_subs) # "index" |
| 469 | invariants.register(code_subs) # "invariants" |
| 470 | languages.register(code_subs) # "languages" |
| 471 | lineage.register(code_subs) # "lineage" |
| 472 | migrate.register(code_subs) # "migrate" |
| 473 | narrative.register(code_subs) # "narrative" |
| 474 | patch.register(code_subs) # "patch" |
| 475 | predict.register(code_subs) # "predict" |
| 476 | query.register(code_subs) # "query" |
| 477 | query_history.register(code_subs) # "query-history" |
| 478 | rename.register(code_subs) # "rename" |
| 479 | code_stage.register_reset(code_subs) # "reset" |
| 480 | semantic_cherry_pick.register(code_subs) # "semantic-cherry-pick" |
| 481 | semantic_test_coverage.register(code_subs) # "semantic-test-coverage" |
| 482 | stable.register(code_subs) # "stable" |
| 483 | symbol_log.register(code_subs) # "symbol-log" |
| 484 | symbols.register(code_subs) # "symbols" |
| 485 | test_cmd.register(code_subs) # "test" |
| 486 | type_cmd.register(code_subs) # "type" |
| 487 | velocity.register(code_subs) # "velocity" |
| 488 | register_namespace("code", code_subs.choices.keys()) |
| 489 | # ───────────────────────────────────────────────────────────────── |
| 490 | |
| 491 | commit.register(subparsers) |
| 492 | commit_graph.register(subparsers) # "commit-graph" |
| 493 | commit_tree.register(subparsers) # "commit-tree" |
| 494 | config_cmd.register(subparsers) # "config" |
| 495 | conflicts.register(subparsers) |
| 496 | content_grep.register(subparsers) # "content-grep" |
| 497 | count_objects.register(subparsers) # "count-objects" |
| 498 | |
| 499 | # ── coord namespace (muse coord …) ─────────────────────────────── |
| 500 | coord_parser = subparsers.add_parser( |
| 501 | "coord", |
| 502 | help="[Tier 3] Multi-agent coordination commands — reservations, intent, conflict forecasting.", |
| 503 | description="Multi-agent coordination commands.", |
| 504 | ) |
| 505 | coord_subs = coord_parser.add_subparsers(dest="coord_command", metavar="COORD_COMMAND") |
| 506 | coord_subs.required = True |
| 507 | |
| 508 | task_queue_cmd.register_cancel_task(coord_subs) # "cancel-task" |
| 509 | task_queue_cmd.register_claim(coord_subs) # "claim" |
| 510 | task_queue_cmd.register_complete(coord_subs) # "complete" |
| 511 | dag_cmd.register(coord_subs) # "dag" |
| 512 | task_queue_cmd.register_enqueue(coord_subs) # "enqueue" |
| 513 | task_queue_cmd.register_fail_task(coord_subs) # "fail-task" |
| 514 | forecast.register(coord_subs) # "forecast" |
| 515 | coord_gc.register(coord_subs) # "gc" |
| 516 | heartbeat_coord.register(coord_subs) # "heartbeat" |
| 517 | intent.register(coord_subs) # "intent" |
| 518 | list_coord.register(coord_subs) # "list" |
| 519 | plan_merge.register(coord_subs) # "plan-merge" |
| 520 | reconcile.register(coord_subs) # "reconcile" |
| 521 | release_coord.register(coord_subs) # "release" |
| 522 | reserve.register(coord_subs) # "reserve" |
| 523 | shard.register(coord_subs) # "shard" |
| 524 | coord_sync.register(coord_subs) # "sync" |
| 525 | task_queue_cmd.register_tasks(coord_subs) # "tasks" |
| 526 | watch_coord.register(coord_subs) # "watch" |
| 527 | # ───────────────────────────────────────────────────────────────── |
| 528 | |
| 529 | describe.register(subparsers) |
| 530 | diff.register(subparsers) |
| 531 | domain_cmd.register(subparsers) # "domain" |
| 532 | domain_info.register(subparsers) # "domain-info" |
| 533 | domains.register(subparsers) |
| 534 | fetch.register(subparsers) |
| 535 | for_each_ref.register(subparsers) # "for-each-ref" |
| 536 | format_patch.register(subparsers) # "format-patch" |
| 537 | gc.register(subparsers) |
| 538 | hash_object.register(subparsers) # "hash-object" |
| 539 | hub.register(subparsers) |
| 540 | init.register(subparsers) |
| 541 | log.register(subparsers) |
| 542 | ls_files.register(subparsers) # "ls-files" |
| 543 | ls_remote.register(subparsers) # "ls-remote" |
| 544 | ls_tree.register(subparsers) # "ls-tree" |
| 545 | maintenance.register(subparsers) # "maintenance" |
| 546 | merge.register(subparsers) |
| 547 | migrate_cmd.register(subparsers) # "migrate" |
| 548 | merge_base.register(subparsers) # "merge-base" |
| 549 | merge_tree.register(subparsers) # "merge-tree" |
| 550 | mist_cmd.register(subparsers) # "mist" |
| 551 | mv.register(subparsers) |
| 552 | harmony.register(subparsers) |
| 553 | social_cmd.register(subparsers) # "social" |
| 554 | |
| 555 | # ── midi namespace (muse midi …) — disabled until midi rollout ─── |
| 556 | # midi_parser = subparsers.add_parser( |
| 557 | # "midi", |
| 558 | # help="[Tier 3] MIDI domain semantic commands.", |
| 559 | # description="MIDI domain semantic commands — analysis, transformation, and multi-agent.", |
| 560 | # ) |
| 561 | # midi_subs = midi_parser.add_subparsers(dest="midi_command", metavar="MIDI_COMMAND") |
| 562 | # midi_subs.required = True |
| 563 | # |
| 564 | # agent_map.register(midi_subs) # "agent-map" |
| 565 | # arpeggiate.register(midi_subs) # "arpeggiate" |
| 566 | # cadence.register(midi_subs) # "cadence" |
| 567 | # midi_check.register(midi_subs) # "check" |
| 568 | # midi_compare.register(midi_subs) # "compare" |
| 569 | # contour.register(midi_subs) # "contour" |
| 570 | # density.register(midi_subs) # "density" |
| 571 | # find_phrase.register(midi_subs) # "find-phrase" |
| 572 | # harmony.register(midi_subs) # "harmony" |
| 573 | # humanize.register(midi_subs) # "humanize" |
| 574 | # instrumentation.register(midi_subs) # "instrumentation" |
| 575 | # invert.register(midi_subs) # "invert" |
| 576 | # mix.register(midi_subs) # "mix" |
| 577 | # motif_detect.register(midi_subs) # "motif" |
| 578 | # velocity_normalize.register(midi_subs) # "normalize" |
| 579 | # note_blame.register(midi_subs) # "note-blame" |
| 580 | # note_hotspots.register(midi_subs) # "note-hotspots" |
| 581 | # note_log.register(midi_subs) # "note-log" |
| 582 | # notes.register(midi_subs) # "notes" |
| 583 | # piano_roll.register(midi_subs) # "piano-roll" |
| 584 | # quantize.register(midi_subs) # "quantize" |
| 585 | # midi_query.register(midi_subs) # "query" |
| 586 | # retrograde.register(midi_subs) # "retrograde" |
| 587 | # rhythm.register(midi_subs) # "rhythm" |
| 588 | # scale_detect.register(midi_subs) # "scale" |
| 589 | # midi_shard.register(midi_subs) # "shard" |
| 590 | # tempo.register(midi_subs) # "tempo" |
| 591 | # tension.register(midi_subs) # "tension" |
| 592 | # transpose.register(midi_subs) # "transpose" |
| 593 | # velocity_profile.register(midi_subs) # "velocity-profile" |
| 594 | # voice_leading.register(midi_subs) # "voice-leading" |
| 595 | # ───────────────────────────────────────────────────────────────── |
| 596 | |
| 597 | name_rev.register(subparsers) # "name-rev" |
| 598 | pack_objects.register(subparsers) # "pack-objects" |
| 599 | patch_id.register(subparsers) # "patch-id" |
| 600 | path_cmd.register(subparsers) # "path" |
| 601 | prune.register(subparsers) |
| 602 | pull.register(subparsers) |
| 603 | push.register(subparsers) |
| 604 | range_diff.register(subparsers) # "range-diff" |
| 605 | rebase.register(subparsers) |
| 606 | read_commit.register(subparsers) # "read-commit" |
| 607 | read_snapshot.register(subparsers) # "read-snapshot" |
| 608 | reflog.register(subparsers) |
| 609 | release_cmd.register(subparsers) # "release" |
| 610 | remote.register(subparsers) |
| 611 | reset.register(subparsers) |
| 612 | resolve_cmd.register(subparsers) # "resolve" |
| 613 | restore.register(subparsers) |
| 614 | rm.register(subparsers) |
| 615 | revert.register(subparsers) |
| 616 | rev_list.register(subparsers) # "rev-list" |
| 617 | rev_parse.register(subparsers) # "rev-parse" |
| 618 | shortlog.register(subparsers) |
| 619 | read.register(subparsers) # "read" |
| 620 | show_ref.register(subparsers) # "show-ref" |
| 621 | sign.register(subparsers) # "sign" |
| 622 | snapshot_cmd.register(subparsers) # "snapshot" |
| 623 | snapshot_diff.register(subparsers) # "snapshot-diff" |
| 624 | sparse_checkout.register(subparsers) # "sparse-checkout" |
| 625 | shelf.register(subparsers) |
| 626 | status.register(subparsers) |
| 627 | switch.register(subparsers) |
| 628 | symlog_cmd.register(subparsers) # "symlog" |
| 629 | symbolic_ref.register(subparsers) # "symbolic-ref" |
| 630 | label.register(subparsers) |
| 631 | tag.register(subparsers) |
| 632 | trust.register(subparsers) |
| 633 | unpack_objects.register(subparsers) # "unpack-objects" |
| 634 | update_ref.register(subparsers) # "update-ref" |
| 635 | verify.register(subparsers) |
| 636 | verify_commit.register(subparsers) # "verify-commit" |
| 637 | verify_object.register(subparsers) # "verify-object" |
| 638 | verify_pack.register(subparsers) # "verify-pack" |
| 639 | verify_tag.register(subparsers) # "verify-tag" |
| 640 | workspace.register(subparsers) |
| 641 | worktree.register(subparsers) |
| 642 | |
| 643 | # ------------------------------------------------------------------ |
| 644 | # Parse and dispatch |
| 645 | # ------------------------------------------------------------------ |
| 646 | args = parser.parse_args(argv) |
| 647 | |
| 648 | if args.show_version: |
| 649 | from muse._version import __version__ |
| 650 | # Output format is stable: exactly "muse <version>\n" on stdout, exit 0. |
| 651 | # Agents and scripts can parse the version without --json: |
| 652 | # |
| 653 | # version = subprocess.check_output(["muse", "--version"]).decode().split()[1] |
| 654 | # |
| 655 | # The token at index 1 (space-split) is always the bare semver string. |
| 656 | # This format will not change without a major-version bump. |
| 657 | print(f"muse {__version__}") |
| 658 | raise SystemExit(0) |
| 659 | |
| 660 | if args.chdir is not None: |
| 661 | from muse.core.validation import sanitize_display |
| 662 | try: |
| 663 | os.chdir(args.chdir) |
| 664 | except OSError as exc: |
| 665 | safe_dir = sanitize_display(args.chdir) |
| 666 | print(f"muse: cannot change to directory '{safe_dir}': {exc}", file=sys.stderr) |
| 667 | raise SystemExit(1) |
| 668 | |
| 669 | if args.command is None or args.command == "help": |
| 670 | _no_command(parser) |
| 671 | return |
| 672 | |
| 673 | if not hasattr(args, "func"): |
| 674 | # A namespace command (midi/code/coord) was given without a subcommand. |
| 675 | # argparse already printed an error above via subs.required = True. |
| 676 | raise SystemExit(2) |
| 677 | |
| 678 | args.func(args) |
| 679 | |
| 680 | if __name__ == "__main__": |
| 681 | main() |
File History
1 commit
sha256:24d9cb81ac1dd071e5d98862c740ad101180588361b4f96a790af79a99aed901
docs: architectural plan for unified-store snapshot-content…
Sonnet 5
10 days ago