Developer Docs Code Intelligence
PHASE 04

Code Intelligence

Muse builds a live, content-addressed symbol graph over every commit. Nine analytics commands query that graph at three altitudes — symbol, file, and repo — giving you blast radius, churn leaderboards, entangled pairs, dead code candidates, and composite risk scores without running a single test.

Before reaching for grep or reading a whole file, ask which command answers the question:

Instead of…UseAnswers
grep / read whole filemuse code grep "Name" --jsonWhere is this symbol declared?
Guessing downstream weightmuse code gravity --jsonWhich symbols are load-bearing infrastructure?
Tracking instability by feelmuse code hotspots --jsonWhich symbols change most often?
Manually tracing callersmuse code impact "file.py::Fn" --jsonWhat breaks if I change this?
Hoping nothing calls itmuse code dead --high-confidence-only --jsonIs this actually dead code?

Symbol graph

Every tracked source file is parsed into a SymbolTree — a dict[address, SymbolRecord] — using TreeSitter grammars (17 languages) plus Python's own ast module for byte-exact normalization. The tree is content-addressed: identical bytes always produce an identical tree.

SymbolRecord fields

FieldTypeDescription
kindSymbolKindOne of: function async_function class method async_method variable import interface type_alias enum struct trait namespace module file directory
namestrBare symbol name (compute_total)
qualified_namestrDotted path for nested symbols (Invoice.compute_total)
content_idsha256:<64-hex>Hash of normalized name + signature + body — changes on any edit
body_hashsha256:<64-hex>Hash of body only — stable across renames
signature_idsha256:<64-hex>Hash of "name(args)->return" — stable when only implementation changes
metadata_idsha256:<64-hex>Hash of decorators / async / bases (v2+); "" for pre-v2 records
canonical_keystr{file}#{scope}#{kind}#{name}#{lineno} — stable handle for history queries
lineno / end_linenointSource location in the file

Language support

Symbol extraction uses TreeSitter grammars for most languages and Python's ast module for Python (byte-exact normalization). Languages marked rc4 were added in the rc4 release.

LanguageParserSymbol kinds extractedNotes
Pythonstdlib ast + TreeSitterfunction, async_function, class, method, async_method, variable, importByte-exact normalization via ast.unparse
TypeScriptTreeSitterfunction, class, interface, type_alias, enumJSX variants supported
JavaScriptTreeSitterfunction, class, variableCommonJS + ESM
GoTreeSitterfunction, method, struct, interface, type_aliasReceiver methods tracked on struct
RustTreeSitterfunction, struct, trait, enum, type_alias, moduleimpl blocks resolved to struct
JavaTreeSitterclass, interface, method, enum
C#TreeSitterclass, interface, method, struct, enum, namespace
C / C++TreeSitterfunction, struct, class, enumHeader + impl deduplication
RubyTreeSitterclass, method, module
KotlinTreeSitterfunction, class, interface, enumrc4
SwiftTreeSitterfunction, class, struct, protocol, enumrc4
Bash / ZshTreeSitterfunctionrc4
HTMLTreeSitterelement (id/class anchors)rc4
CSS / SCSSTreeSitterclass, id, variable, mixinrc4
MarkdownTreeSittersection (heading-anchored)
TOML / YAML / JSONTreeSittertop-level key
UnknownLine-basedsection (heuristic)Fallback — no AST

Symbol addresses

Every analytics command uses a stable symbol address as its primary key. The format is <workspace-relative-path>::<qualified-name>.

# top-level function
src/billing.py::compute_total

# nested method
src/models.py::Invoice.save

# import pseudo-symbol (filtered by most commands by default)
src/models.py::import::json

Pass any address to muse code cat to read its body, to muse code impact to map its callers, or to muse code gravity with --explain to trace its full transitive dependency set.

muse code cat "src/billing.py::compute_total" --json
muse code impact "src/billing.py::compute_total" --json
muse code gravity --explain "src/billing.py::compute_total" --json

Symbol cache

Parsing is expensive. Muse keeps a persistent msgpack cache at .muse/symbol_cache.msgpack, keyed by sha256 of file bytes. A cache hit brings full-snapshot analysis from ~1,300 ms down to ~22 ms (60× speedup).

