@rlabs-inc/memory 0.5.3 → 0.5.9

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.
@@ -4,34 +4,19 @@
4
4
  // Mirrors Curator pattern exactly
5
5
  // ============================================================================
6
6
 
7
- import { join } from 'path'
8
- import { homedir } from 'os'
9
- import { existsSync } from 'fs'
10
- import type { CurationResult } from '../types/memory.ts'
11
- import { logger } from '../utils/logger.ts'
12
-
13
- /**
14
- * Get the Claude CLI command path
15
- * Uses `which` for universal discovery across installation methods
16
- */
17
- function getClaudeCommand(): string {
18
- // 1. Check for explicit override
19
- const envCommand = process.env.CURATOR_COMMAND
20
- if (envCommand) return envCommand
21
-
22
- // 2. Use `which` to find claude in PATH (universal - works with native, homebrew, npm, etc.)
23
- const result = Bun.spawnSync(['which', 'claude'])
24
- if (result.exitCode === 0) {
25
- return result.stdout.toString().trim()
26
- }
27
-
28
- // 3. Legacy fallback - hardcoded native install path
29
- const claudeLocal = join(homedir(), '.claude', 'local', 'claude')
30
- if (existsSync(claudeLocal)) return claudeLocal
31
-
32
- // 4. Last resort
33
- return 'claude'
34
- }
7
+ import { join } from "path";
8
+ import { homedir } from "os";
9
+ import { existsSync } from "fs";
10
+ import type { CurationResult } from "../types/memory.ts";
11
+ import { logger } from "../utils/logger.ts";
12
+ import {
13
+ getClaudeCommand,
14
+ getManagerPromptPath,
15
+ getManagerCwd,
16
+ getCentralStoragePath,
17
+ getStorageMode,
18
+ type StoragePaths,
19
+ } from "../utils/paths.ts";
35
20
 
36
21
  /**
37
22
  * Manager configuration
@@ -42,80 +27,40 @@ export interface ManagerConfig {
42
27
  * When disabled, memories are stored but not organized/linked
43
28
  * Default: true
44
29
  */
45
- enabled?: boolean
30
+ enabled?: boolean;
46
31
 
47
32
  /**
48
33
  * CLI command to use (for subprocess mode)
49
34
  * Default: auto-detected (~/.claude/local/claude or 'claude')
50
35
  */
51
- cliCommand?: string
36
+ cliCommand?: string;
52
37
 
53
38
  /**
54
39
  * Maximum turns for the management agent
55
40
  * Set to undefined for unlimited turns
56
41
  * Default: undefined (unlimited)
57
42
  */
58
- maxTurns?: number
43
+ maxTurns?: number;
59
44
  }
60
45
 
61
- /**
62
- * Storage paths for the management agent
63
- * These are resolved at runtime from the server's actual configuration
64
- */
65
- export interface StoragePaths {
66
- /**
67
- * Root path to project storage directory (NOT the memories subdirectory)
68
- * e.g., ~/.local/share/memory/memory-ts/ (central)
69
- * or /path/to/project/.memory/ (local)
70
- */
71
- projectPath: string
72
-
73
- /**
74
- * Root path to global storage directory (NOT the memories subdirectory)
75
- * Always ~/.local/share/memory/global/
76
- */
77
- globalPath: string
78
-
79
- /**
80
- * Full path to project memories directory
81
- * e.g., ~/.local/share/memory/memory-ts/memories/ (central)
82
- * or /path/to/project/.memory/memories/ (local)
83
- */
84
- projectMemoriesPath: string
85
-
86
- /**
87
- * Full path to global memories directory
88
- * Always ~/.local/share/memory/global/memories/
89
- */
90
- globalMemoriesPath: string
91
-
92
- /**
93
- * Full path to personal primer file
94
- * Always ~/.local/share/memory/global/memories/personal-primer.md
95
- */
96
- personalPrimerPath: string
97
-
98
- /**
99
- * Storage mode for context
100
- */
101
- storageMode: 'central' | 'local'
102
- }
46
+ // Re-export StoragePaths for backwards compatibility
47
+ export type { StoragePaths } from "../utils/paths.ts";
103
48
 
104
49
  /**
105
50
  * Management result - what the agent did
106
51
  */
107
52
  export interface ManagementResult {
108
- success: boolean
109
- superseded: number
110
- resolved: number
111
- linked: number
112
- filesRead: number
113
- filesWritten: number
114
- primerUpdated: boolean
115
- actions: string[] // Detailed action log lines
116
- summary: string // Brief summary for storage
117
- fullReport: string // Complete management report (ACTIONS + SUMMARY sections)
118
- error?: string
53
+ success: boolean;
54
+ superseded: number;
55
+ resolved: number;
56
+ linked: number;
57
+ filesRead: number;
58
+ filesWritten: number;
59
+ primerUpdated: boolean;
60
+ actions: string[]; // Detailed action log lines
61
+ summary: string; // Brief summary for storage
62
+ fullReport: string; // Complete management report (ACTIONS + SUMMARY sections)
63
+ error?: string;
119
64
  }
120
65
 
121
66
  /**
@@ -124,17 +69,17 @@ export interface ManagementResult {
124
69
  */
125
70
  export class Manager {
126
71
  private _config: {
127
- enabled: boolean
128
- cliCommand: string
129
- maxTurns?: number // undefined = unlimited
130
- }
72
+ enabled: boolean;
73
+ cliCommand: string;
74
+ maxTurns?: number; // undefined = unlimited
75
+ };
131
76
 
132
77
  constructor(config: ManagerConfig = {}) {
133
78
  this._config = {
134
79
  enabled: config.enabled ?? true,
135
80
  cliCommand: config.cliCommand ?? getClaudeCommand(),
136
- maxTurns: config.maxTurns, // undefined = unlimited turns
137
- }
81
+ maxTurns: config.maxTurns, // undefined = unlimited turns
82
+ };
138
83
  }
139
84
 
