@jefuriiij/synthra 0.14.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,774 +1,792 @@
1
- # Synthra changelog
2
-
3
- Notable changes per version. This file ships inside the npm tarball — `syn .`
4
- reads it after an auto-update to show you what changed.
5
-
6
- For older versions, see [GitHub Releases](https://github.com/jefuriiij/synthra/releases).
7
-
8
- ---
9
-
10
- ## [0.14.0] — 2026-07-02
11
-
12
- ### Added
13
-
14
- - **`syn remove [path]` cleanly uninstall Synthra from a project.** Ran `syn .`
15
- in the wrong folder, or just want Synthra out of a repo? `syn remove` reverses
16
- the bootstrap: deletes `.synthra-graph/` and `.synthra/`, strips the policy
17
- block from `CLAUDE.md`, Synthra's entries from `.gitignore`, its hooks from
18
- `.claude/` — **your own content in those files survives**; a file is deleted
19
- only when nothing else remains. Also deregisters the MCP entry (with a direct
20
- `.mcp.json` fallback when the `claude` CLI isn't available) and removes the
21
- project from the dashboard registry. Shows a summary and asks `[y/N]` first;
22
- pass `--yes` to skip (required when not running in a terminal).
23
-
24
- ---
25
-
26
- ## [0.13.1] — 2026-06-24
27
-
28
- ### Fixed
29
-
30
- - **Minified/bundle files are no longer indexed.** Committed vendored plugin JS
31
- (`*.min.js`, `*.bundle.js`, `*.min.css`, …) has no readable symbols, so indexing
32
- it only polluted retrieval and caused **useless Moat blocks** on markup-heavy
33
- projects a Grep for CSS classes like `nav|menu|toggle` would spuriously match a
34
- symbol *inside* the minified library and get blocked, only for `graph_continue` to
35
- then find nothing. The scanner now skips these files (cleaner retrieval, smaller
36
- graph, no behavior change for real source).
37
-
38
- ---
39
-
40
- ## [0.13.0] 2026-06-24
41
-
42
- ### Added
43
-
44
- - **The resume digest now lists the symbols that changed since your last session.**
45
- The SessionStart "Since you were last here" primer showed *files* touched; it now
46
- leads its supporting context with the actual **symbols/signatures** that changed —
47
- e.g. `src/auth.ts::login (function) — function login(creds: Creds): Promise<...>`.
48
- Computed from a git diff against the previous session's HEAD (committed **and**
49
- uncommitted changes), overlapped with the current graph. Best-effort: silently
50
- omitted in non-git projects.
51
- - **`call_path(from, to)` trace control flow.** Returns the shortest chain of
52
- calls from one symbol to another (`handler service repo`), so you can see how
53
- one symbol reaches another. The forward complement to `blast_radius` (callers).
54
- Each of `from`/`to` is a `file::symbol` target or a bare symbol name when unique.
55
-
56
- Both reuse the existing call graph + git — no graph schema change, no new dependencies.
57
-
58
- ---
59
-
60
- ## [0.12.0] — 2026-06-24
61
-
62
- ### Added
63
-
64
- - **`find_symbol(name)` reuse before you re-implement.** Before writing a new
65
- helper, ask Synthra whether one already exists: `find_symbol` returns every
66
- exact-name definition (with signatures + ready `graph_read` targets), or if
67
- there's no exact match similarly-named symbols to reuse or extend. "No symbol
68
- matching safe to create" is the green light that it's genuinely new. The
69
- injected policy now nudges the agent to check first.
70
- - **`duplicate_symbols` consolidation candidates.** Lists symbol names defined
71
- in more than one file (functions/classes/types; methods excluded, since shared
72
- method names are normal). Advisory duplicates can be intentional; it never
73
- says "delete."
74
-
75
- Both are built on the symbol index (exact name lookup) — no false-positive risk,
76
- no new dependencies.
77
-
78
- ---
79
-
80
- ## [0.11.0] — 2026-06-24
81
-
82
- ### Added
83
-
84
- - **`graph_read` now shows which tests cover a symbol.** A symbol read appends a
85
- `Tests (file-level): …` line listing the test files linked to the symbol's file
86
- (via the graph's `tests` edges) so after an edit you run the *right* test
87
- instead of guessing or running the whole suite. Ordinary source files with no
88
- linked test get a one-line "none linked" nudge.
89
- - **`blast_radius` is now symbol-aware.** A `file::symbol` target returns the
90
- exact caller **symbols** that transitively call it (`name file:line`), plus a
91
- line naming the test files that guard the impact — the precise view you want
92
- before a rename. A bare file target keeps the existing file-level dependent
93
- list. (The `graph_read` "Used by (N)" footer remains the cheap always-on
94
- direct-caller summary; this is the complete, transitive, on-demand one.)
95
-
96
- ---
97
-
98
- ## [0.10.0] — 2026-06-20
99
-
100
- ### Added
101
-
102
- - **Terminal-bypass visibility (observe-only).** The Moat blocks `Grep`/`Glob`,
103
- but the agent can still explore the codebase through the shell `rg foo src/`,
104
- `cat src/x.ts`, `find …`and every such call is a read the graph could have
105
- served in ~50 tokens. Synthra now watches `Bash` too: it classifies these
106
- exploration commands and records each one with whether the graph could have
107
- answered it to `bash_log.jsonl`, surfaced on the dashboard's Moat card as
108
- "N terminal hunts · M the graph could answer." It is **observe-only — it never
109
- blocks a Bash command** so you can measure the leak before deciding whether
110
- to close it. Conservative by design (it ignores `npm`/`git`/builds, stdin
111
- filters like `… | grep`, and any command with a redirect). Disable with
112
- `SYN_NO_BASH_OBSERVE`.
113
-
114
- ---
115
-
116
- ## [0.9.0] — 2026-06-20
117
-
118
- ### Added
119
-
120
- - **The graph auto-reindexes edited files mid-session it never goes stale.**
121
- Previously the in-memory graph was a snapshot from the last `syn .`: edit a
122
- file and `graph_read` / `blast_radius` / the dependency footer would keep
123
- serving the *old* signature, body, and line ranges until the next manual scan.
124
- Now the running server watches for source changes and, ~1s after edits settle,
125
- re-runs the incremental scan and hot-swaps the graph in place — so reads always
126
- reflect what's on disk. The rescan is incremental (only the changed file hits
127
- tree-sitter; everything else reuses the content-hash parse cache) and debounced
128
- so a burst of saves coalesces into one rebuild. Tune with
129
- `SYN_REINDEX_DEBOUNCE_MS` (default `1000`); disable with `SYN_NO_AUTOREINDEX`.
130
-
131
- ### Fixed
132
-
133
- - **In-session rescans (auto-reindex and branch-switch) no longer rewrite your
134
- `CLAUDE.md` / `.gitignore`.** A rescan now skips the bootstrap step it only
135
- rebuilds the graph. This also closes a feedback loop the new auto-reindex would
136
- otherwise hit (rewriting the watched `CLAUDE.md` on every rescan would retrigger
137
- the watcher endlessly).
138
- - **`CLAUDE.md` no longer accumulates a blank line on every `syn .`.** The policy
139
- block patcher is now idempotent re-running with nothing to change is a true
140
- no-op instead of appending an empty line above the managed block each time.
141
-
142
- ---
143
-
144
- ## [0.8.1] 2026-06-16
145
-
146
- ### Changed
147
-
148
- - **Dashboard polish.** The Overview is now a tidy bento — equal-height cards,
149
- with the Savings and Total-spend cards sized to their content and a tall Moat
150
- spanning the right that scrolls internally. The Arsenal view groups skills,
151
- agents, and MCP servers into labeled sections by source ("In this project",
152
- "Personal · this machine", and one per plugin) so a big toolkit is easy to
153
- scan. Base text bumped to 14px for readability. UI-only; data unchanged.
154
-
155
- ---
156
-
157
- ## [0.8.0]2026-06-15
158
-
159
- ### Changed
160
-
161
- - **The dashboard is rebuilt on Svelte + shadcn-svelte with a real sidebar.** A
162
- persistent, collapsible left sidebar (Overview · Arsenal · FAQ) replaces the
163
- old top-nav + cramped drawer; the **Arsenal is now a roomy first-class view**
164
- (tabs, filter, expandable cards) instead of a 340px slide-out. Same data, same
165
- endpoints, same numbers — only the UI changed. The Svelte/Tailwind/Vite
166
- toolchain is build-time only: it compiles to a single inlined HTML the server
167
- serves exactly as before, so the installed runtime and zero-config setup are
168
- unchanged (no new runtime dependencies).
169
-
170
- ---
171
-
172
- ## [0.7.0] — 2026-06-15
173
-
174
- ### Added
175
-
176
- - **Dashboard "Arsenal" drawer.** A collapsible panel (toggle in the nav) lists
177
- everything Claude Code has available to you — **skills, subagents, and MCP
178
- servers** — scoped project / personal / plugin, each expandable to its
179
- description (agents also show tools + model). It scans your project `.claude/`,
180
- your personal `~/.claude/`, and every installed plugin, so you never have to
181
- drop to the CLI to remember what's in your toolkit. MCP entries are shown as
182
- name / type / url only auth headers and tokens are never read into the view.
183
-
184
- ---
185
-
186
- ## [0.6.0] 2026-06-13
187
-
188
- ### Added
189
-
190
- - **`graph_read` now delivers a symbol's dependency surface.** Reading a symbol
191
- appends a footer built from the call graph: **Depends on** — the symbols it
192
- calls, each with its full one-line signature and a `graph_read` target, so you
193
- can edit against real signatures instead of guessing parameter shapes or
194
- re-reading the callee files; and **Used by** the names of the symbols that
195
- call it, so a change's blast radius is visible at a glance. Budgeted via
196
- `SYN_READ_DEPS_CHARS` (default 900); leaf symbols with no calls add nothing.
197
-
198
- ---
199
-
200
- ## [0.5.0]2026-06-13
201
-
202
- ### Added
203
-
204
- - **`graph_read` hands you the cheap edit recipe.** Reading a symbol slice now
205
- ends with the exact targeted `Read(path, offset, limit)` (covering the symbol
206
- plus a little headroom) that satisfies Claude Code's Edit read-gate, plus a
207
- "do not re-read the whole file" nudge. A `graph_read` slice doesn't satisfy
208
- the gate on its own, so editing a symbol used to force a whole-file Read —
209
- and the same large file would get re-read many times across a session.
210
- Delivering the recipe at the point of use (not just once in the session
211
- primer) keeps edits cheap.
212
-
213
- ### Changed
214
-
215
- - **The Moat stops wasting blocks on styling searches.** Grep/Glob patterns for
216
- CSS custom properties (`var(--brand)`, `--sidebar`), hex color literals
217
- (`#fff`), and all-kebab class names (`cw-code-chip`) now pass through instead
218
- of being blocked and redirected to a graph the symbol index can't answer.
219
- Mixed queries that also name a real symbol still block.
220
-
221
- ---
222
-
223
- ## [0.4.1] 2026-06-10
224
-
225
- ### Added
226
-
227
- - **Claude Fable model family.** Fable turns (`claude-fable-5`, including the
228
- `[1m]` long-context variant) were bucketed as "Other" in the model donut and
229
- billed at the Sonnet fallback rates. The dashboard now prices Fable at its
230
- published rates ($10/M input, $50/M output, $1/M cache read, $12.50/M cache
231
- write) and gives it its own donut segment, legend entry, turn pill, color,
232
- and FAQ rate-table column. Historical Fable turns reprice correctly on the
233
- next dashboard load cost is computed at read time from the raw model IDs
234
- in the token log.
235
-
236
- ---
237
-
238
- ## [0.4.0] — 2026-06-10
239
-
240
- ### Changed
241
-
242
- - **The Moat's block messages now deliver the answer, not just directions.**
243
- When the gate blocks a Grep/Glob, the deny reason used to name the relevant
244
- file paths — and agents responded by Reading those files whole, erasing the
245
- savings the block was meant to create. The block message now carries
246
- copy-pasteable `mcp__synthra__graph_read("file::symbol")` targets with
247
- one-line signatures for the query's best-matching symbols (~300 tokens,
248
- signatures only), plus a `graph_continue` pointer for the full pack. The
249
- cheap path is now the path of least resistance. Budget tunable via
250
- `SYN_GATE_HINT_CHARS` (default 1200 chars). Gate decisions are unchanged
251
- only the message got smarter.
252
- - **Policy v7 — full namespaced tool names.** Agents wasted tool-discovery
253
- round-trips searching for short names like `graph_continue` that don't
254
- resolve. The CLAUDE.md policy block now states the `mcp__synthra__` namespace
255
- requirement up front, provides a ready ToolSearch `select:` line for the
256
- graph tools, and uses the full form in every invocation example. Existing
257
- policy blocks upgrade automatically on the next `syn .`.
258
-
259
- ---
260
-
261
- ## [0.3.1] 2026-06-09
262
-
263
- ### Changed
264
-
265
- - **Dashboard layout.** The Total-spend (cost) hero now sits beside the Savings
266
- card in a responsive two-column row at the top of the center column (collapsing
267
- to one column on narrow viewports), and the new "Hot files" list is height-capped
268
- with its own scrollbar so a long list never crowds the Moat card beneath it.
269
-
270
- ---
271
-
272
- ## [0.3.0] 2026-06-09
273
-
274
- ### Added
275
-
276
- - **Incremental scanner.** `syn .` now re-parses only the files whose content
277
- changed since the last scan, reusing cached parses (symbols, imports, calls)
278
- for everything else via a content-hash parse cache. Rescans of a large repo
279
- after editing a handful of files are dramatically faster; the resulting graph
280
- is byte-identical to a full scan. `syn . --full` forces a clean rebuild. This
281
- makes the long-standing "updated incrementally" claim actually true.
282
- - **Call-graph edges.** Function and method call sites are now captured during
283
- parsing and resolved (name-based, precision-first: same-file wins, else the
284
- unique repo-wide symbol; ambiguous/external calls are skipped) into
285
- symbol→symbol `calls` edges. `blast_radius` therefore surfaces **callers**,
286
- not just importers and tests so the impact of changing a function includes
287
- the code that calls it. Captured across 14 languages (TypeScript, Python, Go,
288
- Rust, Java, C, C++, C#, plus best-effort Kotlin/PHP/Ruby and Svelte/Vue
289
- passthrough); this makes the "call relationships" claim honest.
290
- - **Dashboard "Hot files" card.** The dashboard now surfaces the usage-learning
291
- layer directly: the active project's hottest files by recent, decayed access.
292
- - **Dashboard favicon.** The dashboard tab now carries the Synthra S mark.
293
-
294
- ### Internal
295
-
296
- - The scanner is now under test directory walker, parser dispatch, a
297
- per-language symbol/call smoke suite, and the context packer alongside the
298
- call-resolution and incremental-equivalence tests. CI runs on Node 24 actions
299
- with the test matrix on Node 22 (ubuntu + windows).
300
-
301
- ---
302
-
303
- ## [0.2.1] 2026-06-06
304
-
305
- ### Changed
306
-
307
- - **Keyword retrieval is now IDF-weighted (BM25's term-rarity component).** A
308
- query token that's rare across the repo counts for more than a common one, so
309
- on a multi-term query the files matching the *specific* terms rank above those
310
- matching generic ones instead of every keyword match counting the same. The
311
- weighting is normalized to the query's mean IDF, so a typical match scores the
312
- same as before: overall ranking magnitude — and the confidence / Moat gating
313
- that depends on it — is unchanged. Purely an in-repo ranking refinement, no API
314
- or data-model change. (TF-saturation / length-norm parts of full BM25 don't
315
- apply to the deduped top-N keyword representation.)
316
-
317
- ---
318
-
319
- ## [0.2.0] — 2026-06-06
320
-
321
- ### Added
322
-
323
- - **Cross-session "second brain" — a resume digest at session start.** Synthra
324
- now captures a snapshot at session end (open next-steps/decisions, files
325
- touched, and commits since your last session) and, on the next session, leads
326
- the SessionStart primer with a budget-bounded **"Since you were last here"**
327
- digest. A fresh session arrives already oriented instead of re-paying tokens
328
- to rediscover recent work. The snapshot lives in `.synthra-graph/`
329
- (machine-local) and falls back to the normal primer when there's nothing to
330
- show.
331
- - **Usage learningretrieval that gets smarter the more you use it.** Files
332
- you actually open (`graph_read`) or edit (`graph_register_edit`) accrue a
333
- time-decayed weight (7-day half-life), and retrieval gives genuinely "hot"
334
- files a small, capped re-rank boost. It's anchored to files that already match
335
- your query and capped below the existing seed boost, so it sharpens ranking
336
- without ever overriding relevance. Purely local, per-developer; degrades to
337
- the exact prior ranking when there's no usage history. Tunable via
338
- `SYN_LEARN_HALFLIFE_DAYS` and `SYN_LEARN_BOOST_CAP`.
339
- - **CLAUDE.md policy v6** — teaches the assistant to trust the resume digest and
340
- pull concrete next steps via `context_recall({kind:"next"})` instead of
341
- re-exploring the codebase.
342
-
343
- ### Fixed
344
-
345
- - **`pre-compact.sh` now parses the primer with `jq`, not a greedy `sed`
346
- capture** completing the `jq` migration across all four bash hooks (matches
347
- the Stop/Prime/PreToolUse fixes). The multi-line resume digest contains quotes
348
- and newlines the old `sed` capture would have mangled.
349
-
350
- ### Internal
351
-
352
- - **CI (GitHub Actions), Biome (lint + format), and coverage** added. CI runs on
353
- an ubuntu + windows matrix, so cross-platform hook regressions are caught
354
- automatically. `.gitattributes` enforces LF line endings on every platform.
355
-
356
- ---
357
-
358
- ## [0.1.25] 2026-06-06
359
-
360
- ### Fixed
361
-
362
- - **PreToolUse (Moat) bash hook now parses the gate response with `jq`, not a
363
- greedy `sed` capture (issue #13).** `src/hooks/scripts/pre-tool-use.sh`
364
- extracted the block `reason` via `sed -n 's/.*"reason"…\(.*\)".*/\1/p'`. The
365
- greedy `\(.*\)"` capture over-ran into the trailing JSON fields, and because a
366
- block `reason` legitimately contains double quotes (it quotes the searched
367
- query, e.g. `"login"`), the captured text broke the deny JSON when embedded
368
- raw in the output heredoc — so on a real block Claude Code received malformed
369
- `hookSpecificOutput` and the deny was silently dropped. The hook now reads
370
- `.decision` / `.reason` with `jq -r '… // empty'` and re-emits the deny object
371
- with `jq -nc --arg` (correct escaping), behind a `command -v jq` guard that
372
- silently no-ops when `jq` is absent mirroring the Stop/Prime hooks fixed in
373
- #1. Gate/Moat decision logic is unchanged. This completes the `jq` migration
374
- across all three bash hooks (the last v0.2 item). Verified end-to-end under
375
- bash on Linux: SessionStart primer injection, Grep/Glob Moat blocks with
376
- well-formed escaped deny JSON, and Stop-hook token totals reaching the
377
- dashboard.
378
-
379
- ---
380
-
381
- ## [0.1.24] 2026-06-06
382
-
383
- ### Added
384
-
385
- - **`syn doctor [path]` setup and environment health check (issue #9).** New
386
- read-only CLI subcommand that runs a one-shot checklist and exits. Checks: Node
387
- version, `jq` availability (bash Stop/Prime hooks silently no-op without it),
388
- `claude` CLI on PATH, graph freshness (symbol count, schema version, scan age),
389
- `.mcp.json` project-scope registration (required for Synthra tools to appear in
390
- the Claude Code IDE), CLAUDE.md policy-block version, and hook installation
391
- status. Warnings surface with the exact `syn .` command needed to resolve them.
392
- The command mutates nothing safe to run at any time.
393
-
394
- - **Graph-tool usage metric on the dashboard (issue #2).** The MCP server now
395
- appends a record to `.synthra-graph/tool_log.jsonl` on every Synthra tool call
396
- (`graph_continue`, `graph_read`, `graph_register_edit`, etc.). `delta.ts`
397
- aggregates per-tool call counts into `ProjectStats.tool_calls` (per-project) and
398
- `global.tool_calls` (cross-project totals). The dashboard shows a new "Graph
399
- tools used" card in the right column with per-tool counts. This is a positive
400
- signal complementing the Moat's blocked-Grep count: it captures Synthra pivots
401
- that happen before a Grep fires, which the block counter misses entirely.
402
-
403
- - **Session-aware routing`graph_continue` seeds retrieval with the session's
404
- touched files (issue #14).** Files the human recently saved (last 15 min) and
405
- files the AI registered via `graph_register_edit` now get a ranking boost in
406
- `graph_continue` results, so the returned context tracks what you're actually
407
- working on. Mirrors the `/pack` route, which already seeded retrieval this way.
408
-
409
- ---
410
-
411
- ## [0.1.23] — 2026-06-06
412
-
413
- ### Added
414
-
415
- - **Dashboard token-log dedupe can now be disabled via `SYN_DASHBOARD_DEDUPE=0`.**
416
- By default, `delta.ts` deduplicates `token_log.jsonl` entries that share the
417
- same project, usage totals, and second-rounded timestamp collapsing the
418
- duplicate writes that a co-installed AI tool's Stop hook may produce. Set
419
- `SYN_DASHBOARD_DEDUPE=0` (also accepts `off` or `false`) to see every raw
420
- entry. Useful when debugging multi-tool coexistence or auditing raw log data.
421
-
422
- - **Graph schema-migration check on load.** A new `SCHEMA_VERSION` constant is
423
- exported from `src/graph/types.ts` and stamped into `info_graph.json` by
424
- `buildGraph`. On server start, `http.ts` compares the stored graph's
425
- `schema_version` to the current constant; if they differ it triggers an
426
- automatic one-time rescan instead of serving an incompatible graph. No
427
- behavior change today — all graphs are v1 and schema_version matches — but
428
- this is the forward-safety mechanism for future schema bumps so existing
429
- graphs are never silently misread.
430
-
431
- ### Fixed
432
-
433
- - **JS/TS parser now captures member-assigned functions** (`exports.handler = fn`,
434
- `module.exports.route = () => {}`, `this.x = () => {}`). Previously these
435
- CommonJS/member-export patterns were invisible to the query, so modules that
436
- exclusively use this style extracted zero symbols and degraded to whole-file
437
- reads via `graph_read`. A member-assignment capture has been added to both
438
- `JS_QUERY` and `TS_QUERY` in `src/scanner/parsers/typescript.ts`. Note: a
439
- pure-wiring `server.js` whose only structure is anonymous inline-callback
440
- arguments (e.g. `io.use(...)` / `socket.on(event, fn)`) is genuinely
441
- symbol-less that is correct, and the gate's symbol-hit guard already
442
- prevents blocking such files.
443
-
444
- ### Changed
445
-
446
- - **Policy block v4 v5.** Adds a "large file — pull the symbol, don't
447
- chunk" nudge to address recurring dogfood friction: on large files Claude
448
- was reading successive line-range chunks instead of fetching the specific
449
- symbol via `graph_read("file::symbol")`. The v5 block now explicitly
450
- instructs: when a file is large, use `graph_read("file/path.ts::SymbolName")`
451
- to pull the symbol directly rather than reading successive line-range chunks.
452
- `POLICY_VERSION` bumped `4 5`; existing v4 blocks auto-upgrade on the
453
- next `syn .` run.
454
-
455
- ---
456
-
457
- ## [0.1.22] 2026-06-06
458
-
459
- ### Fixed
460
-
461
- - **`graph_read` now resolves shortened file paths (path-suffix fallback).** Previously
462
- `graph_read` performed an exact `path === target` match only. Passing a shortened path
463
- like `appsettings.json` returned "file not found" even when
464
- `connectwarev2/.../appsettings.json` was indexed. A new `resolveFileTarget` helper (now
465
- exported) tries an exact match first; on a miss it looks for a unique path-suffix match
466
- and serves that file; if multiple files share the suffix it reports them as ambiguous with
467
- candidate paths rather than guessing. Symbol lookups use the resolved path. No API or
468
- protocol change. Roadmap item #11.
469
-
470
- - **Gate content-keyword relaxation now intersects file contents, not just file paths.**
471
- The Moat's recent-activity relaxation previously matched query tokens against the paths of
472
- recently-touched files only. A query like `Grep "login"` would not relax on a recent save
473
- of `auth.ts` unless the word "login" appeared in the path. Now the relaxation also checks
474
- the recently-touched file's graph-node keywords (its indexed content), so a recent save
475
- relaxes a Grep whenever the file *contains* the queried term not just when the path
476
- matches it. Completes roadmap item #3.
477
-
478
- ### Changed
479
-
480
- - **Dashboard Projects card shows a first-run hint in the empty state.** When no projects
481
- have run `syn .` yet, the Projects card now displays "No projects yet — run `syn .` in a
482
- project to start" instead of a blank card. The Recent-turns card already carried this
483
- hint; Projects now matches it. Roadmap item #10.
484
-
485
- - **`bin` path normalization (chore).** Ran `npm pkg fix` to normalize `bin` entries from
486
- `./bin/syn` to `bin/syn`. Silences the cosmetic publish warnings
487
- (`"bin[syn]" script name was cleaned`). `syn` and `synthra` still resolve to the same
488
- entry point. Roadmap item #4.
489
-
490
- ---
491
-
492
- ## [0.1.21] 2026-06-06
493
-
494
- ### Added
495
-
496
- - **HubL (HubSpot CMS) symbol extraction for `.html` and `.hubl` files.**
497
- Previously `.html` files were content-indexed only — keyword search and
498
- whole-file reads, no symbol-level granularity. On HubSpot projects this
499
- meant the graph contributed nothing: zero `graph_continue`/`graph_read`
500
- calls resolved to symbol slices all session. Now `.html` and `.hubl` files
501
- run through a new **regex-based** parser (`parsers/hubl.ts`; there is no
502
- tree-sitter grammar for HubL):
503
- - `{% macro name(args) %}` extracted as a `function` symbol
504
- - `{% block name %}` extracted as a `component` symbol
505
- - `{% include / extends / import / from "path" %}` import edges (relative
506
- paths resolve to local templates; `.html`/`.hubl` added to the resolver's
507
- extension list)
508
-
509
- Plain HTML with no HubL tags is unaffected — the parser yields zero symbols
510
- and zero imports, identical to before. No API, protocol, or policy-block
511
- change. Roadmap item #12.
512
-
513
- ---
514
-
515
- ## [0.1.20]2026-06-06
516
-
517
- ### Fixed
518
-
519
- - **Gate (Moat) no longer blocks Grep/Glob queries the graph cannot answer with a symbol.**
520
- Previously, the PreToolUse gate blocked whenever retrieval confidence was `medium` or `high`,
521
- but confidence is driven by keyword and path hits too — not only by symbol matches. This meant
522
- literal/attribute/CSS-selector patterns (`data-tour=`, `class=`, `: 100%`, `.filter-bar`,
523
- `<div>`) and path-only Globs were blocked and redirected to `graph_read`, which has no symbol
524
- slice to return for those queries, so Claude fell back to Grep or a whole-file Read anyway —
525
- a wasted round-trip. Found across multiple dogfood sessions including well-indexed Svelte
526
- repos. Two new guards close the gap:
527
- - **Query-shape pre-filter** Grep patterns that target markup, CSS, attributes, or string
528
- literals are allowed through up front, before the retrieval step runs.
529
- - **Symbol-hit requirement** — the gate now only blocks when retrieval matched a symbol whose
530
- name the query mentions exactly. `RetrievalResult` gained a `symbolMatched` flag; the scorer
531
- exposes `exactSym`.
532
-
533
- Net effect: genuine symbol lookups still block (verified: `fetchWith429Retry`,
534
- `MAX_ROWS_PER_TABLE`, `verifyPin`, `SOCKET_AUTH_SECRET`, `seedCredentials`); queries that
535
- could never have been answered by the graph now allow through without the wasted redirect.
536
- No API, protocol, or policy-block change — purely server-side gate behavior.
537
-
538
- - **Gate and rank test coverage added** (`tests/gate.test.ts`, `tests/rank.test.ts`).
539
- Chips at the v0.2 backlog item to fill vitest tests beyond `it.todo` placeholders.
540
-
541
- ---
542
-
543
- ## [0.1.19] 2026-06-01
544
-
545
- ### Changed
546
-
547
- - **Policy block v4: targeted Read-before-Edit for graph-discovered files.**
548
- Claude Code's `Edit` tool requires a file to have been opened with its own
549
- `Read` tool; a `graph_read` slice does not satisfy that gate. Previously,
550
- editing a file known only through `graph_read` would fail with *"File has
551
- not been read yet"* and force a whole-file `Read` — eroding token savings on
552
- edit-heavy sessions. The v4 policy now instructs: take the line range already
553
- reported in the `graph_read` header (e.g. `…::handler (L120-168)`), do a
554
- targeted `Read` with matching `offset`/`limit`, then `Edit`. This satisfies
555
- the gate while keeping the read small. Existing v3 blocks auto-upgrade on the
556
- next `syn .` run.
557
-
558
- ---
559
-
560
- ## [0.1.18] — 2026-06-01
561
-
562
- ### Fixed
563
-
564
- - **Stop hook on Linux/macOS no longer posts zero tokens to the dashboard.** The bash
565
- `stop.sh` hook extracted `transcript_path` from the Claude Code Stop payload using a
566
- greedy `sed` capture (`\(.*\)"`). Because the real payload has additional fields after
567
- `transcript_path`, the capture grabbed those trailing fields and produced a
568
- non-existent path string. The `-f` file check therefore always failed, totals were
569
- never POSTed to `/log`, and the dashboard stayed stuck at 0 on every turn (GitHub
570
- issue #1). Fixed by parsing with `jq -r '.transcript_path // empty'` and moving the
571
- `command -v jq` guard above the parse so the hook exits cleanly when `jq` is absent.
572
- - **SessionStart/PreCompact primer hook (`prime.sh`) hardened the same way.** The
573
- `/prime` response is `{"primer":"…","port":…}`, so the old greedy capture accidentally
574
- injected trailing `","port":…` junk into the primer string. Because primer text can
575
- contain inner quotes, a negated-class fix (`[^"]*`) would have truncated it at the
576
- first quote — `jq -r '.primer // empty'` is the correct parse. Switched `printf '%b'`
577
- to `printf '%s'` since `jq -r` already decodes JSON escapes.
578
- - Both fixes are **bash-only**. The Windows PowerShell hooks (`stop.ps1`, `prime.ps1`)
579
- use `ConvertFrom-Json` and were already correct.
580
-
581
- ---
582
-
583
- ## [0.1.17] 2026-05-29
584
-
585
- ### Added
586
-
587
- - **`syn .` scaffolds an agent-onboarding CLAUDE.md on brand-new projects.**
588
- When a project has no CLAUDE.md, Synthra now writes a lean skeleton
589
- `Build & test`, `Conventions`, `Key decisions`, `Gotchas` (with TODO
590
- prompts) *above* its managed policy block, instead of a bare policy
591
- block. This is the durable "why/how" layer the graph can't infer; the
592
- graph still owns "what/where." Fill it in, or run `/init` to auto-draft.
593
- The skeleton is written **once** and lives outside the
594
- `<!-- synthra-policy -->` markers, so re-running `syn .` (which
595
- refreshes the policy block) never clobbers what you've written.
596
- Projects that already have a CLAUDE.md are untouched no skeleton is
597
- injected.
598
-
599
- ---
600
-
601
- ## [0.1.16] — 2026-05-29
602
-
603
- ### Changed
604
-
605
- - **Moat card shows 50 recent gate decisions** (was 12). The inline list
606
- already scrolls within the card, and the `/data` payload already carries
607
- up to 500 gates, so this just renders more of them. The headline block
608
- count was always all-time/uncapped unchanged.
609
-
610
- ---
611
-
612
- ## [0.1.15] 2026-05-29
613
-
614
- ### Changed
615
-
616
- - **Recent turns are paginated.** The dashboard now carries up to 500 turns
617
- (was 25) and shows them 25 per page with Prev/Next controls — so you can
618
- browse history instead of only seeing the last 25. Configurable via
619
- `SYN_DASHBOARD_RECENT_N` (default 500).
620
- - **Model-usage donut is now all-time, not last-25.** It was tallying models
621
- from the capped recent-turns window, so a run of >25 same-model turns showed
622
- that model at 100% and hid the rest. It now sums the uncapped per-project
623
- model counts, so it always reflects your true all-time split.
624
- - **Dashboard poll slowed 2s 10s.** Lighter on resources and steadier to
625
- read; pagination stays live (the current page re-renders each poll).
626
-
627
- ---
628
-
629
- ## [0.1.14] — 2026-05-29
630
-
631
- ### Changed
632
-
633
- - **Dashboard visual refresh.** No API surface change — all visual / UX.
634
- - Removed the hero strip and the standalone Legend card. Date + active
635
- project now live as compact chips inside the top nav (active-project
636
- path uses RTL truncation so the folder name stays visible).
637
- - New **Projects bar chart** in the left column — colored bars ranked
638
- by turn count. Stable per-name OKLCH palette (8 colors, hash-keyed)
639
- so a project keeps the same color across sessions. Click any row to
640
- open its full breakdown.
641
- - **Donut legend** gains a turn-count column alongside the percentage.
642
- - **Savings card** elevated: radial green backdrop, money figure 40px,
643
- soft glow makes the "what Synthra saved you" number the visual
644
- anchor of the dashboard.
645
- - **Recent turns column headers** are now hover-explainable.
646
- - Active-project chip tightens + month name hides under 1100px width.
647
-
648
- ---
649
-
650
- ## [0.1.13] — 2026-05-29
651
-
652
- ### Fixed
653
-
654
- - **Dashboard footer version is now dynamic.** Was hardcoded to `v0.1.8`
655
- in the HTML and never updated. The dashboard server now injects the
656
- running binary's version (from `package.json`) into the footer on every
657
- `GET /` via a `__SYN_VERSION__` placeholder. Re-run `syn .` after an
658
- update and the dashboard reflects the new version automatically.
659
-
660
- ---
661
-
662
- ## [0.1.12] 2026-05-29
663
-
664
- ### Fixed
665
-
666
- - **`Language.query is deprecated` spam at scan time.** Every parsed file
667
- printed the warning — 57 prints on a Flutter codebase, one per parsed
668
- file. Switched all four parsers (TypeScript, JavaScript, Python, Dart,
669
- plus the generic helper) from the deprecated `language.query(QUERY)`
670
- to `new Query(language, QUERY)`. No behavior change, just clean
671
- terminal output.
672
-
673
- ---
674
-
675
- ## [0.1.11] 2026-05-29
676
-
677
- ### Fixed
678
-
679
- - **Dart parser actually runs now.** Was silently broken since v0.1 due to an
680
- ABI mismatch (shipped wasm was ABI v15, pinned `web-tree-sitter` only
681
- supported v13–v14). Every `.dart` file got zero symbols, zero imports —
682
- the exception was swallowed by the parser's try/catch. Bumped
683
- `web-tree-sitter` to `^0.25.10` to fix.
684
- - **Real Dart symbol extraction.** Classes, mixins, extensions, enums,
685
- typedefs, top-level functions, methods, getters, setters, constructors.
686
- - **Dart import normalization.** `package:foo/bar.dart` and `dart:async` are
687
- stripped (cross-project); bare `'sibling.dart'` is rewritten to
688
- `./sibling.dart` so the project resolver can complete them.
689
-
690
- ### Changed
691
-
692
- - **Update check runs on every `syn .`** (no more 24h cache). If you're on
693
- latest, stays silent. If outdated, prompts `[y/N]` as before.
694
- - **Auto-update now shows a changelog.** After `npm install -g …@latest`
695
- succeeds, Synthra prints the new version's section from this file before
696
- telling you to re-run. Catches `npm install` outside of `syn .` too —
697
- next startup compares your current version to `~/.synthra/last-seen-version.json`
698
- and prints if it's newer.
699
-
700
- ---
701
-
702
- ## [0.1.10] 2026-05-29
703
-
704
- ### Changed
705
-
706
- - **CLAUDE.md policy v2 v3.** Session-end now goes through
707
- `context_remember({kind: "task"|"decision"|"next"})` instead of writing
708
- `.synthra/CONTEXT.md` directly. The Stop hook always re-rendered CONTEXT.md
709
- from `context-store.json` — under v2 Claude's direct writes were getting
710
- wiped on session end. Existing v2 blocks auto-upgrade.
711
-
712
- ### Added
713
-
714
- - **Scanner ignores more build caches.** `.dart_tool/`, `.flutter-plugins`,
715
- `.flutter-plugins-dependencies`, `.gradle/`, `target/`, `Pods/`,
716
- `DerivedData/`, `__pycache__/`, `.venv/`, `venv/`, `.tox/`,
717
- `.pytest_cache/`, `.mypy_cache/`, `.ruff_cache/`, `obj/`, `.vs/`.
718
-
719
- ---
720
-
721
- ## [0.1.9] — 2026-05-29
722
-
723
- ### Fixed
724
-
725
- - **Crash on prototype-colliding symbol names.** `buildSymbolIndex` built
726
- the lookup on a plain `{}`, so a symbol named `toString` (which every
727
- Dart class overrides), `constructor`, `valueOf`, etc. resolved to the
728
- inherited `Object.prototype` member and crashed on `.push`. Now uses
729
- `Object.create(null)` on both fresh-build and load-from-disk paths.
730
-
731
- ---
732
-
733
- ## [0.1.8] 2026-05-29
734
-
735
- ### Added
736
-
737
- - **Interactive auto-update.** `syn .` checks npm at startup; if a newer
738
- version is available, prompts `[y/N]`. On `y`, runs
739
- `npm install -g @jefuriiij/synthra@latest` with stdio inherited and
740
- exits with re-run instructions. Non-TTY runs (CI, piped stdin) fall
741
- back to a silent one-line hint. `SYN_NO_UPDATE_CHECK=1` opts out.
742
-
743
- ---
744
-
745
- ## [0.1.7] 2026-05-29
746
-
747
- ### Fixed
748
-
749
- - **JS parser missed CommonJS imports + JS class names.** Unified TS/JS
750
- query only matched ES `import_statement`, and used `(type_identifier)`
751
- for class names which is TS-grammar-only. Result: every `.js`/`.cjs`/
752
- `.mjs` file silently produced zero imports, and any class in a JS file
753
- was skipped. Split into `TS_QUERY` and `JS_QUERY`; JS query adds a
754
- `require()` capture and uses `(identifier)` for class names.
755
-
756
- ---
757
-
758
- ## [0.1.6] 2026-05-29
759
-
760
- ### Fixed
761
-
762
- - **MCP registration now uses `--scope project`** so the Claude Code IDE
763
- extension actually sees Synthra. The previous `--scope local` wrote to
764
- a per-project section of `~/.claude.json` that only the `claude` CLI
765
- reads — invisible to the IDE.
766
-
767
- ---
768
-
769
- ## [0.1.5] and earlier
770
-
771
- See [GitHub commits](https://github.com/jefuriiij/synthra/commits/main) for
772
- detail. v0.1.5 introduced the v2 policy template with namespace + skip rules;
773
- v0.1.4 fixed a DEP0190 deprecation on Windows; v0.1.3 was the dashboard
774
- redesign (Cool Marine palette, FAQ modal, savings audit row).
1
+ # Synthra changelog
2
+
3
+ Notable changes per version. This file ships inside the npm tarball — `syn .`
4
+ reads it after an auto-update to show you what changed.
5
+
6
+ For older versions, see [GitHub Releases](https://github.com/jefuriiij/synthra/releases).
7
+
8
+ ---
9
+
10
+ ## [0.14.1] — 2026-07-02
11
+
12
+ ### Added
13
+
14
+ - **Dashboard: copy button beside every Arsenal card name.** One click copies the
15
+ skill/agent/MCP name to your clipboard (with a flash) handy for invoking a
16
+ skill or referencing an agent without toggling the card open.
17
+
18
+ ### Fixed
19
+
20
+ - **Dashboard: expanding an Arsenal card no longer stretches its neighbors.**
21
+ Opening a skill/agent card made the cards beside it in the same grid row grow
22
+ to matching height looking expanded while their text stayed clamped until
23
+ clicked. Cards now align to the top of their row, so only the clicked card
24
+ grows and neighbors keep their compact size.
25
+
26
+ ---
27
+
28
+ ## [0.14.0] — 2026-07-02
29
+
30
+ ### Added
31
+
32
+ - **`syn remove [path]` cleanly uninstall Synthra from a project.** Ran `syn .`
33
+ in the wrong folder, or just want Synthra out of a repo? `syn remove` reverses
34
+ the bootstrap: deletes `.synthra-graph/` and `.synthra/`, strips the policy
35
+ block from `CLAUDE.md`, Synthra's entries from `.gitignore`, its hooks from
36
+ `.claude/` **your own content in those files survives**; a file is deleted
37
+ only when nothing else remains. Also deregisters the MCP entry (with a direct
38
+ `.mcp.json` fallback when the `claude` CLI isn't available) and removes the
39
+ project from the dashboard registry. Shows a summary and asks `[y/N]` first;
40
+ pass `--yes` to skip (required when not running in a terminal).
41
+
42
+ ---
43
+
44
+ ## [0.13.1] 2026-06-24
45
+
46
+ ### Fixed
47
+
48
+ - **Minified/bundle files are no longer indexed.** Committed vendored plugin JS
49
+ (`*.min.js`, `*.bundle.js`, `*.min.css`, …) has no readable symbols, so indexing
50
+ it only polluted retrieval and caused **useless Moat blocks** on markup-heavy
51
+ projects a Grep for CSS classes like `nav|menu|toggle` would spuriously match a
52
+ symbol *inside* the minified library and get blocked, only for `graph_continue` to
53
+ then find nothing. The scanner now skips these files (cleaner retrieval, smaller
54
+ graph, no behavior change for real source).
55
+
56
+ ---
57
+
58
+ ## [0.13.0] — 2026-06-24
59
+
60
+ ### Added
61
+
62
+ - **The resume digest now lists the symbols that changed since your last session.**
63
+ The SessionStart "Since you were last here" primer showed *files* touched; it now
64
+ leads its supporting context with the actual **symbols/signatures** that changed
65
+ e.g. `src/auth.ts::login (function) function login(creds: Creds): Promise<...>`.
66
+ Computed from a git diff against the previous session's HEAD (committed **and**
67
+ uncommitted changes), overlapped with the current graph. Best-effort: silently
68
+ omitted in non-git projects.
69
+ - **`call_path(from, to)` trace control flow.** Returns the shortest chain of
70
+ calls from one symbol to another (`handler service repo`), so you can see how
71
+ one symbol reaches another. The forward complement to `blast_radius` (callers).
72
+ Each of `from`/`to` is a `file::symbol` target or a bare symbol name when unique.
73
+
74
+ Both reuse the existing call graph + git — no graph schema change, no new dependencies.
75
+
76
+ ---
77
+
78
+ ## [0.12.0] — 2026-06-24
79
+
80
+ ### Added
81
+
82
+ - **`find_symbol(name)` — reuse before you re-implement.** Before writing a new
83
+ helper, ask Synthra whether one already exists: `find_symbol` returns every
84
+ exact-name definition (with signatures + ready `graph_read` targets), or if
85
+ there's no exact match similarly-named symbols to reuse or extend. "No symbol
86
+ matching safe to create" is the green light that it's genuinely new. The
87
+ injected policy now nudges the agent to check first.
88
+ - **`duplicate_symbols` consolidation candidates.** Lists symbol names defined
89
+ in more than one file (functions/classes/types; methods excluded, since shared
90
+ method names are normal). Advisory duplicates can be intentional; it never
91
+ says "delete."
92
+
93
+ Both are built on the symbol index (exact name lookup) no false-positive risk,
94
+ no new dependencies.
95
+
96
+ ---
97
+
98
+ ## [0.11.0] — 2026-06-24
99
+
100
+ ### Added
101
+
102
+ - **`graph_read` now shows which tests cover a symbol.** A symbol read appends a
103
+ `Tests (file-level): …` line listing the test files linked to the symbol's file
104
+ (via the graph's `tests` edges)so after an edit you run the *right* test
105
+ instead of guessing or running the whole suite. Ordinary source files with no
106
+ linked test get a one-line "none linked" nudge.
107
+ - **`blast_radius` is now symbol-aware.** A `file::symbol` target returns the
108
+ exact caller **symbols** that transitively call it (`name file:line`), plus a
109
+ line naming the test files that guard the impact the precise view you want
110
+ before a rename. A bare file target keeps the existing file-level dependent
111
+ list. (The `graph_read` "Used by (N)" footer remains the cheap always-on
112
+ direct-caller summary; this is the complete, transitive, on-demand one.)
113
+
114
+ ---
115
+
116
+ ## [0.10.0] — 2026-06-20
117
+
118
+ ### Added
119
+
120
+ - **Terminal-bypass visibility (observe-only).** The Moat blocks `Grep`/`Glob`,
121
+ but the agent can still explore the codebase through the shell `rg foo src/`,
122
+ `cat src/x.ts`, `find …` — and every such call is a read the graph could have
123
+ served in ~50 tokens. Synthra now watches `Bash` too: it classifies these
124
+ exploration commands and records each one with whether the graph could have
125
+ answered it to `bash_log.jsonl`, surfaced on the dashboard's Moat card as
126
+ "N terminal hunts · M the graph could answer." It is **observe-only it never
127
+ blocks a Bash command** — so you can measure the leak before deciding whether
128
+ to close it. Conservative by design (it ignores `npm`/`git`/builds, stdin
129
+ filters like `… | grep`, and any command with a redirect). Disable with
130
+ `SYN_NO_BASH_OBSERVE`.
131
+
132
+ ---
133
+
134
+ ## [0.9.0]2026-06-20
135
+
136
+ ### Added
137
+
138
+ - **The graph auto-reindexes edited files mid-session it never goes stale.**
139
+ Previously the in-memory graph was a snapshot from the last `syn .`: edit a
140
+ file and `graph_read` / `blast_radius` / the dependency footer would keep
141
+ serving the *old* signature, body, and line ranges until the next manual scan.
142
+ Now the running server watches for source changes and, ~1s after edits settle,
143
+ re-runs the incremental scan and hot-swaps the graph in place — so reads always
144
+ reflect what's on disk. The rescan is incremental (only the changed file hits
145
+ tree-sitter; everything else reuses the content-hash parse cache) and debounced
146
+ so a burst of saves coalesces into one rebuild. Tune with
147
+ `SYN_REINDEX_DEBOUNCE_MS` (default `1000`); disable with `SYN_NO_AUTOREINDEX`.
148
+
149
+ ### Fixed
150
+
151
+ - **In-session rescans (auto-reindex and branch-switch) no longer rewrite your
152
+ `CLAUDE.md` / `.gitignore`.** A rescan now skips the bootstrap step it only
153
+ rebuilds the graph. This also closes a feedback loop the new auto-reindex would
154
+ otherwise hit (rewriting the watched `CLAUDE.md` on every rescan would retrigger
155
+ the watcher endlessly).
156
+ - **`CLAUDE.md` no longer accumulates a blank line on every `syn .`.** The policy
157
+ block patcher is now idempotent re-running with nothing to change is a true
158
+ no-op instead of appending an empty line above the managed block each time.
159
+
160
+ ---
161
+
162
+ ## [0.8.1] 2026-06-16
163
+
164
+ ### Changed
165
+
166
+ - **Dashboard polish.** The Overview is now a tidy bento equal-height cards,
167
+ with the Savings and Total-spend cards sized to their content and a tall Moat
168
+ spanning the right that scrolls internally. The Arsenal view groups skills,
169
+ agents, and MCP servers into labeled sections by source ("In this project",
170
+ "Personal · this machine", and one per plugin) so a big toolkit is easy to
171
+ scan. Base text bumped to 14px for readability. UI-only; data unchanged.
172
+
173
+ ---
174
+
175
+ ## [0.8.0] — 2026-06-15
176
+
177
+ ### Changed
178
+
179
+ - **The dashboard is rebuilt on Svelte + shadcn-svelte with a real sidebar.** A
180
+ persistent, collapsible left sidebar (Overview · Arsenal · FAQ) replaces the
181
+ old top-nav + cramped drawer; the **Arsenal is now a roomy first-class view**
182
+ (tabs, filter, expandable cards) instead of a 340px slide-out. Same data, same
183
+ endpoints, same numbers — only the UI changed. The Svelte/Tailwind/Vite
184
+ toolchain is build-time only: it compiles to a single inlined HTML the server
185
+ serves exactly as before, so the installed runtime and zero-config setup are
186
+ unchanged (no new runtime dependencies).
187
+
188
+ ---
189
+
190
+ ## [0.7.0] 2026-06-15
191
+
192
+ ### Added
193
+
194
+ - **Dashboard "Arsenal" drawer.** A collapsible panel (toggle in the nav) lists
195
+ everything Claude Code has available to you **skills, subagents, and MCP
196
+ servers** scoped project / personal / plugin, each expandable to its
197
+ description (agents also show tools + model). It scans your project `.claude/`,
198
+ your personal `~/.claude/`, and every installed plugin, so you never have to
199
+ drop to the CLI to remember what's in your toolkit. MCP entries are shown as
200
+ name / type / url only auth headers and tokens are never read into the view.
201
+
202
+ ---
203
+
204
+ ## [0.6.0] 2026-06-13
205
+
206
+ ### Added
207
+
208
+ - **`graph_read` now delivers a symbol's dependency surface.** Reading a symbol
209
+ appends a footer built from the call graph: **Depends on** the symbols it
210
+ calls, each with its full one-line signature and a `graph_read` target, so you
211
+ can edit against real signatures instead of guessing parameter shapes or
212
+ re-reading the callee files; and **Used by** — the names of the symbols that
213
+ call it, so a change's blast radius is visible at a glance. Budgeted via
214
+ `SYN_READ_DEPS_CHARS` (default 900); leaf symbols with no calls add nothing.
215
+
216
+ ---
217
+
218
+ ## [0.5.0] 2026-06-13
219
+
220
+ ### Added
221
+
222
+ - **`graph_read` hands you the cheap edit recipe.** Reading a symbol slice now
223
+ ends with the exact targeted `Read(path, offset, limit)` (covering the symbol
224
+ plus a little headroom) that satisfies Claude Code's Edit read-gate, plus a
225
+ "do not re-read the whole file" nudge. A `graph_read` slice doesn't satisfy
226
+ the gate on its own, so editing a symbol used to force a whole-file Read —
227
+ and the same large file would get re-read many times across a session.
228
+ Delivering the recipe at the point of use (not just once in the session
229
+ primer) keeps edits cheap.
230
+
231
+ ### Changed
232
+
233
+ - **The Moat stops wasting blocks on styling searches.** Grep/Glob patterns for
234
+ CSS custom properties (`var(--brand)`, `--sidebar`), hex color literals
235
+ (`#fff`), and all-kebab class names (`cw-code-chip`) now pass through instead
236
+ of being blocked and redirected to a graph the symbol index can't answer.
237
+ Mixed queries that also name a real symbol still block.
238
+
239
+ ---
240
+
241
+ ## [0.4.1] — 2026-06-10
242
+
243
+ ### Added
244
+
245
+ - **Claude Fable model family.** Fable turns (`claude-fable-5`, including the
246
+ `[1m]` long-context variant) were bucketed as "Other" in the model donut and
247
+ billed at the Sonnet fallback rates. The dashboard now prices Fable at its
248
+ published rates ($10/M input, $50/M output, $1/M cache read, $12.50/M cache
249
+ write) and gives it its own donut segment, legend entry, turn pill, color,
250
+ and FAQ rate-table column. Historical Fable turns reprice correctly on the
251
+ next dashboard load — cost is computed at read time from the raw model IDs
252
+ in the token log.
253
+
254
+ ---
255
+
256
+ ## [0.4.0] 2026-06-10
257
+
258
+ ### Changed
259
+
260
+ - **The Moat's block messages now deliver the answer, not just directions.**
261
+ When the gate blocks a Grep/Glob, the deny reason used to name the relevant
262
+ file paths — and agents responded by Reading those files whole, erasing the
263
+ savings the block was meant to create. The block message now carries
264
+ copy-pasteable `mcp__synthra__graph_read("file::symbol")` targets with
265
+ one-line signatures for the query's best-matching symbols (~300 tokens,
266
+ signatures only), plus a `graph_continue` pointer for the full pack. The
267
+ cheap path is now the path of least resistance. Budget tunable via
268
+ `SYN_GATE_HINT_CHARS` (default 1200 chars). Gate decisions are unchanged
269
+ only the message got smarter.
270
+ - **Policy v7 — full namespaced tool names.** Agents wasted tool-discovery
271
+ round-trips searching for short names like `graph_continue` that don't
272
+ resolve. The CLAUDE.md policy block now states the `mcp__synthra__` namespace
273
+ requirement up front, provides a ready ToolSearch `select:` line for the
274
+ graph tools, and uses the full form in every invocation example. Existing
275
+ policy blocks upgrade automatically on the next `syn .`.
276
+
277
+ ---
278
+
279
+ ## [0.3.1] 2026-06-09
280
+
281
+ ### Changed
282
+
283
+ - **Dashboard layout.** The Total-spend (cost) hero now sits beside the Savings
284
+ card in a responsive two-column row at the top of the center column (collapsing
285
+ to one column on narrow viewports), and the new "Hot files" list is height-capped
286
+ with its own scrollbar so a long list never crowds the Moat card beneath it.
287
+
288
+ ---
289
+
290
+ ## [0.3.0] 2026-06-09
291
+
292
+ ### Added
293
+
294
+ - **Incremental scanner.** `syn .` now re-parses only the files whose content
295
+ changed since the last scan, reusing cached parses (symbols, imports, calls)
296
+ for everything else via a content-hash parse cache. Rescans of a large repo
297
+ after editing a handful of files are dramatically faster; the resulting graph
298
+ is byte-identical to a full scan. `syn . --full` forces a clean rebuild. This
299
+ makes the long-standing "updated incrementally" claim actually true.
300
+ - **Call-graph edges.** Function and method call sites are now captured during
301
+ parsing and resolved (name-based, precision-first: same-file wins, else the
302
+ unique repo-wide symbol; ambiguous/external calls are skipped) into
303
+ symbol→symbol `calls` edges. `blast_radius` therefore surfaces **callers**,
304
+ not just importers and tests — so the impact of changing a function includes
305
+ the code that calls it. Captured across 14 languages (TypeScript, Python, Go,
306
+ Rust, Java, C, C++, C#, plus best-effort Kotlin/PHP/Ruby and Svelte/Vue
307
+ passthrough); this makes the "call relationships" claim honest.
308
+ - **Dashboard "Hot files" card.** The dashboard now surfaces the usage-learning
309
+ layer directly: the active project's hottest files by recent, decayed access.
310
+ - **Dashboard favicon.** The dashboard tab now carries the Synthra S mark.
311
+
312
+ ### Internal
313
+
314
+ - The scanner is now under test directory walker, parser dispatch, a
315
+ per-language symbol/call smoke suite, and the context packer alongside the
316
+ call-resolution and incremental-equivalence tests. CI runs on Node 24 actions
317
+ with the test matrix on Node 22 (ubuntu + windows).
318
+
319
+ ---
320
+
321
+ ## [0.2.1] — 2026-06-06
322
+
323
+ ### Changed
324
+
325
+ - **Keyword retrieval is now IDF-weighted (BM25's term-rarity component).** A
326
+ query token that's rare across the repo counts for more than a common one, so
327
+ on a multi-term query the files matching the *specific* terms rank above those
328
+ matching generic ones instead of every keyword match counting the same. The
329
+ weighting is normalized to the query's mean IDF, so a typical match scores the
330
+ same as before: overall ranking magnitude — and the confidence / Moat gating
331
+ that depends on it is unchanged. Purely an in-repo ranking refinement, no API
332
+ or data-model change. (TF-saturation / length-norm parts of full BM25 don't
333
+ apply to the deduped top-N keyword representation.)
334
+
335
+ ---
336
+
337
+ ## [0.2.0] 2026-06-06
338
+
339
+ ### Added
340
+
341
+ - **Cross-session "second brain" — a resume digest at session start.** Synthra
342
+ now captures a snapshot at session end (open next-steps/decisions, files
343
+ touched, and commits since your last session) and, on the next session, leads
344
+ the SessionStart primer with a budget-bounded **"Since you were last here"**
345
+ digest. A fresh session arrives already oriented instead of re-paying tokens
346
+ to rediscover recent work. The snapshot lives in `.synthra-graph/`
347
+ (machine-local) and falls back to the normal primer when there's nothing to
348
+ show.
349
+ - **Usage learning — retrieval that gets smarter the more you use it.** Files
350
+ you actually open (`graph_read`) or edit (`graph_register_edit`) accrue a
351
+ time-decayed weight (7-day half-life), and retrieval gives genuinely "hot"
352
+ files a small, capped re-rank boost. It's anchored to files that already match
353
+ your query and capped below the existing seed boost, so it sharpens ranking
354
+ without ever overriding relevance. Purely local, per-developer; degrades to
355
+ the exact prior ranking when there's no usage history. Tunable via
356
+ `SYN_LEARN_HALFLIFE_DAYS` and `SYN_LEARN_BOOST_CAP`.
357
+ - **CLAUDE.md policy v6** — teaches the assistant to trust the resume digest and
358
+ pull concrete next steps via `context_recall({kind:"next"})` instead of
359
+ re-exploring the codebase.
360
+
361
+ ### Fixed
362
+
363
+ - **`pre-compact.sh` now parses the primer with `jq`, not a greedy `sed`
364
+ capture** — completing the `jq` migration across all four bash hooks (matches
365
+ the Stop/Prime/PreToolUse fixes). The multi-line resume digest contains quotes
366
+ and newlines the old `sed` capture would have mangled.
367
+
368
+ ### Internal
369
+
370
+ - **CI (GitHub Actions), Biome (lint + format), and coverage** added. CI runs on
371
+ an ubuntu + windows matrix, so cross-platform hook regressions are caught
372
+ automatically. `.gitattributes` enforces LF line endings on every platform.
373
+
374
+ ---
375
+
376
+ ## [0.1.25] 2026-06-06
377
+
378
+ ### Fixed
379
+
380
+ - **PreToolUse (Moat) bash hook now parses the gate response with `jq`, not a
381
+ greedy `sed` capture (issue #13).** `src/hooks/scripts/pre-tool-use.sh`
382
+ extracted the block `reason` via `sed -n 's/.*"reason"…\(.*\)".*/\1/p'`. The
383
+ greedy `\(.*\)"` capture over-ran into the trailing JSON fields, and because a
384
+ block `reason` legitimately contains double quotes (it quotes the searched
385
+ query, e.g. `"login"`), the captured text broke the deny JSON when embedded
386
+ raw in the output heredoc — so on a real block Claude Code received malformed
387
+ `hookSpecificOutput` and the deny was silently dropped. The hook now reads
388
+ `.decision` / `.reason` with `jq -r '… // empty'` and re-emits the deny object
389
+ with `jq -nc --arg` (correct escaping), behind a `command -v jq` guard that
390
+ silently no-ops when `jq` is absent mirroring the Stop/Prime hooks fixed in
391
+ #1. Gate/Moat decision logic is unchanged. This completes the `jq` migration
392
+ across all three bash hooks (the last v0.2 item). Verified end-to-end under
393
+ bash on Linux: SessionStart primer injection, Grep/Glob Moat blocks with
394
+ well-formed escaped deny JSON, and Stop-hook token totals reaching the
395
+ dashboard.
396
+
397
+ ---
398
+
399
+ ## [0.1.24] 2026-06-06
400
+
401
+ ### Added
402
+
403
+ - **`syn doctor [path]` setup and environment health check (issue #9).** New
404
+ read-only CLI subcommand that runs a one-shot checklist and exits. Checks: Node
405
+ version, `jq` availability (bash Stop/Prime hooks silently no-op without it),
406
+ `claude` CLI on PATH, graph freshness (symbol count, schema version, scan age),
407
+ `.mcp.json` project-scope registration (required for Synthra tools to appear in
408
+ the Claude Code IDE), CLAUDE.md policy-block version, and hook installation
409
+ status. Warnings surface with the exact `syn .` command needed to resolve them.
410
+ The command mutates nothing — safe to run at any time.
411
+
412
+ - **Graph-tool usage metric on the dashboard (issue #2).** The MCP server now
413
+ appends a record to `.synthra-graph/tool_log.jsonl` on every Synthra tool call
414
+ (`graph_continue`, `graph_read`, `graph_register_edit`, etc.). `delta.ts`
415
+ aggregates per-tool call counts into `ProjectStats.tool_calls` (per-project) and
416
+ `global.tool_calls` (cross-project totals). The dashboard shows a new "Graph
417
+ tools used" card in the right column with per-tool counts. This is a positive
418
+ signal complementing the Moat's blocked-Grep count: it captures Synthra pivots
419
+ that happen before a Grep fires, which the block counter misses entirely.
420
+
421
+ - **Session-aware routing — `graph_continue` seeds retrieval with the session's
422
+ touched files (issue #14).** Files the human recently saved (last 15 min) and
423
+ files the AI registered via `graph_register_edit` now get a ranking boost in
424
+ `graph_continue` results, so the returned context tracks what you're actually
425
+ working on. Mirrors the `/pack` route, which already seeded retrieval this way.
426
+
427
+ ---
428
+
429
+ ## [0.1.23] 2026-06-06
430
+
431
+ ### Added
432
+
433
+ - **Dashboard token-log dedupe can now be disabled via `SYN_DASHBOARD_DEDUPE=0`.**
434
+ By default, `delta.ts` deduplicates `token_log.jsonl` entries that share the
435
+ same project, usage totals, and second-rounded timestamp collapsing the
436
+ duplicate writes that a co-installed AI tool's Stop hook may produce. Set
437
+ `SYN_DASHBOARD_DEDUPE=0` (also accepts `off` or `false`) to see every raw
438
+ entry. Useful when debugging multi-tool coexistence or auditing raw log data.
439
+
440
+ - **Graph schema-migration check on load.** A new `SCHEMA_VERSION` constant is
441
+ exported from `src/graph/types.ts` and stamped into `info_graph.json` by
442
+ `buildGraph`. On server start, `http.ts` compares the stored graph's
443
+ `schema_version` to the current constant; if they differ it triggers an
444
+ automatic one-time rescan instead of serving an incompatible graph. No
445
+ behavior change today — all graphs are v1 and schema_version matches — but
446
+ this is the forward-safety mechanism for future schema bumps so existing
447
+ graphs are never silently misread.
448
+
449
+ ### Fixed
450
+
451
+ - **JS/TS parser now captures member-assigned functions** (`exports.handler = fn`,
452
+ `module.exports.route = () => {}`, `this.x = () => {}`). Previously these
453
+ CommonJS/member-export patterns were invisible to the query, so modules that
454
+ exclusively use this style extracted zero symbols and degraded to whole-file
455
+ reads via `graph_read`. A member-assignment capture has been added to both
456
+ `JS_QUERY` and `TS_QUERY` in `src/scanner/parsers/typescript.ts`. Note: a
457
+ pure-wiring `server.js` whose only structure is anonymous inline-callback
458
+ arguments (e.g. `io.use(...)` / `socket.on(event, fn)`) is genuinely
459
+ symbol-less — that is correct, and the gate's symbol-hit guard already
460
+ prevents blocking such files.
461
+
462
+ ### Changed
463
+
464
+ - **Policy block v4 v5.** Adds a "large file — pull the symbol, don't
465
+ chunk" nudge to address recurring dogfood friction: on large files Claude
466
+ was reading successive line-range chunks instead of fetching the specific
467
+ symbol via `graph_read("file::symbol")`. The v5 block now explicitly
468
+ instructs: when a file is large, use `graph_read("file/path.ts::SymbolName")`
469
+ to pull the symbol directly rather than reading successive line-range chunks.
470
+ `POLICY_VERSION` bumped `4 5`; existing v4 blocks auto-upgrade on the
471
+ next `syn .` run.
472
+
473
+ ---
474
+
475
+ ## [0.1.22]2026-06-06
476
+
477
+ ### Fixed
478
+
479
+ - **`graph_read` now resolves shortened file paths (path-suffix fallback).** Previously
480
+ `graph_read` performed an exact `path === target` match only. Passing a shortened path
481
+ like `appsettings.json` returned "file not found" even when
482
+ `connectwarev2/.../appsettings.json` was indexed. A new `resolveFileTarget` helper (now
483
+ exported) tries an exact match first; on a miss it looks for a unique path-suffix match
484
+ and serves that file; if multiple files share the suffix it reports them as ambiguous with
485
+ candidate paths rather than guessing. Symbol lookups use the resolved path. No API or
486
+ protocol change. Roadmap item #11.
487
+
488
+ - **Gate content-keyword relaxation now intersects file contents, not just file paths.**
489
+ The Moat's recent-activity relaxation previously matched query tokens against the paths of
490
+ recently-touched files only. A query like `Grep "login"` would not relax on a recent save
491
+ of `auth.ts` unless the word "login" appeared in the path. Now the relaxation also checks
492
+ the recently-touched file's graph-node keywords (its indexed content), so a recent save
493
+ relaxes a Grep whenever the file *contains* the queried term — not just when the path
494
+ matches it. Completes roadmap item #3.
495
+
496
+ ### Changed
497
+
498
+ - **Dashboard Projects card shows a first-run hint in the empty state.** When no projects
499
+ have run `syn .` yet, the Projects card now displays "No projects yet — run `syn .` in a
500
+ project to start" instead of a blank card. The Recent-turns card already carried this
501
+ hint; Projects now matches it. Roadmap item #10.
502
+
503
+ - **`bin` path normalization (chore).** Ran `npm pkg fix` to normalize `bin` entries from
504
+ `./bin/syn` to `bin/syn`. Silences the cosmetic publish warnings
505
+ (`"bin[syn]" script name was cleaned`). `syn` and `synthra` still resolve to the same
506
+ entry point. Roadmap item #4.
507
+
508
+ ---
509
+
510
+ ## [0.1.21] 2026-06-06
511
+
512
+ ### Added
513
+
514
+ - **HubL (HubSpot CMS) symbol extraction for `.html` and `.hubl` files.**
515
+ Previously `.html` files were content-indexed only keyword search and
516
+ whole-file reads, no symbol-level granularity. On HubSpot projects this
517
+ meant the graph contributed nothing: zero `graph_continue`/`graph_read`
518
+ calls resolved to symbol slices all session. Now `.html` and `.hubl` files
519
+ run through a new **regex-based** parser (`parsers/hubl.ts`; there is no
520
+ tree-sitter grammar for HubL):
521
+ - `{% macro name(args) %}` extracted as a `function` symbol
522
+ - `{% block name %}` extracted as a `component` symbol
523
+ - `{% include / extends / import / from "path" %}` import edges (relative
524
+ paths resolve to local templates; `.html`/`.hubl` added to the resolver's
525
+ extension list)
526
+
527
+ Plain HTML with no HubL tags is unaffected the parser yields zero symbols
528
+ and zero imports, identical to before. No API, protocol, or policy-block
529
+ change. Roadmap item #12.
530
+
531
+ ---
532
+
533
+ ## [0.1.20] 2026-06-06
534
+
535
+ ### Fixed
536
+
537
+ - **Gate (Moat) no longer blocks Grep/Glob queries the graph cannot answer with a symbol.**
538
+ Previously, the PreToolUse gate blocked whenever retrieval confidence was `medium` or `high`,
539
+ but confidence is driven by keyword and path hits too not only by symbol matches. This meant
540
+ literal/attribute/CSS-selector patterns (`data-tour=`, `class=`, `: 100%`, `.filter-bar`,
541
+ `<div>`) and path-only Globs were blocked and redirected to `graph_read`, which has no symbol
542
+ slice to return for those queries, so Claude fell back to Grep or a whole-file Read anyway —
543
+ a wasted round-trip. Found across multiple dogfood sessions including well-indexed Svelte
544
+ repos. Two new guards close the gap:
545
+ - **Query-shape pre-filter** — Grep patterns that target markup, CSS, attributes, or string
546
+ literals are allowed through up front, before the retrieval step runs.
547
+ - **Symbol-hit requirement** the gate now only blocks when retrieval matched a symbol whose
548
+ name the query mentions exactly. `RetrievalResult` gained a `symbolMatched` flag; the scorer
549
+ exposes `exactSym`.
550
+
551
+ Net effect: genuine symbol lookups still block (verified: `fetchWith429Retry`,
552
+ `MAX_ROWS_PER_TABLE`, `verifyPin`, `SOCKET_AUTH_SECRET`, `seedCredentials`); queries that
553
+ could never have been answered by the graph now allow through without the wasted redirect.
554
+ No API, protocol, or policy-block change purely server-side gate behavior.
555
+
556
+ - **Gate and rank test coverage added** (`tests/gate.test.ts`, `tests/rank.test.ts`).
557
+ Chips at the v0.2 backlog item to fill vitest tests beyond `it.todo` placeholders.
558
+
559
+ ---
560
+
561
+ ## [0.1.19] — 2026-06-01
562
+
563
+ ### Changed
564
+
565
+ - **Policy block v4: targeted Read-before-Edit for graph-discovered files.**
566
+ Claude Code's `Edit` tool requires a file to have been opened with its own
567
+ `Read` tool; a `graph_read` slice does not satisfy that gate. Previously,
568
+ editing a file known only through `graph_read` would fail with *"File has
569
+ not been read yet"* and force a whole-file `Read` eroding token savings on
570
+ edit-heavy sessions. The v4 policy now instructs: take the line range already
571
+ reported in the `graph_read` header (e.g. `…::handler (L120-168)`), do a
572
+ targeted `Read` with matching `offset`/`limit`, then `Edit`. This satisfies
573
+ the gate while keeping the read small. Existing v3 blocks auto-upgrade on the
574
+ next `syn .` run.
575
+
576
+ ---
577
+
578
+ ## [0.1.18] 2026-06-01
579
+
580
+ ### Fixed
581
+
582
+ - **Stop hook on Linux/macOS no longer posts zero tokens to the dashboard.** The bash
583
+ `stop.sh` hook extracted `transcript_path` from the Claude Code Stop payload using a
584
+ greedy `sed` capture (`\(.*\)"`). Because the real payload has additional fields after
585
+ `transcript_path`, the capture grabbed those trailing fields and produced a
586
+ non-existent path string. The `-f` file check therefore always failed, totals were
587
+ never POSTed to `/log`, and the dashboard stayed stuck at 0 on every turn (GitHub
588
+ issue #1). Fixed by parsing with `jq -r '.transcript_path // empty'` and moving the
589
+ `command -v jq` guard above the parse so the hook exits cleanly when `jq` is absent.
590
+ - **SessionStart/PreCompact primer hook (`prime.sh`) hardened the same way.** The
591
+ `/prime` response is `{"primer":"…","port":…}`, so the old greedy capture accidentally
592
+ injected trailing `","port":…` junk into the primer string. Because primer text can
593
+ contain inner quotes, a negated-class fix (`[^"]*`) would have truncated it at the
594
+ first quote — `jq -r '.primer // empty'` is the correct parse. Switched `printf '%b'`
595
+ to `printf '%s'` since `jq -r` already decodes JSON escapes.
596
+ - Both fixes are **bash-only**. The Windows PowerShell hooks (`stop.ps1`, `prime.ps1`)
597
+ use `ConvertFrom-Json` and were already correct.
598
+
599
+ ---
600
+
601
+ ## [0.1.17] — 2026-05-29
602
+
603
+ ### Added
604
+
605
+ - **`syn .` scaffolds an agent-onboarding CLAUDE.md on brand-new projects.**
606
+ When a project has no CLAUDE.md, Synthra now writes a lean skeleton —
607
+ `Build & test`, `Conventions`, `Key decisions`, `Gotchas` (with TODO
608
+ prompts) *above* its managed policy block, instead of a bare policy
609
+ block. This is the durable "why/how" layer the graph can't infer; the
610
+ graph still owns "what/where." Fill it in, or run `/init` to auto-draft.
611
+ The skeleton is written **once** and lives outside the
612
+ `<!-- synthra-policy -->` markers, so re-running `syn .` (which
613
+ refreshes the policy block) never clobbers what you've written.
614
+ Projects that already have a CLAUDE.md are untouched — no skeleton is
615
+ injected.
616
+
617
+ ---
618
+
619
+ ## [0.1.16] — 2026-05-29
620
+
621
+ ### Changed
622
+
623
+ - **Moat card shows 50 recent gate decisions** (was 12). The inline list
624
+ already scrolls within the card, and the `/data` payload already carries
625
+ up to 500 gates, so this just renders more of them. The headline block
626
+ count was always all-time/uncapped — unchanged.
627
+
628
+ ---
629
+
630
+ ## [0.1.15] — 2026-05-29
631
+
632
+ ### Changed
633
+
634
+ - **Recent turns are paginated.** The dashboard now carries up to 500 turns
635
+ (was 25) and shows them 25 per page with Prev/Next controls — so you can
636
+ browse history instead of only seeing the last 25. Configurable via
637
+ `SYN_DASHBOARD_RECENT_N` (default 500).
638
+ - **Model-usage donut is now all-time, not last-25.** It was tallying models
639
+ from the capped recent-turns window, so a run of >25 same-model turns showed
640
+ that model at 100% and hid the rest. It now sums the uncapped per-project
641
+ model counts, so it always reflects your true all-time split.
642
+ - **Dashboard poll slowed 2s 10s.** Lighter on resources and steadier to
643
+ read; pagination stays live (the current page re-renders each poll).
644
+
645
+ ---
646
+
647
+ ## [0.1.14] — 2026-05-29
648
+
649
+ ### Changed
650
+
651
+ - **Dashboard visual refresh.** No API surface change — all visual / UX.
652
+ - Removed the hero strip and the standalone Legend card. Date + active
653
+ project now live as compact chips inside the top nav (active-project
654
+ path uses RTL truncation so the folder name stays visible).
655
+ - New **Projects bar chart** in the left column colored bars ranked
656
+ by turn count. Stable per-name OKLCH palette (8 colors, hash-keyed)
657
+ so a project keeps the same color across sessions. Click any row to
658
+ open its full breakdown.
659
+ - **Donut legend** gains a turn-count column alongside the percentage.
660
+ - **Savings card** elevated: radial green backdrop, money figure 40px,
661
+ soft glow — makes the "what Synthra saved you" number the visual
662
+ anchor of the dashboard.
663
+ - **Recent turns column headers** are now hover-explainable.
664
+ - Active-project chip tightens + month name hides under 1100px width.
665
+
666
+ ---
667
+
668
+ ## [0.1.13] 2026-05-29
669
+
670
+ ### Fixed
671
+
672
+ - **Dashboard footer version is now dynamic.** Was hardcoded to `v0.1.8`
673
+ in the HTML and never updated. The dashboard server now injects the
674
+ running binary's version (from `package.json`) into the footer on every
675
+ `GET /` via a `__SYN_VERSION__` placeholder. Re-run `syn .` after an
676
+ update and the dashboard reflects the new version automatically.
677
+
678
+ ---
679
+
680
+ ## [0.1.12] 2026-05-29
681
+
682
+ ### Fixed
683
+
684
+ - **`Language.query is deprecated` spam at scan time.** Every parsed file
685
+ printed the warning 57 prints on a Flutter codebase, one per parsed
686
+ file. Switched all four parsers (TypeScript, JavaScript, Python, Dart,
687
+ plus the generic helper) from the deprecated `language.query(QUERY)`
688
+ to `new Query(language, QUERY)`. No behavior change, just clean
689
+ terminal output.
690
+
691
+ ---
692
+
693
+ ## [0.1.11] 2026-05-29
694
+
695
+ ### Fixed
696
+
697
+ - **Dart parser actually runs now.** Was silently broken since v0.1 due to an
698
+ ABI mismatch (shipped wasm was ABI v15, pinned `web-tree-sitter` only
699
+ supported v13–v14). Every `.dart` file got zero symbols, zero imports —
700
+ the exception was swallowed by the parser's try/catch. Bumped
701
+ `web-tree-sitter` to `^0.25.10` to fix.
702
+ - **Real Dart symbol extraction.** Classes, mixins, extensions, enums,
703
+ typedefs, top-level functions, methods, getters, setters, constructors.
704
+ - **Dart import normalization.** `package:foo/bar.dart` and `dart:async` are
705
+ stripped (cross-project); bare `'sibling.dart'` is rewritten to
706
+ `./sibling.dart` so the project resolver can complete them.
707
+
708
+ ### Changed
709
+
710
+ - **Update check runs on every `syn .`** (no more 24h cache). If you're on
711
+ latest, stays silent. If outdated, prompts `[y/N]` as before.
712
+ - **Auto-update now shows a changelog.** After `npm install -g …@latest`
713
+ succeeds, Synthra prints the new version's section from this file before
714
+ telling you to re-run. Catches `npm install` outside of `syn .` too —
715
+ next startup compares your current version to `~/.synthra/last-seen-version.json`
716
+ and prints if it's newer.
717
+
718
+ ---
719
+
720
+ ## [0.1.10] — 2026-05-29
721
+
722
+ ### Changed
723
+
724
+ - **CLAUDE.md policy v2 → v3.** Session-end now goes through
725
+ `context_remember({kind: "task"|"decision"|"next"})` instead of writing
726
+ `.synthra/CONTEXT.md` directly. The Stop hook always re-rendered CONTEXT.md
727
+ from `context-store.json` under v2 Claude's direct writes were getting
728
+ wiped on session end. Existing v2 blocks auto-upgrade.
729
+
730
+ ### Added
731
+
732
+ - **Scanner ignores more build caches.** `.dart_tool/`, `.flutter-plugins`,
733
+ `.flutter-plugins-dependencies`, `.gradle/`, `target/`, `Pods/`,
734
+ `DerivedData/`, `__pycache__/`, `.venv/`, `venv/`, `.tox/`,
735
+ `.pytest_cache/`, `.mypy_cache/`, `.ruff_cache/`, `obj/`, `.vs/`.
736
+
737
+ ---
738
+
739
+ ## [0.1.9] — 2026-05-29
740
+
741
+ ### Fixed
742
+
743
+ - **Crash on prototype-colliding symbol names.** `buildSymbolIndex` built
744
+ the lookup on a plain `{}`, so a symbol named `toString` (which every
745
+ Dart class overrides), `constructor`, `valueOf`, etc. resolved to the
746
+ inherited `Object.prototype` member and crashed on `.push`. Now uses
747
+ `Object.create(null)` on both fresh-build and load-from-disk paths.
748
+
749
+ ---
750
+
751
+ ## [0.1.8]2026-05-29
752
+
753
+ ### Added
754
+
755
+ - **Interactive auto-update.** `syn .` checks npm at startup; if a newer
756
+ version is available, prompts `[y/N]`. On `y`, runs
757
+ `npm install -g @jefuriiij/synthra@latest` with stdio inherited and
758
+ exits with re-run instructions. Non-TTY runs (CI, piped stdin) fall
759
+ back to a silent one-line hint. `SYN_NO_UPDATE_CHECK=1` opts out.
760
+
761
+ ---
762
+
763
+ ## [0.1.7] 2026-05-29
764
+
765
+ ### Fixed
766
+
767
+ - **JS parser missed CommonJS imports + JS class names.** Unified TS/JS
768
+ query only matched ES `import_statement`, and used `(type_identifier)`
769
+ for class names — which is TS-grammar-only. Result: every `.js`/`.cjs`/
770
+ `.mjs` file silently produced zero imports, and any class in a JS file
771
+ was skipped. Split into `TS_QUERY` and `JS_QUERY`; JS query adds a
772
+ `require()` capture and uses `(identifier)` for class names.
773
+
774
+ ---
775
+
776
+ ## [0.1.6] — 2026-05-29
777
+
778
+ ### Fixed
779
+
780
+ - **MCP registration now uses `--scope project`** so the Claude Code IDE
781
+ extension actually sees Synthra. The previous `--scope local` wrote to
782
+ a per-project section of `~/.claude.json` that only the `claude` CLI
783
+ reads — invisible to the IDE.
784
+
785
+ ---
786
+
787
+ ## [0.1.5] and earlier
788
+
789
+ See [GitHub commits](https://github.com/jefuriiij/synthra/commits/main) for
790
+ detail. v0.1.5 introduced the v2 policy template with namespace + skip rules;
791
+ v0.1.4 fixed a DEP0190 deprecation on Windows; v0.1.3 was the dashboard
792
+ redesign (Cool Marine palette, FAQ modal, savings audit row).