# cache format
{
  "version": 1,
  "entries": {
    "<sha256-hex-of-file-bytes>": { /* SymbolTree */ }
  }
}

The cache is an internal implementation detail — it is never committed (covered by .museignore) and is rebuilt automatically on any file change.

Gravity — structural weight

Every codebase has load-bearing symbols — the ones where a one-line change ripples into a dozen failures. gravity_pct quantifies that: it's the fraction of all production symbols that transitively depend on this one. Before any refactor, run muse code gravity --top 20 --json. The symbols at the top of that list are the ones that warrant a test plan, a feature branch, and a second opinion.

Algorithm

  1. Load the committed HEAD snapshot and build a forward call graph via AST.
  2. Invert the graph: callee_bare_name → [caller_address, …].
  3. BFS from each symbol through the reversed graph to enumerate all transitive dependents.
  4. Divide the count by total_production_symbols and multiply by 100.
muse code gravity --json                          # top 20 by gravity_pct
muse code gravity --top 5 --json
muse code gravity --explain "src/core.py::Fn" --json

Output fields

FieldTypeDescription
addressstrFull symbol address
namestrBare symbol name
kindstrSymbol kind
filestrSource file path
gravity_pctfloat0–100; percentage of production symbols that transitively depend on this one
direct_dependentsintCount of depth-1 callers
transitive_dependentsintTotal count across all BFS depths
max_depthintMaximum BFS depth reached
depth_distributiondict{"1": N, "2": M, …} — string-keyed depth → dependent count
Test-file callers are excluded by default. Pass --include-tests to include them in the gravity calculation.

Hotspots — commit churn

A symbol that appears in 40 commits isn't just busy — it's unstable. Every churn spike is a merge-conflict waiting to happen and a test that needs updating. muse code hotspots ranks your symbols by commit count so you know exactly which ones are being hammered, and can decide whether that churn is earned complexity or a design problem worth fixing.

Algorithm

  1. BFS through commit history (both merge parents), up to --max-commits (default 10,000).
  2. For each commit, extract symbol-level ops from commit.structured_delta["ops"].
  3. Increment changes[address] once per commit (multiple ops on the same symbol in one commit count as one).
  4. Filter import pseudo-symbols (::import::) by default.
muse code hotspots --json
muse code hotspots --top 20 --json
muse code hotspots --from v1.0.0 --to HEAD --json

Output

{
  "from_ref": null,
  "to_ref": "HEAD",
  "commits_analysed": 847,
  "truncated": false,
  "filters": { "kind": null, "include_imports": false, "min_changes": 1 },
  "hotspots": [
    { "address": "src/billing.py::compute_total", "changes": 43 },
    { "address": "src/models.py::Invoice.save",   "changes": 31 }
  ]
}

Entanglement — hidden coupling

Two symbols in different files, no import between them — and yet they show up together in 80% of commits. That's hidden coupling, and it's the kind that produces "why did this break?" postmortems. muse code entangle finds those pairs by mining co-change frequency from commit history, filtering out structurally-linked pairs you'd expect. What's left is the coupling your architecture doesn't know about yet.

Algorithm

  1. Single BFS pass through history. For each commit, collect the set of symbol addresses that changed.
  2. Build a co-change counter: (addr_a, addr_b) → commit_count (pairs sorted lexicographically).
  3. Compute: co_change_rate = co_changes / min(active_a, active_b).
  4. Check structural linkage: scan HEAD's import pseudo-symbols to see if file_a imports file_b or vice versa.
  5. Commits touching more than 200 symbols are skipped (mass-refactors distort the signal).
muse code entangle --json
muse code entangle --min-rate 0.8 --json   # only pairs with ≥80% co-change rate
muse code entangle --top 10 --json

Output fields per pair

FieldTypeDescription
symbol_a / symbol_bstrFull addresses of the two symbols
file_a / file_bstrSource files
same_fileboolTrue if both symbols live in the same file
structurally_linkedboolTrue if one file imports the other — expected coupling
co_changesintNumber of commits where both changed
commits_both_activeintOpportunity window: min of each symbol's active commit count
co_change_ratefloat 0–1co_changes / commits_both_active
a_in_test / b_in_testboolWhether the symbol lives in a test file
Pairs with structurally_linked: true are expected co-changers (caller + callee). Focus investigation on pairs where structurally_linked is false and co_change_rate is high — those reveal hidden coupling worth extracting.