140
85
  /**
@@ -144,25 +89,34 @@ export class Manager {
144
89
  async buildManagementPrompt(): Promise<string | null> {
145
90
  const skillPaths = [
146
91
  // Development - relative to src/core
147
- join(import.meta.dir, '../../skills/memory-management.md'),
92
+ join(import.meta.dir, "../../skills/memory-management.md"),
148
93
  // Installed via bun global
149
- join(homedir(), '.bun/install/global/node_modules/@rlabs-inc/memory/skills/memory-management.md'),
94
+ join(
95
+ homedir(),
96
+ ".bun/install/global/node_modules/@rlabs-inc/memory/skills/memory-management.md",
97
+ ),
150
98
  // Installed via npm global
151
- join(homedir(), '.npm/global/node_modules/@rlabs-inc/memory/skills/memory-management.md'),
99
+ join(
100
+ homedir(),
101
+ ".npm/global/node_modules/@rlabs-inc/memory/skills/memory-management.md",
102
+ ),
152
103
  // Local node_modules
153
- join(process.cwd(), 'node_modules/@rlabs-inc/memory/skills/memory-management.md'),
154
- ]
104
+ join(
105
+ process.cwd(),
106
+ "node_modules/@rlabs-inc/memory/skills/memory-management.md",
107
+ ),
108
+ ];
155
109
 
156
110
  for (const path of skillPaths) {
157
111
  try {
158
- const content = await Bun.file(path).text()
159
- if (content) return content
112
+ const content = await Bun.file(path).text();
113
+ if (content) return content;
160
114
  } catch {
161
- continue
115
+ continue;
162
116
  }
163
117
  }
164
118
 
165
- return null
119
+ return null;
166
120
  }
167
121
 
168
122
  /**
@@ -173,13 +127,14 @@ export class Manager {
173
127
  projectId: string,
174
128
  sessionNumber: number,
175
129
  result: CurationResult,
176
- storagePaths?: StoragePaths
130
+ storagePaths?: StoragePaths,
177
131
  ): string {
178
- const today = new Date().toISOString().split('T')[0]
132
+ const today = new Date().toISOString().split("T")[0];
179
133
 
180
134
  // Build storage paths section if provided
181
135
  // Includes both root paths (for permissions context) and memories paths (for file operations)
182
- const pathsSection = storagePaths ? `
136
+ const pathsSection = storagePaths
137
+ ? `
183
138
  ## Storage Paths (ACTUAL - use these exact paths)
184
139
 
185
140
  **Storage Mode:** ${storagePaths.storageMode}
@@ -195,7 +150,8 @@ export class Manager {
195
150
 
196
151
  > ⚠️ These paths are resolved from the running server configuration. Use them exactly as provided.
197
152
  > Memories are stored as individual markdown files in the memories directories.
198
- ` : ''
153
+ `
154
+ : "";
199
155
 
200
156
  return `## Curation Data
201
157
 
@@ -204,183 +160,38 @@ export class Manager {
204
160
  **Date:** ${today}
205
161
  ${pathsSection}
206
162
  ### Session Summary
207
- ${result.session_summary || 'No summary provided'}
163
+ ${result.session_summary || "No summary provided"}
208
164
 
209
165
  ### Project Snapshot
210
- ${result.project_snapshot ? `
211
- - Current Phase: ${result.project_snapshot.current_phase || 'N/A'}
212
- - Recent Achievements: ${result.project_snapshot.recent_achievements?.join(', ') || 'None'}
213
- - Active Challenges: ${result.project_snapshot.active_challenges?.join(', ') || 'None'}
214
- - Next Steps: ${result.project_snapshot.next_steps?.join(', ') || 'None'}
215
- ` : 'No snapshot provided'}
166
+ ${
167
+ result.project_snapshot
168
+ ? `
169
+ - Current Phase: ${result.project_snapshot.current_phase || "N/A"}
170
+ - Recent Achievements: ${result.project_snapshot.recent_achievements?.join(", ") || "None"}
171
+ - Active Challenges: ${result.project_snapshot.active_challenges?.join(", ") || "None"}
172
+ - Next Steps: ${result.project_snapshot.next_steps?.join(", ") || "None"}
173
+ `
174
+ : "No snapshot provided"
175
+ }
216
176
 
217
177
  ### New Memories (${result.memories.length})
218
- ${result.memories.map((m, i) => `
178
+ ${result.memories
179
+ .map(
180
+ (m, i) => `
219
181
  #### Memory ${i + 1}
220
182
  - **Content:** ${m.content}
221
183
  - **Type:** ${m.context_type}
222
- - **Scope:** ${m.scope || 'project'}
223
- - **Domain:** ${m.domain || 'N/A'}
184
+ - **Scope:** ${m.scope || "project"}
185
+ - **Domain:** ${m.domain || "N/A"}
224
186
  - **Importance:** ${m.importance_weight}
225
- - **Tags:** ${m.semantic_tags?.join(', ') || 'None'}
226
- `).join('\n')}
187
+ - **Tags:** ${m.semantic_tags?.join(", ") || "None"}
188
+ `,
189
+ )
190
+ .join("\n")}
227
191
 
228
192
  ---
229
193
 
230
- Please process these memories according to your management procedure. Use the exact storage paths provided above to read and write memory files. Update, supersede, or link existing memories as needed. Update the personal primer if any personal memories warrant it.`
231
- }
232
-
233
- /**
234
- * Parse management response from Claude
235
- */
236
- parseManagementResponse(responseJson: string): ManagementResult {
237
- const emptyResult = (error?: string): ManagementResult => ({
238
- success: !error,
239
- superseded: 0,
240
- resolved: 0,
241
- linked: 0,
242
- filesRead: 0,
243
- filesWritten: 0,
244
- primerUpdated: false,
245
- actions: [],
246
- summary: error ? '' : 'No actions taken',
247
- fullReport: error ? `Error: ${error}` : '',
248
- error,
249
- })
250
-
251
- try {
252
- // First, parse the CLI JSON wrapper
253
- const cliOutput = JSON.parse(responseJson)
254
-
255
- // Claude Code now returns an array of events - find the result object
256
- let resultObj: any
257
- if (Array.isArray(cliOutput)) {
258
- // New format: array of events, find the one with type="result"
259
- resultObj = cliOutput.find((item: any) => item.type === 'result')
260
- if (!resultObj) {
261
- return emptyResult('No result found in response')
262
- }
263
- } else {
264
- // Old format: single object (backwards compatibility)
265
- resultObj = cliOutput
266
- }
267
-
268
- // Check for error response
269
- if (resultObj.type === 'error' || resultObj.is_error === true) {
270
- return emptyResult(resultObj.error || 'Unknown error')
271
- }
272
-
273
- // Extract the "result" field (AI's response text)
274
- const resultText = typeof resultObj.result === 'string' ? resultObj.result : ''
275
-
276
- // Extract the full report (everything from === MANAGEMENT ACTIONS === onwards)
277
- const reportMatch = resultText.match(/(=== MANAGEMENT ACTIONS ===[\s\S]*)/)
278
- const fullReport = reportMatch ? reportMatch[1].trim() : resultText
279
-
280
- // Extract actions section
281
- const actionsMatch = resultText.match(/=== MANAGEMENT ACTIONS ===([\s\S]*?)(?:=== SUMMARY ===|$)/)
282
- const actions: string[] = []
283
- if (actionsMatch) {
284
- const actionsText = actionsMatch[1]
285
- // Extract lines that look like actions (TYPE: description or TYPE OK/FAILED: path)
286
- // Note: RECEIVED replaced CREATED - manager receives new memories from curator, doesn't create them
287
- const actionLines = actionsText.split('\n')
288
- .map((line: string) => line.trim())
289
- .filter((line: string) => /^(READ|WRITE|RECEIVED|CREATED|UPDATED|SUPERSEDED|RESOLVED|LINKED|PRIMER|SKIPPED|NO_ACTION)/.test(line))
290
- actions.push(...actionLines)
291
- }
292
-
293
- // Extract stats from result text
294
- const supersededMatch = resultText.match(/memories_superseded[:\s]+(\d+)/i) || resultText.match(/superseded[:\s]+(\d+)/i)
295
- const resolvedMatch = resultText.match(/memories_resolved[:\s]+(\d+)/i) || resultText.match(/resolved[:\s]+(\d+)/i)
296
- const linkedMatch = resultText.match(/memories_linked[:\s]+(\d+)/i) || resultText.match(/linked[:\s]+(\d+)/i)
297
- const filesReadMatch = resultText.match(/files_read[:\s]+(\d+)/i)
298
- const filesWrittenMatch = resultText.match(/files_written[:\s]+(\d+)/i)
299
- const primerUpdated = /primer_updated[:\s]+true/i.test(resultText) || /PRIMER\s+OK/i.test(resultText)
300
-
301
- // Count file operations from actions if not in summary
302
- const readActions = actions.filter((a: string) => a.startsWith('READ OK')).length
303
- const writeActions = actions.filter((a: string) => a.startsWith('WRITE OK')).length
304
-
305
- return {
306
- success: true,
307
- superseded: supersededMatch ? parseInt(supersededMatch[1]) : 0,
308
- resolved: resolvedMatch ? parseInt(resolvedMatch[1]) : 0,
309
- linked: linkedMatch ? parseInt(linkedMatch[1]) : 0,
310
- filesRead: filesReadMatch ? parseInt(filesReadMatch[1]) : readActions,
311
- filesWritten: filesWrittenMatch ? parseInt(filesWrittenMatch[1]) : writeActions,
312
- primerUpdated,
313
- actions,
314
- summary: resultText.slice(0, 500), // Brief summary for storage
315
- fullReport, // Complete report for logging
316
- }
317
- } catch {
318
- return emptyResult('Failed to parse response')
319
- }
320
- }
321
-
322
- /**
323
- * Build a temporary settings file with path-based permissions
324
- *
325
- * Claude CLI supports path restrictions via settings.json permissions, NOT via --allowedTools.
326
- * Syntax: Read(//absolute/path/**) where // means absolute path
327
- *
328
- * This provides real security - the agent can ONLY access memory storage paths.
329
- */
330
- private async _buildSettingsFile(storagePaths?: StoragePaths): Promise<string> {
331
- // Build allow list with path restrictions
332
- const allowRules: string[] = []
333
-
334
- // Global path - always central at ~/.local/share/memory/global
335
- const globalPath = storagePaths?.globalPath
336
- ?? join(homedir(), '.local', 'share', 'memory', 'global')
337
-
338
- // Project path - depends on storage mode
339
- const projectPath = storagePaths?.projectPath
340
- ?? join(homedir(), '.local', 'share', 'memory')
341
-
342
- // Glob and Grep - tool names only (no path syntax in Claude CLI for these)
343
- allowRules.push('Glob')
344
- allowRules.push('Grep')
345
-
346
- // Helper to format path for Claude CLI permissions
347
- // // means "absolute path from filesystem root"
348
- // If path already starts with /, replace it with //
349
- const formatPath = (p: string) => p.startsWith('/') ? '/' + p : '//' + p
350
-
351
- // Read, Write, Edit - use path patterns with // for absolute paths
352
- // Global path (always ~/.local/share/memory/global/)
353
- allowRules.push(`Read(${formatPath(globalPath)}/**)`)
354
- allowRules.push(`Write(${formatPath(globalPath)}/**)`)
355
- allowRules.push(`Edit(${formatPath(globalPath)}/**)`)
356
-
357
- // Project path (always different from global, even in central mode)
358
- // Central: ~/.local/share/memory/{project_id}/
359
- // Local: {project_path}/.memory/{project_id}/
360
- allowRules.push(`Read(${formatPath(projectPath)}/**)`)
361
- allowRules.push(`Write(${formatPath(projectPath)}/**)`)
362
- allowRules.push(`Edit(${formatPath(projectPath)}/**)`)
363
-
364
-
365
- const settings = {
366
- permissions: {
367
- allow: allowRules,
368
- deny: [
369
- // Explicitly deny sensitive paths for Read/Write/Edit
370
- 'Read(/etc/**)',
371
- 'Read(~/.ssh/**)',
372
- 'Read(~/.aws/**)',
373
- 'Read(~/.gnupg/**)',
374
- 'Read(.env)',
375
- 'Read(.env.*)',
376
- ]
377
- }
378
- }
379
-
380
- // Write to temp file
381
- const tempPath = join(homedir(), '.local', 'share', 'memory', '.manager-settings.json')
382
- await Bun.write(tempPath, JSON.stringify(settings, null, 2))
383
- return tempPath
194
+ Please process these memories according to your management procedure. Use the exact storage paths provided above to read and write memory files. Update, supersede, or link existing memories as needed. Update the personal primer if any personal memories warrant it.`;
384
195
  }
385
196
 
386
197
  /**
@@ -391,10 +202,10 @@ Please process these memories according to your management procedure. Use the ex
391
202
  projectId: string,
392
203
  sessionNumber: number,
393
204
  result: CurationResult,
394
- storagePaths?: StoragePaths
205
+ storagePaths?: StoragePaths,
395
206
  ): Promise<ManagementResult> {
396
207
  // Skip if disabled via config or env var
397
- if (!this._config.enabled || process.env.MEMORY_MANAGER_DISABLED === '1') {
208
+ if (!this._config.enabled || process.env.MEMORY_MANAGER_DISABLED === "1") {
398
209
  return {
399
210
  success: true,
400
211
  superseded: 0,
@@ -404,9 +215,9 @@ Please process these memories according to your management procedure. Use the ex
404
215
  filesWritten: 0,
405
216
  primerUpdated: false,
406
217
  actions: [],
407
- summary: 'Management agent disabled',
408
- fullReport: 'Management agent disabled via configuration',
409
- }
218
+ summary: "Management agent disabled",
219
+ fullReport: "Management agent disabled via configuration",
220
+ };
410
221
  }
411
222
 
412
223
  // Skip if no memories
@@ -420,13 +231,13 @@ Please process these memories according to your management procedure. Use the ex
420
231
  filesWritten: 0,
421
232
  primerUpdated: false,
422
233
  actions: [],
423
- summary: 'No memories to process',
424
- fullReport: 'No memories to process - skipped',
425
- }
234
+ summary: "No memories to process",
235
+ fullReport: "No memories to process - skipped",
236
+ };
426
237
  }
427
238
 
428
239
  // Load skill file
429
- const systemPrompt = await this.buildManagementPrompt()
240
+ const systemPrompt = await this.buildManagementPrompt();
430
241
  if (!systemPrompt) {
431
242
  return {
432
243
  success: false,
@@ -437,44 +248,50 @@ Please process these memories according to your management procedure. Use the ex
437
248
  filesWritten: 0,
438
249
  primerUpdated: false,
439
250
  actions: [],
440
- summary: '',
441
- fullReport: 'Error: Management skill file not found',
442
- error: 'Management skill not found',
443
- }
251
+ summary: "",
252
+ fullReport: "Error: Management skill file not found",
253
+ error: "Management skill not found",
254
+ };
444
255
  }
445
256
 
446
- const userMessage = this.buildUserMessage(projectId, sessionNumber, result, storagePaths)
257
+ const userMessage = this.buildUserMessage(
258
+ projectId,
259
+ sessionNumber,
260
+ result,
261
+ storagePaths,
262
+ );
447
263
 
448
264
  try {
449
265
  // Dynamic import to make Agent SDK optional
450
- const { query } = await import('@anthropic-ai/claude-agent-sdk')
266
+ const { query } = await import("@anthropic-ai/claude-agent-sdk");
451
267
 
452
268
  // Build allowed directories for file access
453
- const globalPath = storagePaths?.globalPath ?? join(homedir(), '.local', 'share', 'memory', 'global')
454
- const projectPath = storagePaths?.projectPath ?? join(homedir(), '.local', 'share', 'memory')
269
+ const globalPath =
270
+ storagePaths?.globalPath ?? join(getCentralStoragePath(), "global");
271
+ const projectPath = storagePaths?.projectPath ?? getCentralStoragePath();
455
272
 
456
273
  // Use Agent SDK with file tools
457
274
  const q = query({
458
275
  prompt: userMessage,
459
276
  options: {
460
277
  systemPrompt,
461
- permissionMode: 'bypassPermissions',
462
- model: 'claude-opus-4-5-20251101',
278
+ permissionMode: "bypassPermissions",
279
+ model: "claude-opus-4-5-20251101",
463
280
  // Only allow file tools - no Bash, no web
464
- allowedTools: ['Read', 'Write', 'Edit', 'Glob', 'Grep'],
281
+ allowedTools: ["Read", "Write", "Edit", "Glob", "Grep"],
465
282
  // Allow access to memory directories
466
283
  additionalDirectories: [globalPath, projectPath],
467
284
  // Limit turns if configured
468
285
  maxTurns: this._config.maxTurns,
469
286
  },
470
- })
287
+ });
471
288
 
472
289
  // Iterate through the async generator to get the result
473
- let resultText = ''
290
+ let resultText = "";
474
291
  for await (const msg of q) {
475
- if (msg.type === 'result' && 'result' in msg) {
476
- resultText = msg.result
477
- break
292
+ if (msg.type === "result" && "result" in msg) {
293
+ resultText = msg.result;
294
+ break;
478
295
  }
479
296
  }
480
297
 
@@ -488,12 +305,12 @@ Please process these memories according to your management procedure. Use the ex
488
305
  filesWritten: 0,
489
306
  primerUpdated: false,
490
307
  actions: [],
491
- summary: 'No result from management agent',
492
- fullReport: 'Management agent completed but returned no result',
493
- }
308
+ summary: "No result from management agent",
309
+ fullReport: "Management agent completed but returned no result",
310
+ };
494
311
  }
495
312
 
496
- return this._parseSDKManagementResult(resultText)
313
+ return this._parseSDKManagementResult(resultText);
497
314
  } catch (error: any) {
498
315
  return {
499
316
  success: false,
@@ -504,159 +321,63 @@ Please process these memories according to your management procedure. Use the ex
504
321
  filesWritten: 0,
505
322
  primerUpdated: false,
506
323
  actions: [],
507
- summary: '',
508
- fullReport: `Error: Agent SDK failed: ${error.message}`,
324
+ summary: "",
325
+ fullReport: `Manager Claude - Error: Agent SDK failed: ${error.message}`,
509
326
  error: error.message,
510
- }
327
+ };
511
328
  }
512
329
  }
513
330
 
514
- /**
515
- * Manage using CLI subprocess (for hooks - keeps working while we migrate)
516
- * Similar to Curator.curateWithCLI
517
- */
518
- async manageWithCLI(
519
- projectId: string,
520
- sessionNumber: number,
521
- result: CurationResult,
522
- storagePaths?: StoragePaths
523
- ): Promise<ManagementResult> {
524
- // Skip if disabled via config or env var
525
- if (!this._config.enabled || process.env.MEMORY_MANAGER_DISABLED === '1') {
526
- return {
527
- success: true,
528
- superseded: 0,
529
- resolved: 0,
530
- linked: 0,
531
- filesRead: 0,
532
- filesWritten: 0,
533
- primerUpdated: false,
534
- actions: [],
535
- summary: 'Management agent disabled',
536
- fullReport: 'Management agent disabled via configuration',
537
- }
538
- }
539
-
540
- // Skip if no memories
541
- if (result.memories.length === 0) {
542
- return {
543
- success: true,
544
- superseded: 0,
545
- resolved: 0,
546
- linked: 0,
547
- filesRead: 0,
548
- filesWritten: 0,
549
- primerUpdated: false,
550
- actions: [],
551
- summary: 'No memories to process',
552
- fullReport: 'No memories to process - skipped',
553
- }
554
- }
555
-
556
- // Load skill file
557
- const systemPrompt = await this.buildManagementPrompt()
558
- if (!systemPrompt) {
559
- return {
560
- success: false,
561
- superseded: 0,
562
- resolved: 0,
563
- linked: 0,
564
- filesRead: 0,
565
- filesWritten: 0,
566
- primerUpdated: false,
567
- actions: [],
568
- summary: '',
569
- fullReport: 'Error: Management skill file not found',
570
- error: 'Management skill not found',
571
- }
572
- }
573
-
574
- const userMessage = this.buildUserMessage(projectId, sessionNumber, result, storagePaths)
575
-
576
- // Build settings file with path-based permissions
577
- // This provides real security - the agent can ONLY access memory storage paths
578
- const settingsPath = await this._buildSettingsFile(storagePaths)
579
-
580
- // Build CLI command with settings file for path restrictions
581
- const args = [
582
- '-p', userMessage,
583
- '--append-system-prompt', systemPrompt,
584
- '--output-format', 'json',
585
- '--settings', settingsPath,
586
- ]
587
-
588
- // Add max-turns only if configured (undefined = unlimited)
589
- if (this._config.maxTurns !== undefined) {
590
- args.push('--max-turns', String(this._config.maxTurns))
591
- }
592
-
593
- // Execute CLI
594
- const proc = Bun.spawn([this._config.cliCommand, ...args], {
595
- env: {
596
- ...process.env,
597
- MEMORY_CURATOR_ACTIVE: '1', // Prevent recursive hook triggering
598
- },
599
- stderr: 'pipe',
600
- })
601
-
602
- // Capture output
603
- const [stdout, stderr] = await Promise.all([
604
- new Response(proc.stdout).text(),
605
- new Response(proc.stderr).text(),
606
- ])
607
- const exitCode = await proc.exited
608
-
609
- if (exitCode !== 0) {
610
- const errorMsg = stderr || `Exit code ${exitCode}`
611
- return {
612
- success: false,
613
- superseded: 0,
614
- resolved: 0,
615
- linked: 0,
616
- filesRead: 0,
617
- filesWritten: 0,
618
- primerUpdated: false,
619
- actions: [],
620
- summary: '',
621
- fullReport: `Error: CLI failed with exit code ${exitCode}\n${stderr}`,
622
- error: errorMsg,
623
- }
624
- }
625
-
626
- return this.parseManagementResponse(stdout)
627
- }
628
-
629
331
  /**
630
332
  * Parse management result from Agent SDK response
631
333
  * Similar to parseManagementResponse but for SDK output format
632
334
  */
633
335
  private _parseSDKManagementResult(resultText: string): ManagementResult {
634
336
  // Extract actions section
635
- const actionsMatch = resultText.match(/=== MANAGEMENT ACTIONS ===([\s\S]*?)(?:=== SUMMARY ===|$)/)
636
- const actions: string[] = []
337
+ const actionsMatch = resultText.match(
338
+ /=== MANAGEMENT ACTIONS ===([\s\S]*?)(?:=== SUMMARY ===|$)/,
339
+ );
340
+ const actions: string[] = [];
637
341
  if (actionsMatch) {
638
- const actionsText = actionsMatch[1]
639
- const actionLines = actionsText.split('\n')
342
+ const actionsText = actionsMatch[1];
343
+ const actionLines = actionsText
344
+ .split("\n")
640
345
  .map((line: string) => line.trim())
641
- .filter((line: string) => /^(READ|WRITE|RECEIVED|CREATED|UPDATED|SUPERSEDED|RESOLVED|LINKED|PRIMER|SKIPPED|NO_ACTION)/.test(line))
642
- actions.push(...actionLines)
346
+ .filter((line: string) =>
347
+ /^(READ|WRITE|RECEIVED|CREATED|UPDATED|SUPERSEDED|RESOLVED|LINKED|PRIMER|SKIPPED|NO_ACTION)/.test(
348
+ line,
349
+ ),
350
+ );
351
+ actions.push(...actionLines);
643
352
  }
644
353
 
645
354
  // Extract the full report
646
- const reportMatch = resultText.match(/(=== MANAGEMENT ACTIONS ===[\s\S]*)/)
647
- const fullReport = reportMatch ? reportMatch[1].trim() : resultText
355
+ const reportMatch = resultText.match(/(=== MANAGEMENT ACTIONS ===[\s\S]*)/);
356
+ const fullReport = reportMatch ? reportMatch[1].trim() : resultText;
648
357
 
649
358
  // Extract stats from result text
650
- const supersededMatch = resultText.match(/memories_superseded[:\s]+(\d+)/i) || resultText.match(/superseded[:\s]+(\d+)/i)
651
- const resolvedMatch = resultText.match(/memories_resolved[:\s]+(\d+)/i) || resultText.match(/resolved[:\s]+(\d+)/i)
652
- const linkedMatch = resultText.match(/memories_linked[:\s]+(\d+)/i) || resultText.match(/linked[:\s]+(\d+)/i)
653
- const filesReadMatch = resultText.match(/files_read[:\s]+(\d+)/i)
654
- const filesWrittenMatch = resultText.match(/files_written[:\s]+(\d+)/i)
655
- const primerUpdated = /primer_updated[:\s]+true/i.test(resultText) || /PRIMER\s+OK/i.test(resultText)
359
+ const supersededMatch =
360
+ resultText.match(/memories_superseded[:\s]+(\d+)/i) ||
361
+ resultText.match(/superseded[:\s]+(\d+)/i);
362
+ const resolvedMatch =
363
+ resultText.match(/memories_resolved[:\s]+(\d+)/i) ||
364
+ resultText.match(/resolved[:\s]+(\d+)/i);
365
+ const linkedMatch =
366
+ resultText.match(/memories_linked[:\s]+(\d+)/i) ||
367
+ resultText.match(/linked[:\s]+(\d+)/i);
368
+ const filesReadMatch = resultText.match(/files_read[:\s]+(\d+)/i);
369
+ const filesWrittenMatch = resultText.match(/files_written[:\s]+(\d+)/i);
370
+ const primerUpdated =
371
+ /primer_updated[:\s]+true/i.test(resultText) ||
372
+ /PRIMER\s+OK/i.test(resultText);
656
373
 
657
374
  // Count file operations from actions if not in summary
658
- const readActions = actions.filter((a: string) => a.startsWith('READ OK')).length
659
- const writeActions = actions.filter((a: string) => a.startsWith('WRITE OK')).length
375
+ const readActions = actions.filter((a: string) =>
376
+ a.startsWith("READ OK"),
377
+ ).length;
378
+ const writeActions = actions.filter((a: string) =>
379
+ a.startsWith("WRITE OK"),
380
+ ).length;
660
381
 
661
382
  return {
662
383
  success: true,
@@ -664,12 +385,14 @@ Please process these memories according to your management procedure. Use the ex
664
385
  resolved: resolvedMatch ? parseInt(resolvedMatch[1]) : 0,
665
386
  linked: linkedMatch ? parseInt(linkedMatch[1]) : 0,
666
387
  filesRead: filesReadMatch ? parseInt(filesReadMatch[1]) : readActions,
667
- filesWritten: filesWrittenMatch ? parseInt(filesWrittenMatch[1]) : writeActions,
388
+ filesWritten: filesWrittenMatch
389
+ ? parseInt(filesWrittenMatch[1])
390
+ : writeActions,
668
391
  primerUpdated,
669
392
  actions,
670
393
  summary: resultText.slice(0, 500),
671
394
  fullReport,
672
- }
395
+ };
673
396
  }
674
397
 
675
398
  /**
@@ -681,10 +404,11 @@ Please process these memories according to your management procedure. Use the ex
681
404
  projectId: string,
682
405
  sessionNumber: number,
683
406
  result: CurationResult,
684
- storagePaths?: StoragePaths
407
+ storagePaths?: StoragePaths,
408
+ apiKey?: string,
685
409
  ): Promise<ManagementResult> {
686
410
  // Skip if disabled via config or env var
687
- if (!this._config.enabled || process.env.MEMORY_MANAGER_DISABLED === '1') {
411
+ if (!this._config.enabled || process.env.MEMORY_MANAGER_DISABLED === "1") {
688
412
  return {
689
413
  success: true,
690
414
  superseded: 0,
@@ -694,9 +418,9 @@ Please process these memories according to your management procedure. Use the ex
694
418
  filesWritten: 0,
695
419
  primerUpdated: false,
696
420
  actions: [],
697
- summary: 'Management agent disabled',
698
- fullReport: 'Management agent disabled via configuration',
699
- }
421
+ summary: "Manager Gemini - Management agent disabled",
422
+ fullReport: "Management agent disabled via configuration",
423
+ };
700
424
  }
701
425
 
702
426
  // Skip if no memories
@@ -710,13 +434,13 @@ Please process these memories according to your management procedure. Use the ex
710
434
  filesWritten: 0,
711
435
  primerUpdated: false,
712
436
  actions: [],
713
- summary: 'No memories to process',
714
- fullReport: 'No memories to process - skipped',
715
- }
437
+ summary: "Manager Gemini - No memories to process",
438
+ fullReport: "No memories to process - skipped",
439
+ };
716
440
  }
717
441
 
718
442
  // Load skill file
719
- const systemPrompt = await this.buildManagementPrompt()
443
+ const systemPrompt = await this.buildManagementPrompt();
720
444
  if (!systemPrompt) {
721
445
  return {
722
446
  success: false,
@@ -727,26 +451,28 @@ Please process these memories according to your management procedure. Use the ex
727
451
  filesWritten: 0,
728
452
  primerUpdated: false,
729
453
  actions: [],
730
- summary: '',
731
- fullReport: 'Error: Management skill file not found',
732
- error: 'Management skill not found',
733
- }
454
+ summary: "",
455
+ fullReport:
456
+ "Manager Gemini - Error: Management skill not file not found",
457
+ error: "Management skill not found",
458
+ };
734
459
  }
735
460
 
736
- const userMessage = this.buildUserMessage(projectId, sessionNumber, result, storagePaths)
737
-
738
- // Resolve storage paths from config
739
- const centralPath = join(homedir(), '.local', 'share', 'memory')
740
- const projectPath = storagePaths?.projectPath ?? join(centralPath, projectId)
741
- const globalPath = storagePaths?.globalPath ?? join(centralPath, 'global')
742
- const storageMode = storagePaths?.storageMode ?? 'central'
743
-
744
- // Determine cwd based on storage mode:
745
- // - Central: cwd = centralPath (parent of both project and global, write access to both)
746
- // - Local: cwd = projectPath (write access to project only, global is read-only)
747
- const managerCwd = storageMode === 'central' ? centralPath : projectPath
748
-
749
- // Write system prompt to temp file (in cwd so it's accessible)
461
+ const userMessage = this.buildUserMessage(
462
+ projectId,
463
+ sessionNumber,
464
+ result,
465
+ storagePaths,
466
+ );
467
+
468
+ // Resolve paths using centralized utilities
469
+ const managerCwd = getManagerCwd(storagePaths);
470
+ const projectPath =
471
+ storagePaths?.projectPath ?? join(getCentralStoragePath(), projectId);
472
+ const globalPath =
473
+ storagePaths?.globalPath ?? join(getCentralStoragePath(), "global");
474
+
475
+ // Write system prompt to temp file (tmpdir always exists)
750
476
  const geminiSystemPrompt = `${systemPrompt}
751
477
 
752
478
  ## Available Tools
@@ -756,88 +482,113 @@ You have access to the following tools to manage memory files:
756
482
  \${AvailableTools}
757
483
 
758
484
  Use these tools to read existing memories, write updates, and manage the memory filesystem.
759
- `
760
- const tempPromptPath = join(managerCwd, '.gemini-manager-prompt.md')
761
- await Bun.write(tempPromptPath, geminiSystemPrompt)
485
+ `;
486
+ const tempPromptPath = getManagerPromptPath();
487
+ await Bun.write(tempPromptPath, geminiSystemPrompt);
762
488
 
763
489
  // Copy user's Gemini settings to managerCwd with hooks disabled
764
490
  // This prevents the manager's session from triggering hooks recursively
765
- const userSettingsPath = join(homedir(), '.gemini', 'settings.json')
766
- const managerSettingsDir = join(managerCwd, '.gemini')
767
- const managerSettingsPath = join(managerSettingsDir, 'settings.json')
491
+ const userSettingsPath = join(homedir(), ".gemini", "settings.json");
492
+ const managerSettingsDir = join(managerCwd, ".gemini");
493
+ const managerSettingsPath = join(managerSettingsDir, "settings.json");
768
494
 
769
495
  try {
770
- let settings: any = {}
771
-
772
- // Load user's settings if they exist
773
- if (existsSync(userSettingsPath)) {
774
- const userSettings = await Bun.file(userSettingsPath).text()
775
- settings = JSON.parse(userSettings)
776
- }
777
-
778
- // Disable hooks (hooks.enabled inside hooks object)
779
- if (!settings.hooks) {
780
- settings.hooks = {}
781
- }
782
- settings.hooks.enabled = false
496
+ // Disable memory hooks to prevent recursive curation
497
+ // Format: hooks.disabled array with hook command names
498
+ const settings = {
499
+ hooks: {
500
+ disabled: [
501
+ "inject-memories",
502
+ "load-session-primer",
503
+ "curate-memories",
504
+ "curate-memories",
505
+ ],
506
+ },
507
+ };
783
508
 
784
509
  // Ensure .gemini directory exists in managerCwd
785
510
  if (!existsSync(managerSettingsDir)) {
786
- const { mkdirSync } = await import('fs')
787
- mkdirSync(managerSettingsDir, { recursive: true })
511
+ const { mkdirSync } = await import("fs");
512
+ mkdirSync(managerSettingsDir, { recursive: true });
788
513
  }
789
514
 
790
- await Bun.write(managerSettingsPath, JSON.stringify(settings, null, 2))
791
- logger.debug(`Manager Gemini: Created settings with hooks disabled at ${managerSettingsPath}`, 'manager')
515
+ await Bun.write(managerSettingsPath, JSON.stringify(settings, null, 2));
516
+ logger.debug(
517
+ `Manager Gemini: Created settings with hooks disabled at ${managerSettingsPath}`,
518
+ "manager",
519
+ );
792
520
  } catch (err: any) {
793
- logger.debug(`Manager Gemini: Could not create settings file: ${err.message}`, 'manager')
521
+ logger.debug(
522
+ `Manager Gemini: Could not create settings file: ${err.message}`,
523
+ "manager",
524
+ );
794
525
  }
795
526
 
796
- logger.debug(`Manager Gemini: Starting management for project ${projectId}`, 'manager')
797
- logger.debug(`Manager Gemini: Processing ${result.memories.length} memories`, 'manager')
798
- logger.debug(`Manager Gemini: Storage mode: ${storageMode}, cwd: ${managerCwd}`, 'manager')
527
+ logger.debug(
528
+ `Manager Gemini - Starting management for project ${projectId}`,
529
+ "manager",
530
+ );
531
+ logger.debug(
532
+ `Manager Gemini - Processing ${result.memories.length} memories`,
533
+ "manager",
534
+ );
535
+ logger.debug(
536
+ `Manager Gemini - Storage mode: ${getStorageMode(storagePaths)}, cwd: ${managerCwd}`,
537
+ "manager",
538
+ );
799
539
 
800
540
  // Build CLI command
801
541
  // - cwd gives write access to that tree
802
542
  // - --include-directories adds read access to other paths
803
543
  const args = [
804
- '-p', userMessage,
805
- '--output-format', 'json',
806
- '--yolo', // Auto-approve file operations
807
- '--include-directories', projectPath,
808
- '--include-directories', globalPath,
809
- ]
810
-
811
- logger.debug(`Manager Gemini: Spawning gemini CLI from ${managerCwd}`, 'manager')
544
+ "-p",
545
+ userMessage,
546
+ "--output-format",
547
+ "json",
548
+ "--yolo", // Auto-approve file operations
549
+ "--include-directories",
550
+ projectPath,
551
+ "--include-directories",
552
+ globalPath,
553
+ ];
554
+
555
+ logger.debug(
556
+ `Manager Gemini: Spawning gemini CLI from ${managerCwd}`,
557
+ "manager",
558
+ );
812
559
 
