@jefuriiij/synthra 0.1.17 → 0.1.19
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 +40 -0
- package/dist/cli/index.js +28 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/index.js +1 -1
- package/dist/dashboard/index.js.map +1 -1
- package/dist/server/index.js +12 -1
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,46 @@ For older versions, see [GitHub Releases](https://github.com/jefuriiij/synthra/r
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.1.19] — 2026-06-01
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **Policy block v4: targeted Read-before-Edit for graph-discovered files.**
|
|
15
|
+
Claude Code's `Edit` tool requires a file to have been opened with its own
|
|
16
|
+
`Read` tool; a `graph_read` slice does not satisfy that gate. Previously,
|
|
17
|
+
editing a file known only through `graph_read` would fail with *"File has
|
|
18
|
+
not been read yet"* and force a whole-file `Read` — eroding token savings on
|
|
19
|
+
edit-heavy sessions. The v4 policy now instructs: take the line range already
|
|
20
|
+
reported in the `graph_read` header (e.g. `…::handler (L120-168)`), do a
|
|
21
|
+
targeted `Read` with matching `offset`/`limit`, then `Edit`. This satisfies
|
|
22
|
+
the gate while keeping the read small. Existing v3 blocks auto-upgrade on the
|
|
23
|
+
next `syn .` run.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## [0.1.18] — 2026-06-01
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **Stop hook on Linux/macOS no longer posts zero tokens to the dashboard.** The bash
|
|
32
|
+
`stop.sh` hook extracted `transcript_path` from the Claude Code Stop payload using a
|
|
33
|
+
greedy `sed` capture (`\(.*\)"`). Because the real payload has additional fields after
|
|
34
|
+
`transcript_path`, the capture grabbed those trailing fields and produced a
|
|
35
|
+
non-existent path string. The `-f` file check therefore always failed, totals were
|
|
36
|
+
never POSTed to `/log`, and the dashboard stayed stuck at 0 on every turn (GitHub
|
|
37
|
+
issue #1). Fixed by parsing with `jq -r '.transcript_path // empty'` and moving the
|
|
38
|
+
`command -v jq` guard above the parse so the hook exits cleanly when `jq` is absent.
|
|
39
|
+
- **SessionStart/PreCompact primer hook (`prime.sh`) hardened the same way.** The
|
|
40
|
+
`/prime` response is `{"primer":"…","port":…}`, so the old greedy capture accidentally
|
|
41
|
+
injected trailing `","port":…` junk into the primer string. Because primer text can
|
|
42
|
+
contain inner quotes, a negated-class fix (`[^"]*`) would have truncated it at the
|
|
43
|
+
first quote — `jq -r '.primer // empty'` is the correct parse. Switched `printf '%b'`
|
|
44
|
+
to `printf '%s'` since `jq -r` already decodes JSON escapes.
|
|
45
|
+
- Both fixes are **bash-only**. The Windows PowerShell hooks (`stop.ps1`, `prime.ps1`)
|
|
46
|
+
use `ConvertFrom-Json` and were already correct.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
10
50
|
## [0.1.17] — 2026-05-29
|
|
11
51
|
|
|
12
52
|
### Added
|
package/dist/cli/index.js
CHANGED
|
@@ -18,7 +18,7 @@ var init_package = __esm({
|
|
|
18
18
|
"package.json"() {
|
|
19
19
|
package_default = {
|
|
20
20
|
name: "@jefuriiij/synthra",
|
|
21
|
-
version: "0.1.
|
|
21
|
+
version: "0.1.19",
|
|
22
22
|
publishConfig: {
|
|
23
23
|
access: "public"
|
|
24
24
|
},
|
|
@@ -1466,12 +1466,18 @@ if [ ! -f "$PORT_FILE" ]; then exit 0; fi
|
|
|
1466
1466
|
PORT=$(cat "$PORT_FILE" 2>/dev/null | tr -d '[:space:]')
|
|
1467
1467
|
if [ -z "$PORT" ]; then exit 0; fi
|
|
1468
1468
|
|
|
1469
|
+
# Parse the primer with jq, not sed. Primer text legitimately contains quotes, so a
|
|
1470
|
+
# negated-class capture ([^"]*) would truncate it at the first inner quote, while the
|
|
1471
|
+
# old greedy capture (.*") over-ran into the trailing "port" field and injected junk.
|
|
1472
|
+
# jq -r also decodes JSON escapes, so we print with %s (not %b). No jq \u2192 no primer.
|
|
1473
|
+
if ! command -v jq >/dev/null 2>&1; then exit 0; fi
|
|
1474
|
+
|
|
1469
1475
|
PRIMER=$(curl -sS --max-time 3 "http://127.0.0.1:$PORT/prime" 2>/dev/null \\
|
|
1470
|
-
|
|
|
1476
|
+
| jq -r '.primer // empty' 2>/dev/null \\
|
|
1471
1477
|
| head -c 8000)
|
|
1472
1478
|
|
|
1473
1479
|
if [ -n "$PRIMER" ]; then
|
|
1474
|
-
printf '%
|
|
1480
|
+
printf '%s\\n' "$PRIMER"
|
|
1475
1481
|
fi
|
|
1476
1482
|
exit 0
|
|
1477
1483
|
`;
|
|
@@ -1564,7 +1570,13 @@ set +e\r
|
|
|
1564
1570
|
INPUT=$(cat 2>/dev/null)\r
|
|
1565
1571
|
if [ -z "$INPUT" ]; then exit 0; fi\r
|
|
1566
1572
|
\r
|
|
1567
|
-
|
|
1573
|
+
# jq is required for the parsing below \u2014 bail early (silent no-op) if it's absent.\r
|
|
1574
|
+
if ! command -v jq >/dev/null 2>&1; then exit 0; fi\r
|
|
1575
|
+
\r
|
|
1576
|
+
# Extract transcript_path with jq, not sed. A greedy sed capture (\\(.*\\)") grabs the\r
|
|
1577
|
+
# trailing JSON fields after transcript_path and yields a path that doesn't exist, so\r
|
|
1578
|
+
# the -f check below always failed and totals were never POSTed to /log. (issue #1)\r
|
|
1579
|
+
TRANSCRIPT=$(printf '%s' "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)\r
|
|
1568
1580
|
if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then exit 0; fi\r
|
|
1569
1581
|
\r
|
|
1570
1582
|
PORT_FILE="$PWD/.synthra-graph/mcp_port"\r
|
|
@@ -1572,8 +1584,6 @@ if [ ! -f "$PORT_FILE" ]; then exit 0; fi\r
|
|
|
1572
1584
|
PORT=$(cat "$PORT_FILE" 2>/dev/null | tr -d '[:space:]')\r
|
|
1573
1585
|
if [ -z "$PORT" ]; then exit 0; fi\r
|
|
1574
1586
|
\r
|
|
1575
|
-
if ! command -v jq >/dev/null 2>&1; then exit 0; fi\r
|
|
1576
|
-
\r
|
|
1577
1587
|
OFFSET_FILE="\${TRANSCRIPT}.stopoffset"\r
|
|
1578
1588
|
START_OFFSET=0\r
|
|
1579
1589
|
if [ -f "$OFFSET_FILE" ]; then\r
|
|
@@ -3203,7 +3213,7 @@ import { basename as basename4 } from "path";
|
|
|
3203
3213
|
// src/hooks/claude-md.ts
|
|
3204
3214
|
import { readFile as readFile9, writeFile as writeFile4 } from "fs/promises";
|
|
3205
3215
|
import { basename as basename3, dirname as dirname6 } from "path";
|
|
3206
|
-
var POLICY_VERSION =
|
|
3216
|
+
var POLICY_VERSION = 4;
|
|
3207
3217
|
var POLICY_BEGIN = `<!-- synthra-policy v${POLICY_VERSION} BEGIN -->`;
|
|
3208
3218
|
var POLICY_END = `<!-- synthra-policy v${POLICY_VERSION} END -->`;
|
|
3209
3219
|
var ANY_BLOCK_RE = /<!--\s*synthra-policy\s+v\d+\s+BEGIN\s*-->[\s\S]*?<!--\s*synthra-policy\s+v\d+\s+END\s*-->\s*/g;
|
|
@@ -3272,6 +3282,17 @@ function policyBlock() {
|
|
|
3272
3282
|
"- If `graph_continue`'s `Files` list contains a `::` entry, pass it",
|
|
3273
3283
|
" verbatim to `graph_read`.",
|
|
3274
3284
|
"",
|
|
3285
|
+
"### Editing a file",
|
|
3286
|
+
"",
|
|
3287
|
+
"Claude Code's `Edit` tool (and `Write` when overwriting) only accepts a",
|
|
3288
|
+
"file that was opened with the **`Read` tool** \u2014 a `graph_read` slice does",
|
|
3289
|
+
'not count, and editing such a file fails with *"File has not been read',
|
|
3290
|
+
'yet."* So before editing a file you only know through `graph_read`: take',
|
|
3291
|
+
"the line range from its header (e.g. `\u2026::handler (L120-168)`), `Read` that",
|
|
3292
|
+
"file with a matching `offset`/`limit`, then `Edit`. That satisfies the",
|
|
3293
|
+
"gate while keeping the read small \u2014 don't whole-file `Read` unless the",
|
|
3294
|
+
"edit spans most of the file.",
|
|
3295
|
+
"",
|
|
3275
3296
|
"### Don'ts",
|
|
3276
3297
|
"",
|
|
3277
3298
|
"- Don't Grep / Glob before calling `graph_continue` when required \u2014 the",
|