@jefuriiij/synthra 0.14.0 → 0.15.0

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