813
560
  // Execute CLI with system prompt via environment variable
814
561
  // cwd determines write access, --include-directories adds read access
815
- const proc = Bun.spawn(['gemini', ...args], {
562
+ const proc = Bun.spawn(["gemini", ...args], {
816
563
  cwd: managerCwd,
817
564
  env: {
818
565
  ...process.env,
819
- MEMORY_CURATOR_ACTIVE: '1', // Prevent recursive hook triggering
820
- GEMINI_SYSTEM_MD: tempPromptPath, // Inject our management prompt
566
+ MEMORY_CURATOR_ACTIVE: "1", // Prevent recursive hook triggering
567
+ GEMINI_SYSTEM_MD: tempPromptPath, // Inject our management prompt
568
+ ...(apiKey ? { GEMINI_API_KEY: apiKey } : {}),
821
569
  },
822
- stdout: 'pipe',
823
- stderr: 'pipe',
824
- })
570
+ stdout: "pipe",
571
+ stderr: "pipe",
572
+ });
825
573
 
826
574
  // Capture output
827
575
  const [stdout, stderr] = await Promise.all([
828
576
  new Response(proc.stdout).text(),
829
577
  new Response(proc.stderr).text(),
830
- ])
831
- const exitCode = await proc.exited
578
+ ]);
579
+ const exitCode = await proc.exited;
832
580
 
