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… | Use | Answers |
|---|---|---|
grep / read whole file | muse code grep "Name" --json | Where is this symbol declared? |
| Guessing downstream weight | muse code gravity --json | Which symbols are load-bearing infrastructure? |
| Tracking instability by feel | muse code hotspots --json | Which symbols change most often? |
| Manually tracing callers | muse code impact "file.py::Fn" --json | What breaks if I change this? |
| Hoping nothing calls it | muse code dead --high-confidence-only --json | Is 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
| Field | Type | Description |
|---|---|---|
kind | SymbolKind | One of: function async_function class method async_method variable import interface type_alias enum struct trait namespace module file directory |
name | str | Bare symbol name (compute_total) |
qualified_name | str | Dotted path for nested symbols (Invoice.compute_total) |
content_id | sha256:<64-hex> | Hash of normalized name + signature + body — changes on any edit |
body_hash | sha256:<64-hex> | Hash of body only — stable across renames |
signature_id | sha256:<64-hex> | Hash of "name(args)->return" — stable when only implementation changes |
metadata_id | sha256:<64-hex> | Hash of decorators / async / bases (v2+); "" for pre-v2 records |
canonical_key | str | {file}#{scope}#{kind}#{name}#{lineno} — stable handle for history queries |
lineno / end_lineno | int | Source 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.
| Language | Parser | Symbol kinds extracted | Notes |
|---|---|---|---|
| Python | stdlib ast + TreeSitter | function, async_function, class, method, async_method, variable, import | Byte-exact normalization via ast.unparse |
| TypeScript | TreeSitter | function, class, interface, type_alias, enum | JSX variants supported |
| JavaScript | TreeSitter | function, class, variable | CommonJS + ESM |
| Go | TreeSitter | function, method, struct, interface, type_alias | Receiver methods tracked on struct |
| Rust | TreeSitter | function, struct, trait, enum, type_alias, module | impl blocks resolved to struct |
| Java | TreeSitter | class, interface, method, enum | |
| C# | TreeSitter | class, interface, method, struct, enum, namespace | |
| C / C++ | TreeSitter | function, struct, class, enum | Header + impl deduplication |
| Ruby | TreeSitter | class, method, module | |
| Kotlin | TreeSitter | function, class, interface, enum | rc4 |
| Swift | TreeSitter | function, class, struct, protocol, enum | rc4 |
| Bash / Zsh | TreeSitter | function | rc4 |
| HTML | TreeSitter | element (id/class anchors) | rc4 |
| CSS / SCSS | TreeSitter | class, id, variable, mixin | rc4 |
| Markdown | TreeSitter | section (heading-anchored) | |
| TOML / YAML / JSON | TreeSitter | top-level key | |
| Unknown | Line-based | section (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
- Load the committed HEAD snapshot and build a forward call graph via AST.
- Invert the graph:
callee_bare_name → [caller_address, …]. - BFS from each symbol through the reversed graph to enumerate all transitive dependents.
- Divide the count by
total_production_symbolsand 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
| Field | Type | Description |
|---|---|---|
address | str | Full symbol address |
name | str | Bare symbol name |
kind | str | Symbol kind |
file | str | Source file path |
gravity_pct | float | 0–100; percentage of production symbols that transitively depend on this one |
direct_dependents | int | Count of depth-1 callers |
transitive_dependents | int | Total count across all BFS depths |
max_depth | int | Maximum BFS depth reached |
depth_distribution | dict | {"1": N, "2": M, …} — string-keyed depth → dependent count |
--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
- BFS through commit history (both merge parents), up to
--max-commits(default 10,000). - For each commit, extract symbol-level ops from
commit.structured_delta["ops"]. - Increment
changes[address]once per commit (multiple ops on the same symbol in one commit count as one). - 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
- Single BFS pass through history. For each commit, collect the set of symbol addresses that changed.
- Build a co-change counter:
(addr_a, addr_b) → commit_count(pairs sorted lexicographically). - Compute:
co_change_rate = co_changes / min(active_a, active_b). - Check structural linkage: scan HEAD's import pseudo-symbols to see if file_a imports file_b or vice versa.
- 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
| Field | Type | Description |
|---|---|---|
symbol_a / symbol_b | str | Full addresses of the two symbols |
file_a / file_b | str | Source files |
same_file | bool | True if both symbols live in the same file |
structurally_linked | bool | True if one file imports the other — expected coupling |
co_changes | int | Number of commits where both changed |
commits_both_active | int | Opportunity window: min of each symbol's active commit count |
co_change_rate | float 0–1 | co_changes / commits_both_active |
a_in_test / b_in_test | bool | Whether the symbol lives in a test file |
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
- Parse HEAD snapshot into a SymbolTree. Collect all
ast.Name.idandast.Attribute.attrreferences across every source file in parallel (8 workers, 512 KB per-file limit). - Build a reference set of all bare names that appear at least once as a call or attribute access.
- Build a module-import set of all file paths that are imported by at least one other file (via import pseudo-symbols).
- Inject implicit references for framework entry points — routes, tasks, signal handlers — so they are never falsely flagged.
- 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.
- 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
| Confidence | Condition | Action |
|---|---|---|
| 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
| Field | Type | Description |
|---|---|---|
address | str | Symbol address |
file_path | str | Source file |
kind | str | Symbol kind |
referenced | bool | Always false for dead candidates |
module_imported | bool | Whether the module is imported anywhere |
confidence | str | "high" or "medium" |
reason | str | Human-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
- Parse HEAD snapshot into a forward call graph:
caller_address → [callee_bare_name, …]. - Invert: build
callee_bare_name → [caller_address, …]. - BFS from the target symbol's bare name through the inverted graph, up to
--depthhops (0 = unlimited). - Group results by BFS depth into a string-keyed dict:
"1" → direct callers,"2" → callers of callers, etc. - Classify any caller that is a framework endpoint (FastAPI route, Celery task) as an
entry_point. - 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
}
"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
- 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. - Symbol forward mode: parse the target symbol's body via AST; extract all
Name.idandAttribute.attrcall references and match them against known symbol names in HEAD →calls. With--transitive, BFS through the production call graph up to--depthhops. - 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
| Mode | Key fields | Description |
|---|---|---|
| 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
- Parse all working-tree source files into a SymbolTree (uncommitted changes included).
- Load the committed HEAD SymbolTree from the symbol cache.
- 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.
- 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. - Exit 1 if any errors exist. With
--strict, exit 1 on warnings too.
| Check | Severity | Condition |
|---|---|---|
| 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
- Partition HEAD symbols into test functions (file path matches
test_*/*_test.py) and production symbols. - For each test function, extract all
Name.idreferences — direct calls at depth 1. - Mark any production symbol whose bare name appears in any test's reference set as covered at depth 1.
- 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
--depthhops or 10 hops for--transitive. - Compute:
coverage_pct = covered_symbols / total_production_symbols × 100. - With
--min-coverage N, exit 1 ifcoverage_pct < N.
Depth model
| Depth | Meaning |
|---|---|
1 (default) | Test function directly calls the production symbol by bare name — conservative, high precision |
N > 1 | Extends coverage N−1 hops through the production call graph; test_process_order → process_order → Invoice.compute_total covers Invoice.compute_total at depth 2 |
--transitive | Unlimited 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
- 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). - 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). - Compute composite:
risk = round(0.40 × impact_score + 0.30 × churn_score + 0.20 × test_gap_score + 0.10 × coupling_score). - 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
| Field | Description |
|---|---|
address | Symbol address |
risk | Composite score 0–100 |
impact_raw | Direct caller count |
churn_raw | Commits where the symbol changed |
test_gap_raw | Fraction of callers in production code (0–1) |
coupling_raw | Count of co-change file partners |
impact_score / churn_score / test_gap_score / coupling_score | Normalized 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
- Enumerate every branch tip in the repo (
muse branch --json). - For each branch, load the HEAD SymbolTree (from cache if available).
- Match symbols whose
nameequals the query string (case-sensitive), or whosebody_hashstarts with the--hashprefix. - Return all matches annotated with branch, commit_id, address, kind, and lineno.
# 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
- Load the SymbolTree at
from_refand the SymbolTree atto_ref. - Added: addresses in
to_refnot infrom_ref(excluding renames). - Removed: addresses in
from_refnot into_ref(excluding renames). - Renamed: an address disappeared from
from_refand a new address appeared into_refwith the samebody_hash— linked as a rename regardless of file or name change. - Modified: address present in both refs,
body_hashdiffers (content changed, not just renamed). - Unchanged: address in both with identical
content_id.
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
- Run the full symbol-log walk for the address (see below) to collect every commit that touched the symbol.
- Extract
first_seenfrom the oldest log entry andlast_modifiedfrom the newest. - Compute
age_daysasnow − first_seen. - Collect unique authors and
agent_idvalues across all log entries. - Count
signature_changes: the number of commits wheresignature_iddiffered from the previous entry. - Template the collected fields into the human-readable
narrativestring.
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
- Run the symbol-log walk to find the symbol's
first_seencommit and its body at that point. - Load the current body from HEAD's SymbolTree.
- Split both bodies into normalised lines (stripped, blank lines removed).
- Compute the longest-common-subsequence (LCS) of the two line sets:
surviving_lines = |LCS|. survival_pct = surviving_lines / original_lines × 100.- Walk the symbol-log forward from
first_seen; the commit wherebody_hashchanged most relative to the prior entry is recorded aslast_full_rewrite.
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
- Walk the commit DAG backward from HEAD (or
--fromref if supplied). - At each commit, load the SymbolTree and look up the target address.
- Compare the
content_idfor the address against the parent commit's SymbolTree. - Emit a log entry if: the address is new (not in parent) →
op: create;content_idchanged →op: modify; the address is absent but the samebody_hashappeared at a different address in the parent →op: rename; the address disappeared →op: delete. - Omit commits where the symbol's
content_idis identical to the parent's.
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
- Start with the current address at HEAD; record it as the first lineage entry (
event: modifiedorcreatedbased on whether the parent has the same address). - At each step, load the parent commit's SymbolTree and look up the current
body_hash. - If the
body_hashis found at a different address in the parent → record arenamed_fromentry for that prior address and continue the walk from it. - If the same address exists in the parent with the same
body_hash→ continue backward without emitting an entry (no change). - If the same address exists but
body_hashdiffers → emit amodifiedentry and continue. - Stop when the address (or any rename chain predecessor) is not found in any further ancestor, marking the oldest entry as
created.
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
- Resolve
--fromto a commit ID; load the SymbolTree at that commit. - For each target address, look it up in the source SymbolTree and fetch its body bytes from the object store.
- Load the matching file from the current working tree and parse its SymbolTree.
- Locate the target symbol's byte span (start line, end line) in the working-tree file.
- Replace that span with the source body; count
lines_changedas the absolute line delta. - Write the modified file back to disk (skipped if
--dry-run). The working tree is modified but nothing is staged or committed.
# 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
}
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.
| Command | Effect | When 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 |
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.
| Task | Command |
|---|---|
| Read symbol body | muse code cat "file.py::Symbol" --json |
| List symbols in file | muse code symbols --file file.py --json |
| Find symbol (HEAD) | muse code grep "Name" --json |
| Find across all branches | muse code find-symbol "Name" --json |
| Symbol diff between refs | muse code compare HEAD~10 HEAD --json |
| Structural weight (gravity) | muse code gravity --json |
| Explain one symbol's gravity | muse code gravity --explain "file.py::Fn" --json |
| Commit churn leaderboard | muse code hotspots --top 20 --json |
| Hidden co-change coupling | muse code entangle --json |
| Dead code candidates | muse code dead --high-confidence-only --json |
| Blast radius (callers) | muse code impact "file.py::Symbol" --json |
| Import / call graph | muse code deps "file.py" --json |
| Transitive symbol deps | muse code deps "file.py::Fn" --transitive --json |
| Working-tree invariant check | muse code breakage --json |
| Static test coverage | muse code semantic-test-coverage --json |
| Coverage gate (CI) | muse code semantic-test-coverage --min-coverage 80 --json |
| Composite risk score | muse code blast-risk --top 20 --json |
| File coupling pairs | muse code coupling --json |
| Symbol life story | muse code narrative "file.py::Fn" --json |
| Code survival rate | muse code age --explain "file.py::Fn" --json |
| Commits touching one symbol | muse code symbol-log "file.py::Fn" --json |
| Full rename lineage | muse code lineage "file.py::Fn" --json |
| Surgical symbol cherry-pick | muse code semantic-cherry-pick "file.py::Fn" --from branch --json |
| Most stable symbols | muse code stable --top 20 --json |
| Detect refactor opportunities | muse code detect-refactor --json |
| Index status | muse code index status --json |
| Rebuild index | muse code index rebuild --json |
| Purge index | muse 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
# 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
# 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
# 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
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
# 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