v0.27.1
pi-shazam v0.27.1
Emergency fix release. v0.27.0 shipped the full 0.27 feature set (V3 ProtoBuf
cache, impact provenance + Mermaid graph, overview module-density ranking, lookup
provenance edges, GitHub Action wrapper, and more) but was pulled because a
packaging defect excluded the runtime protobufjs dependency, breaking the MCP
server and V3 cache on a fresh install. v0.27.1 contains everything in v0.27.0
plus the dependency fix below. If you installed v0.27.0, upgrade to v0.27.1.
What's Changed in v0.27.1
Fixed
- Runtime
protobufjsmissing from published package (#722):protobufjswas
declared indevDependenciesbut imported at runtime bycore/proto-schema.ts,
so the published 0.27.0 tarball excluded it and the MCP server / V3 cache path
failed withERR_MODULE_NOT_FOUNDon a fresh install. Movedprotobufjsto
dependencies(keptprotobufjs-cliin devDependencies) so it ships in the package.
v0.27.0 Feature Highlights (included in v0.27.1)
Added
-
V3 ProtoBuf cache format (#628, #646): New compact binary cache that
encodes edge data as columnar ProtoBuf arrays behind a 4-byteSHA\3
magic header. On a 1000-symbol project the on-disk cache drops from
~800KB (V2 JSON) to ~30% smaller in practice; the V2 JSON format stays
readable for backward compatibility. Schema lives incore/graph.proto
(source of truth) with a JSON runtime mirror incore/proto-schema.ts
(protobufjs, no generated static module indist/).saveGraphCache
now writes V3 by default;loadGraphCachedetects the magic header and
falls back to V2 for older caches, preserving thefileMtimesmap so
mtime-based invalidation is unchanged. -
Impact enrichment: provenance breakdown + Mermaid call graph (#631 B):
shazam_impactJSON now carries aprovenanceCountsfield per affected
symbol (resolved vs. heuristic at a glance) plus a compactR:N N:M H:K
badge on the affected-file markdown line; call-chain edges carry an
explicitprovenance. EveryCallChainEntryalso gains a self-contained
Mermaidflowchart TDblock (mermaidfield) with provenance as edge
labels and solid/dashed arrows, capped at 30 nodes. -
Overview module-density ranking (#631 B):
shazam_overviewsurfaces a
topByDensitylist on the JSON envelope and a "Module Density (Top 10 by
symbols-per-file)" markdown section to flag "god module" candidates. -
Lookup provenance edges + provenance-weight sort (#631 B, #643):
shazam_lookupJSON mode now exposesincomingEdges/outgoingEdges
(up to 20 each, withprovenance) and a precomputedprovenanceCounts
summary per entry (#643). Entries are sorted by provenance weight
(resolved > name_match > heuristic > unresolved, PageRank tiebreak) so the
most-trustworthy matches come first; markdown output keeps the original
PageRank ordering. -
Changes structural (line-count) working-tree view (#631 B):
shazam_changesadds astructuralChangesfield with added/removed/
modified line counts fromgit diff --numstat(staged + unstaged, binary
files skipped, capped at 20 files), plus a compact(+N -M lines across K files)markdown badge. -
GitHub Action wrapper for
shazam_verify(#638, #652): Composite
action at.github/actions/shazam-verify/runsshazam_verifyon every
PR and posts a risk-scored review as a PR comment (plus step summary).
Addstools/verify-comment.ts(formatVerifyComment), anaction.yml
manifest, orchestrator/entrypoint scripts, andtests/github-action-*.test.ts
(25 tests). Adds theyamldevDependency. -
ProtoBuf schema mirror consistency test (
tests/cache-proto-schema.test.ts):
Parsescore/graph.prototext and asserts the field set, IDs,
types, andrepeatedflags match the JSON mirror in
core/proto-schema.ts. Catches drift between the two schema
sources before it can corrupt a cache file. -
V3.0 / V3.1 legacy deserializers (
deserializeGraphV3V0,
deserializeGraphV3V1,+WithMetadatavariants) plus
encodeGraphPayloadV31/decodeGraphPayloadV31for tests.
Public surface for the in-place converter; kept so the load
path can decode legacy wire formats. -
Real-world size sanity check in
tests/benchmark-v3.test.ts:
Documented the V3.1 ratio on a self-scan of pi-shazam (3499
symbols, 1264 symbol-level edges, 7236 file-level rows): V3.1
is 96% of V2 on a real project (vs. 53% on the edge-heavy
synthetic benchmark), because real projects spend most bytes
on the JSON metadata + non-deduped file-level rows.
Changed
-
Cyclomatic complexity via tree-sitter AST (#642): Replaced the regex
sweep that countedif|else|for|while|case|catch+&&/||/:?across
whole source slices (which inflated scores by counting keywords inside
comments/strings) with a tree-sittercomplexityquery matching real
branching AST nodes only, applying the else-if rule (plainelseadds 0,
else ifadds 1) on a baseline score of 1. Queries added for
TS/TSX/JS/Python/Go/Rust; the original regex is kept as a fallback for
languages without a compiled query, preserving coverage. -
Tool defaults tightened for LLM agents + per-edge provenance (#629,
#633, #634, #635, #636; #640): Bundled four related quality PRs. Unified
Affected-Tests detection (core/test-patterns.tsis now the single source
of truth; symbol-mode JSON carriesaffectedTests). Display polish +
path-traversal UX (classifyFilePathdistinguishes traversal vs. missing;
did-you-meanfor lookup/impact/format). Per-edgeprovenanceonEdge
with LSP-driven promotion (upgradeEdgesToResolved/upgradeEdgesForHotspots,
zero extra LSP latency). Always-on defaults:### Hotspotsalways in
overview text andJSON.hotspots.{byPageRank,byComplexity};verifyuses a
single-line-per-diagnostic format with a summary header (drops suggestedFixes
text +.shazamauto-export);inferImpactMode()picks symbol vs. files
mode from input shape, removing the strict mutual-exclusion error. -
Verify flags migrated to
.pi-shazam/config.json;lookup mode=state
removed (#630): Newcore/config.tsloads.pi-shazam/config.json
(missing/empty = silent defaults, malformed = warn).shazam_verify
maxFilesnow resolves fromconfig.verify.maxFiles(default 100); dead
noCascade/noSecretsoptions removed.shazam_lookup mode=statewas
first deprecated (warn) then fully removed — callers now get a clear error
pointing at--name/--direction. -
Default scan now excludes
tests/(#632):scanProjectskips the
tests/directory by default (override withincludeTests). -
Typed result builders across all tools (#631 A): Introduced pure,
typed builders —buildVerifyResult,buildFormatResult,
buildOverviewResult,buildImpactResult+buildCallChainResult,
buildChangesResult(+renderChangesMarkdown), typedXxxResultfor all
three lookup modes, and akinddiscriminator withexecuteRenameSymbolJson.
Tool internals now build a typed model before rendering JSON/markdown,
reducing envelope drift and enabling direct result assertions in tests. -
V3.1 cache: string table for symbol ID dedup (#647): The on-disk
graph cache (core/cache.tsV3 format) now stores each unique
symbol ID exactly once in a top-levelstring_table; every edge's
source/targetis referenced by int32 index. On a 1000-symbol
graph with ~3 edges per node this brings the V3 cache to ~53% of
the V2 size (down from ~68% in V3.0), close to the plan's 50%
target. The on-disk magic header bumped fromSHA\3toSHA\4;
old V3.0 caches fall through to the V2 JSON path on load (which
fails to parse) and the scanner rebuilds them on the next scan.
Schema changes are additive and live incore/graph.proto(source
of truth) +core/proto-schema.ts(JSON runtime mirror). -
V3.2 cache: kind int enum + silent in-place upgrade (#647 follow-ups
D + E): Edgekindis encoded as int32 (1 byte varint) instead
of string (5-7 bytes per row) — on a real-world self-scan this
shaves another ~30-50 KB on top of V3.1. The on-disk magic header
bumped fromSHA\4toSHA\5.loadGraphCachenow auto-upgrades
legacy V3.0 (SHA\3, no string table) and V3.1 (SHA\4, kind as
string) caches to V3.2 in place on first read, so users on v0.27.0
(PR-G) and the just-shipped V3.1 release of this branch pay no
re-scan cost on upgrade. The V3.1-specific ProtoBuf schema
(kind: string) lives incore/proto-schema.tsas a separate
Rootso the in-place decoder can interpret the legacy wire
format; the V3.2 writer always produces the canonical int32 form.
Fixed
-
Batch code-review fixes (code-review findings #687-#701): Resolves
15code-review-labelled findings across the tool surface. Highlights:- Rust import resolution (#687): Non-prefixed multi-segment
use
paths (use utils::helper::doThing) are now resolved from the crate
root (Rust 2018+ semantics) instead of being dropped as external crates,
soshazam_impact/shazam_verifyno longer miss local Rust edges. - Symlink path-traversal in auto-format (#688, P0):
hooks/shazam-guide.ts
autoFormatFilenow validates viavalidatePathInProject(realpathSync
symlink-escape check) instead of the string-onlyisPathInRoot, and
imports it fromtools/_factory.ts(resolving the hook→tools layer
violation flagged in #697). - Silent audit-log discards (#689): Two empty
catchblocks in
index.tsshazam-doctor parsing now log via_logWarninstead of
swallowing malformedinternal.log/shazam-calls.loglines. - Silent cache-write failures (#690):
saveGraphCachecatch blocks in
core/scanner.tsnow emit_logWarn(incremental + full paths) so
ENOSPC/EACCES/EROFS cache drops are observable, not silent. - MAX_FILES truncation has no output signal (#693):
RepoGraphgains a
truncatedflag set whencollectSourceFileshits the cap; overview
output now warns the agent the graph is incomplete. - Edge provenance lost in diff identity (#695):
edgeIdentity/
edgeIdentityFromRownow includeprovenance, soshazam_changes
reports provenance-only edge upgrades (heuristic → resolved). - Concurrent getGraph duplicate scan (#691):
mcp/entry.tsgetGraph()
gains a re-entrancy guard so overlapping MCP calls share one scan
(defensive against the_scanningre-entry throw). - Impact compact mode drops affected tests (#692):
shazam_impact --compact
now reports an "Affected Tests (must re-run): N" count line. - LSP false PASS on large projects (#694):
initializetimeout raised
8s → 30s and diagnostic poll 1s → 10s soshazam_verifyno longer
reports PASS before the language server finishes indexing. - MCP
depthnot coerced from string (#696):dispatchImpactnow
coerces a stringdepthto a finite number (falls back to 3 on
NaN/ non-numeric), preventing undefined traversal behavior. - Wrong
(mcp)label in verify PR comment (#698):formatVerifyComment
now emits(verify)for 1-9 incoming-caller critical paths. - Over-broad unhandledRejection suppression (#699):
vitest.setup.ts
now suppressesERR_STREAM_DESTROYEDonly when opted in by LSP teardown
tests, so non-LSP tests that throw it correctly fail instead of passing. - GBK config files fail silently (#700):
loadConfigreads via
readFileAdaptive(UTF-8 → GBK → GB2312) instead of hardcoded
UTF-8, so Windows GBK.pi-shazam/config.jsonparses correctly. - LspEnrichContext interface missing mtime (#701):
trackOpenedFile
gains an optionalmtime?param matchingLspManager, preventing a
cache-invalidation regression if callers are refactored to the interface.
- Rust import resolution (#687): Non-prefixed multi-segment
-
MCP graph released on idle; memory / native-heap leak fixes (#626, #627):
mcp/entry.tsgetGraph()now honours a TTL (default 10 min,
PI_SHAZAM_GRAPH_TTL_MS; 0 disables) and nulls the cachedRepoGraphon
idle so the next access rebuilds from the persistent disk cache instead of
holding ~500MB-1GB forever.dispatchVerifyno longer callsresetCache()
(avoids two graphs coexisting during verify).lsp/manager.tsgains
closeOpenedFiles()(called after diagnostics) so the LSP child releases
per-document AST.core/scanner.tsresetCache()now preserves the
TreeSitterAdaptersingleton, stopping 100-300MB native-heap inflation per
verify cycle in long-lived MCP mode. -
LSP per-file AST cache invalidated on mtime change (#641): When a source
file was edited betweenverifycalls, the LSP server's per-document AST
returned diagnostics against stale content (requiring a manualtouch/
restart).LspManagernow records each opened file's mtime and, before each
didOpen, sendsdidClosefor any file whose mtime changed so the server
drops the old AST. -
shazam_changeskeeps## Change Summaryheader in compact output:
The compact path introduced in #640 dropped the header, breaking
tests/mcp-pi-parity.test.ts#238which expects both Pi and MCP to emit it.
The header is now kept (the shortcut is the absence of the other section
headers). -
benchmark-v3timing assertion no longer red CI under load (#650):
tests/benchmark-v3.test.ts(the V3 cache encode/decode speed
assertions) is flaky only when run inside the fullnpm test
suite under parallel load — it passes in isolation. It is now
excluded from the broadnpm test/ciruns via
vitest run --exclude tests/benchmark-v3.test.ts, and is instead
executed by the dedicatedbenchmarkCI job in isolation alongside
tests/benchmark.test.ts. This removes environment-dependent CI
noise from unrelated PRs without loosening the performance
regression thresholds. -
shazam_changescompact/full output parity test parameterized (#644):
Split the single fragile assertion into explicit clean/dirty tree variants
so the compact no-op (#634) and full output are each verified for the exact
sections they emit. -
Batch code-review fixes (Groups A-D, #656/#666/#658/#665/#664,
#667/#660/#663/#661/#659, #662, #668): Addresses thecode-review
labelled findings across the tool surface. Highlights:- Impact direction naming (#656):
computeFileDirectionreturns a
stable "upstream caller" / "downstream callee" / "both" label (tie ->
"both") so callers no longer infer direction from raw counts. - Impact rename-gate guard (#666):
recordCallChainnow resolves the
symbol throughgraph.nameIndexfirst; a non-existent symbol can no
longer satisfy the rename safety gate (reopens #569). - Format apply result (#658):
buildFormatResultruns the formatters
when not in dry-run and exposesformatResults/recommendedCommands
onFormatResult(both text and JSON paths). - Verify description clean-up (#665): dropped stale "graph diffs" and
"cache bypassed" claims from theshazam_verifydescription. - Docstring cache stat logging (#664):
_extractDocstringlogs
non-ENOENTstatSyncfailures via_logWarninstead of swallowing them;
ENOENT still falls back to mtime=0 silently. - Resolve-import Rust default + path normalization (#667, #660, #663):
.rsimports now returnnullby default;moduleMatchesFile,
isNonSourceFile, andvalidatePathInProjectCorenormalize backslash
paths so Windows graph keys match forward-slash comparisons. - Project-root default (#661):
path-utils/_factorydefault the
project root togetEffectiveRoot()instead ofprocess.cwd(). - Tree-sitter types exported (#659):
Tree/SyntaxNodeinterfaces
are exported fromcore/treesitter.tsfor shared AST consumers. - Overview JSON exposes text-only sections (#662):
buildOverviewResult
now surfacesdataStructures,entryPoints,httpRoutes,
complexityHotspots,suggestedReadingOrder,parserWarnings, and
moduleStructure(filter mode omits them, matching the text view);
module-structure / density grouping normalizes Windows backslash paths. - MCP
HOME_ONLYaccepts Windows projects (#668):validateProjectRoot
now treats a project underUSERPROFILE(Windows) as in-home, not just
the POSIXHOME.
- Impact direction naming (#656):
-
Windows path normalization for Git-Bash and WSL styles (#673, #675):
core/path-utils.tsnormalizePathInput()now collapses Git-Bash
/c/fooand WSL/mnt/c/foopaths toC:\fooat ingress, so the MCP
server and tool paths resolve correctly on Windows regardless of the
invoking shell. All user-supplied paths must route through it. -
Platform-aware timeouts for Windows (#677): LSP initialization and
other blocking operations scale their timeout by 1.5x onwin32, and
vitest.config.tsraises the win32testTimeoutto 180s, eliminating the
Windows-specific timeouts that were failing the local full suite. -
MCP entry no longer kills the test worker on a bad root (#676, #679):
mcp/entry.tsmoved theprocess.exit(1)for an invalidPROJECT_ROOT
out of module load and intomain()(behind theisMainModuleguard).
Importing the module under vitest no longer aborts the worker, which had
cascade-failed 12 tests that share the file;getGraph()falls back to
cwdwhen imported under test. The real-server fail-closed behavior
(exit on bad root) is preserved. -
Hook injection content and context delivery fixed (#674): Corrected
the content and context delivered by the before-agent-start hook so the
injected project snapshot matches what the tools consume. -
Local full-suite flaky hardening (#678, #680, #681): Windows test
stability improvements — symlink tests probe privilege at module load and
it.skipIf(!canCreateSymlinks())(no admin/Developer-Mode required);
verify-worktreebeforeAllhookTimeoutraised to 30s with retry+backoff
on every git op;benchmark-v3speed assertions use vitestretry: 2.
No production code touched; CI (windows/macos/ubuntu) remains authoritative.
Security
- Escape backslashes before quotes in Mermaid label sanitization (#2):
CodeQLjs/incomplete-sanitization(high) attools/impact.ts. The
previous sanitizer escaped quotes but not backslashes, so a name like
x\"yproduced a malformed Mermaid diagram and a potential XSS sink when
embedded in HTML. Now escapes backslashes first, then quotes
(mermaidSafeNameexported for isolated unit testing).
Dependencies
- Bump
prettier3.9.1 → 3.9.4 (#625). - Bump runtime, lsp, and
@types/nodedependency groups (#622, #623, #624).
Chore
- Review rules: memory-leak / output-parity / MCP long-lived checks (#637):
Extendeddocs/review-rules.mdwith P0 patterns (resource leaks incl.
tree-sitter native handle recreation and caches without TTL; text/JSON
output data drift; MCP stale-graph leak) and P1 patterns (test pollution
fromtests/; Affected Tests on both impact modes; output placeholder
strings; summary-counter math; path-traversal vs file-missing distinction).
Upgrade
pi install npm:pi-shazam@latestOr for MCP clients:
{ "mcpServers": { "pi-shazam": { "command": "npx", "args": ["-y", "-p", "pi-shazam@latest", "pi-shazam-mcp"] } } }Full Changelog: v0.26.0...v0.27.1