833
- logger.debug(`Manager Gemini: Exit code ${exitCode}`, 'manager')
581
+ logger.debug(`Manager Gemini - Exit code ${exitCode}`, "manager");
834
582
  if (stderr && stderr.trim()) {
835
- logger.debug(`Manager Gemini stderr: ${stderr}`, 'manager')
583
+ logger.debug(`Manager Gemini - stderr: ${stderr}`, "manager");
836
584
  }
837
585
 
838
586
  if (exitCode !== 0) {
839
- logger.debug(`Manager Gemini: Failed with exit code ${exitCode}`, 'manager')
840
- const errorMsg = stderr || `Exit code ${exitCode}`
587
+ logger.debug(
588
+ `Manager Gemini - Failed with exit code ${exitCode}`,
589
+ "manager",
590
+ );
591
+ const errorMsg = stderr || `Exit code ${exitCode}`;
841
592
  return {
842
593
  success: false,
843
594
  superseded: 0,
@@ -847,22 +598,31 @@ Use these tools to read existing memories, write updates, and manage the memory
847
598
  filesWritten: 0,
848
599
  primerUpdated: false,
849
600
  actions: [],
850
- summary: '',
851
- fullReport: `Error: Gemini CLI failed with exit code ${exitCode}\n${stderr}`,
601
+ summary: "",
602
+ fullReport: `Manager Gemini - Error: Gemini CLI failed with exit code ${exitCode}\n${stderr}`,
852
603
  error: errorMsg,
853
- }
604
+ };
854
605
  }
