@rlabs-inc/memory 0.5.2 → 0.5.8

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,126 +321,11 @@ 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
- }
511
- }
512
- }
513
-
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))
327
+ };
591
328
  }
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
329
  }
628
330
 
629
331
  /**
@@ -632,31 +334,50 @@ Please process these memories according to your management procedure. Use the ex
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,10 @@ 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,
685
408
  ): Promise<ManagementResult> {
686
409
  // Skip if disabled via config or env var
687
- if (!this._config.enabled || process.env.MEMORY_MANAGER_DISABLED === '1') {
410
+ if (!this._config.enabled || process.env.MEMORY_MANAGER_DISABLED === "1") {
688
411
  return {
689
412
  success: true,
690
413
  superseded: 0,
@@ -694,9 +417,9 @@ Please process these memories according to your management procedure. Use the ex
694
417
  filesWritten: 0,
695
418
  primerUpdated: false,
696
419
  actions: [],
697
- summary: 'Management agent disabled',
698
- fullReport: 'Management agent disabled via configuration',
699
- }
420
+ summary: "Manager Gemini - Management agent disabled",
421
+ fullReport: "Management agent disabled via configuration",
422
+ };
700
423
  }
701
424
 
702
425
  // Skip if no memories
@@ -710,13 +433,13 @@ Please process these memories according to your management procedure. Use the ex
710
433
  filesWritten: 0,
711
434
  primerUpdated: false,
712
435
  actions: [],
713
- summary: 'No memories to process',
714
- fullReport: 'No memories to process - skipped',
715
- }
436
+ summary: "Manager Gemini - No memories to process",
437
+ fullReport: "No memories to process - skipped",
438
+ };
716
439
  }
717
440
 
718
441
  // Load skill file
719
- const systemPrompt = await this.buildManagementPrompt()
442
+ const systemPrompt = await this.buildManagementPrompt();
720
443
  if (!systemPrompt) {
721
444
  return {
722
445
  success: false,
@@ -727,16 +450,28 @@ Please process these memories according to your management procedure. Use the ex
727
450
  filesWritten: 0,
728
451
  primerUpdated: false,
729
452
  actions: [],
730
- summary: '',
731
- fullReport: 'Error: Management skill file not found',
732
- error: 'Management skill not found',
733
- }
453
+ summary: "",
454
+ fullReport:
455
+ "Manager Gemini - Error: Management skill not file not found",
456
+ error: "Management skill not found",
457
+ };
734
458
  }
735
459
 
736
- const userMessage = this.buildUserMessage(projectId, sessionNumber, result, storagePaths)
737
-
738
- // Write system prompt to temp file
739
- // Include Gemini's tool variables so the manager has access to file tools
460
+ const userMessage = this.buildUserMessage(
461
+ projectId,
462
+ sessionNumber,
463
+ result,
464
+ storagePaths,
465
+ );
466
+
467
+ // Resolve paths using centralized utilities
468
+ const managerCwd = getManagerCwd(storagePaths);
469
+ const projectPath =
470
+ storagePaths?.projectPath ?? join(getCentralStoragePath(), projectId);
471
+ const globalPath =
472
+ storagePaths?.globalPath ?? join(getCentralStoragePath(), "global");
473
+
474
+ // Write system prompt to temp file (tmpdir always exists)
740
475
  const geminiSystemPrompt = `${systemPrompt}
741
476
 
742
477
  ## Available Tools
@@ -746,65 +481,112 @@ You have access to the following tools to manage memory files:
746
481
  \${AvailableTools}
747
482
 
748
483
  Use these tools to read existing memories, write updates, and manage the memory filesystem.
749
- `
750
- const tempPromptPath = join(homedir(), '.local', 'share', 'memory', '.gemini-manager-prompt.md')
484
+ `;
485
+ const tempPromptPath = getManagerPromptPath();
486
+ await Bun.write(tempPromptPath, geminiSystemPrompt);
751
487
 
752
- // Ensure directory exists
753
- const tempDir = join(homedir(), '.local', 'share', 'memory')
754
- if (!existsSync(tempDir)) {
755
- const { mkdirSync } = await import('fs')
756
- mkdirSync(tempDir, { recursive: true })
757
- }
488
+ // Copy user's Gemini settings to managerCwd with hooks disabled
489
+ // This prevents the manager's session from triggering hooks recursively
490
+ const userSettingsPath = join(homedir(), ".gemini", "settings.json");
491
+ const managerSettingsDir = join(managerCwd, ".gemini");
492
+ const managerSettingsPath = join(managerSettingsDir, "settings.json");
758
493
 
759
- await Bun.write(tempPromptPath, geminiSystemPrompt)
494
+ try {
495
+ // Disable memory hooks to prevent recursive curation
496
+ // Format: hooks.disabled array with hook command names
497
+ const settings = {
498
+ hooks: {
499
+ disabled: [
500
+ "inject-memories",
501
+ "load-session-primer",
502
+ "curate-memories",
503
+ "curate-memories",
504
+ ],
505
+ },
506
+ };
760
507
 
761
- logger.debug(`Manager Gemini: Starting management for project ${projectId}`, 'manager')
762
- logger.debug(`Manager Gemini: Processing ${result.memories.length} memories`, 'manager')
763
- logger.debug(`Manager Gemini: Including directory: ${join(homedir(), '.local', 'share', 'memory')}`, 'manager')
508
+ // Ensure .gemini directory exists in managerCwd
509
+ if (!existsSync(managerSettingsDir)) {
510
+ const { mkdirSync } = await import("fs");
511
+ mkdirSync(managerSettingsDir, { recursive: true });
512
+ }
764
513
 
765
- // Build CLI command (no --resume needed, manager operates on provided data)
766
- // CRITICAL: Add --include-directories to allow Gemini to access memory storage
767
- // Without this, Gemini is sandboxed to the project directory and can't read/write memories
768
- const memoryStoragePath = join(homedir(), '.local', 'share', 'memory')
769
- const args = [
770
- '-p', userMessage,
771
- '--output-format', 'json',
772
- '--yolo', // Auto-approve file operations
773
- '--include-directories', memoryStoragePath, // Allow access to memory storage
774
- ]
514
+ await Bun.write(managerSettingsPath, JSON.stringify(settings, null, 2));
515
+ logger.debug(
516
+ `Manager Gemini: Created settings with hooks disabled at ${managerSettingsPath}`,
517
+ "manager",
518
+ );
519
+ } catch (err: any) {
520
+ logger.debug(
521
+ `Manager Gemini: Could not create settings file: ${err.message}`,
522
+ "manager",
523
+ );
524
+ }
775
525
 
776
- logger.debug(`Manager Gemini: Spawning gemini CLI`, 'manager')
777
- logger.debug(`Manager Gemini: Running from directory: ${memoryStoragePath}`, 'manager')
526
+ logger.debug(
527
+ `Manager Gemini - Starting management for project ${projectId}`,
528
+ "manager",
529
+ );
530
+ logger.debug(
531
+ `Manager Gemini - Processing ${result.memories.length} memories`,
532
+ "manager",
533
+ );
534
+ logger.debug(
535
+ `Manager Gemini - Storage mode: ${getStorageMode(storagePaths)}, cwd: ${managerCwd}`,
536
+ "manager",
537
+ );
538
+
539
+ // Build CLI command
540
+ // - cwd gives write access to that tree
541
+ // - --include-directories adds read access to other paths
542
+ const args = [
543
+ "-p",
544
+ userMessage,
545
+ "--output-format",
546
+ "json",
547
+ "--yolo", // Auto-approve file operations
548
+ "--include-directories",
549
+ projectPath,
550
+ "--include-directories",
551
+ globalPath,
552
+ ];
553
+
554
+ logger.debug(
555
+ `Manager Gemini: Spawning gemini CLI from ${managerCwd}`,
556
+ "manager",
557
+ );
778
558
 
779
559
  // Execute CLI with system prompt via environment variable
780
- // CRITICAL: Run from memory storage directory so it becomes the primary workspace
781
- // This allows both READ and WRITE operations (--include-directories only allows reads)
782
- const proc = Bun.spawn(['gemini', ...args], {
783
- cwd: memoryStoragePath, // Run FROM memory directory to allow writes
560
+ // cwd determines write access, --include-directories adds read access
561
+ const proc = Bun.spawn(["gemini", ...args], {
562
+ cwd: managerCwd,
784
563
  env: {
785
564
  ...process.env,
786
- MEMORY_CURATOR_ACTIVE: '1', // Prevent recursive hook triggering
787
- GEMINI_SYSTEM_MD: tempPromptPath, // Inject our management prompt
565
+ MEMORY_CURATOR_ACTIVE: "1", // Prevent recursive hook triggering
566
+ GEMINI_SYSTEM_MD: tempPromptPath, // Inject our management prompt
788
567
  },
789
- stdout: 'pipe',
790
- stderr: 'pipe',
791
- })
568
+ stdout: "pipe",
569
+ stderr: "pipe",
570
+ });
792
571
 
793
572
  // Capture output
794
573
  const [stdout, stderr] = await Promise.all([
795
574
  new Response(proc.stdout).text(),
796
575
  new Response(proc.stderr).text(),
797
- ])
798
- const exitCode = await proc.exited
576
+ ]);
577
+ const exitCode = await proc.exited;
799
578
 
800
- logger.debug(`Manager Gemini: Exit code ${exitCode}`, 'manager')
579
+ logger.debug(`Manager Gemini - Exit code ${exitCode}`, "manager");
801
580
  if (stderr && stderr.trim()) {
802
- logger.debug(`Manager Gemini stderr: ${stderr}`, 'manager')
581
+ logger.debug(`Manager Gemini - stderr: ${stderr}`, "manager");
803
582
  }
804
583
 
805
584
  if (exitCode !== 0) {
806
- logger.debug(`Manager Gemini: Failed with exit code ${exitCode}`, 'manager')
807
- const errorMsg = stderr || `Exit code ${exitCode}`
585
+ logger.debug(
586
+ `Manager Gemini - Failed with exit code ${exitCode}`,
587
+ "manager",
588
+ );
589
+ const errorMsg = stderr || `Exit code ${exitCode}`;
808
590
  return {
809
591
  success: false,
810
592
  superseded: 0,
@@ -814,22 +596,31 @@ Use these tools to read existing memories, write updates, and manage the memory
814
596
  filesWritten: 0,
815
597
  primerUpdated: false,
816
598
  actions: [],
817
- summary: '',
818
- fullReport: `Error: Gemini CLI failed with exit code ${exitCode}\n${stderr}`,
599
+ summary: "",
600
+ fullReport: `Manager Gemini - Error: Gemini CLI failed with exit code ${exitCode}\n${stderr}`,
819
601
  error: errorMsg,
820
- }
602
+ };
821
603
  }
822
604
 
823
605
  // Parse Gemini JSON output
824
606
  // Note: Gemini CLI outputs log messages before AND after the JSON
825
607
  // We need to extract just the JSON object
826
- logger.debug(`Manager Gemini: Parsing response (${stdout.length} chars)`, 'manager')
608
+ logger.debug(
609
+ `Manager Gemini - Parsing response (${stdout.length} chars)`,
610
+ "manager",
611
+ );
827
612
  try {
828
613
  // Find the JSON object - it starts with { and we need to find the matching }
829
- const jsonStart = stdout.indexOf('{')
614
+ const jsonStart = stdout.indexOf("{");
830
615
  if (jsonStart === -1) {
831
- logger.debug('Manager Gemini: No JSON object found in output', 'manager')
832
- logger.debug(`Manager Gemini: Raw stdout: ${stdout.slice(0, 500)}`, 'manager')
616
+ logger.debug(
617
+ "Manager Gemini - No JSON object found in output",
618
+ "manager",
619
+ );
620
+ // logger.debug(
621
+ // `Manager Gemini - Raw stdout: ${stdout.slice(0, 500)}`,
622
+ // "manager",
623
+ // );
833
624
  return {
834
625
  success: false,
835
626
  superseded: 0,
@@ -839,25 +630,29 @@ Use these tools to read existing memories, write updates, and manage the memory
839
630
  filesWritten: 0,
840
631
  primerUpdated: false,
841
632
  actions: [],
842
- summary: 'No JSON in Gemini response',
843
- fullReport: 'Manager failed: No JSON object in Gemini CLI output',
844
- }
633
+ summary: "Manager Gemini - No JSON in Gemini response",
634
+ fullReport:
635
+ "Manager Gemini - Failed: No JSON object in Gemini CLI output",
636
+ };
845
637
  }
846
638
 
847
639
  // Find the matching closing brace by counting braces
848
- let braceCount = 0
849
- let jsonEnd = -1
640
+ let braceCount = 0;
641
+ let jsonEnd = -1;
850
642
  for (let i = jsonStart; i < stdout.length; i++) {
851
- if (stdout[i] === '{') braceCount++
852
- if (stdout[i] === '}') braceCount--
643
+ if (stdout[i] === "{") braceCount++;
644
+ if (stdout[i] === "}") braceCount--;
853
645
  if (braceCount === 0) {
854
- jsonEnd = i + 1
855
- break
646
+ jsonEnd = i + 1;
647
+ break;
856
648
  }
857
649
  }
858
650
 
859
651
  if (jsonEnd === -1) {
860
- logger.debug('Manager Gemini: Could not find matching closing brace', 'manager')
652
+ logger.debug(
653
+ "Manager Gemini - Could not find matching closing brace",
654
+ "manager",
655
+ );
861
656
  return {
862
657
  success: false,
863
658
  superseded: 0,
@@ -867,21 +662,25 @@ Use these tools to read existing memories, write updates, and manage the memory
867
662
  filesWritten: 0,
868
663
  primerUpdated: false,
869
664
  actions: [],
870
- summary: 'Incomplete JSON in Gemini response',
871
- fullReport: 'Manager failed: Could not find complete JSON object',
872
- }
665
+ summary: "Manager Gemini - Incomplete JSON in Gemini response",
666
+ fullReport:
667
+ "Manager Gemini - Failed: Could not find complete JSON object",
668
+ };
873
669
  }
874
670
 
875
- const jsonStr = stdout.slice(jsonStart, jsonEnd)
876
- logger.debug(`Manager Gemini: Extracted JSON (${jsonStr.length} chars)`, 'manager')
671
+ const jsonStr = stdout.slice(jsonStart, jsonEnd);
672
+ logger.debug(
673
+ `Manager Gemini - Extracted JSON (${jsonStr.length} chars)`,
674
+ "manager",
675
+ );
877
676
 
878
- const geminiOutput = JSON.parse(jsonStr)
677
+ const geminiOutput = JSON.parse(jsonStr);
879
678
 
880
679
  // Gemini returns { response: "...", stats: {...} }
881
- const aiResponse = geminiOutput.response || ''
680
+ const aiResponse = geminiOutput.response || "";
882
681
 
883
682
  if (!aiResponse) {
884
- logger.debug('Manager Gemini: No response field in output', 'manager')
683
+ logger.debug("Manager Gemini - No response field in output", "manager");
885
684
  return {
886
685
  success: true,
887
686
  superseded: 0,
@@ -891,20 +690,29 @@ Use these tools to read existing memories, write updates, and manage the memory
891
690
  filesWritten: 0,
892
691
  primerUpdated: false,
893
692
  actions: [],
894
- summary: 'No response from Gemini',
895
- fullReport: 'Management completed but no response returned',
896
- }
693
+ summary: "Managetr Gemini - No response from Gemini",
694
+ fullReport: "Management completed but no response returned",
695
+ };
897
696
  }
898
697
 
899
- logger.debug(`Manager Gemini: Got response (${aiResponse.length} chars)`, 'manager')
698
+ logger.debug(
699
+ `Manager Gemini - Got response (${aiResponse.length} chars)`,
700
+ "manager",
701
+ );
900
702
 
901
703
  // Parse using our existing SDK parser (same format expected)
902
- const result = this._parseSDKManagementResult(aiResponse)
903
- logger.debug(`Manager Gemini: Parsed result - superseded: ${result.superseded}, resolved: ${result.resolved}, linked: ${result.linked}`, 'manager')
904
- return result
704
+ const result = this._parseSDKManagementResult(aiResponse);
705
+ logger.debug(
706
+ `Manager Gemini - Parsed result - superseded: ${result.superseded}, resolved: ${result.resolved}, linked: ${result.linked}`,
707
+ "manager",
708
+ );
709
+ return result;
905
710
  } catch (error: any) {
906
- logger.debug(`Manager Gemini: Parse error: ${error.message}`, 'manager')
907
- logger.debug(`Manager Gemini: Raw stdout (first 500 chars): ${stdout.slice(0, 500)}`, 'manager')
711
+ logger.debug(`Manager Gemini - Parse error: ${error.message}`, "manager");
712
+ logger.debug(
713
+ `Manager Gemini - Raw stdout (first 500 chars): ${stdout.slice(0, 500)}`,
714
+ "manager",
715
+ );
908
716
  return {
909
717
  success: false,
910
718
  superseded: 0,
@@ -914,10 +722,10 @@ Use these tools to read existing memories, write updates, and manage the memory
914
722
  filesWritten: 0,
915
723
  primerUpdated: false,
916
724
  actions: [],
917
- summary: '',
918
- fullReport: `Error: Failed to parse Gemini response: ${error.message}`,
725
+ summary: "",
726
+ fullReport: `Manager Gemini - Error: Failed to parse Gemini response: ${error.message}`,
919
727
  error: error.message,
920
- }
728
+ };
921
729
  }
922
730
  }
923
731
  }
@@ -926,5 +734,5 @@ Use these tools to read existing memories, write updates, and manage the memory
926
734
  * Create a new manager
927
735
  */
928
736
  export function createManager(config?: ManagerConfig): Manager {
929
- return new Manager(config)
737
+ return new Manager(config);
930
738
  }