@brianluby/agent-brain 1.1.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.
@@ -0,0 +1,554 @@
1
+ #!/usr/bin/env node
2
+ import { randomBytes } from 'crypto';
3
+ import { existsSync, statSync, readFileSync, mkdirSync, writeFileSync, renameSync, rmSync } from 'fs';
4
+ import { basename, resolve, dirname, relative, isAbsolute, sep } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { tmpdir } from 'os';
7
+ import lockfile from 'proper-lockfile';
8
+
9
+ function generateId() {
10
+ return randomBytes(8).toString("hex");
11
+ }
12
+ async function readStdin() {
13
+ const chunks = [];
14
+ return new Promise((resolve5, reject) => {
15
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
16
+ process.stdin.on("end", () => resolve5(Buffer.concat(chunks).toString("utf8")));
17
+ process.stdin.on("error", reject);
18
+ });
19
+ }
20
+ function writeOutput(output) {
21
+ console.log(JSON.stringify(output));
22
+ process.exit(0);
23
+ }
24
+ function debug(message) {
25
+ if (process.env.MEMVID_MIND_DEBUG === "1") {
26
+ console.error(`[memvid-mind] ${message}`);
27
+ }
28
+ }
29
+
30
+ // src/platforms/registry.ts
31
+ var AdapterRegistry = class {
32
+ adapters = /* @__PURE__ */ new Map();
33
+ register(adapter) {
34
+ this.adapters.set(adapter.platform, adapter);
35
+ }
36
+ resolve(platform) {
37
+ return this.adapters.get(platform) || null;
38
+ }
39
+ listPlatforms() {
40
+ return [...this.adapters.keys()].sort();
41
+ }
42
+ };
43
+
44
+ // src/platforms/events.ts
45
+ function createEventId() {
46
+ return generateId();
47
+ }
48
+
49
+ // src/platforms/adapters/create-adapter.ts
50
+ var CONTRACT_VERSION = "1.0.0";
51
+ function createAdapter(platform) {
52
+ function projectContext(input) {
53
+ return {
54
+ platformProjectId: input.project_id,
55
+ canonicalPath: input.cwd,
56
+ cwd: input.cwd
57
+ };
58
+ }
59
+ return {
60
+ platform,
61
+ contractVersion: CONTRACT_VERSION,
62
+ normalizeSessionStart(input) {
63
+ return {
64
+ eventId: createEventId(),
65
+ eventType: "session_start",
66
+ platform,
67
+ contractVersion: input.contract_version?.trim() || CONTRACT_VERSION,
68
+ sessionId: input.session_id,
69
+ timestamp: Date.now(),
70
+ projectContext: projectContext(input),
71
+ payload: {
72
+ hookEventName: input.hook_event_name,
73
+ permissionMode: input.permission_mode,
74
+ transcriptPath: input.transcript_path
75
+ }
76
+ };
77
+ },
78
+ normalizeToolObservation(input) {
79
+ if (!input.tool_name) return null;
80
+ return {
81
+ eventId: createEventId(),
82
+ eventType: "tool_observation",
83
+ platform,
84
+ contractVersion: input.contract_version?.trim() || CONTRACT_VERSION,
85
+ sessionId: input.session_id,
86
+ timestamp: Date.now(),
87
+ projectContext: projectContext(input),
88
+ payload: {
89
+ toolName: input.tool_name,
90
+ toolInput: input.tool_input,
91
+ toolResponse: input.tool_response
92
+ }
93
+ };
94
+ },
95
+ normalizeSessionStop(input) {
96
+ return {
97
+ eventId: createEventId(),
98
+ eventType: "session_stop",
99
+ platform,
100
+ contractVersion: input.contract_version?.trim() || CONTRACT_VERSION,
101
+ sessionId: input.session_id,
102
+ timestamp: Date.now(),
103
+ projectContext: projectContext(input),
104
+ payload: {
105
+ transcriptPath: input.transcript_path
106
+ }
107
+ };
108
+ }
109
+ };
110
+ }
111
+
112
+ // src/platforms/adapters/claude.ts
113
+ var claudeAdapter = createAdapter("claude");
114
+
115
+ // src/platforms/adapters/opencode.ts
116
+ var opencodeAdapter = createAdapter("opencode");
117
+
118
+ // src/platforms/contract.ts
119
+ var SUPPORTED_ADAPTER_CONTRACT_MAJOR = 1;
120
+ var SEMVER_PATTERN = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/;
121
+ function parseContractMajor(version) {
122
+ const match = SEMVER_PATTERN.exec(version.trim());
123
+ if (!match) {
124
+ return null;
125
+ }
126
+ return Number(match[1]);
127
+ }
128
+ function validateAdapterContractVersion(version, supportedMajor = SUPPORTED_ADAPTER_CONTRACT_MAJOR) {
129
+ const adapterMajor = parseContractMajor(version);
130
+ if (adapterMajor === null) {
131
+ return {
132
+ compatible: false,
133
+ supportedMajor,
134
+ adapterMajor: null,
135
+ reason: "invalid_contract_version"
136
+ };
137
+ }
138
+ if (adapterMajor !== supportedMajor) {
139
+ return {
140
+ compatible: false,
141
+ supportedMajor,
142
+ adapterMajor,
143
+ reason: "incompatible_contract_major"
144
+ };
145
+ }
146
+ return {
147
+ compatible: true,
148
+ supportedMajor,
149
+ adapterMajor
150
+ };
151
+ }
152
+
153
+ // src/platforms/diagnostics.ts
154
+ var DIAGNOSTIC_RETENTION_DAYS = 30;
155
+ var DAY_MS = 24 * 60 * 60 * 1e3;
156
+ var DIAGNOSTIC_FILE_NAME = "platform-diagnostics.json";
157
+ var TEST_DIAGNOSTIC_FILE_NAME = `memvid-platform-diagnostics-${process.pid}.json`;
158
+ function sanitizeFieldNames(fieldNames) {
159
+ if (!fieldNames || fieldNames.length === 0) {
160
+ return void 0;
161
+ }
162
+ return [...new Set(fieldNames)].slice(0, 20);
163
+ }
164
+ function resolveDiagnosticStorePath() {
165
+ const explicitPath = process.env.MEMVID_DIAGNOSTIC_PATH?.trim();
166
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
167
+ if (explicitPath) {
168
+ return resolve(projectDir, explicitPath);
169
+ }
170
+ if (process.env.VITEST) {
171
+ return resolve(tmpdir(), TEST_DIAGNOSTIC_FILE_NAME);
172
+ }
173
+ return resolve(projectDir, ".claude", DIAGNOSTIC_FILE_NAME);
174
+ }
175
+ function isDiagnosticRecord(value) {
176
+ if (!value || typeof value !== "object") {
177
+ return false;
178
+ }
179
+ const record = value;
180
+ return typeof record.diagnosticId === "string" && typeof record.timestamp === "number" && typeof record.platform === "string" && typeof record.errorType === "string" && (record.fieldNames === void 0 || Array.isArray(record.fieldNames) && record.fieldNames.every((name) => typeof name === "string")) && (record.severity === "warning" || record.severity === "error") && record.redacted === true && typeof record.retentionDays === "number" && typeof record.expiresAt === "number";
181
+ }
182
+ function pruneExpired(records, now = Date.now()) {
183
+ return records.filter((record) => record.expiresAt > now);
184
+ }
185
+ var DiagnosticPersistence = class {
186
+ filePath;
187
+ constructor(filePath) {
188
+ this.filePath = filePath;
189
+ }
190
+ append(record, now = Date.now()) {
191
+ this.withFileLock(() => {
192
+ const latest = this.loadFromDisk();
193
+ const next = pruneExpired([...latest, record], now);
194
+ this.persist(next);
195
+ });
196
+ }
197
+ list(now = Date.now()) {
198
+ return this.withFileLock(() => {
199
+ const latest = this.loadFromDisk();
200
+ const pruned = pruneExpired(latest, now);
201
+ if (pruned.length !== latest.length) {
202
+ this.persist(pruned);
203
+ }
204
+ return [...pruned];
205
+ });
206
+ }
207
+ loadFromDisk() {
208
+ if (!existsSync(this.filePath)) {
209
+ return [];
210
+ }
211
+ try {
212
+ const raw = readFileSync(this.filePath, "utf-8").trim();
213
+ if (!raw) {
214
+ return [];
215
+ }
216
+ const parsed = JSON.parse(raw);
217
+ if (!Array.isArray(parsed)) {
218
+ return [];
219
+ }
220
+ return parsed.filter(isDiagnosticRecord);
221
+ } catch {
222
+ return [];
223
+ }
224
+ }
225
+ withFileLock(fn) {
226
+ mkdirSync(dirname(this.filePath), { recursive: true });
227
+ const release = lockfile.lockSync(this.filePath, { realpath: false });
228
+ try {
229
+ return fn();
230
+ } finally {
231
+ release();
232
+ }
233
+ }
234
+ persist(records) {
235
+ mkdirSync(dirname(this.filePath), { recursive: true });
236
+ const tmpPath = `${this.filePath}.tmp-${process.pid}-${Date.now()}`;
237
+ try {
238
+ writeFileSync(tmpPath, `${JSON.stringify(records, null, 2)}
239
+ `, "utf-8");
240
+ try {
241
+ renameSync(tmpPath, this.filePath);
242
+ } catch {
243
+ rmSync(this.filePath, { force: true });
244
+ renameSync(tmpPath, this.filePath);
245
+ }
246
+ } finally {
247
+ rmSync(tmpPath, { force: true });
248
+ }
249
+ }
250
+ };
251
+ var persistence = null;
252
+ var persistenceFilePath = null;
253
+ var warnedPathChange = false;
254
+ function getDiagnosticPersistence() {
255
+ const resolvedPath = resolveDiagnosticStorePath();
256
+ if (!persistence) {
257
+ persistence = new DiagnosticPersistence(resolvedPath);
258
+ persistenceFilePath = resolvedPath;
259
+ warnedPathChange = false;
260
+ return persistence;
261
+ }
262
+ if (persistenceFilePath && persistenceFilePath !== resolvedPath && !warnedPathChange) {
263
+ warnedPathChange = true;
264
+ console.error(
265
+ `[memvid-mind] Diagnostic store path changed from "${persistenceFilePath}" to "${resolvedPath}" after initialization; continuing with the original path.`
266
+ );
267
+ }
268
+ return persistence;
269
+ }
270
+ function createRedactedDiagnostic(input) {
271
+ const timestamp = input.now ?? Date.now();
272
+ const diagnostic = {
273
+ diagnosticId: generateId(),
274
+ timestamp,
275
+ platform: input.platform,
276
+ errorType: input.errorType,
277
+ fieldNames: sanitizeFieldNames(input.fieldNames),
278
+ severity: input.severity ?? "warning",
279
+ redacted: true,
280
+ retentionDays: DIAGNOSTIC_RETENTION_DAYS,
281
+ expiresAt: timestamp + DIAGNOSTIC_RETENTION_DAYS * DAY_MS
282
+ };
283
+ try {
284
+ getDiagnosticPersistence().append(diagnostic);
285
+ } catch {
286
+ }
287
+ return diagnostic;
288
+ }
289
+ function resolveCanonicalProjectPath(context) {
290
+ if (context.canonicalPath) {
291
+ return resolve(context.canonicalPath);
292
+ }
293
+ if (context.cwd) {
294
+ return resolve(context.cwd);
295
+ }
296
+ return void 0;
297
+ }
298
+ function resolveProjectIdentityKey(context) {
299
+ if (context.platformProjectId && context.platformProjectId.trim().length > 0) {
300
+ return {
301
+ key: context.platformProjectId.trim(),
302
+ source: "platform_project_id",
303
+ canonicalPath: resolveCanonicalProjectPath(context)
304
+ };
305
+ }
306
+ const canonicalPath = resolveCanonicalProjectPath(context);
307
+ if (canonicalPath) {
308
+ return {
309
+ key: canonicalPath,
310
+ source: "canonical_path",
311
+ canonicalPath
312
+ };
313
+ }
314
+ return {
315
+ key: null,
316
+ source: "unresolved"
317
+ };
318
+ }
319
+ function defaultPlatformRelativePath(platform) {
320
+ const normalizedPlatform = platform.trim().toLowerCase();
321
+ const safePlatform = normalizedPlatform.replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "") || "unknown";
322
+ return `.agent-brain/mind-${safePlatform}.mv2`;
323
+ }
324
+ function resolveInsideProject(projectDir, candidatePath) {
325
+ if (isAbsolute(candidatePath)) {
326
+ return resolve(candidatePath);
327
+ }
328
+ const root = resolve(projectDir);
329
+ const resolved = resolve(root, candidatePath);
330
+ const rel = relative(root, resolved);
331
+ if (rel === ".." || rel.startsWith(`..${sep}`)) {
332
+ throw new Error("Resolved memory path must stay inside projectDir");
333
+ }
334
+ return resolved;
335
+ }
336
+ function resolveMemoryPathPolicy(input) {
337
+ const mode = input.platformOptIn ? "platform_opt_in" : "legacy_first";
338
+ const canonicalRelativePath = input.platformOptIn ? input.platformRelativePath || defaultPlatformRelativePath(input.platform) : input.defaultRelativePath;
339
+ const canonicalPath = resolveInsideProject(input.projectDir, canonicalRelativePath);
340
+ if (existsSync(canonicalPath)) {
341
+ return {
342
+ mode,
343
+ memoryPath: canonicalPath,
344
+ canonicalPath
345
+ };
346
+ }
347
+ const fallbackPaths = (input.legacyRelativePaths || []).map((relativePath) => resolveInsideProject(input.projectDir, relativePath));
348
+ for (const fallbackPath of fallbackPaths) {
349
+ if (existsSync(fallbackPath)) {
350
+ return {
351
+ mode,
352
+ memoryPath: fallbackPath,
353
+ canonicalPath,
354
+ migrationSuggestion: {
355
+ fromPath: fallbackPath,
356
+ toPath: canonicalPath
357
+ }
358
+ };
359
+ }
360
+ }
361
+ if (input.platformOptIn) {
362
+ return {
363
+ mode: "platform_opt_in",
364
+ memoryPath: canonicalPath,
365
+ canonicalPath
366
+ };
367
+ }
368
+ return {
369
+ mode: "legacy_first",
370
+ memoryPath: canonicalPath,
371
+ canonicalPath
372
+ };
373
+ }
374
+
375
+ // src/platforms/pipeline.ts
376
+ function skipWithDiagnostic(platform, errorType, fieldNames) {
377
+ return {
378
+ skipped: true,
379
+ reason: errorType,
380
+ diagnostic: createRedactedDiagnostic({
381
+ platform,
382
+ errorType,
383
+ fieldNames,
384
+ severity: "warning"
385
+ })
386
+ };
387
+ }
388
+ function processPlatformEvent(event) {
389
+ const contractValidation = validateAdapterContractVersion(
390
+ event.contractVersion,
391
+ SUPPORTED_ADAPTER_CONTRACT_MAJOR
392
+ );
393
+ if (!contractValidation.compatible) {
394
+ return skipWithDiagnostic(event.platform, contractValidation.reason ?? "incompatible_contract", ["contractVersion"]);
395
+ }
396
+ const identity = resolveProjectIdentityKey(event.projectContext);
397
+ if (!identity.key) {
398
+ return skipWithDiagnostic(event.platform, "missing_project_identity", [
399
+ "platformProjectId",
400
+ "canonicalPath",
401
+ "cwd"
402
+ ]);
403
+ }
404
+ return {
405
+ skipped: false,
406
+ projectIdentityKey: identity.key
407
+ };
408
+ }
409
+
410
+ // src/platforms/platform-detector.ts
411
+ function normalizePlatform(value) {
412
+ if (!value) return void 0;
413
+ const normalized = value.trim().toLowerCase();
414
+ return normalized.length > 0 ? normalized : void 0;
415
+ }
416
+ function detectPlatformFromEnv() {
417
+ const explicitFromEnv = normalizePlatform(process.env.MEMVID_PLATFORM);
418
+ if (explicitFromEnv) {
419
+ return explicitFromEnv;
420
+ }
421
+ if (process.env.OPENCODE === "1") {
422
+ return "opencode";
423
+ }
424
+ return "claude";
425
+ }
426
+ function detectPlatform(input) {
427
+ const explicitFromHook = normalizePlatform(input.platform);
428
+ if (explicitFromHook) {
429
+ return explicitFromHook;
430
+ }
431
+ return detectPlatformFromEnv();
432
+ }
433
+
434
+ // src/platforms/index.ts
435
+ var defaultRegistry = null;
436
+ function getDefaultAdapterRegistry() {
437
+ if (!defaultRegistry) {
438
+ const registry = new AdapterRegistry();
439
+ registry.register(claudeAdapter);
440
+ registry.register(opencodeAdapter);
441
+ defaultRegistry = Object.freeze({
442
+ resolve: (platform) => registry.resolve(platform),
443
+ listPlatforms: () => registry.listPlatforms()
444
+ });
445
+ }
446
+ return defaultRegistry;
447
+ }
448
+
449
+ // src/hooks/session-start.ts
450
+ function buildContextLines(projectName, memoryDisplayPath, memoryExists, fileSizeKB, platform, warning, migrationPrompt) {
451
+ const contextLines = [];
452
+ contextLines.push("<memvid-mind-context>");
453
+ const displayName = platform === "claude" ? "Claude" : platform.charAt(0).toUpperCase() + platform.slice(1);
454
+ contextLines.push(memoryExists ? `# \u{1F9E0} ${displayName} Mind Active` : `# \u{1F9E0} ${displayName} Mind Ready`);
455
+ contextLines.push("");
456
+ contextLines.push(`\u{1F4C1} Project: **${projectName}**`);
457
+ contextLines.push(`\u{1F916} Platform: **${platform}**`);
458
+ if (memoryExists) {
459
+ contextLines.push(`\u{1F4BE} Memory: \`${memoryDisplayPath}\` (${fileSizeKB} KB)`);
460
+ } else {
461
+ contextLines.push(`\u{1F4BE} Memory will be created at: \`${memoryDisplayPath}\``);
462
+ }
463
+ if (warning) {
464
+ contextLines.push("");
465
+ contextLines.push(`\u26A0\uFE0F ${warning}`);
466
+ }
467
+ if (migrationPrompt) {
468
+ contextLines.push("");
469
+ contextLines.push("\u2753 Legacy memory detected.");
470
+ contextLines.push(`Move it to the platform-agnostic path? Run: \`${migrationPrompt}\``);
471
+ }
472
+ contextLines.push("");
473
+ contextLines.push("**Commands:**");
474
+ contextLines.push("- `/mind:search <query>` - Search memories");
475
+ contextLines.push("- `/mind:ask <question>` - Ask your memory");
476
+ contextLines.push("- `/mind:recent` - View timeline");
477
+ contextLines.push("- `/mind:stats` - View statistics");
478
+ contextLines.push("");
479
+ contextLines.push("_Memories are captured automatically from your tool use._");
480
+ contextLines.push("</memvid-mind-context>");
481
+ return contextLines;
482
+ }
483
+ function buildSessionStartOutput(hookInput) {
484
+ const projectDir = hookInput.cwd || process.env.CLAUDE_PROJECT_DIR || process.cwd();
485
+ const projectName = basename(projectDir);
486
+ const platform = detectPlatform(hookInput);
487
+ const pathPolicy = resolveMemoryPathPolicy({
488
+ projectDir,
489
+ platform,
490
+ defaultRelativePath: ".agent-brain/mind.mv2",
491
+ legacyRelativePaths: [".claude/mind.mv2"],
492
+ platformRelativePath: process.env.MEMVID_PLATFORM_MEMORY_PATH,
493
+ platformOptIn: process.env.MEMVID_PLATFORM_PATH_OPT_IN === "1"
494
+ });
495
+ const memoryExists = existsSync(pathPolicy.memoryPath);
496
+ let fileSizeKB = 0;
497
+ if (memoryExists) {
498
+ try {
499
+ fileSizeKB = Math.round(statSync(pathPolicy.memoryPath).size / 1024);
500
+ } catch {
501
+ fileSizeKB = 0;
502
+ }
503
+ }
504
+ const registry = getDefaultAdapterRegistry();
505
+ const adapter = registry.resolve(platform);
506
+ let warning;
507
+ let migrationPrompt;
508
+ if (pathPolicy.migrationSuggestion) {
509
+ const fromAbs = resolve(pathPolicy.migrationSuggestion.fromPath);
510
+ const toAbs = resolve(pathPolicy.migrationSuggestion.toPath);
511
+ migrationPrompt = `mkdir -p "${dirname(toAbs)}" && mv "${fromAbs}" "${toAbs}"`;
512
+ }
513
+ if (!adapter) {
514
+ warning = "Unsupported platform detected: memory capture disabled for this session.";
515
+ } else {
516
+ const event = adapter.normalizeSessionStart(hookInput);
517
+ const result = processPlatformEvent(event);
518
+ if (result.skipped) {
519
+ warning = `Memory capture disabled for this session (${result.reason}).`;
520
+ }
521
+ }
522
+ const output = { continue: true };
523
+ output.hookSpecificOutput = {
524
+ hookEventName: "SessionStart",
525
+ additionalContext: buildContextLines(
526
+ projectName,
527
+ relative(projectDir, pathPolicy.memoryPath) || basename(pathPolicy.memoryPath),
528
+ memoryExists,
529
+ fileSizeKB,
530
+ platform,
531
+ warning,
532
+ migrationPrompt
533
+ ).join("\n")
534
+ };
535
+ return output;
536
+ }
537
+ async function runSessionStartHook() {
538
+ try {
539
+ const input = await readStdin();
540
+ const hookInput = JSON.parse(input);
541
+ debug(`Session starting: ${hookInput.session_id}`);
542
+ writeOutput(buildSessionStartOutput(hookInput));
543
+ } catch (error) {
544
+ debug(`Error: ${error}`);
545
+ writeOutput({ continue: true });
546
+ }
547
+ }
548
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
549
+ void runSessionStartHook();
550
+ }
551
+
552
+ export { buildSessionStartOutput, runSessionStartHook };
553
+ //# sourceMappingURL=session-start.js.map
554
+ //# sourceMappingURL=session-start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/helpers.ts","../../src/platforms/registry.ts","../../src/platforms/events.ts","../../src/platforms/adapters/create-adapter.ts","../../src/platforms/adapters/claude.ts","../../src/platforms/adapters/opencode.ts","../../src/platforms/contract.ts","../../src/platforms/diagnostics.ts","../../src/platforms/diagnostic-store.ts","../../src/platforms/identity.ts","../../src/platforms/path-policy.ts","../../src/platforms/pipeline.ts","../../src/platforms/platform-detector.ts","../../src/platforms/index.ts","../../src/hooks/session-start.ts"],"names":["resolve","pathRelative","existsSync","dirname"],"mappings":"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","file":"session-start.js","sourcesContent":["/**\n * Memvid Mind - Utility Helpers\n */\n\nimport { randomBytes } from \"node:crypto\";\n\n/**\n * Generate a unique ID\n */\nexport function generateId(): string {\n return randomBytes(8).toString(\"hex\");\n}\n\n/**\n * Estimate token count for text (rough approximation)\n * ~4 characters per token for English text\n */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\n/**\n * Truncate text to fit within token limit\n */\nexport function truncateToTokens(text: string, maxTokens: number): string {\n const maxChars = maxTokens * 4;\n if (text.length <= maxChars) return text;\n return text.slice(0, maxChars - 3) + \"...\";\n}\n\n/**\n * Format timestamp to human-readable string\n */\nexport function formatTimestamp(ts: number): string {\n const date = new Date(ts);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n\n if (diffMins < 1) return \"just now\";\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n\n return date.toLocaleDateString();\n}\n\n/**\n * Parse JSON safely\n */\nexport function safeJsonParse<T>(text: string, fallback: T): T {\n try {\n return JSON.parse(text) as T;\n } catch {\n return fallback;\n }\n}\n\n/**\n * Read all stdin as string\n */\nexport async function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n\n return new Promise((resolve, reject) => {\n process.stdin.on(\"data\", (chunk) => chunks.push(chunk));\n process.stdin.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf8\")));\n process.stdin.on(\"error\", reject);\n });\n}\n\n/**\n * Write JSON to stdout and exit immediately\n * (Prevents SDK background tasks from blocking process exit)\n */\nexport function writeOutput(output: unknown): never {\n console.log(JSON.stringify(output));\n process.exit(0);\n}\n\n/**\n * Log debug message to stderr\n */\nexport function debug(message: string): void {\n if (process.env.MEMVID_MIND_DEBUG === \"1\") {\n console.error(`[memvid-mind] ${message}`);\n }\n}\n\n/**\n * Extract key information from tool output\n */\nexport function extractKeyInfo(toolName: string, output: string): string {\n // Truncate very long outputs\n const maxLength = 2000;\n const truncated = output.length > maxLength\n ? output.slice(0, maxLength) + \"\\n... (truncated)\"\n : output;\n\n // Tool-specific extraction\n switch (toolName) {\n case \"Read\":\n // Extract file summary from read output\n return extractFileReadSummary(truncated);\n case \"Bash\":\n // Extract command summary\n return extractBashSummary(truncated);\n case \"Edit\":\n // Extract edit summary\n return extractEditSummary(truncated);\n case \"Grep\":\n case \"Glob\":\n // Extract search summary\n return extractSearchSummary(truncated);\n default:\n return truncated;\n }\n}\n\nfunction extractFileReadSummary(output: string): string {\n const lines = output.split(\"\\n\");\n if (lines.length <= 20) return output;\n return `${lines.slice(0, 10).join(\"\\n\")}\\n... (${lines.length} lines total)`;\n}\n\nfunction extractBashSummary(output: string): string {\n const lines = output.split(\"\\n\");\n if (lines.length <= 30) return output;\n return [\n ...lines.slice(0, 10),\n `... (${lines.length - 20} lines omitted)`,\n ...lines.slice(-10),\n ].join(\"\\n\");\n}\n\nfunction extractEditSummary(output: string): string {\n // Edits are usually compact, return as-is\n return output;\n}\n\nfunction extractSearchSummary(output: string): string {\n const lines = output.split(\"\\n\").filter(Boolean);\n if (lines.length <= 20) return output;\n return [\n ...lines.slice(0, 15),\n `... and ${lines.length - 15} more results`,\n ].join(\"\\n\");\n}\n\n/**\n * Classify observation type from tool and output\n */\nexport function classifyObservationType(\n toolName: string,\n output: string\n): \"discovery\" | \"decision\" | \"problem\" | \"solution\" | \"pattern\" | \"warning\" | \"success\" | \"refactor\" | \"bugfix\" | \"feature\" {\n const lowerOutput = output.toLowerCase();\n\n // Error detection\n if (\n lowerOutput.includes(\"error\") ||\n lowerOutput.includes(\"failed\") ||\n lowerOutput.includes(\"exception\")\n ) {\n return \"problem\";\n }\n\n // Success detection\n if (\n lowerOutput.includes(\"success\") ||\n lowerOutput.includes(\"passed\") ||\n lowerOutput.includes(\"completed\")\n ) {\n return \"success\";\n }\n\n // Warning detection\n if (lowerOutput.includes(\"warning\") || lowerOutput.includes(\"deprecated\")) {\n return \"warning\";\n }\n\n // Tool-based classification\n switch (toolName) {\n case \"Read\":\n case \"Glob\":\n case \"Grep\":\n return \"discovery\";\n case \"Edit\":\n if (lowerOutput.includes(\"fix\") || lowerOutput.includes(\"bug\")) {\n return \"bugfix\";\n }\n return \"refactor\";\n case \"Write\":\n return \"feature\";\n default:\n return \"discovery\";\n }\n}\n","import type { PlatformAdapter } from \"./contract.js\";\n\nexport interface ReadonlyAdapterRegistry {\n resolve(platform: string): PlatformAdapter | null;\n listPlatforms(): string[];\n}\n\nexport class AdapterRegistry implements ReadonlyAdapterRegistry {\n private adapters = new Map<string, PlatformAdapter>();\n\n register(adapter: PlatformAdapter): void {\n this.adapters.set(adapter.platform, adapter);\n }\n\n resolve(platform: string): PlatformAdapter | null {\n return this.adapters.get(platform) || null;\n }\n\n listPlatforms(): string[] {\n return [...this.adapters.keys()].sort();\n }\n}\n","import { generateId } from \"../utils/helpers.js\";\n\nexport type PlatformEventType = \"session_start\" | \"tool_observation\" | \"session_stop\";\n\nexport interface PlatformProjectContext {\n platformProjectId?: string;\n canonicalPath?: string;\n cwd?: string;\n}\n\nexport interface PlatformEventBase {\n eventId: string;\n eventType: PlatformEventType;\n platform: string;\n contractVersion: string;\n sessionId: string;\n timestamp: number;\n projectContext: PlatformProjectContext;\n}\n\nexport interface SessionStartPayload {\n hookEventName?: string;\n permissionMode?: string;\n transcriptPath?: string;\n}\n\nexport interface ToolObservationPayload {\n toolName?: string;\n toolInput?: Record<string, unknown>;\n toolResponse?: unknown;\n}\n\nexport interface SessionStopPayload {\n transcriptPath?: string;\n}\n\nexport interface SessionStartEvent extends PlatformEventBase {\n eventType: \"session_start\";\n payload: SessionStartPayload;\n}\n\nexport interface ToolObservationEvent extends PlatformEventBase {\n eventType: \"tool_observation\";\n payload: ToolObservationPayload;\n}\n\nexport interface SessionStopEvent extends PlatformEventBase {\n eventType: \"session_stop\";\n payload: SessionStopPayload;\n}\n\nexport type PlatformEvent = SessionStartEvent | ToolObservationEvent | SessionStopEvent;\n\nexport function createEventId(): string {\n return generateId();\n}\n","import {\n createEventId,\n type SessionStartEvent,\n type SessionStopEvent,\n type ToolObservationEvent,\n} from \"../events.js\";\nimport type { PlatformAdapter } from \"../contract.js\";\nimport type { HookInput } from \"../../types.js\";\n\nconst CONTRACT_VERSION = \"1.0.0\";\n\nexport function createAdapter(platform: string): PlatformAdapter {\n function projectContext(input: HookInput) {\n return {\n platformProjectId: input.project_id,\n canonicalPath: input.cwd,\n cwd: input.cwd,\n };\n }\n\n return {\n platform,\n contractVersion: CONTRACT_VERSION,\n\n normalizeSessionStart(input: HookInput): SessionStartEvent {\n return {\n eventId: createEventId(),\n eventType: \"session_start\",\n platform,\n contractVersion: input.contract_version?.trim() || CONTRACT_VERSION,\n sessionId: input.session_id,\n timestamp: Date.now(),\n projectContext: projectContext(input),\n payload: {\n hookEventName: input.hook_event_name,\n permissionMode: input.permission_mode,\n transcriptPath: input.transcript_path,\n },\n };\n },\n\n normalizeToolObservation(input: HookInput): ToolObservationEvent | null {\n if (!input.tool_name) return null;\n return {\n eventId: createEventId(),\n eventType: \"tool_observation\",\n platform,\n contractVersion: input.contract_version?.trim() || CONTRACT_VERSION,\n sessionId: input.session_id,\n timestamp: Date.now(),\n projectContext: projectContext(input),\n payload: {\n toolName: input.tool_name,\n toolInput: input.tool_input,\n toolResponse: input.tool_response,\n },\n };\n },\n\n normalizeSessionStop(input: HookInput): SessionStopEvent {\n return {\n eventId: createEventId(),\n eventType: \"session_stop\",\n platform,\n contractVersion: input.contract_version?.trim() || CONTRACT_VERSION,\n sessionId: input.session_id,\n timestamp: Date.now(),\n projectContext: projectContext(input),\n payload: {\n transcriptPath: input.transcript_path,\n },\n };\n },\n };\n}\n","import { createAdapter } from \"./create-adapter.js\";\n\nexport const claudeAdapter = createAdapter(\"claude\");\n","import { createAdapter } from \"./create-adapter.js\";\n\nexport const opencodeAdapter = createAdapter(\"opencode\");\n","import type { HookInput } from \"../types.js\";\nimport type {\n SessionStartEvent,\n SessionStopEvent,\n ToolObservationEvent,\n} from \"./events.js\";\n\nexport const SUPPORTED_ADAPTER_CONTRACT_MAJOR = 1;\n\nconst SEMVER_PATTERN = /^(\\d+)\\.(\\d+)\\.(\\d+)(?:[-+].*)?$/;\n\nexport interface ContractValidationResult {\n compatible: boolean;\n supportedMajor: number;\n adapterMajor: number | null;\n reason?: string;\n}\n\nexport function parseContractMajor(version: string): number | null {\n const match = SEMVER_PATTERN.exec(version.trim());\n if (!match) {\n return null;\n }\n return Number(match[1]);\n}\n\nexport function validateAdapterContractVersion(\n version: string,\n supportedMajor = SUPPORTED_ADAPTER_CONTRACT_MAJOR\n): ContractValidationResult {\n const adapterMajor = parseContractMajor(version);\n if (adapterMajor === null) {\n return {\n compatible: false,\n supportedMajor,\n adapterMajor: null,\n reason: \"invalid_contract_version\",\n };\n }\n\n if (adapterMajor !== supportedMajor) {\n return {\n compatible: false,\n supportedMajor,\n adapterMajor,\n reason: \"incompatible_contract_major\",\n };\n }\n\n return {\n compatible: true,\n supportedMajor,\n adapterMajor,\n };\n}\n\nexport interface PlatformAdapter {\n platform: string;\n contractVersion: string;\n normalizeSessionStart(input: HookInput): SessionStartEvent;\n normalizeToolObservation(input: HookInput): ToolObservationEvent | null;\n normalizeSessionStop(input: HookInput): SessionStopEvent;\n}\n","export const DIAGNOSTIC_RETENTION_DAYS = 30;\n\nexport type DiagnosticSeverity = \"warning\" | \"error\";\n\nexport interface AdapterDiagnostic {\n diagnosticId: string;\n timestamp: number;\n platform: string;\n errorType: string;\n fieldNames?: string[];\n severity: DiagnosticSeverity;\n redacted: true;\n retentionDays: number;\n expiresAt: number;\n}\n","import { tmpdir } from \"node:os\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n renameSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { generateId } from \"../utils/helpers.js\";\nimport {\n DIAGNOSTIC_RETENTION_DAYS,\n type AdapterDiagnostic,\n type DiagnosticSeverity,\n} from \"./diagnostics.js\";\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst DIAGNOSTIC_FILE_NAME = \"platform-diagnostics.json\";\nconst TEST_DIAGNOSTIC_FILE_NAME = `memvid-platform-diagnostics-${process.pid}.json`;\n\nexport interface CreateDiagnosticInput {\n platform: string;\n errorType: string;\n fieldNames?: string[];\n severity?: DiagnosticSeverity;\n now?: number;\n}\n\nfunction sanitizeFieldNames(fieldNames: string[] | undefined): string[] | undefined {\n if (!fieldNames || fieldNames.length === 0) {\n return undefined;\n }\n return [...new Set(fieldNames)].slice(0, 20);\n}\n\nfunction resolveDiagnosticStorePath(): string {\n const explicitPath = process.env.MEMVID_DIAGNOSTIC_PATH?.trim();\n const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();\n\n if (explicitPath) {\n return resolve(projectDir, explicitPath);\n }\n\n if (process.env.VITEST) {\n return resolve(tmpdir(), TEST_DIAGNOSTIC_FILE_NAME);\n }\n\n return resolve(projectDir, \".claude\", DIAGNOSTIC_FILE_NAME);\n}\n\nfunction isDiagnosticRecord(value: unknown): value is AdapterDiagnostic {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const record = value as Record<string, unknown>;\n return (\n typeof record.diagnosticId === \"string\" &&\n typeof record.timestamp === \"number\" &&\n typeof record.platform === \"string\" &&\n typeof record.errorType === \"string\" &&\n (record.fieldNames === undefined ||\n (Array.isArray(record.fieldNames)\n && record.fieldNames.every((name) => typeof name === \"string\"))) &&\n (record.severity === \"warning\" || record.severity === \"error\") &&\n record.redacted === true &&\n typeof record.retentionDays === \"number\" &&\n typeof record.expiresAt === \"number\"\n );\n}\n\nfunction pruneExpired(records: AdapterDiagnostic[], now = Date.now()): AdapterDiagnostic[] {\n return records.filter((record) => record.expiresAt > now);\n}\n\nclass DiagnosticPersistence {\n readonly filePath: string;\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n\n append(record: AdapterDiagnostic, now = Date.now()): void {\n this.withFileLock(() => {\n const latest = this.loadFromDisk();\n const next = pruneExpired([...latest, record], now);\n this.persist(next);\n });\n }\n\n list(now = Date.now()): AdapterDiagnostic[] {\n return this.withFileLock(() => {\n const latest = this.loadFromDisk();\n const pruned = pruneExpired(latest, now);\n if (pruned.length !== latest.length) {\n this.persist(pruned);\n }\n return [...pruned];\n });\n }\n\n private loadFromDisk(): AdapterDiagnostic[] {\n if (!existsSync(this.filePath)) {\n return [];\n }\n\n try {\n const raw = readFileSync(this.filePath, \"utf-8\").trim();\n if (!raw) {\n return [];\n }\n\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n return [];\n }\n\n return parsed.filter(isDiagnosticRecord);\n } catch {\n return [];\n }\n }\n\n private withFileLock<T>(fn: () => T): T {\n mkdirSync(dirname(this.filePath), { recursive: true });\n const release = lockfile.lockSync(this.filePath, { realpath: false });\n try {\n return fn();\n } finally {\n release();\n }\n }\n\n private persist(records: AdapterDiagnostic[]): void {\n mkdirSync(dirname(this.filePath), { recursive: true });\n const tmpPath = `${this.filePath}.tmp-${process.pid}-${Date.now()}`;\n try {\n writeFileSync(tmpPath, `${JSON.stringify(records, null, 2)}\\n`, \"utf-8\");\n try {\n renameSync(tmpPath, this.filePath);\n } catch {\n rmSync(this.filePath, { force: true });\n renameSync(tmpPath, this.filePath);\n }\n } finally {\n rmSync(tmpPath, { force: true });\n }\n }\n}\n\nlet persistence: DiagnosticPersistence | null = null;\nlet persistenceFilePath: string | null = null;\nlet warnedPathChange = false;\n\nfunction getDiagnosticPersistence(): DiagnosticPersistence {\n const resolvedPath = resolveDiagnosticStorePath();\n\n if (!persistence) {\n persistence = new DiagnosticPersistence(resolvedPath);\n persistenceFilePath = resolvedPath;\n warnedPathChange = false;\n return persistence;\n }\n\n if (persistenceFilePath && persistenceFilePath !== resolvedPath && !warnedPathChange) {\n warnedPathChange = true;\n console.error(\n `[memvid-mind] Diagnostic store path changed from \"${persistenceFilePath}\" to \"${resolvedPath}\" after initialization; continuing with the original path.`\n );\n }\n\n return persistence;\n}\n\nexport function resetDiagnosticPersistenceForTests(): void {\n persistence = null;\n persistenceFilePath = null;\n warnedPathChange = false;\n}\n\nexport function listPersistedDiagnostics(now = Date.now()): AdapterDiagnostic[] {\n return getDiagnosticPersistence().list(now);\n}\n\nexport function createRedactedDiagnostic(input: CreateDiagnosticInput): AdapterDiagnostic {\n const timestamp = input.now ?? Date.now();\n const diagnostic: AdapterDiagnostic = {\n diagnosticId: generateId(),\n timestamp,\n platform: input.platform,\n errorType: input.errorType,\n fieldNames: sanitizeFieldNames(input.fieldNames),\n severity: input.severity ?? \"warning\",\n redacted: true,\n retentionDays: DIAGNOSTIC_RETENTION_DAYS,\n expiresAt: timestamp + (DIAGNOSTIC_RETENTION_DAYS * DAY_MS),\n };\n\n try {\n getDiagnosticPersistence().append(diagnostic);\n } catch {\n // Fail-open: never block event processing on diagnostic persistence failures.\n }\n\n return diagnostic;\n}\n","import { resolve } from \"node:path\";\nimport type { PlatformProjectContext } from \"./events.js\";\n\nexport type ProjectIdentitySource =\n | \"platform_project_id\"\n | \"canonical_path\"\n | \"unresolved\";\n\nexport interface ProjectIdentityResolution {\n key: string | null;\n source: ProjectIdentitySource;\n canonicalPath?: string;\n}\n\nexport function resolveCanonicalProjectPath(\n context: PlatformProjectContext\n): string | undefined {\n if (context.canonicalPath) {\n return resolve(context.canonicalPath);\n }\n if (context.cwd) {\n return resolve(context.cwd);\n }\n return undefined;\n}\n\nexport function resolveProjectIdentityKey(\n context: PlatformProjectContext\n): ProjectIdentityResolution {\n if (context.platformProjectId && context.platformProjectId.trim().length > 0) {\n return {\n key: context.platformProjectId.trim(),\n source: \"platform_project_id\",\n canonicalPath: resolveCanonicalProjectPath(context),\n };\n }\n\n const canonicalPath = resolveCanonicalProjectPath(context);\n if (canonicalPath) {\n return {\n key: canonicalPath,\n source: \"canonical_path\",\n canonicalPath,\n };\n }\n\n return {\n key: null,\n source: \"unresolved\",\n };\n}\n","import { existsSync } from \"node:fs\";\nimport { isAbsolute, relative as pathRelative, resolve, sep } from \"node:path\";\n\nexport type MemoryPathMode = \"legacy_first\" | \"platform_opt_in\";\n\nexport interface MemoryPathPolicyInput {\n projectDir: string;\n platform: string;\n defaultRelativePath: string;\n platformRelativePath?: string;\n platformOptIn?: boolean;\n legacyRelativePaths?: string[];\n}\n\nexport interface MemoryMigrationSuggestion {\n fromPath: string;\n toPath: string;\n}\n\nexport interface MemoryPathPolicyResult {\n mode: MemoryPathMode;\n memoryPath: string;\n canonicalPath: string;\n migrationSuggestion?: MemoryMigrationSuggestion;\n}\n\nfunction defaultPlatformRelativePath(platform: string): string {\n const normalizedPlatform = platform.trim().toLowerCase();\n const safePlatform = normalizedPlatform\n .replace(/[^a-z0-9_-]/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"unknown\";\n return `.agent-brain/mind-${safePlatform}.mv2`;\n}\n\nfunction resolveInsideProject(projectDir: string, candidatePath: string): string {\n if (isAbsolute(candidatePath)) {\n return resolve(candidatePath);\n }\n const root = resolve(projectDir);\n const resolved = resolve(root, candidatePath);\n const rel = pathRelative(root, resolved);\n if (rel === \"..\" || rel.startsWith(`..${sep}`)) {\n throw new Error(\"Resolved memory path must stay inside projectDir\");\n }\n return resolved;\n}\n\nexport function resolveMemoryPathPolicy(input: MemoryPathPolicyInput): MemoryPathPolicyResult {\n const mode: MemoryPathMode = input.platformOptIn ? \"platform_opt_in\" : \"legacy_first\";\n const canonicalRelativePath = input.platformOptIn\n ? input.platformRelativePath || defaultPlatformRelativePath(input.platform)\n : input.defaultRelativePath;\n const canonicalPath = resolveInsideProject(input.projectDir, canonicalRelativePath);\n\n if (existsSync(canonicalPath)) {\n return {\n mode,\n memoryPath: canonicalPath,\n canonicalPath,\n };\n }\n\n const fallbackPaths = (input.legacyRelativePaths || [])\n .map((relativePath) => resolveInsideProject(input.projectDir, relativePath));\n\n for (const fallbackPath of fallbackPaths) {\n if (existsSync(fallbackPath)) {\n return {\n mode,\n memoryPath: fallbackPath,\n canonicalPath,\n migrationSuggestion: {\n fromPath: fallbackPath,\n toPath: canonicalPath,\n },\n };\n }\n }\n\n if (input.platformOptIn) {\n return {\n mode: \"platform_opt_in\",\n memoryPath: canonicalPath,\n canonicalPath,\n };\n }\n\n return {\n mode: \"legacy_first\",\n memoryPath: canonicalPath,\n canonicalPath,\n };\n}\n","import {\n SUPPORTED_ADAPTER_CONTRACT_MAJOR,\n validateAdapterContractVersion,\n} from \"./contract.js\";\nimport { createRedactedDiagnostic } from \"./diagnostic-store.js\";\nimport type { AdapterDiagnostic } from \"./diagnostics.js\";\nimport type { PlatformEvent } from \"./events.js\";\nimport { resolveProjectIdentityKey } from \"./identity.js\";\n\nexport interface ProcessPlatformEventResult {\n skipped: boolean;\n reason?: string;\n projectIdentityKey?: string;\n diagnostic?: AdapterDiagnostic;\n}\n\nfunction skipWithDiagnostic(\n platform: string,\n errorType: string,\n fieldNames?: string[]\n): ProcessPlatformEventResult {\n return {\n skipped: true,\n reason: errorType,\n diagnostic: createRedactedDiagnostic({\n platform,\n errorType,\n fieldNames,\n severity: \"warning\",\n }),\n };\n}\n\nexport function processPlatformEvent(\n event: PlatformEvent\n): ProcessPlatformEventResult {\n const contractValidation = validateAdapterContractVersion(\n event.contractVersion,\n SUPPORTED_ADAPTER_CONTRACT_MAJOR\n );\n if (!contractValidation.compatible) {\n return skipWithDiagnostic(event.platform, contractValidation.reason ?? \"incompatible_contract\", [\"contractVersion\"]);\n }\n\n const identity = resolveProjectIdentityKey(event.projectContext);\n if (!identity.key) {\n return skipWithDiagnostic(event.platform, \"missing_project_identity\", [\n \"platformProjectId\",\n \"canonicalPath\",\n \"cwd\",\n ]);\n }\n\n return {\n skipped: false,\n projectIdentityKey: identity.key,\n };\n}\n","import type { HookInput } from \"../types.js\";\n\nfunction normalizePlatform(value: string | undefined): string | undefined {\n if (!value) return undefined;\n const normalized = value.trim().toLowerCase();\n return normalized.length > 0 ? normalized : undefined;\n}\n\nexport function detectPlatformFromEnv(): string {\n const explicitFromEnv = normalizePlatform(process.env.MEMVID_PLATFORM);\n if (explicitFromEnv) {\n return explicitFromEnv;\n }\n\n if (process.env.OPENCODE === \"1\") {\n return \"opencode\";\n }\n\n return \"claude\";\n}\n\nexport function detectPlatform(input: HookInput): string {\n const explicitFromHook = normalizePlatform(input.platform);\n if (explicitFromHook) {\n return explicitFromHook;\n }\n\n return detectPlatformFromEnv();\n}\n","import { AdapterRegistry } from \"./registry.js\";\nimport type { ReadonlyAdapterRegistry } from \"./registry.js\";\nimport { claudeAdapter, opencodeAdapter } from \"./adapters/index.js\";\n\nexport * from \"./contract.js\";\nexport * from \"./diagnostics.js\";\nexport * from \"./diagnostic-store.js\";\nexport * from \"./events.js\";\nexport * from \"./identity.js\";\nexport * from \"./path-policy.js\";\nexport * from \"./pipeline.js\";\nexport * from \"./platform-detector.js\";\nexport type { ReadonlyAdapterRegistry } from \"./registry.js\";\nexport { AdapterRegistry } from \"./registry.js\";\nexport * from \"./adapters/index.js\";\n\nlet defaultRegistry: ReadonlyAdapterRegistry | null = null;\n\nexport function getDefaultAdapterRegistry(): ReadonlyAdapterRegistry {\n if (!defaultRegistry) {\n const registry = new AdapterRegistry();\n registry.register(claudeAdapter);\n registry.register(opencodeAdapter);\n defaultRegistry = Object.freeze({\n resolve: (platform: string) => registry.resolve(platform),\n listPlatforms: () => registry.listPlatforms(),\n });\n }\n\n return defaultRegistry;\n}\n\nexport function resetDefaultAdapterRegistry(): void {\n defaultRegistry = null;\n}\n","#!/usr/bin/env node\n/**\n * Memvid Mind - Session Start Hook\n *\n * Lightweight startup path that performs adapter validation,\n * fail-open checks, and context injection without loading the SDK.\n */\n\nimport { readStdin, writeOutput, debug } from \"../utils/helpers.js\";\nimport type { HookInput } from \"../types.js\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { basename, dirname, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n detectPlatform,\n getDefaultAdapterRegistry,\n processPlatformEvent,\n resolveMemoryPathPolicy,\n} from \"../platforms/index.js\";\n\nfunction buildContextLines(\n projectName: string,\n memoryDisplayPath: string,\n memoryExists: boolean,\n fileSizeKB: number,\n platform: string,\n warning?: string,\n migrationPrompt?: string\n): string[] {\n const contextLines: string[] = [];\n contextLines.push(\"<memvid-mind-context>\");\n const displayName = platform === \"claude\" ? \"Claude\" : platform.charAt(0).toUpperCase() + platform.slice(1);\n contextLines.push(memoryExists ? `# 🧠 ${displayName} Mind Active` : `# 🧠 ${displayName} Mind Ready`);\n contextLines.push(\"\");\n contextLines.push(`📁 Project: **${projectName}**`);\n contextLines.push(`🤖 Platform: **${platform}**`);\n\n if (memoryExists) {\n contextLines.push(`💾 Memory: \\`${memoryDisplayPath}\\` (${fileSizeKB} KB)`);\n } else {\n contextLines.push(`💾 Memory will be created at: \\`${memoryDisplayPath}\\``);\n }\n\n if (warning) {\n contextLines.push(\"\");\n contextLines.push(`⚠️ ${warning}`);\n }\n\n if (migrationPrompt) {\n contextLines.push(\"\");\n contextLines.push(\"❓ Legacy memory detected.\");\n contextLines.push(`Move it to the platform-agnostic path? Run: \\`${migrationPrompt}\\``);\n }\n\n contextLines.push(\"\");\n contextLines.push(\"**Commands:**\");\n contextLines.push(\"- `/mind:search <query>` - Search memories\");\n contextLines.push(\"- `/mind:ask <question>` - Ask your memory\");\n contextLines.push(\"- `/mind:recent` - View timeline\");\n contextLines.push(\"- `/mind:stats` - View statistics\");\n contextLines.push(\"\");\n contextLines.push(\"_Memories are captured automatically from your tool use._\");\n contextLines.push(\"</memvid-mind-context>\");\n return contextLines;\n}\n\nexport function buildSessionStartOutput(hookInput: HookInput): Record<string, unknown> {\n const projectDir = hookInput.cwd || process.env.CLAUDE_PROJECT_DIR || process.cwd();\n const projectName = basename(projectDir);\n const platform = detectPlatform(hookInput);\n const pathPolicy = resolveMemoryPathPolicy({\n projectDir,\n platform,\n defaultRelativePath: \".agent-brain/mind.mv2\",\n legacyRelativePaths: [\".claude/mind.mv2\"],\n platformRelativePath: process.env.MEMVID_PLATFORM_MEMORY_PATH,\n platformOptIn: process.env.MEMVID_PLATFORM_PATH_OPT_IN === \"1\",\n });\n\n const memoryExists = existsSync(pathPolicy.memoryPath);\n let fileSizeKB = 0;\n if (memoryExists) {\n try {\n fileSizeKB = Math.round(statSync(pathPolicy.memoryPath).size / 1024);\n } catch {\n fileSizeKB = 0;\n }\n }\n\n const registry = getDefaultAdapterRegistry();\n const adapter = registry.resolve(platform);\n\n let warning: string | undefined;\n let migrationPrompt: string | undefined;\n\n if (pathPolicy.migrationSuggestion) {\n const fromAbs = resolve(pathPolicy.migrationSuggestion.fromPath);\n const toAbs = resolve(pathPolicy.migrationSuggestion.toPath);\n migrationPrompt = `mkdir -p \"${dirname(toAbs)}\" && mv \"${fromAbs}\" \"${toAbs}\"`;\n }\n\n if (!adapter) {\n warning = \"Unsupported platform detected: memory capture disabled for this session.\";\n } else {\n const event = adapter.normalizeSessionStart(hookInput);\n const result = processPlatformEvent(event);\n if (result.skipped) {\n warning = `Memory capture disabled for this session (${result.reason}).`;\n }\n }\n\n const output: Record<string, unknown> = { continue: true };\n output.hookSpecificOutput = {\n hookEventName: \"SessionStart\",\n additionalContext: buildContextLines(\n projectName,\n relative(projectDir, pathPolicy.memoryPath) || basename(pathPolicy.memoryPath),\n memoryExists,\n fileSizeKB,\n platform,\n warning,\n migrationPrompt\n ).join(\"\\n\"),\n };\n\n return output;\n}\n\nexport async function runSessionStartHook(): Promise<void> {\n try {\n const input = await readStdin();\n const hookInput: HookInput = JSON.parse(input);\n debug(`Session starting: ${hookInput.session_id}`);\n writeOutput(buildSessionStartOutput(hookInput));\n } catch (error) {\n debug(`Error: ${error}`);\n writeOutput({ continue: true });\n }\n}\n\nif (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {\n void runSessionStartHook();\n}\n"]}