@jefuriiij/synthra 0.1.24 → 0.1.25
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 +23 -0
- package/LICENSE +21 -21
- package/README.md +222 -222
- package/dist/cli/index.js +180 -172
- 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.map +1 -1
- package/package.json +66 -66
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@ For older versions, see [GitHub Releases](https://github.com/jefuriiij/synthra/r
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
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
|
+
|
|
10
33
|
## [0.1.24] — 2026-06-06
|
|
11
34
|
|
|
12
35
|
### Added
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 jefuriiij
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jefuriiij
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,222 +1,222 @@
|
|
|
1
|
-
# Synthra
|
|
2
|
-
|
|
3
|
-
> Local context engine for AI coding assistants. **Install once. Fire and forget.** Claude Code stops burning tokens on grep → glob → read → repeat — it queries a structured graph instead.
|
|
4
|
-
|
|
5
|
-
Built first for Claude Code (IDE extension + CLI). Anything that speaks the Model Context Protocol can plug in.
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install -g @jefuriiij/synthra
|
|
9
|
-
cd your-project
|
|
10
|
-
syn .
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
That's the whole setup. Open the Claude Code IDE extension in the same folder and work normally; `Ctrl+C` the terminal when you're done. Synthra hangs out in the background, watching for file saves, blocking redundant Greps, and tracking every turn — until you have a reason to look at the dashboard.
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## What it saves you (real measurement)
|
|
18
|
-
|
|
19
|
-
Same prompt, same Opus model, same Svelte/Node codebase. Dogfooding on a real production project (`windsor-stables`) shows:
|
|
20
|
-
|
|
21
|
-
| Setup | Cost per session |
|
|
22
|
-
|---|---|
|
|
23
|
-
| Vanilla Claude Code | **$7.97** |
|
|
24
|
-
| Synthra w/ MCP fixes (v0.1.6) | **$4.26** (-46%) |
|
|
25
|
-
| Synthra w/ full graph tools (v0.1.7+) | **$2.05** (-74%) |
|
|
26
|
-
|
|
27
|
-
A 3-turn WebSocket-auth walkthrough across 4 files. The savings come from Claude reading function signatures (`graph_read("file.ts::Symbol")`, ~50 tokens) instead of whole files (~2,000+), and from skipping Greps that the Moat blocks because the graph already has the answer.
|
|
28
|
-
|
|
29
|
-
The math behind the dashboard's **Savings (floor)** card is shown live (`blocks × 500 tokens × $3/M`) so you can verify it for your own sessions instead of trusting the number on faith.
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## What it does
|
|
34
|
-
|
|
35
|
-
AI coding assistants burn tokens exploring the codebase on every turn. They lose context between turns and across sessions. They're blind to what *you* are doing in your editor between AI turns.
|
|
36
|
-
|
|
37
|
-
Synthra is a tiny local CLI (~273 KB tarball, no SaaS, no telemetry, MIT) that sits between you and your AI:
|
|
38
|
-
|
|
39
|
-
- **Pre-injects** a structured ~4K-token context pack (signatures + top function bodies + linked tests) at session start
|
|
40
|
-
- **The Moat** — deterministically blocks Grep/Glob at the PreToolUse hook layer when the graph already has the answer. Returns `{"decision":"block"}` — Claude literally cannot disobey.
|
|
41
|
-
- **Remembers** decisions and notes branch-by-branch in a git-tracked `.synthra/` directory so teammates inherit context
|
|
42
|
-
- **Watches** file saves, branch switches, and uncommitted diffs (chokidar + a `.git/HEAD` watcher) so the AI knows what just changed
|
|
43
|
-
- **Tracks** every token via Claude's transcript and reports estimated cost + savings on a live dashboard
|
|
44
|
-
- **Updates with consent** — daily npm check prompts you on a TTY (`Update now? [y/N]`) and stays silent on CI
|
|
45
|
-
|
|
46
|
-
When `syn .` runs, you see:
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
[syn] ✅ scanned 123 files · 490 symbols · 574 edges
|
|
50
|
-
[syn] 🧠 MCP http://127.0.0.1:8080 → registered as 'synthra'
|
|
51
|
-
[syn] 📊 Dashboard http://127.0.0.1:8901
|
|
52
|
-
[syn] 🪝 Hooks installed in .claude/settings.local.json
|
|
53
|
-
|
|
54
|
-
[syn] 🤖 Ready — open the Claude Code IDE extension (or run `claude` in another terminal).
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## MCP tools
|
|
60
|
-
|
|
61
|
-
Ten tools exposed over HTTP MCP (namespaced as `mcp__synthra__*`). Claude calls these instead of Grep / Glob / Read for navigation:
|
|
62
|
-
|
|
63
|
-
| Tool | Purpose |
|
|
64
|
-
|---|---|
|
|
65
|
-
| `graph_continue(query)` | Return the structured context pack — `Confidence` label + `Files` list + signatures + top function bodies |
|
|
66
|
-
| `graph_read(target)` | Fetch source for `file/path.ts` or `file/path.ts::SymbolName` (the latter returns ~50 tokens vs thousands for a whole file) |
|
|
67
|
-
| `graph_register_edit(files)` | Tell Synthra you edited files (boosts their ranking, avoids stale snapshots) |
|
|
68
|
-
| `context_remember(text, kind)` | Persist a decision / task / fact / next-step / blocker, branch-aware |
|
|
69
|
-
| `context_recall(kind?)` | Read previously-stored entries |
|
|
70
|
-
| `recent_activity(since_ms?)` | What the human just saved / branch-switched / changed |
|
|
71
|
-
| `count_tokens(text)` | Char/4 estimate for prompt budgeting |
|
|
72
|
-
| `blast_radius(target, depth?)` | All files that depend on `target` transitively |
|
|
73
|
-
| `dead_code(limit?)` | Files no other file imports and no test references |
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
## Languages
|
|
78
|
-
|
|
79
|
-
Full symbol extraction:
|
|
80
|
-
|
|
81
|
-
- **TypeScript** (`.ts`, `.tsx`, `.cts`, `.mts`, `.jsx`) — interfaces, type aliases, enums, classes, methods, arrow consts, ES imports
|
|
82
|
-
- **JavaScript** (`.js`, `.cjs`, `.mjs`) — uses a JS-specific tree-sitter query (v0.1.7+) that captures CommonJS `require()` calls in addition to ES `import` statements, and uses the JS grammar's `(identifier)` node for class names
|
|
83
|
-
- **Python** (`.py`, `.pyi`)
|
|
84
|
-
- **Svelte** (`.svelte`) — `<script>` blocks reparsed as TS
|
|
85
|
-
- **Vue** (`.vue`) — same
|
|
86
|
-
- **Go** (`.go`)
|
|
87
|
-
- **Rust** (`.rs`)
|
|
88
|
-
- **Java** (`.java`)
|
|
89
|
-
- **Kotlin** (`.kt`, `.kts`)
|
|
90
|
-
- **PHP** (`.php`)
|
|
91
|
-
- **Ruby** (`.rb`)
|
|
92
|
-
- **C** (`.c`, `.h`)
|
|
93
|
-
- **C++** (`.cpp`, `.cc`, `.cxx`, `.hpp`, `.hh`, `.hxx`)
|
|
94
|
-
- **C#** / .NET (`.cs`)
|
|
95
|
-
- **Dart** (`.dart`) — content indexed; symbol extraction is best-effort in v0.1
|
|
96
|
-
|
|
97
|
-
Files in other formats (HTML, CSS, JSON, YAML, Markdown, images, etc.) are walked and content-indexed so keyword search still finds them — just no symbol-level granularity.
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## Commands
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
syn . # Default: scan + MCP + dashboard + hooks + claude mcp add.
|
|
105
|
-
# Background-service; Ctrl+C to stop.
|
|
106
|
-
syn . --launch-cli # Also spawn the `claude` CLI in this terminal.
|
|
107
|
-
syn . --resume <id> # Resume a Claude session (requires --launch-cli).
|
|
108
|
-
syn scan [path] # Scan only — walk + parse + write graph.
|
|
109
|
-
syn serve [path] # Start the MCP server only.
|
|
110
|
-
syn dashboard [path] # Run only the token dashboard (standalone process).
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## Storage layout
|
|
116
|
-
|
|
117
|
-
When `syn .` runs in a project:
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
your-project/
|
|
121
|
-
├── .gitignore # appended: .synthra-graph/, .mcp.json (with comments)
|
|
122
|
-
├── .mcp.json # Synthra registers here at --scope project so the IDE sees it.
|
|
123
|
-
│ # Gitignored by default — remove the .gitignore line to share with teammates.
|
|
124
|
-
├── CLAUDE.md # appended: <!-- synthra-policy v2 ... -->
|
|
125
|
-
├── .claude/
|
|
126
|
-
│ ├── settings.local.json # hooks merged (tagged with meta: "synthra-hook=true")
|
|
127
|
-
│ └── hooks/ # synthra-prime.ps1, synthra-pre-tool-use.ps1, …
|
|
128
|
-
├── .synthra-graph/ # GITIGNORED — heavy machine-local state
|
|
129
|
-
│ ├── info_graph.json
|
|
130
|
-
│ ├── symbol_index.json
|
|
131
|
-
│ ├── activity.jsonl
|
|
132
|
-
│ ├── token_log.jsonl
|
|
133
|
-
│ ├── gate_log.jsonl
|
|
134
|
-
│ └── mcp_port
|
|
135
|
-
└── .synthra/ # GIT-TRACKED — team's shared memory
|
|
136
|
-
├── context-store.json # decisions, tasks, facts (default branch)
|
|
137
|
-
├── CONTEXT.md # narrative summary
|
|
138
|
-
└── branches/<sanitized>/ # per-branch overrides
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
A global registry at `~/.synthra/projects.json` lists every project where Synthra has run, so `syn dashboard` can show aggregate stats across all of them.
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## Coexistence
|
|
146
|
-
|
|
147
|
-
Synthra plays nicely alongside other AI-context tools. It only writes to its own `.synthra/`, `.synthra-graph/`, and a single `synthra` entry inside `.mcp.json` (existing entries are preserved). It only modifies `CLAUDE.md` inside `<!-- synthra-policy v2 -->` markers. It tags hook entries with `meta: "synthra-hook=true"` so re-runs strip only its own entries from `settings.local.json`. If another tool also logs to a shared `token_log.jsonl`, the dashboard dedupes overlapping entries on read so totals don't double-count.
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## Configuration
|
|
152
|
-
|
|
153
|
-
Environment variables (all optional):
|
|
154
|
-
|
|
155
|
-
| Variable | Default | Purpose |
|
|
156
|
-
|---|---|---|
|
|
157
|
-
| `SYN_MCP_PORT` | (auto 8080–8099) | Pin the MCP server port |
|
|
158
|
-
| `SYN_DASHBOARD_PORT` | `8901` | Dashboard preferred port (falls back through 8901–8910) |
|
|
159
|
-
| `SYN_HARD_MAX_READ_CHARS` | `4000` | Soft token budget for `graph_continue` packs |
|
|
160
|
-
| `SYN_LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error` |
|
|
161
|
-
| `SYN_CLAUDE_BIN` | `claude` | Override the `claude` binary location |
|
|
162
|
-
| `SYN_NO_UPDATE_CHECK` | `0` | Set to `1` to disable the daily version-check ping |
|
|
163
|
-
|
|
164
|
-
---
|
|
165
|
-
|
|
166
|
-
## How the moat works
|
|
167
|
-
|
|
168
|
-
The PreToolUse hook fires on every `Grep` / `Glob` call. The hook POSTs the tool input to Synthra's local server. The server runs the query through the graph and returns:
|
|
169
|
-
|
|
170
|
-
- `decision: "allow"` if the graph has no confident match (low confidence)
|
|
171
|
-
- `decision: "allow"` if the user just edited a matching file (recent-activity relaxation)
|
|
172
|
-
- `decision: "block"` otherwise, with a reason pointing Claude at `graph_continue`
|
|
173
|
-
|
|
174
|
-
Claude Code honors the block and pivots to the MCP tool. The structured pack is cheaper, faster, and pre-ranked. The CLAUDE.md policy block (managed by Synthra inside its own `<!-- synthra-policy v2 -->` markers) tells Claude when to call `graph_continue` and — importantly — when to skip it, so follow-up turns don't pay the pack cost again.
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## Self-update
|
|
179
|
-
|
|
180
|
-
Every `syn .` checks the npm registry for a newer version (no cache — always fresh; 2s hard timeout; silent fallthrough on network failure). When you're on latest, the check stays silent and `syn .` proceeds. When you're outdated:
|
|
181
|
-
|
|
182
|
-
- **Interactive shell** (TTY) → you see `[syn] Synthra X.Y.Z is available (you have A.B.C). Update now? [y/N]:` *before* the scan starts. Type `y` to install; press Enter (or anything else) to skip and continue with the current version.
|
|
183
|
-
- **Non-interactive** (CI, piped stdin) → silent one-line hint, no prompt.
|
|
184
|
-
- **Disabled entirely** → set `SYN_NO_UPDATE_CHECK=1`.
|
|
185
|
-
|
|
186
|
-
On `y`, Synthra spawns `npm install -g @jefuriiij/synthra@latest` with stdio inherited (you see npm's progress), then **prints the new version's `CHANGELOG.md` section** (so you know what you just got), then exits with re-run instructions — the running Node process is still the old version and can't hot-swap its own code mid-run.
|
|
187
|
-
|
|
188
|
-
If you upgrade via `npm install -g @jefuriiij/synthra@latest` directly (outside Synthra's prompt), the next `syn .` notices and prints the changelog anyway. It tracks the last-seen version at `~/.synthra/last-seen-version.json`.
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
|
|
192
|
-
## Platform support
|
|
193
|
-
|
|
194
|
-
| Platform | Status |
|
|
195
|
-
|---|---|
|
|
196
|
-
| **Windows** | ✅ Tested. PowerShell hook scripts; primary development target. |
|
|
197
|
-
| **macOS / Linux** | ⚠️ Best-effort. Bash hook scripts ship and the installer selects them automatically, but the full `syn .` flow hasn't been verified on POSIX yet. The Stop hook (token logging) needs `jq` on PATH or it silently no-ops. |
|
|
198
|
-
|
|
199
|
-
The platform-agnostic parts — `syn scan`, `syn serve`, `syn dashboard`, the MCP server, and the dashboard — are pure Node and run anywhere Node 18+ does. The hook integration is what's Windows-verified; POSIX is wired but untested. If you run it on macOS/Linux, [open an issue](https://github.com/jefuriiij/synthra/issues) with what you find.
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
## Development
|
|
204
|
-
|
|
205
|
-
```bash
|
|
206
|
-
git clone https://github.com/jefuriiij/synthra
|
|
207
|
-
cd synthra
|
|
208
|
-
npm install
|
|
209
|
-
npm link # makes `syn` available globally; rebuilds reflect immediately
|
|
210
|
-
npm run build # tsup → dist/
|
|
211
|
-
npm run dev # tsup --watch
|
|
212
|
-
npm test # vitest
|
|
213
|
-
npm run typecheck # tsc --noEmit
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
See [`ROADMAP.md`](./ROADMAP.md) for milestone history (M1 scanner → M6 dashboard) and the v0.2 backlog.
|
|
217
|
-
|
|
218
|
-
---
|
|
219
|
-
|
|
220
|
-
## License
|
|
221
|
-
|
|
222
|
-
[MIT](./LICENSE) — fork freely, ship freely, just keep the attribution. If Synthra ends up useful inside your own tool or product, a link back is appreciated but not required.
|
|
1
|
+
# Synthra
|
|
2
|
+
|
|
3
|
+
> Local context engine for AI coding assistants. **Install once. Fire and forget.** Claude Code stops burning tokens on grep → glob → read → repeat — it queries a structured graph instead.
|
|
4
|
+
|
|
5
|
+
Built first for Claude Code (IDE extension + CLI). Anything that speaks the Model Context Protocol can plug in.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @jefuriiij/synthra
|
|
9
|
+
cd your-project
|
|
10
|
+
syn .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
That's the whole setup. Open the Claude Code IDE extension in the same folder and work normally; `Ctrl+C` the terminal when you're done. Synthra hangs out in the background, watching for file saves, blocking redundant Greps, and tracking every turn — until you have a reason to look at the dashboard.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## What it saves you (real measurement)
|
|
18
|
+
|
|
19
|
+
Same prompt, same Opus model, same Svelte/Node codebase. Dogfooding on a real production project (`windsor-stables`) shows:
|
|
20
|
+
|
|
21
|
+
| Setup | Cost per session |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Vanilla Claude Code | **$7.97** |
|
|
24
|
+
| Synthra w/ MCP fixes (v0.1.6) | **$4.26** (-46%) |
|
|
25
|
+
| Synthra w/ full graph tools (v0.1.7+) | **$2.05** (-74%) |
|
|
26
|
+
|
|
27
|
+
A 3-turn WebSocket-auth walkthrough across 4 files. The savings come from Claude reading function signatures (`graph_read("file.ts::Symbol")`, ~50 tokens) instead of whole files (~2,000+), and from skipping Greps that the Moat blocks because the graph already has the answer.
|
|
28
|
+
|
|
29
|
+
The math behind the dashboard's **Savings (floor)** card is shown live (`blocks × 500 tokens × $3/M`) so you can verify it for your own sessions instead of trusting the number on faith.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## What it does
|
|
34
|
+
|
|
35
|
+
AI coding assistants burn tokens exploring the codebase on every turn. They lose context between turns and across sessions. They're blind to what *you* are doing in your editor between AI turns.
|
|
36
|
+
|
|
37
|
+
Synthra is a tiny local CLI (~273 KB tarball, no SaaS, no telemetry, MIT) that sits between you and your AI:
|
|
38
|
+
|
|
39
|
+
- **Pre-injects** a structured ~4K-token context pack (signatures + top function bodies + linked tests) at session start
|
|
40
|
+
- **The Moat** — deterministically blocks Grep/Glob at the PreToolUse hook layer when the graph already has the answer. Returns `{"decision":"block"}` — Claude literally cannot disobey.
|
|
41
|
+
- **Remembers** decisions and notes branch-by-branch in a git-tracked `.synthra/` directory so teammates inherit context
|
|
42
|
+
- **Watches** file saves, branch switches, and uncommitted diffs (chokidar + a `.git/HEAD` watcher) so the AI knows what just changed
|
|
43
|
+
- **Tracks** every token via Claude's transcript and reports estimated cost + savings on a live dashboard
|
|
44
|
+
- **Updates with consent** — daily npm check prompts you on a TTY (`Update now? [y/N]`) and stays silent on CI
|
|
45
|
+
|
|
46
|
+
When `syn .` runs, you see:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
[syn] ✅ scanned 123 files · 490 symbols · 574 edges
|
|
50
|
+
[syn] 🧠 MCP http://127.0.0.1:8080 → registered as 'synthra'
|
|
51
|
+
[syn] 📊 Dashboard http://127.0.0.1:8901
|
|
52
|
+
[syn] 🪝 Hooks installed in .claude/settings.local.json
|
|
53
|
+
|
|
54
|
+
[syn] 🤖 Ready — open the Claude Code IDE extension (or run `claude` in another terminal).
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## MCP tools
|
|
60
|
+
|
|
61
|
+
Ten tools exposed over HTTP MCP (namespaced as `mcp__synthra__*`). Claude calls these instead of Grep / Glob / Read for navigation:
|
|
62
|
+
|
|
63
|
+
| Tool | Purpose |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `graph_continue(query)` | Return the structured context pack — `Confidence` label + `Files` list + signatures + top function bodies |
|
|
66
|
+
| `graph_read(target)` | Fetch source for `file/path.ts` or `file/path.ts::SymbolName` (the latter returns ~50 tokens vs thousands for a whole file) |
|
|
67
|
+
| `graph_register_edit(files)` | Tell Synthra you edited files (boosts their ranking, avoids stale snapshots) |
|
|
68
|
+
| `context_remember(text, kind)` | Persist a decision / task / fact / next-step / blocker, branch-aware |
|
|
69
|
+
| `context_recall(kind?)` | Read previously-stored entries |
|
|
70
|
+
| `recent_activity(since_ms?)` | What the human just saved / branch-switched / changed |
|
|
71
|
+
| `count_tokens(text)` | Char/4 estimate for prompt budgeting |
|
|
72
|
+
| `blast_radius(target, depth?)` | All files that depend on `target` transitively |
|
|
73
|
+
| `dead_code(limit?)` | Files no other file imports and no test references |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Languages
|
|
78
|
+
|
|
79
|
+
Full symbol extraction:
|
|
80
|
+
|
|
81
|
+
- **TypeScript** (`.ts`, `.tsx`, `.cts`, `.mts`, `.jsx`) — interfaces, type aliases, enums, classes, methods, arrow consts, ES imports
|
|
82
|
+
- **JavaScript** (`.js`, `.cjs`, `.mjs`) — uses a JS-specific tree-sitter query (v0.1.7+) that captures CommonJS `require()` calls in addition to ES `import` statements, and uses the JS grammar's `(identifier)` node for class names
|
|
83
|
+
- **Python** (`.py`, `.pyi`)
|
|
84
|
+
- **Svelte** (`.svelte`) — `<script>` blocks reparsed as TS
|
|
85
|
+
- **Vue** (`.vue`) — same
|
|
86
|
+
- **Go** (`.go`)
|
|
87
|
+
- **Rust** (`.rs`)
|
|
88
|
+
- **Java** (`.java`)
|
|
89
|
+
- **Kotlin** (`.kt`, `.kts`)
|
|
90
|
+
- **PHP** (`.php`)
|
|
91
|
+
- **Ruby** (`.rb`)
|
|
92
|
+
- **C** (`.c`, `.h`)
|
|
93
|
+
- **C++** (`.cpp`, `.cc`, `.cxx`, `.hpp`, `.hh`, `.hxx`)
|
|
94
|
+
- **C#** / .NET (`.cs`)
|
|
95
|
+
- **Dart** (`.dart`) — content indexed; symbol extraction is best-effort in v0.1
|
|
96
|
+
|
|
97
|
+
Files in other formats (HTML, CSS, JSON, YAML, Markdown, images, etc.) are walked and content-indexed so keyword search still finds them — just no symbol-level granularity.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Commands
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
syn . # Default: scan + MCP + dashboard + hooks + claude mcp add.
|
|
105
|
+
# Background-service; Ctrl+C to stop.
|
|
106
|
+
syn . --launch-cli # Also spawn the `claude` CLI in this terminal.
|
|
107
|
+
syn . --resume <id> # Resume a Claude session (requires --launch-cli).
|
|
108
|
+
syn scan [path] # Scan only — walk + parse + write graph.
|
|
109
|
+
syn serve [path] # Start the MCP server only.
|
|
110
|
+
syn dashboard [path] # Run only the token dashboard (standalone process).
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Storage layout
|
|
116
|
+
|
|
117
|
+
When `syn .` runs in a project:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
your-project/
|
|
121
|
+
├── .gitignore # appended: .synthra-graph/, .mcp.json (with comments)
|
|
122
|
+
├── .mcp.json # Synthra registers here at --scope project so the IDE sees it.
|
|
123
|
+
│ # Gitignored by default — remove the .gitignore line to share with teammates.
|
|
124
|
+
├── CLAUDE.md # appended: <!-- synthra-policy v2 ... -->
|
|
125
|
+
├── .claude/
|
|
126
|
+
│ ├── settings.local.json # hooks merged (tagged with meta: "synthra-hook=true")
|
|
127
|
+
│ └── hooks/ # synthra-prime.ps1, synthra-pre-tool-use.ps1, …
|
|
128
|
+
├── .synthra-graph/ # GITIGNORED — heavy machine-local state
|
|
129
|
+
│ ├── info_graph.json
|
|
130
|
+
│ ├── symbol_index.json
|
|
131
|
+
│ ├── activity.jsonl
|
|
132
|
+
│ ├── token_log.jsonl
|
|
133
|
+
│ ├── gate_log.jsonl
|
|
134
|
+
│ └── mcp_port
|
|
135
|
+
└── .synthra/ # GIT-TRACKED — team's shared memory
|
|
136
|
+
├── context-store.json # decisions, tasks, facts (default branch)
|
|
137
|
+
├── CONTEXT.md # narrative summary
|
|
138
|
+
└── branches/<sanitized>/ # per-branch overrides
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
A global registry at `~/.synthra/projects.json` lists every project where Synthra has run, so `syn dashboard` can show aggregate stats across all of them.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Coexistence
|
|
146
|
+
|
|
147
|
+
Synthra plays nicely alongside other AI-context tools. It only writes to its own `.synthra/`, `.synthra-graph/`, and a single `synthra` entry inside `.mcp.json` (existing entries are preserved). It only modifies `CLAUDE.md` inside `<!-- synthra-policy v2 -->` markers. It tags hook entries with `meta: "synthra-hook=true"` so re-runs strip only its own entries from `settings.local.json`. If another tool also logs to a shared `token_log.jsonl`, the dashboard dedupes overlapping entries on read so totals don't double-count.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Configuration
|
|
152
|
+
|
|
153
|
+
Environment variables (all optional):
|
|
154
|
+
|
|
155
|
+
| Variable | Default | Purpose |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| `SYN_MCP_PORT` | (auto 8080–8099) | Pin the MCP server port |
|
|
158
|
+
| `SYN_DASHBOARD_PORT` | `8901` | Dashboard preferred port (falls back through 8901–8910) |
|
|
159
|
+
| `SYN_HARD_MAX_READ_CHARS` | `4000` | Soft token budget for `graph_continue` packs |
|
|
160
|
+
| `SYN_LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error` |
|
|
161
|
+
| `SYN_CLAUDE_BIN` | `claude` | Override the `claude` binary location |
|
|
162
|
+
| `SYN_NO_UPDATE_CHECK` | `0` | Set to `1` to disable the daily version-check ping |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## How the moat works
|
|
167
|
+
|
|
168
|
+
The PreToolUse hook fires on every `Grep` / `Glob` call. The hook POSTs the tool input to Synthra's local server. The server runs the query through the graph and returns:
|
|
169
|
+
|
|
170
|
+
- `decision: "allow"` if the graph has no confident match (low confidence)
|
|
171
|
+
- `decision: "allow"` if the user just edited a matching file (recent-activity relaxation)
|
|
172
|
+
- `decision: "block"` otherwise, with a reason pointing Claude at `graph_continue`
|
|
173
|
+
|
|
174
|
+
Claude Code honors the block and pivots to the MCP tool. The structured pack is cheaper, faster, and pre-ranked. The CLAUDE.md policy block (managed by Synthra inside its own `<!-- synthra-policy v2 -->` markers) tells Claude when to call `graph_continue` and — importantly — when to skip it, so follow-up turns don't pay the pack cost again.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Self-update
|
|
179
|
+
|
|
180
|
+
Every `syn .` checks the npm registry for a newer version (no cache — always fresh; 2s hard timeout; silent fallthrough on network failure). When you're on latest, the check stays silent and `syn .` proceeds. When you're outdated:
|
|
181
|
+
|
|
182
|
+
- **Interactive shell** (TTY) → you see `[syn] Synthra X.Y.Z is available (you have A.B.C). Update now? [y/N]:` *before* the scan starts. Type `y` to install; press Enter (or anything else) to skip and continue with the current version.
|
|
183
|
+
- **Non-interactive** (CI, piped stdin) → silent one-line hint, no prompt.
|
|
184
|
+
- **Disabled entirely** → set `SYN_NO_UPDATE_CHECK=1`.
|
|
185
|
+
|
|
186
|
+
On `y`, Synthra spawns `npm install -g @jefuriiij/synthra@latest` with stdio inherited (you see npm's progress), then **prints the new version's `CHANGELOG.md` section** (so you know what you just got), then exits with re-run instructions — the running Node process is still the old version and can't hot-swap its own code mid-run.
|
|
187
|
+
|
|
188
|
+
If you upgrade via `npm install -g @jefuriiij/synthra@latest` directly (outside Synthra's prompt), the next `syn .` notices and prints the changelog anyway. It tracks the last-seen version at `~/.synthra/last-seen-version.json`.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Platform support
|
|
193
|
+
|
|
194
|
+
| Platform | Status |
|
|
195
|
+
|---|---|
|
|
196
|
+
| **Windows** | ✅ Tested. PowerShell hook scripts; primary development target. |
|
|
197
|
+
| **macOS / Linux** | ⚠️ Best-effort. Bash hook scripts ship and the installer selects them automatically, but the full `syn .` flow hasn't been verified on POSIX yet. The Stop hook (token logging) needs `jq` on PATH or it silently no-ops. |
|
|
198
|
+
|
|
199
|
+
The platform-agnostic parts — `syn scan`, `syn serve`, `syn dashboard`, the MCP server, and the dashboard — are pure Node and run anywhere Node 18+ does. The hook integration is what's Windows-verified; POSIX is wired but untested. If you run it on macOS/Linux, [open an issue](https://github.com/jefuriiij/synthra/issues) with what you find.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Development
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
git clone https://github.com/jefuriiij/synthra
|
|
207
|
+
cd synthra
|
|
208
|
+
npm install
|
|
209
|
+
npm link # makes `syn` available globally; rebuilds reflect immediately
|
|
210
|
+
npm run build # tsup → dist/
|
|
211
|
+
npm run dev # tsup --watch
|
|
212
|
+
npm test # vitest
|
|
213
|
+
npm run typecheck # tsc --noEmit
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
See [`ROADMAP.md`](./ROADMAP.md) for milestone history (M1 scanner → M6 dashboard) and the v0.2 backlog.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
[MIT](./LICENSE) — fork freely, ship freely, just keep the attribution. If Synthra ends up useful inside your own tool or product, a link back is appreciated but not required.
|