@exaudeus/memory-mcp 1.9.5 → 1.9.6

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.
@@ -35,6 +35,12 @@ export declare class ConfigManager {
35
35
  constructor(configPath: string, initial: LoadedConfig, initialStores: Map<string, MarkdownMemoryStore>, initialHealth: Map<string, LobeHealth>);
36
36
  /** Derive alwaysInclude lobe names from config — pure, no side effects. */
37
37
  private static computeAlwaysIncludeLobes;
38
+ /**
39
+ * Adopt a newly created config file that didn't exist at startup.
40
+ * Used by memory_bootstrap when it creates memory-config.json from scratch.
41
+ * Switches the manager from default/env mode to file-based mode and reloads.
42
+ */
43
+ adoptNewConfigFile(filePath: string): Promise<void>;
38
44
  /**
39
45
  * Ensure config is fresh. Call at the start of every tool handler.
40
46
  * Stats config file, reloads if mtime changed. Graceful on all errors.
@@ -36,6 +36,17 @@ export class ConfigManager {
36
36
  .filter(([, config]) => config.alwaysInclude === true)
37
37
  .map(([name]) => name);
38
38
  }
39
+ /**
40
+ * Adopt a newly created config file that didn't exist at startup.
41
+ * Used by memory_bootstrap when it creates memory-config.json from scratch.
42
+ * Switches the manager from default/env mode to file-based mode and reloads.
43
+ */
44
+ async adoptNewConfigFile(filePath) {
45
+ this.configPath = filePath;
46
+ this.configOrigin = { source: 'file', path: filePath };
47
+ this.configMtime = 0; // Force reload on next ensureFresh
48
+ await this.ensureFresh();
49
+ }
39
50
  /**
40
51
  * Ensure config is fresh. Call at the start of every tool handler.
41
52
  * Stats config file, reloads if mtime changed. Graceful on all errors.
package/dist/index.js CHANGED
@@ -201,6 +201,13 @@ async function resolveLobesForRead(isFirstMemoryToolCall = true) {
201
201
  /** Build the shared lobe property for tool schemas — called on each ListTools request
202
202
  * so the description and enum stay in sync after a hot-reload adds or removes lobes. */
203
203
  function buildLobeProperty(currentLobeNames) {
204
+ if (currentLobeNames.length === 0) {
205
+ return {
206
+ type: 'string',
207
+ description: 'Memory lobe name. No lobes configured yet — run memory_bootstrap(lobe: "your-project", root: "/absolute/path/to/repo") first.',
208
+ enum: undefined,
209
+ };
210
+ }
204
211
  const isSingle = currentLobeNames.length === 1;
205
212
  return {
206
213
  type: 'string',
@@ -563,6 +570,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
563
570
  sections.push(crashSection);
564
571
  if (degradedSection)
565
572
  sections.push(degradedSection);
573
+ // Zero lobes — guide agent to bootstrap before anything else
574
+ if (configManager.getLobeNames().length === 0) {
575
+ sections.push('## No Lobes Configured\n\n' +
576
+ 'No memory lobes exist yet. Run **memory_bootstrap** to create one for this project:\n\n' +
577
+ '```\nmemory_bootstrap(lobe: "your-project-name", root: "/absolute/path/to/repo")\n```\n\n' +
578
+ 'After bootstrapping, call **brief** again to load project context.');
579
+ return { content: [{ type: 'text', text: sections.join('\n\n---\n\n') }] };
580
+ }
566
581
  // Collect briefing across all lobes (or specified lobe + alwaysInclude)
567
582
  const briefingLobeNames = allBriefingLobes;
568
583
  const allStale = [];
@@ -1492,39 +1507,44 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1492
1507
  root: z.string().optional(),
1493
1508
  budgetMB: z.number().positive().optional(),
1494
1509
  }).parse(args);
1495
- // Auto-create lobe: if the lobe is unknown AND root is provided AND config is
1496
- // file-based, write the new lobe entry into memory-config.json and hot-reload.
1497
- // This lets the agent bootstrap a brand-new repo in a single tool call.
1510
+ // Auto-create lobe: if the lobe is unknown AND root is provided, write the new
1511
+ // lobe entry into memory-config.json and hot-reload. If no config file exists yet,
1512
+ // create one at cwd()/memory-config.json this is the zero-config bootstrap path.
1498
1513
  if (rawLobe && root && !configManager.getStore(rawLobe)) {
1499
1514
  const origin = configManager.getConfigOrigin();
1500
- if (origin.source !== 'file') {
1501
- return {
1502
- content: [{
1503
- type: 'text',
1504
- text: `Cannot auto-add lobe "${rawLobe}": config is not file-based (source: ${origin.source}).\n\n` +
1505
- `Create memory-config.json next to the memory MCP server with a "lobes" block, then retry.`,
1506
- }],
1507
- isError: true,
1508
- };
1509
- }
1515
+ // Determine the config file path to write to:
1516
+ // - If already file-based, use the existing file
1517
+ // - Otherwise, create memory-config.json in cwd() (the server's working directory)
1518
+ const targetConfigPath = origin.source === 'file'
1519
+ ? origin.path
1520
+ : path.join(process.cwd(), 'memory-config.json');
1510
1521
  try {
1511
- const raw = await readFile(origin.path, 'utf-8');
1512
- const config = JSON.parse(raw);
1522
+ let config = {};
1523
+ if (origin.source === 'file') {
1524
+ const raw = await readFile(targetConfigPath, 'utf-8');
1525
+ config = JSON.parse(raw);
1526
+ }
1513
1527
  if (!config.lobes || typeof config.lobes !== 'object')
1514
1528
  config.lobes = {};
1515
1529
  config.lobes[rawLobe] = { root, budgetMB: budgetMB ?? 2 };
1516
- await writeFile(origin.path, JSON.stringify(config, null, 2) + '\n', 'utf-8');
1517
- process.stderr.write(`[memory-mcp] Auto-added lobe "${rawLobe}" (root: ${root}) to memory-config.json\n`);
1530
+ await writeFile(targetConfigPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
1531
+ process.stderr.write(`[memory-mcp] Auto-added lobe "${rawLobe}" (root: ${root}) to ${targetConfigPath}\n`);
1518
1532
  }
1519
1533
  catch (err) {
1520
1534
  const message = err instanceof Error ? err.message : String(err);
1521
1535
  return {
1522
- content: [{ type: 'text', text: `Failed to auto-add lobe "${rawLobe}" to memory-config.json: ${message}` }],
1536
+ content: [{ type: 'text', text: `Failed to write lobe "${rawLobe}" to ${targetConfigPath}: ${message}` }],
1523
1537
  isError: true,
1524
1538
  };
1525
1539
  }
1526
- // Reload config to pick up the new lobe (hot-reload detects the updated mtime)
1527
- await configManager.ensureFresh();
1540
+ // Reload config to pick up the new lobe.
1541
+ // If the file was just created from scratch, adopt it as the new config source.
1542
+ if (origin.source !== 'file') {
1543
+ await configManager.adoptNewConfigFile(targetConfigPath);
1544
+ }
1545
+ else {
1546
+ await configManager.ensureFresh();
1547
+ }
1528
1548
  }
1529
1549
  // Resolve store — after this point, rawLobe is never used again
1530
1550
  const ctx = resolveToolContext(rawLobe);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/memory-mcp",
3
- "version": "1.9.5",
3
+ "version": "1.9.6",
4
4
  "description": "Codebase memory MCP server - persistent, evolving knowledge for AI coding agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",