synabun 2026.3.308 → 2026.3.310
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/CLAUDE.md +150 -0
- package/hooks/claude-code/post-plan.mjs +1 -1
- package/hooks/claude-code/session-start.mjs +2 -0
- package/neural-interface/public/shared/styles.css +2 -2
- package/neural-interface/public/shared/ui-settings.js +2 -2
- package/neural-interface/server.js +82 -8
- package/neural-interface/templates/CLAUDE-template.md +9 -1
- package/package.json +4 -2
- package/preuninstall.js +28 -0
- package/setup.js +86 -1
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
====================================END SYNBUN
|
|
4
|
+
|
|
5
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
6
|
+
|
|
7
|
+
## What is SynaBun
|
|
8
|
+
|
|
9
|
+
Persistent vector memory for AI assistants — SQLite + local Transformers.js embeddings (all-MiniLM-L6-v2, 384 dims). No external services, no API keys, no Docker. Installed via `npm install -g synabun` or cloned from GitHub.
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Start everything (installs deps, builds MCP, launches Neural Interface)
|
|
15
|
+
npm start # runs setup.js
|
|
16
|
+
|
|
17
|
+
# Neural Interface only (Express server on :3344)
|
|
18
|
+
node neural-interface/server.js
|
|
19
|
+
|
|
20
|
+
# Build MCP server (required after any TypeScript change)
|
|
21
|
+
cd mcp-server && npm run build # ALWAYS use npm run build, never npx tsc
|
|
22
|
+
|
|
23
|
+
# Dev mode (auto-rebuild on change)
|
|
24
|
+
cd mcp-server && npm run dev # tsx src/index.ts
|
|
25
|
+
|
|
26
|
+
# Tests
|
|
27
|
+
cd mcp-server && npm test # vitest run
|
|
28
|
+
cd mcp-server && npm run test:watch # vitest watch mode
|
|
29
|
+
|
|
30
|
+
# Register MCP with Claude Code
|
|
31
|
+
claude mcp add SynaBun -s user -- node "/path/to/Synabun/mcp-server/run.mjs"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Architecture
|
|
35
|
+
|
|
36
|
+
Two independently running processes that share one SQLite database:
|
|
37
|
+
|
|
38
|
+
### MCP Server (`mcp-server/`)
|
|
39
|
+
TypeScript, compiles to `dist/`. Entry point: `run.mjs` → `dist/preload.js` → `dist/index.js`. Communicates with AI tools via MCP protocol (stdio or HTTP). 72 tools across 9 groups: Memory (8), Browser (39), Whiteboard (5), Cards (5), Discord (8), Leonardo (4), Git (1), Loop (1), TicTacToe (1).
|
|
40
|
+
|
|
41
|
+
- `src/index.ts` — tool registration, schema refresh, server creation
|
|
42
|
+
- `src/config.ts` — env config, project detection from `claude-code-projects.json`
|
|
43
|
+
- `src/types.ts` — `MemoryPayload` interface
|
|
44
|
+
- `src/http.ts` — HTTP MCP transport (stateless, fresh server per request)
|
|
45
|
+
- `src/services/sqlite.ts` — storage layer, vectors as Float32Array BLOBs, cosine similarity in JS
|
|
46
|
+
- `src/services/local-embeddings.ts` — Transformers.js embedding generation
|
|
47
|
+
- `src/services/categories.ts` — category CRUD, hierarchy, dynamic schema refresh
|
|
48
|
+
- `src/tools/` — one file per tool (or per tool group with a registration function)
|
|
49
|
+
|
|
50
|
+
### Neural Interface (`neural-interface/`)
|
|
51
|
+
Express server (`server.js`, ~15K lines) serving a vanilla JS + Three.js frontend. 55+ REST API endpoints. Runs on port 3344.
|
|
52
|
+
|
|
53
|
+
- `server.js` — monolithic Express server with all API routes, WebSocket, terminal (node-pty), Playwright browser management, session registry, loop orchestration
|
|
54
|
+
- `public/index.html` — 3D force-directed graph (Three.js)
|
|
55
|
+
- `public/index2d.html` — 2D canvas variant
|
|
56
|
+
- `public/onboarding.html` — first-time setup wizard
|
|
57
|
+
- `public/claude-chat.html` — standalone Claude Code chat page
|
|
58
|
+
- `public/shared/` — modular JS/CSS (~50 files): `ui-*.js` modules, `storage.js` (server-synced state), `styles.css`, `utils.js`
|
|
59
|
+
- `lib/db.js` — shared SQLite access (same DB as MCP server)
|
|
60
|
+
- `lib/session-indexer.js` — conversation session indexing
|
|
61
|
+
- `skins/` — theme system (CSS + JSON per skin)
|
|
62
|
+
- `i18n/` — internationalization (en.json)
|
|
63
|
+
- `templates/CLAUDE-template.md` — source of truth for memory rulesets injected into project CLAUDE.md files
|
|
64
|
+
|
|
65
|
+
### Hooks (`hooks/claude-code/`)
|
|
66
|
+
7 ESM (.mjs) lifecycle hooks for Claude Code, registered in `.claude/settings.json`:
|
|
67
|
+
|
|
68
|
+
| Hook | Event | Purpose |
|
|
69
|
+
|------|-------|---------|
|
|
70
|
+
| `session-start.mjs` | SessionStart | Greeting, boot sequence, compaction recovery, loop detection |
|
|
71
|
+
| `prompt-submit.mjs` | UserPromptSubmit | Tiered recall nudges, loop iteration injection |
|
|
72
|
+
| `pre-compact.mjs` | PreCompact | Transcript capture before context compaction |
|
|
73
|
+
| `stop.mjs` | Stop | Combined obligations: compaction, loops, task memory, user learning |
|
|
74
|
+
| `post-remember.mjs` | PostToolUse | Edit tracking, flag clearing, user learning |
|
|
75
|
+
| `pre-websearch.mjs` | PreToolUse | Blocks WebSearch/WebFetch during active browser sessions |
|
|
76
|
+
| `post-plan.mjs` | PostToolUse | Auto-stores plans as memories on ExitPlanMode |
|
|
77
|
+
|
|
78
|
+
`shared.mjs` contains utilities used across hooks.
|
|
79
|
+
|
|
80
|
+
### Skills (`skills/`)
|
|
81
|
+
Claude Code slash commands with `SKILL.md` entry points and `modules/` subdirectories:
|
|
82
|
+
- `synabun/` — `/synabun` command hub (brainstorm, audit, health, search)
|
|
83
|
+
- `leonardo/` — `/leonardo` master media prompter (video, image, model advisor, prompt library)
|
|
84
|
+
|
|
85
|
+
### Data Flow
|
|
86
|
+
```
|
|
87
|
+
AI tool calls remember → MCP server embeds content → SQLite stores vector + payload
|
|
88
|
+
AI tool calls recall → MCP server embeds query → cosine similarity search → re-ranked results
|
|
89
|
+
Neural Interface → reads same SQLite DB → renders 3D graph, REST API, WebSocket
|
|
90
|
+
Hooks → fire on Claude Code events → inject context, enforce memory obligations
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Shared State
|
|
94
|
+
- `data/memory.db` — single SQLite database (memories, session chunks, trash)
|
|
95
|
+
- `data/ui-state.json` — Neural Interface UI state (synced via `storage.js`)
|
|
96
|
+
- `data/pending-compact/` — per-session compaction flags
|
|
97
|
+
- `data/pending-remember/` — per-session edit tracking flags
|
|
98
|
+
- `mcp-server/data/custom-categories-{connId}.json` — per-connection category definitions
|
|
99
|
+
|
|
100
|
+
## Key Patterns
|
|
101
|
+
|
|
102
|
+
- **Category descriptions are routing instructions**: Claude reads all category descriptions from the MCP tool schema to decide where to store memories. Descriptions must be prescriptive rules, not vague labels.
|
|
103
|
+
- **Dynamic schema refresh**: Category changes trigger `refreshCategorySchemas()` → `notifications/tools/list_changed` → Claude re-fetches tool list. No restart needed.
|
|
104
|
+
- **Per-tab session isolation**: Project/model/effort state stored per-tab in sidepanel (`ui-claude-panel.js`) and per-tab via `sessionStorage` in standalone (`claude-chat.js`). Global `ui-state.json` keys are "last used" defaults only.
|
|
105
|
+
- **Browser sessions**: Playwright with persistent Chromium profiles. Multi-session isolation via dedicated browser session per automation. Leonardo tools are 100% browser-based (no API key).
|
|
106
|
+
- **Compaction chain**: PreCompact → flag → SessionStart injects → Claude remembers(conversations) → PostToolUse clears → Stop allows.
|
|
107
|
+
- **MCP calls must be sequential**: Parallel MCP tool calls can cascade failures. Always call memory tools one at a time.
|
|
108
|
+
|
|
109
|
+
## Environment
|
|
110
|
+
|
|
111
|
+
- Node.js 22.5+ required (for built-in `node:sqlite`)
|
|
112
|
+
- `.env` at project root — `SQLITE_DB_PATH`, `NEURAL_PORT` (default 3344), `DISCORD_BOT_TOKEN`, `DISCORD_GUILD_ID`
|
|
113
|
+
- CLAUDE.md is gitignored (user-local)
|
|
114
|
+
|
|
115
|
+
## Persistent Memory
|
|
116
|
+
|
|
117
|
+
You have persistent vector memory via the `SynaBun` MCP server (72 tools).
|
|
118
|
+
Core memory tools: remember, recall, forget, restore, reflect, memories, sync, category.
|
|
119
|
+
|
|
120
|
+
### Auto-Recall
|
|
121
|
+
- Session start: recall current project context
|
|
122
|
+
- Topic mentioned: recall what you know
|
|
123
|
+
- Before architecture decisions: recall past decisions
|
|
124
|
+
- Debugging: recall similar bugs
|
|
125
|
+
|
|
126
|
+
### Auto-Remember (MANDATORY)
|
|
127
|
+
After ANY task (bug fix, feature, refactor, config change, investigation, architecture decision), MUST `remember` BEFORE responding.
|
|
128
|
+
|
|
129
|
+
Steps:
|
|
130
|
+
1. `remember` — what + why + how, appropriate category, set project, related_files, importance (5=routine, 6-7=significant, 8+=critical), and 3-5 tags
|
|
131
|
+
|
|
132
|
+
`remember` returns the full UUID and accepts all fields (tags, importance) directly. No need to recall+reflect afterward.
|
|
133
|
+
|
|
134
|
+
**NOT triggered by:** Simple Q&A, file reads with no findings, trivial typos.
|
|
135
|
+
|
|
136
|
+
**Importance scale:** 1-2=trivial, 3-4=low, 5=normal, 6-7=significant, 8-9=critical, 10=foundational. User says "remember this" → 8+. Architecture decisions → 8+.
|
|
137
|
+
|
|
138
|
+
### Response Ordering
|
|
139
|
+
When finishing a task: call `remember` (and any other memory tools) **FIRST**, then write your completion summary **LAST**. Never summary-then-tools — the stop hook will block you.
|
|
140
|
+
|
|
141
|
+
### Tool Quirks
|
|
142
|
+
- `remember` returns the full UUID and accepts tags + importance directly.
|
|
143
|
+
- `reflect` requires FULL UUID — use the one from `remember`, or `recall` to find existing memories.
|
|
144
|
+
- Sequential MCP calls only — never parallel.
|
|
145
|
+
|
|
146
|
+
### Plan Mode
|
|
147
|
+
When in plan mode and you need clarification:
|
|
148
|
+
- ALWAYS use `AskUserQuestion` to present options — never plain text questions
|
|
149
|
+
- Use `ExitPlanMode` for final plan approval
|
|
150
|
+
- Use `multiSelect: true` in AskUserQuestion when options aren't mutually exclusive
|
|
@@ -309,7 +309,7 @@ async function main() {
|
|
|
309
309
|
if (toolName === 'EnterPlanMode') {
|
|
310
310
|
debugLog('EnterPlanMode fired — injecting AskUserQuestion reminder');
|
|
311
311
|
process.stdout.write(JSON.stringify({
|
|
312
|
-
additionalContext: `SynaBun: You are now in plan mode.
|
|
312
|
+
additionalContext: `SynaBun: You are now in plan mode.\n\n**CRITICAL CONSTRAINT**: Do NOT make code changes (Edit, Write, NotebookEdit). Plan mode is RESEARCH AND PLANNING ONLY. Investigate, read files, search code — then present your plan. Do NOT implement anything until the user explicitly approves the plan and you exit plan mode.\n\nWhen you have questions or need clarification, you MUST use the \`AskUserQuestion\` tool — do NOT write questions as plain text. Load it via \`ToolSearch\` if its schema is not yet available. Use \`ExitPlanMode\` for final plan approval.\n\n**Plan file location**: Write plan files to \`${PLANS_DIR}/\` — NOT \`~/.claude/plans/\`. This avoids sensitive-file permission prompts.`,
|
|
313
313
|
}));
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
@@ -464,6 +464,8 @@ async function main() {
|
|
|
464
464
|
``,
|
|
465
465
|
`If an autonomous loop was active before compaction, the Stop hook will resume it automatically. Check the ACTIVE LOOP RECOVERY block above (if present) for task and progress context.`,
|
|
466
466
|
``,
|
|
467
|
+
`**Plan files**: Write plan files to the project's \`data/plans/\` directory — NOT \`~/.claude/plans/\`. This avoids sensitive-file permission prompts that don't persist across compactions.`,
|
|
468
|
+
``,
|
|
467
469
|
`---`,
|
|
468
470
|
``,
|
|
469
471
|
);
|
|
@@ -8925,10 +8925,10 @@
|
|
|
8925
8925
|
.settings-panel {
|
|
8926
8926
|
position: fixed;
|
|
8927
8927
|
z-index: 300000;
|
|
8928
|
-
width:
|
|
8928
|
+
width: 900px;
|
|
8929
8929
|
max-width: 95vw;
|
|
8930
8930
|
min-width: 480px;
|
|
8931
|
-
height:
|
|
8931
|
+
height: 650px;
|
|
8932
8932
|
max-height: 88vh;
|
|
8933
8933
|
min-height: 320px;
|
|
8934
8934
|
display: flex;
|
|
@@ -2678,8 +2678,8 @@ export async function openSettingsModal() {
|
|
|
2678
2678
|
overlay.id = 'settings-panel';
|
|
2679
2679
|
|
|
2680
2680
|
// Always open centered at default size
|
|
2681
|
-
overlay.style.left = Math.max(20, (window.innerWidth -
|
|
2682
|
-
overlay.style.top = Math.max(48, (window.innerHeight -
|
|
2681
|
+
overlay.style.left = Math.max(20, (window.innerWidth - 900) / 2) + 'px';
|
|
2682
|
+
overlay.style.top = Math.max(48, (window.innerHeight - 650) / 2) + 'px';
|
|
2683
2683
|
|
|
2684
2684
|
// ── Build variant tab bodies ──
|
|
2685
2685
|
let variantTabBodies = '';
|
|
@@ -6161,9 +6161,22 @@ app.get('/api/system/backup', async (req, res) => {
|
|
|
6161
6161
|
for (const f of ['ui-state.json', 'greeting-config.json', 'hook-features.json',
|
|
6162
6162
|
'claude-code-projects.json', 'mcp-api-key.json', 'keybinds.json', 'cli-config.json',
|
|
6163
6163
|
'invite-key.json', 'invite-proxy.json', 'invite-permissions.json',
|
|
6164
|
-
'loop-templates.json', 'loop-schedules.json', 'auto-backup-config.json'
|
|
6164
|
+
'loop-templates.json', 'loop-schedules.json', 'auto-backup-config.json',
|
|
6165
|
+
'cost-tracking.json', 'skin-config.json', 'image-favorites.json',
|
|
6166
|
+
'browser-config.json', 'browser-storage.json', 'last-session.json',
|
|
6167
|
+
'invite-sessions.json']) {
|
|
6165
6168
|
addFile(`data/${f}`, resolve(dataDir, f));
|
|
6166
6169
|
}
|
|
6170
|
+
|
|
6171
|
+
// Session history caches (dynamically named per project cwd)
|
|
6172
|
+
if (existsSync(dataDir)) {
|
|
6173
|
+
for (const f of readdirSync(dataDir)) {
|
|
6174
|
+
if (f.startsWith('sessions-cache-') && f.endsWith('.json')) {
|
|
6175
|
+
addFile(`data/${f}`, resolve(dataDir, f));
|
|
6176
|
+
}
|
|
6177
|
+
}
|
|
6178
|
+
}
|
|
6179
|
+
|
|
6167
6180
|
addJsonDir('data/pending-remember', resolve(dataDir, 'pending-remember'));
|
|
6168
6181
|
addJsonDir('data/pending-compact', resolve(dataDir, 'pending-compact'));
|
|
6169
6182
|
addJsonDir('data/loop', resolve(dataDir, 'loop'));
|
|
@@ -6172,6 +6185,9 @@ app.get('/api/system/backup', async (req, res) => {
|
|
|
6172
6185
|
addFile('data/custom-icons.json', resolve(dataDir, 'custom-icons.json'));
|
|
6173
6186
|
addDirRecursive('data/custom-icons', resolve(dataDir, 'custom-icons'));
|
|
6174
6187
|
|
|
6188
|
+
// Screenshots, whiteboard captures, staged images
|
|
6189
|
+
addDirRecursive('data/images', resolve(dataDir, 'images'));
|
|
6190
|
+
|
|
6175
6191
|
// 3. mcp-server/data/
|
|
6176
6192
|
if (existsSync(CATEGORIES_DATA_DIR)) {
|
|
6177
6193
|
for (const f of readdirSync(CATEGORIES_DATA_DIR)) {
|
|
@@ -6209,7 +6225,19 @@ app.get('/api/system/backup', async (req, res) => {
|
|
|
6209
6225
|
}
|
|
6210
6226
|
}
|
|
6211
6227
|
|
|
6212
|
-
// 7.
|
|
6228
|
+
// 7. Installed skins (user-customizable themes)
|
|
6229
|
+
const skinsDir = join(__dirname, 'skins');
|
|
6230
|
+
if (existsSync(skinsDir)) {
|
|
6231
|
+
addDirRecursive('skins', skinsDir);
|
|
6232
|
+
}
|
|
6233
|
+
|
|
6234
|
+
// 8. Global Claude Code settings (hooks, permissions)
|
|
6235
|
+
const home = process.env.USERPROFILE || process.env.HOME || '';
|
|
6236
|
+
if (home) {
|
|
6237
|
+
addFile('claude-settings.json', join(home, '.claude', 'settings.json'));
|
|
6238
|
+
}
|
|
6239
|
+
|
|
6240
|
+
// 9. SQLite database file
|
|
6213
6241
|
const dbPath = getDbPath();
|
|
6214
6242
|
if (existsSync(dbPath)) {
|
|
6215
6243
|
try {
|
|
@@ -6346,6 +6374,25 @@ app.post('/api/system/restore',
|
|
|
6346
6374
|
writeFileSync(target, entry.getData());
|
|
6347
6375
|
results.files.push(rel);
|
|
6348
6376
|
}
|
|
6377
|
+
|
|
6378
|
+
// Installed skins → neural-interface/skins/
|
|
6379
|
+
if (rel.startsWith('skins/') && rel !== 'skins/') {
|
|
6380
|
+
const target = join(__dirname, rel);
|
|
6381
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
6382
|
+
writeFileSync(target, entry.getData());
|
|
6383
|
+
results.files.push(rel);
|
|
6384
|
+
}
|
|
6385
|
+
|
|
6386
|
+
// Global Claude Code settings → ~/.claude/settings.json
|
|
6387
|
+
if (rel === 'claude-settings.json') {
|
|
6388
|
+
const home = process.env.USERPROFILE || process.env.HOME || '';
|
|
6389
|
+
if (home) {
|
|
6390
|
+
const target = join(home, '.claude', 'settings.json');
|
|
6391
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
6392
|
+
writeFileSync(target, entry.getData());
|
|
6393
|
+
results.files.push(rel);
|
|
6394
|
+
}
|
|
6395
|
+
}
|
|
6349
6396
|
}
|
|
6350
6397
|
}
|
|
6351
6398
|
|
|
@@ -6480,15 +6527,31 @@ function buildBackupZipToFile(destPath) {
|
|
|
6480
6527
|
for (const f of ['ui-state.json', 'greeting-config.json', 'hook-features.json',
|
|
6481
6528
|
'claude-code-projects.json', 'mcp-api-key.json', 'keybinds.json', 'cli-config.json',
|
|
6482
6529
|
'invite-key.json', 'invite-proxy.json', 'invite-permissions.json',
|
|
6483
|
-
'loop-templates.json', 'loop-schedules.json'
|
|
6530
|
+
'loop-templates.json', 'loop-schedules.json', 'auto-backup-config.json',
|
|
6531
|
+
'cost-tracking.json', 'skin-config.json', 'image-favorites.json',
|
|
6532
|
+
'browser-config.json', 'browser-storage.json', 'last-session.json',
|
|
6533
|
+
'invite-sessions.json']) {
|
|
6484
6534
|
addFile(`data/${f}`, resolve(dataDir, f));
|
|
6485
6535
|
}
|
|
6536
|
+
|
|
6537
|
+
// Session history caches (dynamically named per project cwd)
|
|
6538
|
+
if (existsSync(dataDir)) {
|
|
6539
|
+
for (const f of readdirSync(dataDir)) {
|
|
6540
|
+
if (f.startsWith('sessions-cache-') && f.endsWith('.json')) {
|
|
6541
|
+
addFile(`data/${f}`, resolve(dataDir, f));
|
|
6542
|
+
}
|
|
6543
|
+
}
|
|
6544
|
+
}
|
|
6545
|
+
|
|
6486
6546
|
addJsonDir('data/pending-remember', resolve(dataDir, 'pending-remember'));
|
|
6487
6547
|
addJsonDir('data/pending-compact', resolve(dataDir, 'pending-compact'));
|
|
6488
6548
|
addJsonDir('data/loop', resolve(dataDir, 'loop'));
|
|
6489
6549
|
addFile('data/custom-icons.json', resolve(dataDir, 'custom-icons.json'));
|
|
6490
6550
|
addDirRecursive('data/custom-icons', resolve(dataDir, 'custom-icons'));
|
|
6491
6551
|
|
|
6552
|
+
// Screenshots, whiteboard captures, staged images
|
|
6553
|
+
addDirRecursive('data/images', resolve(dataDir, 'images'));
|
|
6554
|
+
|
|
6492
6555
|
if (existsSync(CATEGORIES_DATA_DIR)) {
|
|
6493
6556
|
for (const f of readdirSync(CATEGORIES_DATA_DIR)) {
|
|
6494
6557
|
if (f.endsWith('.json')) addFile(`mcp-data/${f}`, resolve(CATEGORIES_DATA_DIR, f));
|
|
@@ -6518,6 +6581,18 @@ function buildBackupZipToFile(destPath) {
|
|
|
6518
6581
|
}
|
|
6519
6582
|
}
|
|
6520
6583
|
|
|
6584
|
+
// Installed skins (user-customizable themes)
|
|
6585
|
+
const skinsDir = join(__dirname, 'skins');
|
|
6586
|
+
if (existsSync(skinsDir)) {
|
|
6587
|
+
addDirRecursive('skins', skinsDir);
|
|
6588
|
+
}
|
|
6589
|
+
|
|
6590
|
+
// Global Claude Code settings (hooks, permissions)
|
|
6591
|
+
const home = process.env.USERPROFILE || process.env.HOME || '';
|
|
6592
|
+
if (home) {
|
|
6593
|
+
addFile('claude-settings.json', join(home, '.claude', 'settings.json'));
|
|
6594
|
+
}
|
|
6595
|
+
|
|
6521
6596
|
const dbPath = getDbPath();
|
|
6522
6597
|
if (existsSync(dbPath)) {
|
|
6523
6598
|
try {
|
|
@@ -7548,9 +7623,9 @@ function ensureSynaBunPermissions(settings) {
|
|
|
7548
7623
|
if (!settings.permissions) settings.permissions = {};
|
|
7549
7624
|
if (!Array.isArray(settings.permissions.allow)) settings.permissions.allow = [];
|
|
7550
7625
|
|
|
7551
|
-
// Plan files
|
|
7552
|
-
// No
|
|
7553
|
-
// Clean up
|
|
7626
|
+
// Plan files write to data/plans/ (project-local) to avoid ~/.claude/ sensitive-file
|
|
7627
|
+
// permission prompts. No Write permission rule needed — data/ is within project root.
|
|
7628
|
+
// Clean up any stale Write(~/.claude/plans/*) rules from older installations.
|
|
7554
7629
|
const home = process.env.USERPROFILE || process.env.HOME || '';
|
|
7555
7630
|
const legacyPlanRule = `Write(${join(home, '.claude', 'plans', '*')})`;
|
|
7556
7631
|
const legacyIdx = settings.permissions.allow.indexOf(legacyPlanRule);
|
|
@@ -8625,9 +8700,8 @@ app.put('/api/greeting/config/:project', (req, res) => {
|
|
|
8625
8700
|
// Supports ?format=claude (default) | cursor | generic
|
|
8626
8701
|
app.get('/api/claude-code/ruleset', (req, res) => {
|
|
8627
8702
|
try {
|
|
8628
|
-
const claudeMdPath = resolve(PROJECT_ROOT, 'CLAUDE.md');
|
|
8629
8703
|
const templatePath = resolve(__dirname, 'templates', 'CLAUDE-template.md');
|
|
8630
|
-
const sourcePath =
|
|
8704
|
+
const sourcePath = templatePath;
|
|
8631
8705
|
if (!existsSync(sourcePath)) {
|
|
8632
8706
|
return res.status(404).json({ error: 'CLAUDE.md template not found' });
|
|
8633
8707
|
}
|
|
@@ -66,7 +66,9 @@ Never store directly in parent categories.
|
|
|
66
66
|
|
|
67
67
|
### Plan Mode (MANDATORY)
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
**CRITICAL**: Plan mode = research and planning ONLY. Do NOT use Edit, Write, or NotebookEdit. Read files, search code, investigate — then present the plan. Do NOT implement until the user approves and you exit plan mode.
|
|
70
|
+
|
|
71
|
+
When you need clarification or have questions:
|
|
70
72
|
- **ALWAYS** use `AskUserQuestion` to present options — never write questions as plain text
|
|
71
73
|
- Load the tool via `ToolSearch` first if its schema is not yet available
|
|
72
74
|
- Structure as distinct choices (2-4 options per question, max 4 questions)
|
|
@@ -161,6 +163,12 @@ Tools: remember, recall, forget, restore, reflect, memories, sync, category_*
|
|
|
161
163
|
|
|
162
164
|
---
|
|
163
165
|
|
|
166
|
+
## Plan Files
|
|
167
|
+
|
|
168
|
+
When in plan mode, write plan files to the project's `data/plans/` directory — **NOT** `~/.claude/plans/`. The `~/.claude/` directory is treated as sensitive by Claude Code, causing repeated permission prompts that don't persist across context compactions. The `data/plans/` directory is within the project root and requires no special permissions.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
164
172
|
## Hook System
|
|
165
173
|
|
|
166
174
|
7 Claude Code hooks in `hooks/claude-code/`, registered in `~/.claude/settings.json`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "synabun",
|
|
3
|
-
"version": "2026.3.
|
|
3
|
+
"version": "2026.3.310",
|
|
4
4
|
"description": "Persistent vector memory for AI assistants",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "The SynaBun Authors",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"files": [
|
|
17
17
|
"setup.js",
|
|
18
18
|
"postinstall.js",
|
|
19
|
+
"preuninstall.js",
|
|
19
20
|
"mcp-server/package.json",
|
|
20
21
|
"mcp-server/dist/",
|
|
21
22
|
"mcp-server/tsconfig.json",
|
|
@@ -40,7 +41,8 @@
|
|
|
40
41
|
],
|
|
41
42
|
"scripts": {
|
|
42
43
|
"start": "node setup.js",
|
|
43
|
-
"postinstall": "node postinstall.js"
|
|
44
|
+
"postinstall": "node postinstall.js",
|
|
45
|
+
"preuninstall": "node preuninstall.js"
|
|
44
46
|
},
|
|
45
47
|
"keywords": [
|
|
46
48
|
"claude",
|
package/preuninstall.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SynaBun — NPM preuninstall script
|
|
5
|
+
*
|
|
6
|
+
* Runs before `npm uninstall synabun`.
|
|
7
|
+
* Removes nested node_modules that cause EPERM/deep-path errors on Windows.
|
|
8
|
+
* Graceful — if cleanup fails, don't block the uninstall.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { rmSync } from 'node:fs';
|
|
12
|
+
import { resolve, dirname } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
|
|
17
|
+
const dirs = [
|
|
18
|
+
resolve(__dirname, 'neural-interface', 'node_modules'),
|
|
19
|
+
resolve(__dirname, 'mcp-server', 'node_modules'),
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
for (const dir of dirs) {
|
|
23
|
+
try {
|
|
24
|
+
rmSync(dir, { recursive: true, force: true });
|
|
25
|
+
} catch {
|
|
26
|
+
// Non-fatal — npm will handle remaining cleanup
|
|
27
|
+
}
|
|
28
|
+
}
|
package/setup.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { execSync, spawn, exec } from 'node:child_process';
|
|
16
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { existsSync, readFileSync, cpSync } from 'node:fs';
|
|
17
17
|
import { resolve, dirname } from 'node:path';
|
|
18
18
|
import { fileURLToPath } from 'node:url';
|
|
19
19
|
import { platform } from 'node:os';
|
|
@@ -37,6 +37,45 @@ function warn(msg) { console.log(` ${c.yellow}!${c.reset} ${msg}`); }
|
|
|
37
37
|
function fail(msg) { console.log(` ${c.red}\u2717${c.reset} ${msg}`); }
|
|
38
38
|
function info(msg) { console.log(` ${c.cyan}\u2192${c.reset} ${msg}`); }
|
|
39
39
|
|
|
40
|
+
// ── Global install detection & CWD scaffold ──
|
|
41
|
+
|
|
42
|
+
function isGlobalInstall() {
|
|
43
|
+
const normalized = __dirname.replace(/\\/g, '/');
|
|
44
|
+
return normalized.includes('/node_modules/synabun');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isAlreadyScaffolded(dir) {
|
|
48
|
+
return existsSync(resolve(dir, 'neural-interface', 'server.js'));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getLocalVersion(dir) {
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(readFileSync(resolve(dir, 'package.json'), 'utf-8')).version || '0.0.0';
|
|
54
|
+
} catch { return '0.0.0'; }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getGlobalVersion() {
|
|
58
|
+
return JSON.parse(readFileSync(resolve(__dirname, 'package.json'), 'utf-8')).version;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function scaffoldToCwd(targetDir) {
|
|
62
|
+
info('Scaffolding SynaBun into current directory...');
|
|
63
|
+
|
|
64
|
+
cpSync(__dirname, targetDir, {
|
|
65
|
+
recursive: true,
|
|
66
|
+
filter: (src) => {
|
|
67
|
+
const normalized = src.replace(/\\/g, '/');
|
|
68
|
+
// Skip node_modules (installed fresh by setup)
|
|
69
|
+
if (normalized.includes('/node_modules')) return false;
|
|
70
|
+
// Skip .env (generated fresh per installation)
|
|
71
|
+
if (normalized.endsWith('/.env')) return false;
|
|
72
|
+
return true;
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
ok(`Scaffolded to ${targetDir}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
40
79
|
// ── Phase 1: Prerequisite checks ──
|
|
41
80
|
|
|
42
81
|
function checkNodeVersion() {
|
|
@@ -209,6 +248,52 @@ function startServer() {
|
|
|
209
248
|
// ── Main ──
|
|
210
249
|
|
|
211
250
|
function main() {
|
|
251
|
+
// ── Global install: scaffold to CWD + delegate ──
|
|
252
|
+
if (isGlobalInstall()) {
|
|
253
|
+
const cwd = process.cwd();
|
|
254
|
+
const globalVer = getGlobalVersion();
|
|
255
|
+
|
|
256
|
+
console.log('');
|
|
257
|
+
console.log(` ${c.cyan}╔═══════════════════════════════════╗${c.reset}`);
|
|
258
|
+
console.log(` ${c.cyan}║${c.reset} ${c.cyan}║${c.reset}`);
|
|
259
|
+
console.log(` ${c.cyan}║${c.reset} ${c.bold}${c.cyan}[██] [██]${c.reset} ${c.bold}${c.cyan}SynaBun${c.reset} ${c.cyan}║${c.reset}`);
|
|
260
|
+
console.log(` ${c.cyan}║${c.reset} ${c.cyan}║${c.reset}`);
|
|
261
|
+
console.log(` ${c.cyan}║${c.reset} ${c.dim}Persistent Vector Memory${c.reset} ${c.cyan}║${c.reset}`);
|
|
262
|
+
console.log(` ${c.cyan}║${c.reset} ${c.cyan}║${c.reset}`);
|
|
263
|
+
console.log(` ${c.cyan}╚═══════════════════════════════════╝${c.reset}`);
|
|
264
|
+
console.log(` ${c.dim}synabun.ai${c.reset}`);
|
|
265
|
+
console.log('');
|
|
266
|
+
|
|
267
|
+
if (!isAlreadyScaffolded(cwd)) {
|
|
268
|
+
info(`Scaffolding SynaBun v${globalVer} into:`);
|
|
269
|
+
console.log(` ${c.cyan}${cwd}${c.reset}`);
|
|
270
|
+
console.log('');
|
|
271
|
+
scaffoldToCwd(cwd);
|
|
272
|
+
} else {
|
|
273
|
+
const localVer = getLocalVersion(cwd);
|
|
274
|
+
if (localVer !== globalVer) {
|
|
275
|
+
info(`Updating SynaBun: ${localVer} → ${globalVer}`);
|
|
276
|
+
scaffoldToCwd(cwd);
|
|
277
|
+
ok('Updated');
|
|
278
|
+
} else {
|
|
279
|
+
ok(`SynaBun v${globalVer} up to date`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
console.log('');
|
|
284
|
+
|
|
285
|
+
// Delegate to the local copy so __dirname resolves to CWD
|
|
286
|
+
const child = spawn('node', [resolve(cwd, 'setup.js')], {
|
|
287
|
+
cwd,
|
|
288
|
+
stdio: 'inherit',
|
|
289
|
+
});
|
|
290
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
291
|
+
process.on('SIGINT', () => { child.kill('SIGINT'); });
|
|
292
|
+
process.on('SIGTERM', () => { child.kill('SIGTERM'); });
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ── Local/dev install: normal flow ──
|
|
212
297
|
console.log('');
|
|
213
298
|
console.log(` ${c.cyan}╔═══════════════════════════════════╗${c.reset}`);
|
|
214
299
|
console.log(` ${c.cyan}║${c.reset} ${c.cyan}║${c.reset}`);
|