synabun 2026.3.308 → 2026.3.311
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/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 +88 -1
|
@@ -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.311",
|
|
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,47 @@ 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
|
+
// Use path relative to global package dir — the full path contains
|
|
68
|
+
// node_modules/synabun/ which would false-positive the filter
|
|
69
|
+
const rel = src.slice(__dirname.length).replace(/\\/g, '/');
|
|
70
|
+
// Skip nested node_modules (neural-interface/node_modules, etc.)
|
|
71
|
+
if (rel.includes('/node_modules')) return false;
|
|
72
|
+
// Skip .env (generated fresh per installation)
|
|
73
|
+
if (rel === '/.env') return false;
|
|
74
|
+
return true;
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
ok(`Scaffolded to ${targetDir}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
40
81
|
// ── Phase 1: Prerequisite checks ──
|
|
41
82
|
|
|
42
83
|
function checkNodeVersion() {
|
|
@@ -209,6 +250,52 @@ function startServer() {
|
|
|
209
250
|
// ── Main ──
|
|
210
251
|
|
|
211
252
|
function main() {
|
|
253
|
+
// ── Global install: scaffold to CWD + delegate ──
|
|
254
|
+
if (isGlobalInstall()) {
|
|
255
|
+
const cwd = process.cwd();
|
|
256
|
+
const globalVer = getGlobalVersion();
|
|
257
|
+
|
|
258
|
+
console.log('');
|
|
259
|
+
console.log(` ${c.cyan}╔═══════════════════════════════════╗${c.reset}`);
|
|
260
|
+
console.log(` ${c.cyan}║${c.reset} ${c.cyan}║${c.reset}`);
|
|
261
|
+
console.log(` ${c.cyan}║${c.reset} ${c.bold}${c.cyan}[██] [██]${c.reset} ${c.bold}${c.cyan}SynaBun${c.reset} ${c.cyan}║${c.reset}`);
|
|
262
|
+
console.log(` ${c.cyan}║${c.reset} ${c.cyan}║${c.reset}`);
|
|
263
|
+
console.log(` ${c.cyan}║${c.reset} ${c.dim}Persistent Vector Memory${c.reset} ${c.cyan}║${c.reset}`);
|
|
264
|
+
console.log(` ${c.cyan}║${c.reset} ${c.cyan}║${c.reset}`);
|
|
265
|
+
console.log(` ${c.cyan}╚═══════════════════════════════════╝${c.reset}`);
|
|
266
|
+
console.log(` ${c.dim}synabun.ai${c.reset}`);
|
|
267
|
+
console.log('');
|
|
268
|
+
|
|
269
|
+
if (!isAlreadyScaffolded(cwd)) {
|
|
270
|
+
info(`Scaffolding SynaBun v${globalVer} into:`);
|
|
271
|
+
console.log(` ${c.cyan}${cwd}${c.reset}`);
|
|
272
|
+
console.log('');
|
|
273
|
+
scaffoldToCwd(cwd);
|
|
274
|
+
} else {
|
|
275
|
+
const localVer = getLocalVersion(cwd);
|
|
276
|
+
if (localVer !== globalVer) {
|
|
277
|
+
info(`Updating SynaBun: ${localVer} → ${globalVer}`);
|
|
278
|
+
scaffoldToCwd(cwd);
|
|
279
|
+
ok('Updated');
|
|
280
|
+
} else {
|
|
281
|
+
ok(`SynaBun v${globalVer} up to date`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
console.log('');
|
|
286
|
+
|
|
287
|
+
// Delegate to the local copy so __dirname resolves to CWD
|
|
288
|
+
const child = spawn('node', [resolve(cwd, 'setup.js')], {
|
|
289
|
+
cwd,
|
|
290
|
+
stdio: 'inherit',
|
|
291
|
+
});
|
|
292
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
293
|
+
process.on('SIGINT', () => { child.kill('SIGINT'); });
|
|
294
|
+
process.on('SIGTERM', () => { child.kill('SIGTERM'); });
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ── Local/dev install: normal flow ──
|
|
212
299
|
console.log('');
|
|
213
300
|
console.log(` ${c.cyan}╔═══════════════════════════════════╗${c.reset}`);
|
|
214
301
|
console.log(` ${c.cyan}║${c.reset} ${c.cyan}║${c.reset}`);
|