855
606
 
856
607
  // Parse Gemini JSON output
857
608
  // Note: Gemini CLI outputs log messages before AND after the JSON
858
609
  // We need to extract just the JSON object
859
- logger.debug(`Manager Gemini: Parsing response (${stdout.length} chars)`, 'manager')
610
+ logger.debug(
611
+ `Manager Gemini - Parsing response (${stdout.length} chars)`,
612
+ "manager",
613
+ );
860
614
  try {
861
615
  // Find the JSON object - it starts with { and we need to find the matching }
862
- const jsonStart = stdout.indexOf('{')
616
+ const jsonStart = stdout.indexOf("{");
863
617
  if (jsonStart === -1) {
864
- logger.debug('Manager Gemini: No JSON object found in output', 'manager')
865
- logger.debug(`Manager Gemini: Raw stdout: ${stdout.slice(0, 500)}`, 'manager')
618
+ logger.debug(
619
+ "Manager Gemini - No JSON object found in output",
620
+ "manager",
621
+ );
622
+ // logger.debug(
623
+ // `Manager Gemini - Raw stdout: ${stdout.slice(0, 500)}`,
624
+ // "manager",
625
+ // );
866
626
  return {
867
627
  success: false,
868
628
  superseded: 0,
@@ -872,25 +632,29 @@ Use these tools to read existing memories, write updates, and manage the memory
872
632
  filesWritten: 0,
873
633
  primerUpdated: false,
874
634
  actions: [],
875
- summary: 'No JSON in Gemini response',
876
- fullReport: 'Manager failed: No JSON object in Gemini CLI output',
877
- }
635
+ summary: "Manager Gemini - No JSON in Gemini response",
636
+ fullReport:
637
+ "Manager Gemini - Failed: No JSON object in Gemini CLI output",
638
+ };
878
639
  }