Dead code detection

Dead code isn't just waste — it's maintenance surface you're paying for without getting anything back. muse code dead finds symbols that are never referenced and whose module nobody imports, with framework entry points (FastAPI routes, Celery tasks, Flask views) pre-excluded so you don't spend time chasing false positives. High-confidence candidates are safe to delete; medium-confidence ones may be public API exports worth a second look.

Algorithm

  1. Parse HEAD snapshot into a SymbolTree. Collect all ast.Name.id and ast.Attribute.attr references across every source file in parallel (8 workers, 512 KB per-file limit).
  2. Build a reference set of all bare names that appear at least once as a call or attribute access.
  3. Build a module-import set of all file paths that are imported by at least one other file (via import pseudo-symbols).
  4. Inject implicit references for framework entry points — routes, tasks, signal handlers — so they are never falsely flagged.
  5. A symbol is high-confidence dead if its bare name is not in the reference set AND its module is not in the import set.
  6. A symbol is medium-confidence dead if its bare name is not referenced but its module IS imported — it may be a public API export.
muse code dead --high-confidence-only --json
muse code dead --include-tests --json
muse code dead --file src/legacy.py --json

Confidence levels

ConfidenceConditionAction
high Not referenced AND module not imported Safe to delete — verify with muse code impact first
medium Not referenced, but module IS imported elsewhere May be a public API export — inspect callers before deleting

Output fields per candidate

FieldTypeDescription
addressstrSymbol address
file_pathstrSource file
kindstrSymbol kind
referencedboolAlways false for dead candidates
module_importedboolWhether the module is imported anywhere
confidencestr"high" or "medium"
reasonstrHuman-readable explanation of why it was flagged

Impact — blast radius

Before you touch a symbol, know exactly what you're touching. muse code impact BFS-walks the reversed call graph and returns every caller grouped by depth — direct callers at depth 1, their callers at depth 2, and so on. Framework entry points are flagged explicitly so you can see when a change reaches a live API route. Total ≥ 10 callers is high-impact; treat it accordingly.

Algorithm

  1. Parse HEAD snapshot into a forward call graph: caller_address → [callee_bare_name, …].
  2. Invert: build callee_bare_name → [caller_address, …].
  3. BFS from the target symbol's bare name through the inverted graph, up to --depth hops (0 = unlimited).
  4. Group results by BFS depth into a string-keyed dict: "1" → direct callers, "2" → callers of callers, etc.
  5. Classify any caller that is a framework endpoint (FastAPI route, Celery task) as an entry_point.
  6. When --compare <ref> is given, repeat analysis against that ref's snapshot and diff the caller sets.
muse code impact "src/billing.py::compute_total" --json
muse code impact "src/billing.py::compute_total" --depth 2 --json
muse code impact "src/billing.py::compute_total" --compare HEAD~10 --json

Output

{
  "mode": "reverse",
  "address": "src/billing.py::compute_total",
  "target_name": "compute_total",
  "commit_id": "sha256:abc…",
  "depth_limit": 0,   // 0 = unlimited
  "total": 14,
  "blast_radius": {
    "1": ["src/api/orders.py::create_order", "src/api/cart.py::checkout"],
    "2": ["src/api/orders.py::update_order", "tests/test_billing.py::test_total"]
  },
  "entry_points": ["src/api/orders.py::create_order"],
  "added_callers": [],    // present when --compare used
  "removed_callers": [],
  "net_change": 0
}
Depth keys are strings, not integers: "1", "2", … This is intentional — JSON object keys are always strings, and Muse's codec enforces it. Never coerce with int(key) — iterate with .items().

Impact heuristics: total ≥ 10 → high-impact; ≥ 3 → medium; < 3 → low.

Deps — dependency graph

muse code deps gives you the full import and call picture for any file or symbol — outbound (what this thing depends on) and inbound (what depends on it). Pass a file path for module-level import edges; pass a symbol address for call-level edges. Add --transitive to follow the chain as deep as it goes.

