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