879
640
 
880
641
  // Find the matching closing brace by counting braces
881
- let braceCount = 0
882
- let jsonEnd = -1
642
+ let braceCount = 0;
643
+ let jsonEnd = -1;
883
644
  for (let i = jsonStart; i < stdout.length; i++) {
884
- if (stdout[i] === '{') braceCount++
885
- if (stdout[i] === '}') braceCount--
645
+ if (stdout[i] === "{") braceCount++;
646
+ if (stdout[i] === "}") braceCount--;
886
647
  if (braceCount === 0) {
887
- jsonEnd = i + 1
888
- break
648
+ jsonEnd = i + 1;
649
+ break;
889
650
  }
890
651
  }
891
652
 
892
653
  if (jsonEnd === -1) {
893
- logger.debug('Manager Gemini: Could not find matching closing brace', 'manager')
654
+ logger.debug(
655
+ "Manager Gemini - Could not find matching closing brace",
656
+ "manager",
657
+ );
894
658
  return {
895
659
  success: false,
896
660
  superseded: 0,
@@ -900,21 +664,25 @@ Use these tools to read existing memories, write updates, and manage the memory
900
664
  filesWritten: 0,
901
665
  primerUpdated: false,
902
666
  actions: [],
903
- summary: 'Incomplete JSON in Gemini response',
904
- fullReport: 'Manager failed: Could not find complete JSON object',
905
- }
667
+ summary: "Manager Gemini - Incomplete JSON in Gemini response",
668
+ fullReport:
669
+ "Manager Gemini - Failed: Could not find complete JSON object",
670
+ };
906
671
  }