Algorithm

  1. File mode: collect all import pseudo-symbols from the target file → imports. Scan every other file's import pseudo-symbols for references to the target module → imported_by.
  2. Symbol forward mode: parse the target symbol's body via AST; extract all Name.id and Attribute.attr call references and match them against known symbol names in HEAD → calls. With --transitive, BFS through the production call graph up to --depth hops.
  3. Symbol reverse mode: invert the call graph and BFS from the target, same depth logic as impact. Returns called_by.
# file mode — what does this file import, and who imports it?
muse code deps src/billing.py --json

# symbol mode — what does this function call, and who calls it?
muse code deps "src/billing.py::compute_total" --json
muse code deps "src/billing.py::compute_total" --transitive --json
ModeKey fieldsDescription
File imports, imported_by Module names imported by this file; files that import this file
Symbol forward calls, by_depth Direct callees at depth 1; all transitive callees by string-keyed depth
Symbol reverse called_by, by_depth Direct callers; BFS up to --depth N or unlimited with --transitive

Breakage — structural invariants

The fastest feedback loop before a commit. muse code breakage parses your working tree and compares it against the committed HEAD snapshot — catching stale imports and removed public methods before they land in history. No test runner, no CI round-trip. If it exits clean, your structural invariants are intact.

Algorithm

  1. Parse all working-tree source files into a SymbolTree (uncommitted changes included).
  2. Load the committed HEAD SymbolTree from the symbol cache.
  3. Stale import check: for each working-tree file, compare its import pseudo-symbols against the HEAD symbol name set. Any imported name absent from HEAD and not defined locally → warning.
  4. Removed public method check: for every class present in both HEAD and working tree, diff its public methods (any method not prefixed with _). Any HEAD public method missing from the working tree → error.
  5. Exit 1 if any errors exist. With --strict, exit 1 on warnings too.
CheckSeverityCondition
Stale import warning Working-tree file imports a name that exists nowhere in HEAD and is not defined locally
Removed public method error A class present in both HEAD and working tree is missing a public method that HEAD had
muse code breakage --json
muse code breakage --strict --json   # exit 1 on warnings too

Output

{
  "commit": "sha256:3ce6…",
  "branch": "task/my-feature",
  "file_count": 42,
  "issues": [
    {
      "issue_type": "removed_public_method",
      "file_path": "src/models.py",
      "description": "Class 'Invoice' lost public method 'compute_total'",
      "severity": "error"
    }
  ],
  "total": 1,
  "errors": 1,
  "warning_count": 0
}

Semantic test coverage

Coverage without running tests. muse code semantic-test-coverage maps test functions to production symbols via the call graph — no instrumentation, no pytest, no CI. A symbol is covered if any test calls it directly or reaches it transitively. Use --min-coverage 80 as a CI gate; use --uncovered-only to find exactly what's missing.

Algorithm

  1. Partition HEAD symbols into test functions (file path matches test_* / *_test.py) and production symbols.
  2. For each test function, extract all Name.id references — direct calls at depth 1.
  3. Mark any production symbol whose bare name appears in any test's reference set as covered at depth 1.
  4. At depth N > 1: extend through the production call graph — a symbol is covered at depth N if any test covers one of its callers at depth N−1. BFS terminates at --depth hops or 10 hops for --transitive.
  5. Compute: coverage_pct = covered_symbols / total_production_symbols × 100.
  6. With --min-coverage N, exit 1 if coverage_pct < N.

Depth model

DepthMeaning
1 (default)Test function directly calls the production symbol by bare name — conservative, high precision
N > 1Extends coverage N−1 hops through the production call graph; test_process_order → process_order → Invoice.compute_total covers Invoice.compute_total at depth 2
--transitiveUnlimited BFS depth — max 10 hops
muse code semantic-test-coverage --json
muse code semantic-test-coverage --file src/billing.py --json
muse code semantic-test-coverage --uncovered-only --json
muse code semantic-test-coverage --min-coverage 80 --json  # exit 1 if below threshold

Per-symbol output

{
  "address": "src/billing.py::compute_total",
  "name": "compute_total",
  "kind": "function",
  "covered": true,
  "test_functions": [
    "tests/test_billing.py::test_total_with_discount",
    "tests/test_billing.py::test_total_empty_cart"
  ]
}

