v2.0.0 — Correctness Rewrite
If you used v1.x, re-index. v1's graph was largely non-functional — three defects made most of what Glyph advertised return nothing. Each was verified against a live 57,121-symbol index before and after the fix.
The three that mattered
1. Symbol names were corrupt — 46% of them.
Extraction sliced a decoded string using tree-sitter's byte offsets. One non-ASCII character (an em-dash, a box-drawing char, an emoji) shifted every later name in that file. Measured: 26,240 of 57,121 symbols came out as fragments like '\n\n const totalPushed = ...'. Extraction now slices the source bytes and decodes per node.
2. Graph traversal returned nothing, ever.
Edge resolution hardcoded source_id = None, so 0 of 18,927 edges had a source. glyph deps, glyph path and glyph bridges were permanently empty on every project. Edges now carry a real source symbol, tracked through the AST walk, plus a src_file_id so module-level imports stay attributable.
3. The index rotted as you used it.
An incremental scan deleted the inbound edges of every re-parsed file and never rebuilt them — because those edges live in other files that weren't re-parsed. Measured: editing 10 files dropped one repo from 3,265 to 2,109 edges (−35%). Edges now store target_name beside target_id, and a repair pass re-points them after a re-parse. Verified lossless across repeated edit cycles.
Also fixed
orphansalways returned zero rows —NOT INagainst a subquery containing NULL yields NULL, never true.- Imports were essentially never extracted: the walker looked up a field named
clause, which doesn't exist in the TS grammar (it'simport_clause). 24 import edges across 4,710 files. .d.tsand.min.jswere never ignored —Path("x.d.ts").suffixis.ts.watchcalledscan_project(name, "")with an empty root and was a silent no-op.- Destructuring bindings (
const { a, b } = x) were stored as one unsearchable blob; each name is now bound individually. - A cursor was reused inside its own iteration, silently truncating after the first project.
Resolution now enforces export visibility — a non-exported symbol is only reachable inside its own file. That alone removed 678 false edges on one repo, where member calls like parsed.error.flatten() were linking to an unrelated module-local flatten().
Performance
Same machine, two production Next.js codebases:
| v1.2.0 | v2.0.0 | |
|---|---|---|
| Full scan — 3,161 files | 7.79 s | 2.57 s (3.0×) |
| Full scan — 1,576 files | 3.31 s | 0.99 s (3.3×) |
| No-op incremental | 0.29 s | 0.17 s |
| Edges extracted (1,576 files) | 3,265 | 31,491 |
| Edges resolved to a symbol | 0 | 14,048 |
| Corrupt symbol names | 46% | 0% |
From stat()-based change detection (unchanged files are never read or hashed), parallel parsing across processes, cached parsers, iterative AST walking, up-front id allocation, and GROUP BY joins replacing correlated subqueries.
Accuracy spot-checked against grep: 260 caller files for withAuth vs grep's 263; 405 vs 401 for the db singleton.
New
glyph context <project> <symbol>— definition, line range, absolute path, every caller, every callee, and the file's other symbols in one JSON call. Built for AI agents.glyph doctor— index self-check: malformed names, dangling edge targets, resolution rate.glyph refresh— incrementally re-scan every indexed project (0.28 s for 5,015 files across 3 projects; cheap enough to cron).glyph hotspots— churn × structural centrality, from git history.--jsonon every query command, with a consistent contract on not-found paths.- argparse CLI with real flags,
ON DELETE CASCADEthroughout, schema versioning.
Upgrading
The v1 database is migrated automatically on first run. Your project names and paths are kept; derived data is rebuilt on the next scan. The old DB is backed up to ~/.glyph/glyph.db.v1-backup-<timestamp> first.
v1 data isn't salvageable — 46% corrupt names and zero edge sources — so a re-scan is required, not optional:
git pull && ./install.sh
glyph scan myproject /path/to/repo --full
glyph doctorKnown limits
Roughly 45–50% of edges resolve to a project symbol; the rest are calls into node_modules/stdlib, which Glyph deliberately does not index. An unresolved edge means "points outside the project", not "not called". Dynamic dispatch (obj[key](), DI containers, string-keyed handlers) is invisible to AST analysis. Use Glyph to find and orient fast, then read the code before you change it.
Full changelog: CHANGES.md · v1.2.0...v2.0.0