@jefuriiij/synthra 0.1.25 → 0.2.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,426 +1,465 @@
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.1.25] — 2026-06-06
11
-
12
- ### Fixed
13
-
14
- - **PreToolUse (Moat) bash hook now parses the gate response with `jq`, not a
15
- greedy `sed` capture (issue #13).** `src/hooks/scripts/pre-tool-use.sh`
16
- extracted the block `reason` via `sed -n 's/.*"reason"…\(.*\)".*/\1/p'`. The
17
- greedy `\(.*\)"` capture over-ran into the trailing JSON fields, and because a
18
- block `reason` legitimately contains double quotes (it quotes the searched
19
- query, e.g. `"login"`), the captured text broke the deny JSON when embedded
20
- raw in the output heredoc so on a real block Claude Code received malformed
21
- `hookSpecificOutput` and the deny was silently dropped. The hook now reads
22
- `.decision` / `.reason` with `jq -r '… // empty'` and re-emits the deny object
23
- with `jq -nc --arg` (correct escaping), behind a `command -v jq` guard that
24
- silently no-ops when `jq` is absent mirroring the Stop/Prime hooks fixed in
25
- #1. Gate/Moat decision logic is unchanged. This completes the `jq` migration
26
- across all three bash hooks (the last v0.2 item). Verified end-to-end under
27
- bash on Linux: SessionStart primer injection, Grep/Glob Moat blocks with
28
- well-formed escaped deny JSON, and Stop-hook token totals reaching the
29
- dashboard.
30
-
31
- ---
32
-
33
- ## [0.1.24] — 2026-06-06
34
-
35
- ### Added
36
-
37
- - **`syn doctor [path]` setup and environment health check (issue #9).** New
38
- read-only CLI subcommand that runs a one-shot checklist and exits. Checks: Node
39
- version, `jq` availability (bash Stop/Prime hooks silently no-op without it),
40
- `claude` CLI on PATH, graph freshness (symbol count, schema version, scan age),
41
- `.mcp.json` project-scope registration (required for Synthra tools to appear in
42
- the Claude Code IDE), CLAUDE.md policy-block version, and hook installation
43
- status. Warnings surface with the exact `syn .` command needed to resolve them.
44
- The command mutates nothing safe to run at any time.
45
-
46
- - **Graph-tool usage metric on the dashboard (issue #2).** The MCP server now
47
- appends a record to `.synthra-graph/tool_log.jsonl` on every Synthra tool call
48
- (`graph_continue`, `graph_read`, `graph_register_edit`, etc.). `delta.ts`
49
- aggregates per-tool call counts into `ProjectStats.tool_calls` (per-project) and
50
- `global.tool_calls` (cross-project totals). The dashboard shows a new "Graph
51
- tools used" card in the right column with per-tool counts. This is a positive
52
- signal complementing the Moat's blocked-Grep count: it captures Synthra pivots
53
- that happen before a Grep fires, which the block counter misses entirely.
54
-
55
- - **Session-aware routing `graph_continue` seeds retrieval with the session's
56
- touched files (issue #14).** Files the human recently saved (last 15 min) and
57
- files the AI registered via `graph_register_edit` now get a ranking boost in
58
- `graph_continue` results, so the returned context tracks what you're actually
59
- working on. Mirrors the `/pack` route, which already seeded retrieval this way.
60
-
61
- ---
62
-
63
- ## [0.1.23]2026-06-06
64
-
65
- ### Added
66
-
67
- - **Dashboard token-log dedupe can now be disabled via `SYN_DASHBOARD_DEDUPE=0`.**
68
- By default, `delta.ts` deduplicates `token_log.jsonl` entries that share the
69
- same project, usage totals, and second-rounded timestamp — collapsing the
70
- duplicate writes that a co-installed AI tool's Stop hook may produce. Set
71
- `SYN_DASHBOARD_DEDUPE=0` (also accepts `off` or `false`) to see every raw
72
- entry. Useful when debugging multi-tool coexistence or auditing raw log data.
73
-
74
- - **Graph schema-migration check on load.** A new `SCHEMA_VERSION` constant is
75
- exported from `src/graph/types.ts` and stamped into `info_graph.json` by
76
- `buildGraph`. On server start, `http.ts` compares the stored graph's
77
- `schema_version` to the current constant; if they differ it triggers an
78
- automatic one-time rescan instead of serving an incompatible graph. No
79
- behavior change today all graphs are v1 and schema_version matches — but
80
- this is the forward-safety mechanism for future schema bumps so existing
81
- graphs are never silently misread.
82
-
83
- ### Fixed
84
-
85
- - **JS/TS parser now captures member-assigned functions** (`exports.handler = fn`,
86
- `module.exports.route = () => {}`, `this.x = () => {}`). Previously these
87
- CommonJS/member-export patterns were invisible to the query, so modules that
88
- exclusively use this style extracted zero symbols and degraded to whole-file
89
- reads via `graph_read`. A member-assignment capture has been added to both
90
- `JS_QUERY` and `TS_QUERY` in `src/scanner/parsers/typescript.ts`. Note: a
91
- pure-wiring `server.js` whose only structure is anonymous inline-callback
92
- arguments (e.g. `io.use(...)` / `socket.on(event, fn)`) is genuinely
93
- symbol-less — that is correct, and the gate's symbol-hit guard already
94
- prevents blocking such files.
95
-
96
- ### Changed
97
-
98
- - **Policy block v4 v5.** Adds a "large file pull the symbol, don't
99
- chunk" nudge to address recurring dogfood friction: on large files Claude
100
- was reading successive line-range chunks instead of fetching the specific
101
- symbol via `graph_read("file::symbol")`. The v5 block now explicitly
102
- instructs: when a file is large, use `graph_read("file/path.ts::SymbolName")`
103
- to pull the symbol directly rather than reading successive line-range chunks.
104
- `POLICY_VERSION` bumped `4 → 5`; existing v4 blocks auto-upgrade on the
105
- next `syn .` run.
106
-
107
- ---
108
-
109
- ## [0.1.22] 2026-06-06
110
-
111
- ### Fixed
112
-
113
- - **`graph_read` now resolves shortened file paths (path-suffix fallback).** Previously
114
- `graph_read` performed an exact `path === target` match only. Passing a shortened path
115
- like `appsettings.json` returned "file not found" even when
116
- `connectwarev2/.../appsettings.json` was indexed. A new `resolveFileTarget` helper (now
117
- exported) tries an exact match first; on a miss it looks for a unique path-suffix match
118
- and serves that file; if multiple files share the suffix it reports them as ambiguous with
119
- candidate paths rather than guessing. Symbol lookups use the resolved path. No API or
120
- protocol change. Roadmap item #11.
121
-
122
- - **Gate content-keyword relaxation now intersects file contents, not just file paths.**
123
- The Moat's recent-activity relaxation previously matched query tokens against the paths of
124
- recently-touched files only. A query like `Grep "login"` would not relax on a recent save
125
- of `auth.ts` unless the word "login" appeared in the path. Now the relaxation also checks
126
- the recently-touched file's graph-node keywords (its indexed content), so a recent save
127
- relaxes a Grep whenever the file *contains* the queried term — not just when the path
128
- matches it. Completes roadmap item #3.
129
-
130
- ### Changed
131
-
132
- - **Dashboard Projects card shows a first-run hint in the empty state.** When no projects
133
- have run `syn .` yet, the Projects card now displays "No projects yet — run `syn .` in a
134
- project to start" instead of a blank card. The Recent-turns card already carried this
135
- hint; Projects now matches it. Roadmap item #10.
136
-
137
- - **`bin` path normalization (chore).** Ran `npm pkg fix` to normalize `bin` entries from
138
- `./bin/syn` to `bin/syn`. Silences the cosmetic publish warnings
139
- (`"bin[syn]" script name was cleaned`). `syn` and `synthra` still resolve to the same
140
- entry point. Roadmap item #4.
141
-
142
- ---
143
-
144
- ## [0.1.21] 2026-06-06
145
-
146
- ### Added
147
-
148
- - **HubL (HubSpot CMS) symbol extraction for `.html` and `.hubl` files.**
149
- Previously `.html` files were content-indexed only — keyword search and
150
- whole-file reads, no symbol-level granularity. On HubSpot projects this
151
- meant the graph contributed nothing: zero `graph_continue`/`graph_read`
152
- calls resolved to symbol slices all session. Now `.html` and `.hubl` files
153
- run through a new **regex-based** parser (`parsers/hubl.ts`; there is no
154
- tree-sitter grammar for HubL):
155
- - `{% macro name(args) %}` extracted as a `function` symbol
156
- - `{% block name %}` extracted as a `component` symbol
157
- - `{% include / extends / import / from "path" %}` import edges (relative
158
- paths resolve to local templates; `.html`/`.hubl` added to the resolver's
159
- extension list)
160
-
161
- Plain HTML with no HubL tags is unaffected the parser yields zero symbols
162
- and zero imports, identical to before. No API, protocol, or policy-block
163
- change. Roadmap item #12.
164
-
165
- ---
166
-
167
- ## [0.1.20] 2026-06-06
168
-
169
- ### Fixed
170
-
171
- - **Gate (Moat) no longer blocks Grep/Glob queries the graph cannot answer with a symbol.**
172
- Previously, the PreToolUse gate blocked whenever retrieval confidence was `medium` or `high`,
173
- but confidence is driven by keyword and path hits too not only by symbol matches. This meant
174
- literal/attribute/CSS-selector patterns (`data-tour=`, `class=`, `: 100%`, `.filter-bar`,
175
- `<div>`) and path-only Globs were blocked and redirected to `graph_read`, which has no symbol
176
- slice to return for those queries, so Claude fell back to Grep or a whole-file Read anyway —
177
- a wasted round-trip. Found across multiple dogfood sessions including well-indexed Svelte
178
- repos. Two new guards close the gap:
179
- - **Query-shape pre-filter** Grep patterns that target markup, CSS, attributes, or string
180
- literals are allowed through up front, before the retrieval step runs.
181
- - **Symbol-hit requirement** — the gate now only blocks when retrieval matched a symbol whose
182
- name the query mentions exactly. `RetrievalResult` gained a `symbolMatched` flag; the scorer
183
- exposes `exactSym`.
184
-
185
- Net effect: genuine symbol lookups still block (verified: `fetchWith429Retry`,
186
- `MAX_ROWS_PER_TABLE`, `verifyPin`, `SOCKET_AUTH_SECRET`, `seedCredentials`); queries that
187
- could never have been answered by the graph now allow through without the wasted redirect.
188
- No API, protocol, or policy-block changepurely server-side gate behavior.
189
-
190
- - **Gate and rank test coverage added** (`tests/gate.test.ts`, `tests/rank.test.ts`).
191
- Chips at the v0.2 backlog item to fill vitest tests beyond `it.todo` placeholders.
192
-
193
- ---
194
-
195
- ## [0.1.19] 2026-06-01
196
-
197
- ### Changed
198
-
199
- - **Policy block v4: targeted Read-before-Edit for graph-discovered files.**
200
- Claude Code's `Edit` tool requires a file to have been opened with its own
201
- `Read` tool; a `graph_read` slice does not satisfy that gate. Previously,
202
- editing a file known only through `graph_read` would fail with *"File has
203
- not been read yet"* and force a whole-file `Read` — eroding token savings on
204
- edit-heavy sessions. The v4 policy now instructs: take the line range already
205
- reported in the `graph_read` header (e.g. `…::handler (L120-168)`), do a
206
- targeted `Read` with matching `offset`/`limit`, then `Edit`. This satisfies
207
- the gate while keeping the read small. Existing v3 blocks auto-upgrade on the
208
- next `syn .` run.
209
-
210
- ---
211
-
212
- ## [0.1.18]2026-06-01
213
-
214
- ### Fixed
215
-
216
- - **Stop hook on Linux/macOS no longer posts zero tokens to the dashboard.** The bash
217
- `stop.sh` hook extracted `transcript_path` from the Claude Code Stop payload using a
218
- greedy `sed` capture (`\(.*\)"`). Because the real payload has additional fields after
219
- `transcript_path`, the capture grabbed those trailing fields and produced a
220
- non-existent path string. The `-f` file check therefore always failed, totals were
221
- never POSTed to `/log`, and the dashboard stayed stuck at 0 on every turn (GitHub
222
- issue #1). Fixed by parsing with `jq -r '.transcript_path // empty'` and moving the
223
- `command -v jq` guard above the parse so the hook exits cleanly when `jq` is absent.
224
- - **SessionStart/PreCompact primer hook (`prime.sh`) hardened the same way.** The
225
- `/prime` response is `{"primer":"…","port":…}`, so the old greedy capture accidentally
226
- injected trailing `","port":…` junk into the primer string. Because primer text can
227
- contain inner quotes, a negated-class fix (`[^"]*`) would have truncated it at the
228
- first quote — `jq -r '.primer // empty'` is the correct parse. Switched `printf '%b'`
229
- to `printf '%s'` since `jq -r` already decodes JSON escapes.
230
- - Both fixes are **bash-only**. The Windows PowerShell hooks (`stop.ps1`, `prime.ps1`)
231
- use `ConvertFrom-Json` and were already correct.
232
-
233
- ---
234
-
235
- ## [0.1.17] — 2026-05-29
236
-
237
- ### Added
238
-
239
- - **`syn .` scaffolds an agent-onboarding CLAUDE.md on brand-new projects.**
240
- When a project has no CLAUDE.md, Synthra now writes a lean skeleton —
241
- `Build & test`, `Conventions`, `Key decisions`, `Gotchas` (with TODO
242
- prompts) *above* its managed policy block, instead of a bare policy
243
- block. This is the durable "why/how" layer the graph can't infer; the
244
- graph still owns "what/where." Fill it in, or run `/init` to auto-draft.
245
- The skeleton is written **once** and lives outside the
246
- `<!-- synthra-policy -->` markers, so re-running `syn .` (which
247
- refreshes the policy block) never clobbers what you've written.
248
- Projects that already have a CLAUDE.md are untouched — no skeleton is
249
- injected.
250
-
251
- ---
252
-
253
- ## [0.1.16] — 2026-05-29
254
-
255
- ### Changed
256
-
257
- - **Moat card shows 50 recent gate decisions** (was 12). The inline list
258
- already scrolls within the card, and the `/data` payload already carries
259
- up to 500 gates, so this just renders more of them. The headline block
260
- count was always all-time/uncapped unchanged.
261
-
262
- ---
263
-
264
- ## [0.1.15] 2026-05-29
265
-
266
- ### Changed
267
-
268
- - **Recent turns are paginated.** The dashboard now carries up to 500 turns
269
- (was 25) and shows them 25 per page with Prev/Next controls — so you can
270
- browse history instead of only seeing the last 25. Configurable via
271
- `SYN_DASHBOARD_RECENT_N` (default 500).
272
- - **Model-usage donut is now all-time, not last-25.** It was tallying models
273
- from the capped recent-turns window, so a run of >25 same-model turns showed
274
- that model at 100% and hid the rest. It now sums the uncapped per-project
275
- model counts, so it always reflects your true all-time split.
276
- - **Dashboard poll slowed 2s → 10s.** Lighter on resources and steadier to
277
- read; pagination stays live (the current page re-renders each poll).
278
-
279
- ---
280
-
281
- ## [0.1.14] 2026-05-29
282
-
283
- ### Changed
284
-
285
- - **Dashboard visual refresh.** No API surface change — all visual / UX.
286
- - Removed the hero strip and the standalone Legend card. Date + active
287
- project now live as compact chips inside the top nav (active-project
288
- path uses RTL truncation so the folder name stays visible).
289
- - New **Projects bar chart** in the left column — colored bars ranked
290
- by turn count. Stable per-name OKLCH palette (8 colors, hash-keyed)
291
- so a project keeps the same color across sessions. Click any row to
292
- open its full breakdown.
293
- - **Donut legend** gains a turn-count column alongside the percentage.
294
- - **Savings card** elevated: radial green backdrop, money figure 40px,
295
- soft glow — makes the "what Synthra saved you" number the visual
296
- anchor of the dashboard.
297
- - **Recent turns column headers** are now hover-explainable.
298
- - Active-project chip tightens + month name hides under 1100px width.
299
-
300
- ---
301
-
302
- ## [0.1.13] — 2026-05-29
303
-
304
- ### Fixed
305
-
306
- - **Dashboard footer version is now dynamic.** Was hardcoded to `v0.1.8`
307
- in the HTML and never updated. The dashboard server now injects the
308
- running binary's version (from `package.json`) into the footer on every
309
- `GET /` via a `__SYN_VERSION__` placeholder. Re-run `syn .` after an
310
- update and the dashboard reflects the new version automatically.
311
-
312
- ---
313
-
314
- ## [0.1.12] 2026-05-29
315
-
316
- ### Fixed
317
-
318
- - **`Language.query is deprecated` spam at scan time.** Every parsed file
319
- printed the warning — 57 prints on a Flutter codebase, one per parsed
320
- file. Switched all four parsers (TypeScript, JavaScript, Python, Dart,
321
- plus the generic helper) from the deprecated `language.query(QUERY)`
322
- to `new Query(language, QUERY)`. No behavior change, just clean
323
- terminal output.
324
-
325
- ---
326
-
327
- ## [0.1.11] 2026-05-29
328
-
329
- ### Fixed
330
-
331
- - **Dart parser actually runs now.** Was silently broken since v0.1 due to an
332
- ABI mismatch (shipped wasm was ABI v15, pinned `web-tree-sitter` only
333
- supported v13–v14). Every `.dart` file got zero symbols, zero imports
334
- the exception was swallowed by the parser's try/catch. Bumped
335
- `web-tree-sitter` to `^0.25.10` to fix.
336
- - **Real Dart symbol extraction.** Classes, mixins, extensions, enums,
337
- typedefs, top-level functions, methods, getters, setters, constructors.
338
- - **Dart import normalization.** `package:foo/bar.dart` and `dart:async` are
339
- stripped (cross-project); bare `'sibling.dart'` is rewritten to
340
- `./sibling.dart` so the project resolver can complete them.
341
-
342
- ### Changed
343
-
344
- - **Update check runs on every `syn .`** (no more 24h cache). If you're on
345
- latest, stays silent. If outdated, prompts `[y/N]` as before.
346
- - **Auto-update now shows a changelog.** After `npm install -g …@latest`
347
- succeeds, Synthra prints the new version's section from this file before
348
- telling you to re-run. Catches `npm install` outside of `syn .` too
349
- next startup compares your current version to `~/.synthra/last-seen-version.json`
350
- and prints if it's newer.
351
-
352
- ---
353
-
354
- ## [0.1.10] — 2026-05-29
355
-
356
- ### Changed
357
-
358
- - **CLAUDE.md policy v2 v3.** Session-end now goes through
359
- `context_remember({kind: "task"|"decision"|"next"})` instead of writing
360
- `.synthra/CONTEXT.md` directly. The Stop hook always re-rendered CONTEXT.md
361
- from `context-store.json` under v2 Claude's direct writes were getting
362
- wiped on session end. Existing v2 blocks auto-upgrade.
363
-
364
- ### Added
365
-
366
- - **Scanner ignores more build caches.** `.dart_tool/`, `.flutter-plugins`,
367
- `.flutter-plugins-dependencies`, `.gradle/`, `target/`, `Pods/`,
368
- `DerivedData/`, `__pycache__/`, `.venv/`, `venv/`, `.tox/`,
369
- `.pytest_cache/`, `.mypy_cache/`, `.ruff_cache/`, `obj/`, `.vs/`.
370
-
371
- ---
372
-
373
- ## [0.1.9] — 2026-05-29
374
-
375
- ### Fixed
376
-
377
- - **Crash on prototype-colliding symbol names.** `buildSymbolIndex` built
378
- the lookup on a plain `{}`, so a symbol named `toString` (which every
379
- Dart class overrides), `constructor`, `valueOf`, etc. resolved to the
380
- inherited `Object.prototype` member and crashed on `.push`. Now uses
381
- `Object.create(null)` on both fresh-build and load-from-disk paths.
382
-
383
- ---
384
-
385
- ## [0.1.8] 2026-05-29
386
-
387
- ### Added
388
-
389
- - **Interactive auto-update.** `syn .` checks npm at startup; if a newer
390
- version is available, prompts `[y/N]`. On `y`, runs
391
- `npm install -g @jefuriiij/synthra@latest` with stdio inherited and
392
- exits with re-run instructions. Non-TTY runs (CI, piped stdin) fall
393
- back to a silent one-line hint. `SYN_NO_UPDATE_CHECK=1` opts out.
394
-
395
- ---
396
-
397
- ## [0.1.7] 2026-05-29
398
-
399
- ### Fixed
400
-
401
- - **JS parser missed CommonJS imports + JS class names.** Unified TS/JS
402
- query only matched ES `import_statement`, and used `(type_identifier)`
403
- for class names — which is TS-grammar-only. Result: every `.js`/`.cjs`/
404
- `.mjs` file silently produced zero imports, and any class in a JS file
405
- was skipped. Split into `TS_QUERY` and `JS_QUERY`; JS query adds a
406
- `require()` capture and uses `(identifier)` for class names.
407
-
408
- ---
409
-
410
- ## [0.1.6] — 2026-05-29
411
-
412
- ### Fixed
413
-
414
- - **MCP registration now uses `--scope project`** so the Claude Code IDE
415
- extension actually sees Synthra. The previous `--scope local` wrote to
416
- a per-project section of `~/.claude.json` that only the `claude` CLI
417
- reads invisible to the IDE.
418
-
419
- ---
420
-
421
- ## [0.1.5] and earlier
422
-
423
- See [GitHub commits](https://github.com/jefuriiij/synthra/commits/main) for
424
- detail. v0.1.5 introduced the v2 policy template with namespace + skip rules;
425
- v0.1.4 fixed a DEP0190 deprecation on Windows; v0.1.3 was the dashboard
426
- 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.2.0] — 2026-06-06
11
+
12
+ ### Added
13
+
14
+ - **Cross-session "second brain" a resume digest at session start.** Synthra
15
+ now captures a snapshot at session end (open next-steps/decisions, files
16
+ touched, and commits since your last session) and, on the next session, leads
17
+ the SessionStart primer with a budget-bounded **"Since you were last here"**
18
+ digest. A fresh session arrives already oriented instead of re-paying tokens
19
+ to rediscover recent work. The snapshot lives in `.synthra-graph/`
20
+ (machine-local) and falls back to the normal primer when there's nothing to
21
+ show.
22
+ - **Usage learning retrieval that gets smarter the more you use it.** Files
23
+ you actually open (`graph_read`) or edit (`graph_register_edit`) accrue a
24
+ time-decayed weight (7-day half-life), and retrieval gives genuinely "hot"
25
+ files a small, capped re-rank boost. It's anchored to files that already match
26
+ your query and capped below the existing seed boost, so it sharpens ranking
27
+ without ever overriding relevance. Purely local, per-developer; degrades to
28
+ the exact prior ranking when there's no usage history. Tunable via
29
+ `SYN_LEARN_HALFLIFE_DAYS` and `SYN_LEARN_BOOST_CAP`.
30
+ - **CLAUDE.md policy v6** — teaches the assistant to trust the resume digest and
31
+ pull concrete next steps via `context_recall({kind:"next"})` instead of
32
+ re-exploring the codebase.
33
+
34
+ ### Fixed
35
+
36
+ - **`pre-compact.sh` now parses the primer with `jq`, not a greedy `sed`
37
+ capture** completing the `jq` migration across all four bash hooks (matches
38
+ the Stop/Prime/PreToolUse fixes). The multi-line resume digest contains quotes
39
+ and newlines the old `sed` capture would have mangled.
40
+
41
+ ### Internal
42
+
43
+ - **CI (GitHub Actions), Biome (lint + format), and coverage** added. CI runs on
44
+ an ubuntu + windows matrix, so cross-platform hook regressions are caught
45
+ automatically. `.gitattributes` enforces LF line endings on every platform.
46
+
47
+ ---
48
+
49
+ ## [0.1.25] 2026-06-06
50
+
51
+ ### Fixed
52
+
53
+ - **PreToolUse (Moat) bash hook now parses the gate response with `jq`, not a
54
+ greedy `sed` capture (issue #13).** `src/hooks/scripts/pre-tool-use.sh`
55
+ extracted the block `reason` via `sed -n 's/.*"reason"…\(.*\)".*/\1/p'`. The
56
+ greedy `\(.*\)"` capture over-ran into the trailing JSON fields, and because a
57
+ block `reason` legitimately contains double quotes (it quotes the searched
58
+ query, e.g. `"login"`), the captured text broke the deny JSON when embedded
59
+ raw in the output heredoc so on a real block Claude Code received malformed
60
+ `hookSpecificOutput` and the deny was silently dropped. The hook now reads
61
+ `.decision` / `.reason` with `jq -r '… // empty'` and re-emits the deny object
62
+ with `jq -nc --arg` (correct escaping), behind a `command -v jq` guard that
63
+ silently no-ops when `jq` is absent mirroring the Stop/Prime hooks fixed in
64
+ #1. Gate/Moat decision logic is unchanged. This completes the `jq` migration
65
+ across all three bash hooks (the last v0.2 item). Verified end-to-end under
66
+ bash on Linux: SessionStart primer injection, Grep/Glob Moat blocks with
67
+ well-formed escaped deny JSON, and Stop-hook token totals reaching the
68
+ dashboard.
69
+
70
+ ---
71
+
72
+ ## [0.1.24] 2026-06-06
73
+
74
+ ### Added
75
+
76
+ - **`syn doctor [path]` setup and environment health check (issue #9).** New
77
+ read-only CLI subcommand that runs a one-shot checklist and exits. Checks: Node
78
+ version, `jq` availability (bash Stop/Prime hooks silently no-op without it),
79
+ `claude` CLI on PATH, graph freshness (symbol count, schema version, scan age),
80
+ `.mcp.json` project-scope registration (required for Synthra tools to appear in
81
+ the Claude Code IDE), CLAUDE.md policy-block version, and hook installation
82
+ status. Warnings surface with the exact `syn .` command needed to resolve them.
83
+ The command mutates nothing — safe to run at any time.
84
+
85
+ - **Graph-tool usage metric on the dashboard (issue #2).** The MCP server now
86
+ appends a record to `.synthra-graph/tool_log.jsonl` on every Synthra tool call
87
+ (`graph_continue`, `graph_read`, `graph_register_edit`, etc.). `delta.ts`
88
+ aggregates per-tool call counts into `ProjectStats.tool_calls` (per-project) and
89
+ `global.tool_calls` (cross-project totals). The dashboard shows a new "Graph
90
+ tools used" card in the right column with per-tool counts. This is a positive
91
+ signal complementing the Moat's blocked-Grep count: it captures Synthra pivots
92
+ that happen before a Grep fires, which the block counter misses entirely.
93
+
94
+ - **Session-aware routing — `graph_continue` seeds retrieval with the session's
95
+ touched files (issue #14).** Files the human recently saved (last 15 min) and
96
+ files the AI registered via `graph_register_edit` now get a ranking boost in
97
+ `graph_continue` results, so the returned context tracks what you're actually
98
+ working on. Mirrors the `/pack` route, which already seeded retrieval this way.
99
+
100
+ ---
101
+
102
+ ## [0.1.23] 2026-06-06
103
+
104
+ ### Added
105
+
106
+ - **Dashboard token-log dedupe can now be disabled via `SYN_DASHBOARD_DEDUPE=0`.**
107
+ By default, `delta.ts` deduplicates `token_log.jsonl` entries that share the
108
+ same project, usage totals, and second-rounded timestamp — collapsing the
109
+ duplicate writes that a co-installed AI tool's Stop hook may produce. Set
110
+ `SYN_DASHBOARD_DEDUPE=0` (also accepts `off` or `false`) to see every raw
111
+ entry. Useful when debugging multi-tool coexistence or auditing raw log data.
112
+
113
+ - **Graph schema-migration check on load.** A new `SCHEMA_VERSION` constant is
114
+ exported from `src/graph/types.ts` and stamped into `info_graph.json` by
115
+ `buildGraph`. On server start, `http.ts` compares the stored graph's
116
+ `schema_version` to the current constant; if they differ it triggers an
117
+ automatic one-time rescan instead of serving an incompatible graph. No
118
+ behavior change today all graphs are v1 and schema_version matches but
119
+ this is the forward-safety mechanism for future schema bumps so existing
120
+ graphs are never silently misread.
121
+
122
+ ### Fixed
123
+
124
+ - **JS/TS parser now captures member-assigned functions** (`exports.handler = fn`,
125
+ `module.exports.route = () => {}`, `this.x = () => {}`). Previously these
126
+ CommonJS/member-export patterns were invisible to the query, so modules that
127
+ exclusively use this style extracted zero symbols and degraded to whole-file
128
+ reads via `graph_read`. A member-assignment capture has been added to both
129
+ `JS_QUERY` and `TS_QUERY` in `src/scanner/parsers/typescript.ts`. Note: a
130
+ pure-wiring `server.js` whose only structure is anonymous inline-callback
131
+ arguments (e.g. `io.use(...)` / `socket.on(event, fn)`) is genuinely
132
+ symbol-less that is correct, and the gate's symbol-hit guard already
133
+ prevents blocking such files.
134
+
135
+ ### Changed
136
+
137
+ - **Policy block v4 → v5.** Adds a "large file pull the symbol, don't
138
+ chunk" nudge to address recurring dogfood friction: on large files Claude
139
+ was reading successive line-range chunks instead of fetching the specific
140
+ symbol via `graph_read("file::symbol")`. The v5 block now explicitly
141
+ instructs: when a file is large, use `graph_read("file/path.ts::SymbolName")`
142
+ to pull the symbol directly rather than reading successive line-range chunks.
143
+ `POLICY_VERSION` bumped `4 → 5`; existing v4 blocks auto-upgrade on the
144
+ next `syn .` run.
145
+
146
+ ---
147
+
148
+ ## [0.1.22] 2026-06-06
149
+
150
+ ### Fixed
151
+
152
+ - **`graph_read` now resolves shortened file paths (path-suffix fallback).** Previously
153
+ `graph_read` performed an exact `path === target` match only. Passing a shortened path
154
+ like `appsettings.json` returned "file not found" even when
155
+ `connectwarev2/.../appsettings.json` was indexed. A new `resolveFileTarget` helper (now
156
+ exported) tries an exact match first; on a miss it looks for a unique path-suffix match
157
+ and serves that file; if multiple files share the suffix it reports them as ambiguous with
158
+ candidate paths rather than guessing. Symbol lookups use the resolved path. No API or
159
+ protocol change. Roadmap item #11.
160
+
161
+ - **Gate content-keyword relaxation now intersects file contents, not just file paths.**
162
+ The Moat's recent-activity relaxation previously matched query tokens against the paths of
163
+ recently-touched files only. A query like `Grep "login"` would not relax on a recent save
164
+ of `auth.ts` unless the word "login" appeared in the path. Now the relaxation also checks
165
+ the recently-touched file's graph-node keywords (its indexed content), so a recent save
166
+ relaxes a Grep whenever the file *contains* the queried term — not just when the path
167
+ matches it. Completes roadmap item #3.
168
+
169
+ ### Changed
170
+
171
+ - **Dashboard Projects card shows a first-run hint in the empty state.** When no projects
172
+ have run `syn .` yet, the Projects card now displays "No projects yet — run `syn .` in a
173
+ project to start" instead of a blank card. The Recent-turns card already carried this
174
+ hint; Projects now matches it. Roadmap item #10.
175
+
176
+ - **`bin` path normalization (chore).** Ran `npm pkg fix` to normalize `bin` entries from
177
+ `./bin/syn` to `bin/syn`. Silences the cosmetic publish warnings
178
+ (`"bin[syn]" script name was cleaned`). `syn` and `synthra` still resolve to the same
179
+ entry point. Roadmap item #4.
180
+
181
+ ---
182
+
183
+ ## [0.1.21] — 2026-06-06
184
+
185
+ ### Added
186
+
187
+ - **HubL (HubSpot CMS) symbol extraction for `.html` and `.hubl` files.**
188
+ Previously `.html` files were content-indexed onlykeyword search and
189
+ whole-file reads, no symbol-level granularity. On HubSpot projects this
190
+ meant the graph contributed nothing: zero `graph_continue`/`graph_read`
191
+ calls resolved to symbol slices all session. Now `.html` and `.hubl` files
192
+ run through a new **regex-based** parser (`parsers/hubl.ts`; there is no
193
+ tree-sitter grammar for HubL):
194
+ - `{% macro name(args) %}` → extracted as a `function` symbol
195
+ - `{% block name %}` → extracted as a `component` symbol
196
+ - `{% include / extends / import / from "path" %}` → import edges (relative
197
+ paths resolve to local templates; `.html`/`.hubl` added to the resolver's
198
+ extension list)
199
+
200
+ Plain HTML with no HubL tags is unaffected the parser yields zero symbols
201
+ and zero imports, identical to before. No API, protocol, or policy-block
202
+ change. Roadmap item #12.
203
+
204
+ ---
205
+
206
+ ## [0.1.20] 2026-06-06
207
+
208
+ ### Fixed
209
+
210
+ - **Gate (Moat) no longer blocks Grep/Glob queries the graph cannot answer with a symbol.**
211
+ Previously, the PreToolUse gate blocked whenever retrieval confidence was `medium` or `high`,
212
+ but confidence is driven by keyword and path hits too not only by symbol matches. This meant
213
+ literal/attribute/CSS-selector patterns (`data-tour=`, `class=`, `: 100%`, `.filter-bar`,
214
+ `<div>`) and path-only Globs were blocked and redirected to `graph_read`, which has no symbol
215
+ slice to return for those queries, so Claude fell back to Grep or a whole-file Read anyway —
216
+ a wasted round-trip. Found across multiple dogfood sessions including well-indexed Svelte
217
+ repos. Two new guards close the gap:
218
+ - **Query-shape pre-filter** Grep patterns that target markup, CSS, attributes, or string
219
+ literals are allowed through up front, before the retrieval step runs.
220
+ - **Symbol-hit requirement** the gate now only blocks when retrieval matched a symbol whose
221
+ name the query mentions exactly. `RetrievalResult` gained a `symbolMatched` flag; the scorer
222
+ exposes `exactSym`.
223
+
224
+ Net effect: genuine symbol lookups still block (verified: `fetchWith429Retry`,
225
+ `MAX_ROWS_PER_TABLE`, `verifyPin`, `SOCKET_AUTH_SECRET`, `seedCredentials`); queries that
226
+ could never have been answered by the graph now allow through without the wasted redirect.
227
+ No API, protocol, or policy-block change purely server-side gate behavior.
228
+
229
+ - **Gate and rank test coverage added** (`tests/gate.test.ts`, `tests/rank.test.ts`).
230
+ Chips at the v0.2 backlog item to fill vitest tests beyond `it.todo` placeholders.
231
+
232
+ ---
233
+
234
+ ## [0.1.19] — 2026-06-01
235
+
236
+ ### Changed
237
+
238
+ - **Policy block v4: targeted Read-before-Edit for graph-discovered files.**
239
+ Claude Code's `Edit` tool requires a file to have been opened with its own
240
+ `Read` tool; a `graph_read` slice does not satisfy that gate. Previously,
241
+ editing a file known only through `graph_read` would fail with *"File has
242
+ not been read yet"* and force a whole-file `Read` eroding token savings on
243
+ edit-heavy sessions. The v4 policy now instructs: take the line range already
244
+ reported in the `graph_read` header (e.g. `…::handler (L120-168)`), do a
245
+ targeted `Read` with matching `offset`/`limit`, then `Edit`. This satisfies
246
+ the gate while keeping the read small. Existing v3 blocks auto-upgrade on the
247
+ next `syn .` run.
248
+
249
+ ---
250
+
251
+ ## [0.1.18] — 2026-06-01
252
+
253
+ ### Fixed
254
+
255
+ - **Stop hook on Linux/macOS no longer posts zero tokens to the dashboard.** The bash
256
+ `stop.sh` hook extracted `transcript_path` from the Claude Code Stop payload using a
257
+ greedy `sed` capture (`\(.*\)"`). Because the real payload has additional fields after
258
+ `transcript_path`, the capture grabbed those trailing fields and produced a
259
+ non-existent path string. The `-f` file check therefore always failed, totals were
260
+ never POSTed to `/log`, and the dashboard stayed stuck at 0 on every turn (GitHub
261
+ issue #1). Fixed by parsing with `jq -r '.transcript_path // empty'` and moving the
262
+ `command -v jq` guard above the parse so the hook exits cleanly when `jq` is absent.
263
+ - **SessionStart/PreCompact primer hook (`prime.sh`) hardened the same way.** The
264
+ `/prime` response is `{"primer":"…","port":…}`, so the old greedy capture accidentally
265
+ injected trailing `","port":…` junk into the primer string. Because primer text can
266
+ contain inner quotes, a negated-class fix (`[^"]*`) would have truncated it at the
267
+ first quote — `jq -r '.primer // empty'` is the correct parse. Switched `printf '%b'`
268
+ to `printf '%s'` since `jq -r` already decodes JSON escapes.
269
+ - Both fixes are **bash-only**. The Windows PowerShell hooks (`stop.ps1`, `prime.ps1`)
270
+ use `ConvertFrom-Json` and were already correct.
271
+
272
+ ---
273
+
274
+ ## [0.1.17] 2026-05-29
275
+
276
+ ### Added
277
+
278
+ - **`syn .` scaffolds an agent-onboarding CLAUDE.md on brand-new projects.**
279
+ When a project has no CLAUDE.md, Synthra now writes a lean skeleton —
280
+ `Build & test`, `Conventions`, `Key decisions`, `Gotchas` (with TODO
281
+ prompts)*above* its managed policy block, instead of a bare policy
282
+ block. This is the durable "why/how" layer the graph can't infer; the
283
+ graph still owns "what/where." Fill it in, or run `/init` to auto-draft.
284
+ The skeleton is written **once** and lives outside the
285
+ `<!-- synthra-policy -->` markers, so re-running `syn .` (which
286
+ refreshes the policy block) never clobbers what you've written.
287
+ Projects that already have a CLAUDE.md are untouched no skeleton is
288
+ injected.
289
+
290
+ ---
291
+
292
+ ## [0.1.16] 2026-05-29
293
+
294
+ ### Changed
295
+
296
+ - **Moat card shows 50 recent gate decisions** (was 12). The inline list
297
+ already scrolls within the card, and the `/data` payload already carries
298
+ up to 500 gates, so this just renders more of them. The headline block
299
+ count was always all-time/uncapped — unchanged.
300
+
301
+ ---
302
+
303
+ ## [0.1.15] — 2026-05-29
304
+
305
+ ### Changed
306
+
307
+ - **Recent turns are paginated.** The dashboard now carries up to 500 turns
308
+ (was 25) and shows them 25 per page with Prev/Next controls — so you can
309
+ browse history instead of only seeing the last 25. Configurable via
310
+ `SYN_DASHBOARD_RECENT_N` (default 500).
311
+ - **Model-usage donut is now all-time, not last-25.** It was tallying models
312
+ from the capped recent-turns window, so a run of >25 same-model turns showed
313
+ that model at 100% and hid the rest. It now sums the uncapped per-project
314
+ model counts, so it always reflects your true all-time split.
315
+ - **Dashboard poll slowed 2s → 10s.** Lighter on resources and steadier to
316
+ read; pagination stays live (the current page re-renders each poll).
317
+
318
+ ---
319
+
320
+ ## [0.1.14] 2026-05-29
321
+
322
+ ### Changed
323
+
324
+ - **Dashboard visual refresh.** No API surface change — all visual / UX.
325
+ - Removed the hero strip and the standalone Legend card. Date + active
326
+ project now live as compact chips inside the top nav (active-project
327
+ path uses RTL truncation so the folder name stays visible).
328
+ - New **Projects bar chart** in the left column — colored bars ranked
329
+ by turn count. Stable per-name OKLCH palette (8 colors, hash-keyed)
330
+ so a project keeps the same color across sessions. Click any row to
331
+ open its full breakdown.
332
+ - **Donut legend** gains a turn-count column alongside the percentage.
333
+ - **Savings card** elevated: radial green backdrop, money figure 40px,
334
+ soft glow — makes the "what Synthra saved you" number the visual
335
+ anchor of the dashboard.
336
+ - **Recent turns column headers** are now hover-explainable.
337
+ - Active-project chip tightens + month name hides under 1100px width.
338
+
339
+ ---
340
+
341
+ ## [0.1.13] — 2026-05-29
342
+
343
+ ### Fixed
344
+
345
+ - **Dashboard footer version is now dynamic.** Was hardcoded to `v0.1.8`
346
+ in the HTML and never updated. The dashboard server now injects the
347
+ running binary's version (from `package.json`) into the footer on every
348
+ `GET /` via a `__SYN_VERSION__` placeholder. Re-run `syn .` after an
349
+ update and the dashboard reflects the new version automatically.
350
+
351
+ ---
352
+
353
+ ## [0.1.12] — 2026-05-29
354
+
355
+ ### Fixed
356
+
357
+ - **`Language.query is deprecated` spam at scan time.** Every parsed file
358
+ printed the warning 57 prints on a Flutter codebase, one per parsed
359
+ file. Switched all four parsers (TypeScript, JavaScript, Python, Dart,
360
+ plus the generic helper) from the deprecated `language.query(QUERY)`
361
+ to `new Query(language, QUERY)`. No behavior change, just clean
362
+ terminal output.
363
+
364
+ ---
365
+
366
+ ## [0.1.11] 2026-05-29
367
+
368
+ ### Fixed
369
+
370
+ - **Dart parser actually runs now.** Was silently broken since v0.1 due to an
371
+ ABI mismatch (shipped wasm was ABI v15, pinned `web-tree-sitter` only
372
+ supported v13–v14). Every `.dart` file got zero symbols, zero imports —
373
+ the exception was swallowed by the parser's try/catch. Bumped
374
+ `web-tree-sitter` to `^0.25.10` to fix.
375
+ - **Real Dart symbol extraction.** Classes, mixins, extensions, enums,
376
+ typedefs, top-level functions, methods, getters, setters, constructors.
377
+ - **Dart import normalization.** `package:foo/bar.dart` and `dart:async` are
378
+ stripped (cross-project); bare `'sibling.dart'` is rewritten to
379
+ `./sibling.dart` so the project resolver can complete them.
380
+
381
+ ### Changed
382
+
383
+ - **Update check runs on every `syn .`** (no more 24h cache). If you're on
384
+ latest, stays silent. If outdated, prompts `[y/N]` as before.
385
+ - **Auto-update now shows a changelog.** After `npm install -g …@latest`
386
+ succeeds, Synthra prints the new version's section from this file before
387
+ telling you to re-run. Catches `npm install` outside of `syn .` too —
388
+ next startup compares your current version to `~/.synthra/last-seen-version.json`
389
+ and prints if it's newer.
390
+
391
+ ---
392
+
393
+ ## [0.1.10] 2026-05-29
394
+
395
+ ### Changed
396
+
397
+ - **CLAUDE.md policy v2 → v3.** Session-end now goes through
398
+ `context_remember({kind: "task"|"decision"|"next"})` instead of writing
399
+ `.synthra/CONTEXT.md` directly. The Stop hook always re-rendered CONTEXT.md
400
+ from `context-store.json` — under v2 Claude's direct writes were getting
401
+ wiped on session end. Existing v2 blocks auto-upgrade.
402
+
403
+ ### Added
404
+
405
+ - **Scanner ignores more build caches.** `.dart_tool/`, `.flutter-plugins`,
406
+ `.flutter-plugins-dependencies`, `.gradle/`, `target/`, `Pods/`,
407
+ `DerivedData/`, `__pycache__/`, `.venv/`, `venv/`, `.tox/`,
408
+ `.pytest_cache/`, `.mypy_cache/`, `.ruff_cache/`, `obj/`, `.vs/`.
409
+
410
+ ---
411
+
412
+ ## [0.1.9] — 2026-05-29
413
+
414
+ ### Fixed
415
+
416
+ - **Crash on prototype-colliding symbol names.** `buildSymbolIndex` built
417
+ the lookup on a plain `{}`, so a symbol named `toString` (which every
418
+ Dart class overrides), `constructor`, `valueOf`, etc. resolved to the
419
+ inherited `Object.prototype` member and crashed on `.push`. Now uses
420
+ `Object.create(null)` on both fresh-build and load-from-disk paths.
421
+
422
+ ---
423
+
424
+ ## [0.1.8] 2026-05-29
425
+
426
+ ### Added
427
+
428
+ - **Interactive auto-update.** `syn .` checks npm at startup; if a newer
429
+ version is available, prompts `[y/N]`. On `y`, runs
430
+ `npm install -g @jefuriiij/synthra@latest` with stdio inherited and
431
+ exits with re-run instructions. Non-TTY runs (CI, piped stdin) fall
432
+ back to a silent one-line hint. `SYN_NO_UPDATE_CHECK=1` opts out.
433
+
434
+ ---
435
+
436
+ ## [0.1.7] — 2026-05-29
437
+
438
+ ### Fixed
439
+
440
+ - **JS parser missed CommonJS imports + JS class names.** Unified TS/JS
441
+ query only matched ES `import_statement`, and used `(type_identifier)`
442
+ for class names — which is TS-grammar-only. Result: every `.js`/`.cjs`/
443
+ `.mjs` file silently produced zero imports, and any class in a JS file
444
+ was skipped. Split into `TS_QUERY` and `JS_QUERY`; JS query adds a
445
+ `require()` capture and uses `(identifier)` for class names.
446
+
447
+ ---
448
+
449
+ ## [0.1.6] — 2026-05-29
450
+
451
+ ### Fixed
452
+
453
+ - **MCP registration now uses `--scope project`** so the Claude Code IDE
454
+ extension actually sees Synthra. The previous `--scope local` wrote to
455
+ a per-project section of `~/.claude.json` that only the `claude` CLI
456
+ reads — invisible to the IDE.
457
+
458
+ ---
459
+
460
+ ## [0.1.5] and earlier
461
+
462
+ See [GitHub commits](https://github.com/jefuriiij/synthra/commits/main) for
463
+ detail. v0.1.5 introduced the v2 policy template with namespace + skip rules;
464
+ v0.1.4 fixed a DEP0190 deprecation on Windows; v0.1.3 was the dashboard
465
+ redesign (Cool Marine palette, FAQ modal, savings audit row).