The top-level object also carries total_symbols, covered_symbols, coverage_pct, total_test_functions, and total_production_files.

Blast risk — composite score

Gravity tells you what depends on a symbol. Hotspots tell you how often it changes. Blast risk combines both — plus test gap and coupling — into a single 0–100 score. High gravity plus high churn plus no test coverage is a red flag. muse code blast-risk --top 10 gives you the ten symbols most likely to cause pain on your next deploy.

Algorithm

  1. For every production symbol, collect four raw signals: direct caller count (impact_raw), commit-churn count (churn_raw), fraction of callers in production vs. test code (test_gap_raw, 0–1), and co-change file-partner count (coupling_raw).
  2. Normalize each signal to 0–100: score = min(100, round(raw / max_across_symbols × 100)). test_gap_score = round(test_gap_raw × 100) (already 0–1).
  3. Compute composite: risk = round(0.40 × impact_score + 0.30 × churn_score + 0.20 × test_gap_score + 0.10 × coupling_score).
  4. Sort descending by risk, return top N (default 20).
risk = round(
    0.40 × impact_score    // direct caller count, normalized
  + 0.30 × churn_score     // commit-change count, normalized
  + 0.20 × test_gap_score  // fraction of callers in production (not tests), × 100
  + 0.10 × coupling_score  // co-change file-partner count, normalized
)

Output fields per symbol

FieldDescription
addressSymbol address
riskComposite score 0–100
impact_rawDirect caller count
churn_rawCommits where the symbol changed
test_gap_rawFraction of callers in production code (0–1)
coupling_rawCount of co-change file partners
impact_score / churn_score / test_gap_score / coupling_scoreNormalized dimension scores (0–100)
muse code blast-risk --json
muse code blast-risk --top 10 --json

Cross-branch search — find-symbol

muse code grep only sees HEAD. muse code find-symbol searches every branch — merged or not. That matters when you're hunting for a function someone wrote last week on a feature branch, or verifying that a refactor landed everywhere it needed to. You can also search by body_hash to find a symbol regardless of what it was renamed to.

Algorithm

  1. Enumerate every branch tip in the repo (muse branch --json).
  2. For each branch, load the HEAD SymbolTree (from cache if available).
  3. Match symbols whose name equals the query string (case-sensitive), or whose body_hash starts with the --hash prefix.
  4. Return all matches annotated with branch, commit_id, address, kind, and lineno.
bash
# Find by bare name — all branches
muse code find-symbol "compute_total" --json

# Find by body hash (stable across renames)
muse code find-symbol --hash a3f2c9 --json
{
  "name": "compute_total",
  "results": [
    {
      "address":   "src/billing.py::compute_total",
      "branch":    "dev",
      "commit_id": "sha256:9e21b8…",
      "kind":      "function",
      "lineno":    42
    },
    {
      "address":   "src/billing.py::compute_total",
      "branch":    "feat/v2-billing",
      "commit_id": "sha256:a3f2c9…",
      "kind":      "function",
      "lineno":    38
    }
  ]
}

When the same symbol appears in multiple branches with different line numbers, the divergence shows up immediately — useful for spotting unmerged work or confirming that a refactor landed on all active branches.

Symbol diff — compare

muse diff tells you which lines changed. muse code compare tells you which symbols changed — added, removed, modified, or renamed — between any two refs. Renames are detected by body_hash, so a function that moved files and got a new name still shows up as a rename, not a delete plus add. The right tool for PR reviews and API surface audits.

Algorithm

  1. Load the SymbolTree at from_ref and the SymbolTree at to_ref.
  2. Added: addresses in to_ref not in from_ref (excluding renames).
  3. Removed: addresses in from_ref not in to_ref (excluding renames).
  4. Renamed: an address disappeared from from_ref and a new address appeared in to_ref with the same body_hash — linked as a rename regardless of file or name change.
  5. Modified: address present in both refs, body_hash differs (content changed, not just renamed).
  6. Unchanged: address in both with identical content_id.