907
672
 
908
- const jsonStr = stdout.slice(jsonStart, jsonEnd)
909
- logger.debug(`Manager Gemini: Extracted JSON (${jsonStr.length} chars)`, 'manager')
673
+ const jsonStr = stdout.slice(jsonStart, jsonEnd);
674
+ logger.debug(
675
+ `Manager Gemini - Extracted JSON (${jsonStr.length} chars)`,
676
+ "manager",
677
+ );
910
678
 
911
- const geminiOutput = JSON.parse(jsonStr)
679
+ const geminiOutput = JSON.parse(jsonStr);
912
680
 
913
681
  // Gemini returns { response: "...", stats: {...} }
914
- const aiResponse = geminiOutput.response || ''
682
+ const aiResponse = geminiOutput.response || "";
915
683
 
916
684
  if (!aiResponse) {
917
- logger.debug('Manager Gemini: No response field in output', 'manager')
685
+ logger.debug("Manager Gemini - No response field in output", "manager");
918
686
  return {
919
687
  success: true,
920
688
  superseded: 0,
@@ -924,20 +692,29 @@ Use these tools to read existing memories, write updates, and manage the memory
924
692
  filesWritten: 0,
925
693
  primerUpdated: false,
926
694
  actions: [],
927
- summary: 'No response from Gemini',
928
- fullReport: 'Management completed but no response returned',
929
- }
695
+ summary: "Managetr Gemini - No response from Gemini",
696
+ fullReport: "Management completed but no response returned",
697
+ };
930
698
  }
