synabun 1.3.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 +665 -0
- package/LICENSE +191 -0
- package/README.md +853 -0
- package/hooks/claude-code/post-plan.mjs +391 -0
- package/hooks/claude-code/post-remember.mjs +239 -0
- package/hooks/claude-code/pre-compact.mjs +241 -0
- package/hooks/claude-code/pre-websearch.mjs +90 -0
- package/hooks/claude-code/prompt-submit.mjs +861 -0
- package/hooks/claude-code/session-start.mjs +494 -0
- package/hooks/claude-code/shared.mjs +368 -0
- package/hooks/claude-code/stop.mjs +646 -0
- package/hooks/claude-code/test-user-learning.mjs +531 -0
- package/mcp-server/package.json +38 -0
- package/mcp-server/run.mjs +10 -0
- package/mcp-server/src/config.ts +70 -0
- package/mcp-server/src/http.ts +93 -0
- package/mcp-server/src/index.ts +203 -0
- package/mcp-server/src/preload-http.ts +25 -0
- package/mcp-server/src/preload.ts +29 -0
- package/mcp-server/src/scripts/migrate-qdrant-to-sqlite.ts +506 -0
- package/mcp-server/src/services/categories.ts +429 -0
- package/mcp-server/src/services/discord.ts +702 -0
- package/mcp-server/src/services/file-checksums.ts +33 -0
- package/mcp-server/src/services/local-embeddings.ts +77 -0
- package/mcp-server/src/services/neural-interface.ts +344 -0
- package/mcp-server/src/services/sqlite.ts +823 -0
- package/mcp-server/src/tools/browser-advanced.ts +123 -0
- package/mcp-server/src/tools/browser-interact.ts +255 -0
- package/mcp-server/src/tools/browser-navigate.ts +77 -0
- package/mcp-server/src/tools/browser-observe.ts +1330 -0
- package/mcp-server/src/tools/browser.ts +107 -0
- package/mcp-server/src/tools/card-tools.ts +193 -0
- package/mcp-server/src/tools/card.ts +22 -0
- package/mcp-server/src/tools/category.ts +379 -0
- package/mcp-server/src/tools/discord-channel.ts +274 -0
- package/mcp-server/src/tools/discord-guild.ts +188 -0
- package/mcp-server/src/tools/discord-member.ts +158 -0
- package/mcp-server/src/tools/discord-message.ts +231 -0
- package/mcp-server/src/tools/discord-onboarding.ts +228 -0
- package/mcp-server/src/tools/discord-role.ts +209 -0
- package/mcp-server/src/tools/discord-thread.ts +110 -0
- package/mcp-server/src/tools/discord-webhook.ts +176 -0
- package/mcp-server/src/tools/discord.ts +40 -0
- package/mcp-server/src/tools/forget.ts +38 -0
- package/mcp-server/src/tools/git-tools.ts +141 -0
- package/mcp-server/src/tools/git.ts +13 -0
- package/mcp-server/src/tools/image-tools.ts +98 -0
- package/mcp-server/src/tools/image.ts +17 -0
- package/mcp-server/src/tools/leonardo-browser-tools.ts +281 -0
- package/mcp-server/src/tools/leonardo.ts +28 -0
- package/mcp-server/src/tools/loop.ts +348 -0
- package/mcp-server/src/tools/memories.ts +118 -0
- package/mcp-server/src/tools/recall.ts +323 -0
- package/mcp-server/src/tools/reflect.ts +145 -0
- package/mcp-server/src/tools/remember.ts +115 -0
- package/mcp-server/src/tools/response.ts +26 -0
- package/mcp-server/src/tools/restore.ts +34 -0
- package/mcp-server/src/tools/sync.ts +114 -0
- package/mcp-server/src/tools/tictactoe-tools.ts +98 -0
- package/mcp-server/src/tools/tictactoe.ts +13 -0
- package/mcp-server/src/tools/utils.ts +25 -0
- package/mcp-server/src/tools/whiteboard-tools.ts +260 -0
- package/mcp-server/src/tools/whiteboard.ts +22 -0
- package/mcp-server/src/tui.ts +940 -0
- package/mcp-server/src/types.ts +85 -0
- package/mcp-server/tsconfig.json +19 -0
- package/neural-interface/package.json +27 -0
- package/neural-interface/public/apple-touch-icon.png +0 -0
- package/neural-interface/public/category-logos/claude.svg +1 -0
- package/neural-interface/public/category-logos/gemini-color.svg +1 -0
- package/neural-interface/public/category-logos/openai.svg +1 -0
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude-chat.css +878 -0
- package/neural-interface/public/claude-chat.html +92 -0
- package/neural-interface/public/claude-chat.js +1168 -0
- package/neural-interface/public/favicon-16x16.png +0 -0
- package/neural-interface/public/favicon-32x32.png +0 -0
- package/neural-interface/public/gallery.svg +6 -0
- package/neural-interface/public/icon-192.png +0 -0
- package/neural-interface/public/icon-512.png +0 -0
- package/neural-interface/public/index-legacy.html +14677 -0
- package/neural-interface/public/index.html +212 -0
- package/neural-interface/public/index2d-legacy.html +8193 -0
- package/neural-interface/public/index2d.html +127 -0
- package/neural-interface/public/invite.html +351 -0
- package/neural-interface/public/leonardoai.svg +1 -0
- package/neural-interface/public/logo-synabun.png +0 -0
- package/neural-interface/public/logoHD.png +0 -0
- package/neural-interface/public/manifest.json +14 -0
- package/neural-interface/public/offline.html +299 -0
- package/neural-interface/public/onboarding.html +2335 -0
- package/neural-interface/public/openclaw-logo-text.png +0 -0
- package/neural-interface/public/pin-svgrepo-com.svg +25 -0
- package/neural-interface/public/sessionicon.svg +8 -0
- package/neural-interface/public/shared/ansi-parser.js +305 -0
- package/neural-interface/public/shared/api.js +1043 -0
- package/neural-interface/public/shared/colors.js +153 -0
- package/neural-interface/public/shared/constants.js +141 -0
- package/neural-interface/public/shared/html-shell.js +777 -0
- package/neural-interface/public/shared/html-term-renderer.js +1022 -0
- package/neural-interface/public/shared/i18n.js +147 -0
- package/neural-interface/public/shared/registry.js +82 -0
- package/neural-interface/public/shared/state.js +86 -0
- package/neural-interface/public/shared/storage.js +150 -0
- package/neural-interface/public/shared/styles.css +21212 -0
- package/neural-interface/public/shared/ui-automation-studio.js +3573 -0
- package/neural-interface/public/shared/ui-bookmarks.js +231 -0
- package/neural-interface/public/shared/ui-claude-panel.js +6333 -0
- package/neural-interface/public/shared/ui-command-runner.js +534 -0
- package/neural-interface/public/shared/ui-cost-widget.js +602 -0
- package/neural-interface/public/shared/ui-detail.js +1454 -0
- package/neural-interface/public/shared/ui-explorer.js +1356 -0
- package/neural-interface/public/shared/ui-file-explorer.js +2750 -0
- package/neural-interface/public/shared/ui-games.js +411 -0
- package/neural-interface/public/shared/ui-help.js +107 -0
- package/neural-interface/public/shared/ui-image-gallery.js +461 -0
- package/neural-interface/public/shared/ui-invite.js +519 -0
- package/neural-interface/public/shared/ui-keybinds.js +452 -0
- package/neural-interface/public/shared/ui-layouts.js +297 -0
- package/neural-interface/public/shared/ui-link.js +731 -0
- package/neural-interface/public/shared/ui-loading.js +255 -0
- package/neural-interface/public/shared/ui-menubar.js +499 -0
- package/neural-interface/public/shared/ui-multiselect.js +93 -0
- package/neural-interface/public/shared/ui-navbar.js +222 -0
- package/neural-interface/public/shared/ui-notifications.js +287 -0
- package/neural-interface/public/shared/ui-panels.js +309 -0
- package/neural-interface/public/shared/ui-resume.js +653 -0
- package/neural-interface/public/shared/ui-search.js +145 -0
- package/neural-interface/public/shared/ui-sessions.js +531 -0
- package/neural-interface/public/shared/ui-settings.js +5987 -0
- package/neural-interface/public/shared/ui-sidebar.js +1905 -0
- package/neural-interface/public/shared/ui-skills.js +2805 -0
- package/neural-interface/public/shared/ui-stats.js +38 -0
- package/neural-interface/public/shared/ui-sync.js +201 -0
- package/neural-interface/public/shared/ui-terminal.js +6385 -0
- package/neural-interface/public/shared/ui-tooltip.js +123 -0
- package/neural-interface/public/shared/ui-trash.js +439 -0
- package/neural-interface/public/shared/ui-tutorial-draw.js +875 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +2276 -0
- package/neural-interface/public/shared/ui-tutorial.js +1117 -0
- package/neural-interface/public/shared/ui-update.js +244 -0
- package/neural-interface/public/shared/ui-whiteboard.js +3262 -0
- package/neural-interface/public/shared/ui-workspaces.js +722 -0
- package/neural-interface/public/shared/utils.js +360 -0
- package/neural-interface/public/shared/vterm-buffer.js +1102 -0
- package/neural-interface/public/sw.js +52 -0
- package/neural-interface/public/synabun.png +0 -0
- package/neural-interface/public/toolbar-album.svg +8 -0
- package/neural-interface/public/toolbar-question.svg +6 -0
- package/neural-interface/public/toolbar-sessions.svg +6 -0
- package/neural-interface/public/variant/2d/background.js +113 -0
- package/neural-interface/public/variant/2d/context-menu.js +133 -0
- package/neural-interface/public/variant/2d/gfx.js +45 -0
- package/neural-interface/public/variant/2d/graph.js +1635 -0
- package/neural-interface/public/variant/2d/hulls.js +207 -0
- package/neural-interface/public/variant/2d/lasso.js +135 -0
- package/neural-interface/public/variant/2d/layout.js +594 -0
- package/neural-interface/public/variant/2d/main.js +521 -0
- package/neural-interface/public/variant/2d/minimap.js +312 -0
- package/neural-interface/public/variant/2d/settings-gfx.js +98 -0
- package/neural-interface/public/variant/3d/background.js +884 -0
- package/neural-interface/public/variant/3d/camera.js +661 -0
- package/neural-interface/public/variant/3d/gfx.js +116 -0
- package/neural-interface/public/variant/3d/graph.js +2399 -0
- package/neural-interface/public/variant/3d/main.js +500 -0
- package/neural-interface/public/variant/3d/settings-gfx.js +228 -0
- package/neural-interface/scripts/rebuild-pty.js +100 -0
- package/neural-interface/server.js +15000 -0
- package/neural-interface/templates/CLAUDE-template.md +211 -0
- package/package.json +53 -0
- package/postinstall.js +135 -0
- package/setup.js +260 -0
- package/skills/leonardo/SKILL.md +159 -0
- package/skills/leonardo/icon.svg +1 -0
- package/skills/leonardo/modules/browser-guide.md +378 -0
- package/skills/leonardo/modules/image-prompter.md +277 -0
- package/skills/leonardo/modules/model-advisor.md +100 -0
- package/skills/leonardo/modules/prompt-library.md +110 -0
- package/skills/leonardo/modules/style-guide.md +186 -0
- package/skills/leonardo/modules/video-prompter.md +311 -0
- package/skills/synabun/SKILL.md +185 -0
- package/skills/synabun/icon.png +0 -0
- package/skills/synabun/modules/audit.md +374 -0
- package/skills/synabun/modules/changelog.md +194 -0
- package/skills/synabun/modules/idea.md +144 -0
- package/skills/synabun/modules/memorize.md +188 -0
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SynaBun SessionStart Hook for Claude Code
|
|
5
|
+
*
|
|
6
|
+
* Injects a lean session-start context: greeting, single recall directive,
|
|
7
|
+
* and (when applicable) compaction indexing instructions.
|
|
8
|
+
*
|
|
9
|
+
* Heavy reference data (category tree, user-learning rules) is deferred
|
|
10
|
+
* to prompt-submit and post-remember hooks to keep boot fast and clean.
|
|
11
|
+
*
|
|
12
|
+
* Enable in any project's .claude/settings.json:
|
|
13
|
+
* {
|
|
14
|
+
* "hooks": {
|
|
15
|
+
* "SessionStart": [{
|
|
16
|
+
* "matcher": "",
|
|
17
|
+
* "hooks": [{
|
|
18
|
+
* "type": "command",
|
|
19
|
+
* "command": "node \"<path-to-synabun>/hooks/claude-code/session-start.mjs\"",
|
|
20
|
+
* "timeout": 5
|
|
21
|
+
* }]
|
|
22
|
+
* }]
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { readFileSync, existsSync, unlinkSync, readdirSync, statSync, appendFileSync } from 'node:fs';
|
|
28
|
+
import { execSync } from 'node:child_process';
|
|
29
|
+
import { dirname, join } from 'node:path';
|
|
30
|
+
import { fileURLToPath } from 'node:url';
|
|
31
|
+
import { getHookFeatures, detectProject, ensureProjectCategories, cleanupStaleLoops } from './shared.mjs';
|
|
32
|
+
|
|
33
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const PRECOMPACT_DIR = join(__dirname, '..', '..', 'data', 'precompact');
|
|
35
|
+
const DEBUG_LOG = join(__dirname, '..', '..', 'data', 'compact-debug.log');
|
|
36
|
+
const LOOP_DIR = join(__dirname, '..', '..', 'data', 'loop');
|
|
37
|
+
const GREETING_CONFIG_PATH = join(__dirname, '..', '..', 'data', 'greeting-config.json');
|
|
38
|
+
|
|
39
|
+
// --- Greeting helpers ---
|
|
40
|
+
|
|
41
|
+
function loadGreetingConfig() {
|
|
42
|
+
try {
|
|
43
|
+
if (!existsSync(GREETING_CONFIG_PATH)) return null;
|
|
44
|
+
return JSON.parse(readFileSync(GREETING_CONFIG_PATH, 'utf-8'));
|
|
45
|
+
} catch { return null; }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getProjectGreetingConfig(config, project) {
|
|
49
|
+
if (!config) return null;
|
|
50
|
+
if (config.projects && config.projects[project]) {
|
|
51
|
+
return { ...config.defaults, ...config.projects[project] };
|
|
52
|
+
}
|
|
53
|
+
if (config.global) {
|
|
54
|
+
return { ...config.defaults, ...config.global };
|
|
55
|
+
}
|
|
56
|
+
return config.defaults || null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getTimeGreeting() {
|
|
60
|
+
const hour = new Date().getHours();
|
|
61
|
+
if (hour >= 5 && hour < 12) return 'Good morning';
|
|
62
|
+
if (hour >= 12 && hour < 17) return 'Good afternoon';
|
|
63
|
+
return 'Good evening';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getGitBranch(cwd) {
|
|
67
|
+
try {
|
|
68
|
+
return execSync('git rev-parse --abbrev-ref HEAD', {
|
|
69
|
+
cwd,
|
|
70
|
+
encoding: 'utf-8',
|
|
71
|
+
timeout: 2000,
|
|
72
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
73
|
+
}).trim();
|
|
74
|
+
} catch {
|
|
75
|
+
return 'unknown';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getGitHead(cwd) {
|
|
80
|
+
try {
|
|
81
|
+
return execSync('git rev-parse HEAD', {
|
|
82
|
+
cwd,
|
|
83
|
+
encoding: 'utf-8',
|
|
84
|
+
timeout: 2000,
|
|
85
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
86
|
+
}).trim();
|
|
87
|
+
} catch {
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function resolveTemplate(template, vars) {
|
|
93
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
94
|
+
return vars[key] !== undefined ? vars[key] : match;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function formatReminders(reminders, prefix) {
|
|
99
|
+
if (!reminders || reminders.length === 0) return '';
|
|
100
|
+
const lines = reminders.map((r) => `- **${r.label}:** \`${r.command}\``);
|
|
101
|
+
return `${prefix}\n${lines.join('\n')}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// --- Read stdin (Claude Code passes session JSON) ---
|
|
105
|
+
|
|
106
|
+
function readStdin() {
|
|
107
|
+
return new Promise((resolve) => {
|
|
108
|
+
if (process.stdin.isTTY) return resolve('{}');
|
|
109
|
+
let data = '';
|
|
110
|
+
process.stdin.setEncoding('utf8');
|
|
111
|
+
process.stdin.on('data', (chunk) => { data += chunk; });
|
|
112
|
+
process.stdin.on('end', () => resolve(data));
|
|
113
|
+
setTimeout(() => resolve(data || '{}'), 2000);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// --- Read precompact cache ---
|
|
118
|
+
|
|
119
|
+
function readPrecompactCache(sessionId) {
|
|
120
|
+
if (!sessionId) return null;
|
|
121
|
+
const cachePath = join(PRECOMPACT_DIR, `${sessionId}.json`);
|
|
122
|
+
try {
|
|
123
|
+
if (!existsSync(cachePath)) return null;
|
|
124
|
+
const data = JSON.parse(readFileSync(cachePath, 'utf-8'));
|
|
125
|
+
// Clean up the cache file after reading
|
|
126
|
+
try { unlinkSync(cachePath); } catch { /* ok */ }
|
|
127
|
+
return data;
|
|
128
|
+
} catch { return null; }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// --- Main ---
|
|
132
|
+
|
|
133
|
+
async function main() {
|
|
134
|
+
let cwd = '';
|
|
135
|
+
let source = 'unknown';
|
|
136
|
+
let sessionId = '';
|
|
137
|
+
let transcriptPath = '';
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const raw = await readStdin();
|
|
141
|
+
const input = JSON.parse(raw);
|
|
142
|
+
cwd = input.cwd || '';
|
|
143
|
+
source = input.source || 'unknown';
|
|
144
|
+
sessionId = input.session_id || '';
|
|
145
|
+
transcriptPath = input.transcript_path || '';
|
|
146
|
+
} catch { /* proceed with defaults */ }
|
|
147
|
+
|
|
148
|
+
const project = detectProject(cwd);
|
|
149
|
+
const features = getHookFeatures();
|
|
150
|
+
const isCompactRestart = source === 'compact';
|
|
151
|
+
|
|
152
|
+
// Register session with Neural Interface session monitor (fire-and-forget)
|
|
153
|
+
if (sessionId) {
|
|
154
|
+
const niUrl = process.env.SYNABUN_NI_URL || 'http://localhost:3344';
|
|
155
|
+
try {
|
|
156
|
+
fetch(`${niUrl}/api/sessions/register`, {
|
|
157
|
+
method: 'POST',
|
|
158
|
+
headers: { 'Content-Type': 'application/json' },
|
|
159
|
+
body: JSON.stringify({ claudeSessionId: sessionId, cwd, profile: 'claude-code', source: 'hook', project, terminalType: 'external' }),
|
|
160
|
+
signal: AbortSignal.timeout(3000),
|
|
161
|
+
}).catch(() => { /* NI may not be running */ });
|
|
162
|
+
} catch { /* ok */ }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Ensure all registered projects have their default category trees
|
|
166
|
+
try { ensureProjectCategories(); } catch { /* non-critical */ }
|
|
167
|
+
|
|
168
|
+
// Debug logging
|
|
169
|
+
try {
|
|
170
|
+
const cacheExists = sessionId ? existsSync(join(PRECOMPACT_DIR, `${sessionId}.json`)) : false;
|
|
171
|
+
appendFileSync(DEBUG_LOG, `[${new Date().toISOString()}] SESSION-START source=${source} session_id=${sessionId} isCompactRestart=${isCompactRestart} cacheExists=${cacheExists} convMemory=${features.conversationMemory}\n`);
|
|
172
|
+
} catch { /* ok */ }
|
|
173
|
+
|
|
174
|
+
// --- Build context ---
|
|
175
|
+
|
|
176
|
+
const precompactData = (features.conversationMemory !== false && isCompactRestart)
|
|
177
|
+
? readPrecompactCache(sessionId)
|
|
178
|
+
: null;
|
|
179
|
+
|
|
180
|
+
const context = [];
|
|
181
|
+
|
|
182
|
+
// ============================================================
|
|
183
|
+
// BLOCK 0: GREETING (highest attention, unless compaction or loop)
|
|
184
|
+
// ============================================================
|
|
185
|
+
|
|
186
|
+
// Detect active loop — skip greeting + recall for autonomous loop sessions
|
|
187
|
+
// Check both pending files (first iteration) AND active state files (subsequent iterations after /clear).
|
|
188
|
+
// When SYNABUN_TERMINAL_SESSION is set (server-launched loop), only match loops
|
|
189
|
+
// owned by THIS terminal — prevents unrelated sessions from entering loop mode.
|
|
190
|
+
// First, deactivate stale loops (session died, terminal closed, time expired)
|
|
191
|
+
cleanupStaleLoops(LOOP_DIR);
|
|
192
|
+
const LOOP_STALE_MS = 10 * 60 * 1000;
|
|
193
|
+
const terminalSessionEnv = process.env.SYNABUN_TERMINAL_SESSION || '';
|
|
194
|
+
let isLoopSession = false;
|
|
195
|
+
try {
|
|
196
|
+
if (existsSync(LOOP_DIR)) {
|
|
197
|
+
const now = Date.now();
|
|
198
|
+
const loopFiles = readdirSync(LOOP_DIR).filter(f => f.endsWith('.json'));
|
|
199
|
+
for (const f of loopFiles) {
|
|
200
|
+
try {
|
|
201
|
+
const fullPath = join(LOOP_DIR, f);
|
|
202
|
+
const mtime = statSync(fullPath).mtimeMs;
|
|
203
|
+
if ((now - mtime) >= LOOP_STALE_MS) continue;
|
|
204
|
+
const data = JSON.parse(readFileSync(fullPath, 'utf-8'));
|
|
205
|
+
if (data.stopped === true || data.finishedAt) continue;
|
|
206
|
+
// Active loop detected (pending or already running)
|
|
207
|
+
if (data.active || data.pending) {
|
|
208
|
+
// Multi-loop isolation: only match OUR terminal's loop
|
|
209
|
+
// If the loop belongs to a specific terminal, only match if this session is that same terminal
|
|
210
|
+
if (data.terminalSessionId && data.terminalSessionId !== terminalSessionEnv) continue;
|
|
211
|
+
isLoopSession = true;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
} catch { /* skip */ }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} catch { /* ok */ }
|
|
218
|
+
|
|
219
|
+
if (isLoopSession) {
|
|
220
|
+
// Minimal context for loop sessions — prompt-submit hook handles the rest
|
|
221
|
+
context.push(
|
|
222
|
+
`## SynaBun Persistent Memory`,
|
|
223
|
+
``,
|
|
224
|
+
`SynaBun memory is active. CLAUDE.md contains the memory rules. Follow them throughout this session.`,
|
|
225
|
+
``,
|
|
226
|
+
`**This is an autonomous loop session.** Do NOT output any greeting. Do NOT call recall at startup. Wait for the loop task injection from the UserPromptSubmit hook, then execute immediately.`,
|
|
227
|
+
``,
|
|
228
|
+
`Current project: **${project}**`,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
process.stdout.write(JSON.stringify({
|
|
232
|
+
hookSpecificOutput: {
|
|
233
|
+
hookEventName: 'SessionStart',
|
|
234
|
+
additionalContext: context.join('\n'),
|
|
235
|
+
},
|
|
236
|
+
}));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const greetingEnabled = features.greeting === true;
|
|
241
|
+
|
|
242
|
+
if (greetingEnabled && !isCompactRestart) {
|
|
243
|
+
const greetingConfig = loadGreetingConfig();
|
|
244
|
+
const projectConfig = getProjectGreetingConfig(greetingConfig, project);
|
|
245
|
+
|
|
246
|
+
// Always produce a greeting — fall back to a sensible default if no config
|
|
247
|
+
const DEFAULT_TEMPLATE = '{time_greeting}! Working on **{project_label}** (`{branch}` branch). {date}.';
|
|
248
|
+
|
|
249
|
+
{
|
|
250
|
+
const branch = getGitBranch(cwd);
|
|
251
|
+
const vars = {
|
|
252
|
+
time_greeting: getTimeGreeting(),
|
|
253
|
+
project_name: project,
|
|
254
|
+
project_label: projectConfig?.label || project,
|
|
255
|
+
branch,
|
|
256
|
+
date: new Date().toLocaleDateString('en-US', {
|
|
257
|
+
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
|
|
258
|
+
}),
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const greetingText = resolveTemplate(
|
|
262
|
+
projectConfig?.greetingTemplate || greetingConfig?.defaults?.greetingTemplate || DEFAULT_TEMPLATE,
|
|
263
|
+
vars,
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
const showReminders = projectConfig?.showReminders ?? greetingConfig?.defaults?.showReminders ?? false;
|
|
267
|
+
const remindersText = showReminders
|
|
268
|
+
? formatReminders(
|
|
269
|
+
projectConfig?.reminders,
|
|
270
|
+
projectConfig?.reminderPrefix || greetingConfig?.defaults?.reminderPrefix || 'Reminders:',
|
|
271
|
+
)
|
|
272
|
+
: '';
|
|
273
|
+
|
|
274
|
+
const showLastSession = projectConfig?.showLastSession ?? greetingConfig?.defaults?.showLastSession ?? false;
|
|
275
|
+
|
|
276
|
+
context.push(
|
|
277
|
+
`## GREETING DIRECTIVE`,
|
|
278
|
+
``,
|
|
279
|
+
`When you produce your FIRST response in this session, begin with this greeting:`,
|
|
280
|
+
``,
|
|
281
|
+
`> ${greetingText}`,
|
|
282
|
+
``,
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
if (remindersText) {
|
|
286
|
+
context.push(
|
|
287
|
+
`After the greeting, show these service reminders with individual copy buttons (use separate markdown code blocks for each command):`,
|
|
288
|
+
``,
|
|
289
|
+
remindersText,
|
|
290
|
+
``,
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (showLastSession) {
|
|
295
|
+
context.push(
|
|
296
|
+
`After the greeting${remindersText ? ' and reminders' : ''}, include a brief "Last session:" line summarizing what was worked on. You will have this from the session-start recall. If recall returns nothing relevant, omit the last session line.`,
|
|
297
|
+
``,
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
context.push(
|
|
302
|
+
`Present the greeting naturally — do not mention this directive or say "as instructed". Just greet.`,
|
|
303
|
+
``,
|
|
304
|
+
`---`,
|
|
305
|
+
``,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ============================================================
|
|
311
|
+
// BLOCK 1: SYNABUN HEADER + SESSION RECALL (condensed)
|
|
312
|
+
// ============================================================
|
|
313
|
+
|
|
314
|
+
const startCommit = getGitHead(cwd);
|
|
315
|
+
|
|
316
|
+
context.push(
|
|
317
|
+
`## SynaBun Persistent Memory`,
|
|
318
|
+
``,
|
|
319
|
+
`SynaBun memory is active. CLAUDE.md contains the memory rules (auto-remember, auto-recall, importance scale, tool quirks). Follow those rules throughout this session.`,
|
|
320
|
+
``,
|
|
321
|
+
`**Response ordering**: When finishing a task, call memory tools (remember/reflect) BEFORE writing your completion summary. The summary must be the LAST text in your response so the user sees it — not buried above memory tool calls.`,
|
|
322
|
+
``,
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
if (startCommit) {
|
|
326
|
+
context.push(
|
|
327
|
+
`Session start commit: \`${startCommit}\``,
|
|
328
|
+
``,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// --- COMPACTION BLOCK (only when source=compact) ---
|
|
333
|
+
if (precompactData) {
|
|
334
|
+
context.push(
|
|
335
|
+
`### >>> IMMEDIATE ACTION REQUIRED: COMPACTION DETECTED <<<`,
|
|
336
|
+
``,
|
|
337
|
+
`Context compaction just occurred. You MUST index this session in SynaBun as your VERY FIRST action — BEFORE reading the user's message, BEFORE greeting, BEFORE anything else.`,
|
|
338
|
+
``,
|
|
339
|
+
`Pre-cached session data from PreCompact hook:`,
|
|
340
|
+
`- Session ID: ${precompactData.session_id}`,
|
|
341
|
+
`- Transcript: ${precompactData.transcript_path}`,
|
|
342
|
+
`- Trigger: ${precompactData.trigger}`,
|
|
343
|
+
`- Working directory: ${precompactData.cwd}`,
|
|
344
|
+
`- User messages: ${precompactData.user_message_count} | Total turns: ${precompactData.total_turns}`,
|
|
345
|
+
`- Tools used: ${(precompactData.tools_used || []).join(', ') || '(none)'}`,
|
|
346
|
+
`- Files modified: ${(precompactData.files_modified || []).join(', ') || '(none)'}`,
|
|
347
|
+
``,
|
|
348
|
+
`User message summaries:`,
|
|
349
|
+
);
|
|
350
|
+
for (const msg of (precompactData.user_messages || []).slice(0, 10)) {
|
|
351
|
+
context.push(` - "${msg}"`);
|
|
352
|
+
}
|
|
353
|
+
if ((precompactData.assistant_snippets || []).length > 0) {
|
|
354
|
+
context.push(``, `Assistant response snippets:`);
|
|
355
|
+
for (const snip of precompactData.assistant_snippets.slice(0, 3)) {
|
|
356
|
+
context.push(` - "${snip}"`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
context.push(
|
|
360
|
+
``,
|
|
361
|
+
`Execute these steps IN ORDER, right now:`,
|
|
362
|
+
`1. Call \`recall\` with query containing "${precompactData.session_id}" in category \`conversations\` — check if already stored`,
|
|
363
|
+
`2. If found → \`reflect\` to UPDATE it with latest summary`,
|
|
364
|
+
`3. If NOT found → \`remember\` with the template below, category \`conversations\`, importance 6-8, and descriptive tags`,
|
|
365
|
+
`4. THEN respond to the user`,
|
|
366
|
+
`5. Confirm: "Session indexed in SynaBun."`,
|
|
367
|
+
``,
|
|
368
|
+
`Session template:`,
|
|
369
|
+
'```',
|
|
370
|
+
`## Session: {short slug}`,
|
|
371
|
+
`**Date**: {YYYY-MM-DD}`,
|
|
372
|
+
`**Project**: ${project}`,
|
|
373
|
+
`**Branch**: {branch}`,
|
|
374
|
+
`**Session ID**: ${sessionId || '{from context}'}`,
|
|
375
|
+
`**File**: ${transcriptPath || '{transcript path}'}`,
|
|
376
|
+
``,
|
|
377
|
+
`### Summary`,
|
|
378
|
+
`{What was discussed and accomplished. Topics, decisions, files, bugs, features.}`,
|
|
379
|
+
``,
|
|
380
|
+
`### Key Files Modified`,
|
|
381
|
+
`{Files created or modified}`,
|
|
382
|
+
'```',
|
|
383
|
+
``,
|
|
384
|
+
`---`,
|
|
385
|
+
``,
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
// If a loop was active during compaction, inject recovery context
|
|
389
|
+
if (precompactData?.loop?.active) {
|
|
390
|
+
const lp = precompactData.loop;
|
|
391
|
+
context.push(
|
|
392
|
+
`### >>> ACTIVE LOOP RECOVERY <<<`,
|
|
393
|
+
``,
|
|
394
|
+
`An autonomous loop was running when compaction occurred. Resume it.`,
|
|
395
|
+
``,
|
|
396
|
+
`- Task: ${lp.task}`,
|
|
397
|
+
`- Progress: Iteration ${lp.currentIteration}/${lp.totalIterations}`,
|
|
398
|
+
`- Time cap: ${lp.maxMinutes} min (started: ${lp.startedAt})`,
|
|
399
|
+
lp.context ? `- Context: ${lp.context}` : '',
|
|
400
|
+
lp.usesBrowser ? `- Browser loop (browser session may still be active)` : '',
|
|
401
|
+
``,
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
if (lp.progressSummary) {
|
|
405
|
+
context.push(`Progress Summary: ${lp.progressSummary}`, ``);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (lp.journal && lp.journal.length > 0) {
|
|
409
|
+
context.push(`Recent Journal:`);
|
|
410
|
+
for (const entry of lp.journal) {
|
|
411
|
+
context.push(` - Iteration ${entry.iteration}: ${entry.summary}`);
|
|
412
|
+
}
|
|
413
|
+
context.push(``);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
context.push(
|
|
417
|
+
`RECOVERY STEPS:`,
|
|
418
|
+
`1. Call \`recall\` with query about this loop's task to retrieve any stored progress memories`,
|
|
419
|
+
`2. Call \`remember\` to store the compaction event (category: conversations)`,
|
|
420
|
+
`3. Resume the loop — the Stop hook will continue driving iterations`,
|
|
421
|
+
`4. Use the progress summary and journal above as your working context — do NOT start from scratch`,
|
|
422
|
+
``,
|
|
423
|
+
`---`,
|
|
424
|
+
``,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// --- SESSION BOOT SEQUENCE (fresh sessions only — skip on compaction) ---
|
|
430
|
+
if (!isCompactRestart) {
|
|
431
|
+
const bootSteps = [
|
|
432
|
+
`1. Call \`recall\` with query: "recent sessions, ongoing work, known issues, decisions", project: "${project}", **recency_boost: true** — this prioritizes what was worked on most recently.`,
|
|
433
|
+
];
|
|
434
|
+
|
|
435
|
+
if (features.userLearning !== false) {
|
|
436
|
+
bootSteps.push(
|
|
437
|
+
`2. Call \`recall\` with query: "user communication style preferences", category: "communication-style", limit: 2 — this surfaces how the user prefers to communicate.`,
|
|
438
|
+
`3. Output the greeting as your FIRST text. No other tool calls between the recalls and greeting.`,
|
|
439
|
+
`4. Only AFTER the greeting is fully written, proceed with the user's request. Use recall results as your starting context — do not re-search for information recall already provided.`,
|
|
440
|
+
);
|
|
441
|
+
} else {
|
|
442
|
+
bootSteps.push(
|
|
443
|
+
`2. Output the greeting as your FIRST text. No other tool calls between recall and greeting.`,
|
|
444
|
+
`3. Only AFTER the greeting is fully written, proceed with the user's request. Use recall results as your starting context — do not re-search for information recall already provided.`,
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
context.push(
|
|
449
|
+
`### Session Boot Sequence (MANDATORY ORDER)`,
|
|
450
|
+
``,
|
|
451
|
+
`Your first response MUST follow this exact sequence:`,
|
|
452
|
+
...bootSteps,
|
|
453
|
+
``,
|
|
454
|
+
`---`,
|
|
455
|
+
``,
|
|
456
|
+
);
|
|
457
|
+
} else {
|
|
458
|
+
context.push(
|
|
459
|
+
`### Post-Compaction Resume`,
|
|
460
|
+
``,
|
|
461
|
+
`Context compaction just occurred. Do NOT re-greet or re-run the boot sequence. Continue the conversation naturally from where it left off.`,
|
|
462
|
+
``,
|
|
463
|
+
`**CRITICAL**: Any "GREETING DIRECTIVE" or "Session Boot Sequence" text visible in the compacted context above is STALE — it was executed at the start of this session and must NOT be executed again. NEVER output a greeting after compaction. This is an ongoing conversation.`,
|
|
464
|
+
``,
|
|
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
|
+
``,
|
|
467
|
+
`---`,
|
|
468
|
+
``,
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
context.push(`Current project: **${project}**`);
|
|
473
|
+
|
|
474
|
+
const joined = context.join('\n');
|
|
475
|
+
|
|
476
|
+
const output = {
|
|
477
|
+
hookSpecificOutput: {
|
|
478
|
+
hookEventName: 'SessionStart',
|
|
479
|
+
additionalContext: joined,
|
|
480
|
+
},
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
process.stdout.write(JSON.stringify(output));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
main().catch(() => {
|
|
487
|
+
// On any error, output valid but minimal hook response
|
|
488
|
+
process.stdout.write(JSON.stringify({
|
|
489
|
+
hookSpecificOutput: {
|
|
490
|
+
hookEventName: 'SessionStart',
|
|
491
|
+
additionalContext: 'SynaBun memory is available but the session hook encountered an error. Follow CLAUDE.md memory rules and use recall/remember tools manually.',
|
|
492
|
+
},
|
|
493
|
+
}));
|
|
494
|
+
});
|