bash
muse code compare HEAD~10 HEAD --json
muse code compare v1.0.0 v2.0.0 --json
muse code compare main feat/v2-billing --json
{
  "from_ref":  "HEAD~10",
  "to_ref":    "HEAD",
  "added":    ["src/billing.py::apply_discount", "src/billing.py::DiscountPolicy"],
  "removed":  ["src/billing.py::legacy_discount"],
  "modified": ["src/billing.py::compute_total", "src/models.py::Invoice.save"],
  "renamed":  [
    { "from": "src/billing.py::calc_tax", "to": "src/billing.py::compute_tax" }
  ],
  "unchanged": 138
}

Renames are detected by body hash — if two symbols share the same body_hash across refs, they are treated as a rename regardless of file or name change. This makes compare reliable for PR reviews and API surface audits.

Symbol history — narrative, age, symbol-log, lineage

Most tools tell you what a symbol looks like now. These four tell you what it's been through. Together they answer the questions that matter when you're inheriting code: who wrote this, has it been rewritten or just renamed, which commits actually touched it, and where did the current name come from?

narrative — life story

Ask muse code narrative about a symbol and you get a plain-English summary of its life: when it was born, how many commits touched it, who wrote it, and how its signature changed over time. It's the fastest way to get oriented on a function you've never seen before without reading a single line of its body.

Algorithm

  1. Run the full symbol-log walk for the address (see below) to collect every commit that touched the symbol.
  2. Extract first_seen from the oldest log entry and last_modified from the newest.
  3. Compute age_days as now − first_seen.
  4. Collect unique authors and agent_id values across all log entries.
  5. Count signature_changes: the number of commits where signature_id differed from the previous entry.
  6. Template the collected fields into the human-readable narrative string.
bash
muse code narrative "src/billing.py::compute_total" --json
{
  "address":       "src/billing.py::compute_total",
  "age_days":      147,
  "first_seen":    "2025-12-05T00:00:00Z",
  "last_modified": "2026-04-15T00:00:00Z",
  "authors":       ["gabriel", "claude-code"],
  "commit_count":  43,
  "signature_changes": 2,
  "narrative": "Born Dec 5 2025 as a 14-line utility. Grew to 28 lines by Feb as discount support was added. Signature changed twice — args reordered in Jan, return type annotated in Mar. Most-edited function in the module (43 commits)."
}

age — code survival

A function can be three years old and six months written. muse code age measures what fraction of a symbol's original body is still present — so a 10% survival rate means it's been fully rewritten under the same name. Use it to cut through naming history and find out whether you're dealing with legacy code or just a legacy label.

Algorithm

  1. Run the symbol-log walk to find the symbol's first_seen commit and its body at that point.
  2. Load the current body from HEAD's SymbolTree.
  3. Split both bodies into normalised lines (stripped, blank lines removed).
  4. Compute the longest-common-subsequence (LCS) of the two line sets: surviving_lines = |LCS|.
  5. survival_pct = surviving_lines / original_lines × 100.
  6. Walk the symbol-log forward from first_seen; the commit where body_hash changed most relative to the prior entry is recorded as last_full_rewrite.
bash
muse code age --json                                         # all symbols
muse code age --explain "src/billing.py::compute_total" --json
{
  "address":             "src/billing.py::compute_total",
  "survival_pct":        34.2,
  "original_lines":     14,
  "surviving_lines":    9,
  "last_full_rewrite":  "sha256:b1c2d3…",
  "last_full_rewrite_at":"2026-02-14T00:00:00Z"
}

symbol-log — commit trail

muse log shows you every commit. muse code symbol-log shows you only the commits that mattered for one specific symbol — each one tagged with what happened: create, modify, rename, or delete. Use --from v1.0.0 to scope it to a release window. It's the commit history your code review should have had.

Algorithm

  1. Walk the commit DAG backward from HEAD (or --from ref if supplied).
  2. At each commit, load the SymbolTree and look up the target address.
  3. Compare the content_id for the address against the parent commit's SymbolTree.
  4. Emit a log entry if: the address is new (not in parent) → op: create; content_id changed → op: modify; the address is absent but the same body_hash appeared at a different address in the parent → op: rename; the address disappeared → op: delete.
  5. Omit commits where the symbol's content_id is identical to the parent's.