931
699
 
932
- logger.debug(`Manager Gemini: Got response (${aiResponse.length} chars)`, 'manager')
700
+ logger.debug(
701
+ `Manager Gemini - Got response (${aiResponse.length} chars)`,
702
+ "manager",
703
+ );
933
704
 
934
705
  // Parse using our existing SDK parser (same format expected)
935
- const result = this._parseSDKManagementResult(aiResponse)
936
- logger.debug(`Manager Gemini: Parsed result - superseded: ${result.superseded}, resolved: ${result.resolved}, linked: ${result.linked}`, 'manager')
937
- return result
706
+ const result = this._parseSDKManagementResult(aiResponse);
707
+ logger.debug(
708
+ `Manager Gemini - Parsed result - superseded: ${result.superseded}, resolved: ${result.resolved}, linked: ${result.linked}`,
709
+ "manager",
710
+ );
711
+ return result;
938
712
  } catch (error: any) {
939
- logger.debug(`Manager Gemini: Parse error: ${error.message}`, 'manager')
940
- logger.debug(`Manager Gemini: Raw stdout (first 500 chars): ${stdout.slice(0, 500)}`, 'manager')
713
+ logger.debug(`Manager Gemini - Parse error: ${error.message}`, "manager");
714
+ logger.debug(
715
+ `Manager Gemini - Raw stdout (first 500 chars): ${stdout.slice(0, 500)}`,
716
+ "manager",
717
+ );
941
718
  return {
942
719
  success: false,
943
720
  superseded: 0,
@@ -947,10 +724,10 @@ Use these tools to read existing memories, write updates, and manage the memory
947
724
  filesWritten: 0,
948
725
  primerUpdated: false,
949
726
  actions: [],
950
- summary: '',
951
- fullReport: `Error: Failed to parse Gemini response: ${error.message}`,
727
+ summary: "",
728
+ fullReport: `Manager Gemini - Error: Failed to parse Gemini response: ${error.message}`,
952
729
  error: error.message,
953
- }
730
+ };
954
731
  }
955
732
  }
956
733
  }
@@ -959,5 +736,5 @@ Use these tools to read existing memories, write updates, and manage the memory
959
736
  * Create a new manager
960
737
  */
961
738
  export function createManager(config?: ManagerConfig): Manager {
962
- return new Manager(config)
739
+ return new Manager(config);
963
740
  }