bash
muse code symbol-log "src/billing.py::compute_total" --json
muse code symbol-log "src/billing.py::compute_total" --from v1.0.0 --json
{
  "address": "src/billing.py::compute_total",
  "commits": [
    {
      "commit_id":   "sha256:9e21b8…",
      "message":    "feat: add discount cap",
      "committed_at":"2026-04-15T00:00:00Z",
      "author":     "gabriel",
      "agent_id":   "claude-code",
      "op":         "modify"
    },
    {
      "commit_id":   "sha256:a3f2c9…",
      "message":    "feat: initial billing module",
      "committed_at":"2025-12-05T00:00:00Z",
      "author":     "gabriel",
      "agent_id":   "",
      "op":         "create"
    }
  ]
}

lineage — track renames

Functions get renamed. Files get moved. muse code lineage follows the body_hash through those transitions to reconstruct the full chain of addresses a symbol has had — from the one it had when it was first written to the one it has now. The oldest entry is the original. If the chain surprises you, that's the point.

Algorithm

  1. Start with the current address at HEAD; record it as the first lineage entry (event: modified or created based on whether the parent has the same address).
  2. At each step, load the parent commit's SymbolTree and look up the current body_hash.
  3. If the body_hash is found at a different address in the parent → record a renamed_from entry for that prior address and continue the walk from it.
  4. If the same address exists in the parent with the same body_hash → continue backward without emitting an entry (no change).
  5. If the same address exists but body_hash differs → emit a modified entry and continue.
  6. Stop when the address (or any rename chain predecessor) is not found in any further ancestor, marking the oldest entry as created.
bash
muse code lineage "src/billing.py::compute_total" --json
{
  "address": "src/billing.py::compute_total",
  "lineage": [
    { "address": "src/billing.py::compute_total", "commit_id": "sha256:9e21b8…", "event": "modified"  },
    { "address": "src/billing.py::compute_total", "commit_id": "sha256:c8d5e1…", "event": "created"   },
    { "address": "src/orders.py::calc_total",    "commit_id": "sha256:4c9e79…", "event": "renamed_from" }
  ]
}

The oldest entry in lineage is the original address. This is useful for attribution (who first wrote this function?) and for understanding whether a symbol's current form is genuinely new or a renamed survivor.

Semantic cherry-pick

muse cherry-pick brings an entire commit. muse code semantic-cherry-pick brings one function. Point it at a branch or commit, name the symbol, and it rewrites exactly that symbol's body in your working tree — nothing else moves. Multi-symbol in one shot, --dry-run to preview before writing. The right tool when you need a fix from a feature branch without pulling in everything else it changed.

Algorithm

  1. Resolve --from to a commit ID; load the SymbolTree at that commit.
  2. For each target address, look it up in the source SymbolTree and fetch its body bytes from the object store.
  3. Load the matching file from the current working tree and parse its SymbolTree.
  4. Locate the target symbol's byte span (start line, end line) in the working-tree file.
  5. Replace that span with the source body; count lines_changed as the absolute line delta.
  6. Write the modified file back to disk (skipped if --dry-run). The working tree is modified but nothing is staged or committed.
bash
# Pull one function from feat/v2-billing into the current working tree
muse code semantic-cherry-pick "src/billing.py::compute_total" \
  --from feat/v2-billing --json

# Pull two symbols at once
muse code semantic-cherry-pick \
  "src/auth.py::validate_token" \
  "src/auth.py::refresh_token" \
  --from feat/auth-v2 --json

# Dry run — show what would change without writing
muse code semantic-cherry-pick "src/billing.py::compute_total" \
  --from HEAD~5 --dry-run --json
{
  "address":     "src/billing.py::compute_total",
  "from_ref":    "feat/v2-billing",
  "from_commit": "sha256:a3f2c9…",
  "applied":     true,
  "lines_changed": 8,
  "dry_run":     false
}
The cherry-pick writes to the working tree — it does not stage or commit. Run muse code breakage --json after applying to confirm no structural invariants were broken, then stage and commit as usual.

Index management

Code intelligence indexes are maintained automatically on every commit. Manual intervention is only needed after a large history rewrite or if the index is suspected corrupt.

CommandEffectWhen to use
muse code index status --json Report index coverage — files indexed, stale entries, version Diagnosing slow queries or suspicious results
muse code index rebuild --json Re-parse all files, rewrite symbol cache and call graph After adding a new language, after schema migration, post-recovery
muse code index rebuild --dry-run --json Report what would be rebuilt without writing Before a large rebuild to estimate cost
muse code index purge --json Delete all index files — next query triggers a cold rebuild Corrupt index; schema version mismatch
bash
muse code index status --json
{
  "schema_version":   2,
  "files_indexed":    214,
  "stale_entries":    0,
  "symbols_total":    1847,
  "cache_size_bytes": 284621,
  "last_rebuilt":     "2026-04-29T11:40:22Z"
}

CLI reference

All commands accept --json. Always use it for structured output.

TaskCommand
Read symbol bodymuse code cat "file.py::Symbol" --json
List symbols in filemuse code symbols --file file.py --json
Find symbol (HEAD)muse code grep "Name" --json
Find across all branchesmuse code find-symbol "Name" --json
Symbol diff between refsmuse code compare HEAD~10 HEAD --json
Structural weight (gravity)muse code gravity --json
Explain one symbol's gravitymuse code gravity --explain "file.py::Fn" --json
Commit churn leaderboardmuse code hotspots --top 20 --json
Hidden co-change couplingmuse code entangle --json
Dead code candidatesmuse code dead --high-confidence-only --json
Blast radius (callers)muse code impact "file.py::Symbol" --json
Import / call graphmuse code deps "file.py" --json
Transitive symbol depsmuse code deps "file.py::Fn" --transitive --json
Working-tree invariant checkmuse code breakage --json
Static test coveragemuse code semantic-test-coverage --json
Coverage gate (CI)muse code semantic-test-coverage --min-coverage 80 --json
Composite risk scoremuse code blast-risk --top 20 --json
File coupling pairsmuse code coupling --json
Symbol life storymuse code narrative "file.py::Fn" --json
Code survival ratemuse code age --explain "file.py::Fn" --json
Commits touching one symbolmuse code symbol-log "file.py::Fn" --json
Full rename lineagemuse code lineage "file.py::Fn" --json
Surgical symbol cherry-pickmuse code semantic-cherry-pick "file.py::Fn" --from branch --json
Most stable symbolsmuse code stable --top 20 --json
Detect refactor opportunitiesmuse code detect-refactor --json
Index statusmuse code index status --json
Rebuild indexmuse code index rebuild --json
Purge indexmuse code index purge --json

Rituals

These command sequences are the minimum due diligence before common high-risk operations. Copy them verbatim — the order matters.

Before modifying a symbol

bash
# 1. confirm declaration location
muse code grep "SymbolName" --json

# 2. read current body
muse code cat "file.py::SymbolName" --json

# 3. map blast radius — how many callers will be affected?
muse code impact "file.py::SymbolName" --json

# 4. check test coverage — what tests already cover it?
muse code semantic-test-coverage --file file.py --json

# 5. structural gate — no regressions before committing
muse code breakage --json

Before a large refactor

bash
# Which symbols carry the most downstream weight?
muse code gravity --top 20 --json

# Which symbols change most often (high churn = high merge risk)?
muse code hotspots --top 20 --json

# Are there hidden couplings that will surprise you?
muse code entangle --json

# Outbound dependency map of the target file
muse code deps file.py --json

# Current structural health baseline
muse code breakage --json

Before deleting a symbol or file

bash
# Is anything still calling it? (blast radius must be 0)
muse code impact "file.py::Symbol" --json

# Confirm dead-code classification at high confidence
muse code dead --high-confidence-only --json

# Check all branches — not just HEAD
muse code find-symbol "SymbolName" --json
Only delete when impact returns "total": 0 and dead returns "confidence": "high". A medium-confidence flag means the module is imported somewhere — inspect callers before deleting.

Before merging / releasing

bash
# Structural safety gate (must be zero errors)
muse code breakage --json

# Run tests only for what changed
muse code test --json

# Flag high-churn symbols for human review
muse code hotspots --top 5 --json

# Preview the symbol-level API surface change
muse code compare main HEAD --json