claude-memory-layer 1.0.42 → 1.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -10,7 +10,7 @@ const __dirname = dirname(__filename);
10
10
  import { Command } from "commander";
11
11
  import { exec } from "child_process";
12
12
  import * as fs18 from "fs";
13
- import * as path23 from "path";
13
+ import * as path24 from "path";
14
14
  import * as os13 from "os";
15
15
 
16
16
  // src/services/memory-service.ts
@@ -2241,12 +2241,12 @@ function resolveProjectStoragePath(projectOrHash) {
2241
2241
  import Database from "better-sqlite3";
2242
2242
  import * as fs4 from "fs";
2243
2243
  import * as nodePath from "path";
2244
- function createSQLiteDatabase(path24, options) {
2245
- const dir = nodePath.dirname(path24);
2244
+ function createSQLiteDatabase(path25, options) {
2245
+ const dir = nodePath.dirname(path25);
2246
2246
  if (!fs4.existsSync(dir)) {
2247
2247
  fs4.mkdirSync(dir, { recursive: true });
2248
2248
  }
2249
- const db = new Database(path24, {
2249
+ const db = new Database(path25, {
2250
2250
  readonly: options?.readonly ?? false
2251
2251
  });
2252
2252
  if (!options?.readonly && (options?.walMode ?? true)) {
@@ -2697,18 +2697,18 @@ function emptyOutboxRecoveryResult() {
2697
2697
  function isRecord(value) {
2698
2698
  return typeof value === "object" && value !== null && !Array.isArray(value);
2699
2699
  }
2700
- function getNestedRecord(root, path24) {
2700
+ function getNestedRecord(root, path25) {
2701
2701
  let cursor = root;
2702
- for (const key of path24) {
2702
+ for (const key of path25) {
2703
2703
  if (!isRecord(cursor))
2704
2704
  return void 0;
2705
2705
  cursor = cursor[key];
2706
2706
  }
2707
2707
  return isRecord(cursor) ? cursor : void 0;
2708
2708
  }
2709
- function getNestedString(root, path24) {
2709
+ function getNestedString(root, path25) {
2710
2710
  let cursor = root;
2711
- for (const key of path24) {
2711
+ for (const key of path25) {
2712
2712
  if (!isRecord(cursor))
2713
2713
  return void 0;
2714
2714
  cursor = cursor[key];
@@ -7777,9 +7777,9 @@ function safeParseObject(value) {
7777
7777
  return void 0;
7778
7778
  }
7779
7779
  }
7780
- function nestedString(root, path24) {
7780
+ function nestedString(root, path25) {
7781
7781
  let cursor = root;
7782
- for (const key of path24) {
7782
+ for (const key of path25) {
7783
7783
  if (!isRecord2(cursor))
7784
7784
  return void 0;
7785
7785
  cursor = cursor[key];
@@ -7856,12 +7856,12 @@ var GraphPathService = class {
7856
7856
  }
7857
7857
  }
7858
7858
  }
7859
- const paths = Array.from(bestByTarget.entries()).map(([key, path24]) => ({
7859
+ const paths = Array.from(bestByTarget.entries()).map(([key, path25]) => ({
7860
7860
  target: graph.node(nodeFromKey(key)),
7861
- hops: path24.hops,
7862
- totalCost: path24.totalCost,
7863
- scoreContribution: path24.totalCost > 0 ? 1 / path24.totalCost : 0,
7864
- steps: path24.steps
7861
+ hops: path25.hops,
7862
+ totalCost: path25.totalCost,
7863
+ scoreContribution: path25.totalCost > 0 ? 1 / path25.totalCost : 0,
7864
+ steps: path25.steps
7865
7865
  })).sort((a, b) => b.scoreContribution - a.scoreContribution || a.hops - b.hops || a.target.name.localeCompare(b.target.name)).slice(0, maxResults);
7866
7866
  return { startNodes, effectiveMaxHops, paths };
7867
7867
  }
@@ -10843,14 +10843,14 @@ var Retriever = class {
10843
10843
  direction: "both"
10844
10844
  });
10845
10845
  const titleByEntityId = new Map(startNodes.map((node) => [node.entityId, node.title]));
10846
- for (const path24 of expansion.paths) {
10847
- if (path24.target.type !== "event")
10846
+ for (const path25 of expansion.paths) {
10847
+ if (path25.target.type !== "event")
10848
10848
  continue;
10849
- const target = await this.eventStore.getEvent(path24.target.id);
10849
+ const target = await this.eventStore.getEvent(path25.target.id);
10850
10850
  if (!target)
10851
10851
  continue;
10852
- const graphPath = toRetrievalGraphPathDebug(path24, titleByEntityId);
10853
- const score = graphPathScore(path24, opts.hopPenalty);
10852
+ const graphPath = toRetrievalGraphPathDebug(path25, titleByEntityId);
10853
+ const score = graphPathScore(path25, opts.hopPenalty);
10854
10854
  const existing = byId.get(target.id);
10855
10855
  const graphPaths = mergeGraphPaths(existing?.graphPaths ?? [], [graphPath]);
10856
10856
  const graphLane = {
@@ -10859,7 +10859,7 @@ var Retriever = class {
10859
10859
  score
10860
10860
  };
10861
10861
  const row = {
10862
- id: existing?.id ?? `graph-path-${path24.hops}-${target.id}`,
10862
+ id: existing?.id ?? `graph-path-${path25.hops}-${target.id}`,
10863
10863
  eventId: target.id,
10864
10864
  content: target.content,
10865
10865
  score: Math.max(existing?.score ?? 0, score),
@@ -11206,8 +11206,8 @@ _Context:_ ${sessionContext}`;
11206
11206
  matchesMetadataScope(metadata, expected) {
11207
11207
  if (!metadata)
11208
11208
  return false;
11209
- return Object.entries(expected).every(([path24, value]) => {
11210
- const actual = path24.split(".").reduce((acc, key) => {
11209
+ return Object.entries(expected).every(([path25, value]) => {
11210
+ const actual = path25.split(".").reduce((acc, key) => {
11211
11211
  if (typeof acc !== "object" || acc === null)
11212
11212
  return void 0;
11213
11213
  return acc[key];
@@ -11267,22 +11267,22 @@ function uniqueEntityStartNodes(candidates) {
11267
11267
  }
11268
11268
  return nodes;
11269
11269
  }
11270
- function toRetrievalGraphPathDebug(path24, titleByEntityId) {
11271
- const firstStep = path24.steps[0];
11270
+ function toRetrievalGraphPathDebug(path25, titleByEntityId) {
11271
+ const firstStep = path25.steps[0];
11272
11272
  const startNode = firstStep?.direction === "incoming" ? firstStep.to : firstStep?.from;
11273
11273
  const startEntityId = startNode?.type === "entity" ? startNode.id : "";
11274
11274
  return {
11275
11275
  startEntityId,
11276
11276
  startEntityTitle: titleByEntityId.get(startEntityId) ?? startNode?.name,
11277
- targetId: path24.target.id,
11278
- targetType: path24.target.type,
11279
- hops: path24.hops,
11280
- relationPath: path24.steps.map((step) => step.relationType)
11277
+ targetId: path25.target.id,
11278
+ targetType: path25.target.type,
11279
+ hops: path25.hops,
11280
+ relationPath: path25.steps.map((step) => step.relationType)
11281
11281
  };
11282
11282
  }
11283
- function graphPathScore(path24, hopPenalty) {
11284
- const base = Math.min(0.95, Math.max(0, path24.scoreContribution));
11285
- return Math.max(0.05, base - hopPenalty * Math.max(0, path24.hops - 1));
11283
+ function graphPathScore(path25, hopPenalty) {
11284
+ const base = Math.min(0.95, Math.max(0, path25.scoreContribution));
11285
+ return Math.max(0.05, base - hopPenalty * Math.max(0, path25.hops - 1));
11286
11286
  }
11287
11287
  function clampGraphHops(maxHops) {
11288
11288
  if (!Number.isFinite(maxHops))
@@ -11291,15 +11291,15 @@ function clampGraphHops(maxHops) {
11291
11291
  }
11292
11292
  function mergeGraphPaths(existing, incoming) {
11293
11293
  const byKey = /* @__PURE__ */ new Map();
11294
- for (const path24 of [...existing, ...incoming]) {
11295
- const key = [path24.startEntityId, path24.targetType, path24.targetId, path24.hops, ...path24.relationPath].join("\0");
11294
+ for (const path25 of [...existing, ...incoming]) {
11295
+ const key = [path25.startEntityId, path25.targetType, path25.targetId, path25.hops, ...path25.relationPath].join("\0");
11296
11296
  if (!byKey.has(key))
11297
- byKey.set(key, path24);
11297
+ byKey.set(key, path25);
11298
11298
  }
11299
11299
  return [...byKey.values()].sort((a, b) => a.hops - b.hops || compareStable(graphPathSignature(a), graphPathSignature(b))).slice(0, 3);
11300
11300
  }
11301
- function graphPathSignature(path24) {
11302
- return [path24.startEntityId, path24.targetType, path24.targetId, path24.hops, ...path24.relationPath].join("|");
11301
+ function graphPathSignature(path25) {
11302
+ return [path25.startEntityId, path25.targetType, path25.targetId, path25.hops, ...path25.relationPath].join("|");
11303
11303
  }
11304
11304
  function compareStable(a, b) {
11305
11305
  if (a < b)
@@ -21007,6 +21007,9 @@ function formatDuration2(ms) {
21007
21007
  return remainingHours === 0 ? `${days}d` : `${days}d ${remainingHours}h`;
21008
21008
  }
21009
21009
 
21010
+ // src/apps/cli/mongo-sync-command.ts
21011
+ import * as path23 from "node:path";
21012
+
21010
21013
  // src/core/worker-lock.ts
21011
21014
  import { closeSync, existsSync as existsSync14, mkdirSync as mkdirSync10, openSync, readFileSync as readFileSync7, unlinkSync, writeFileSync as writeFileSync7 } from "node:fs";
21012
21015
  import * as os12 from "node:os";
@@ -21151,24 +21154,129 @@ function defaultIsProcessRunning(pid) {
21151
21154
  }
21152
21155
  }
21153
21156
 
21157
+ // src/apps/cli/mongo-sync-command.ts
21158
+ var DEFAULT_PROCESS_INTERVAL_MS = 12e4;
21159
+ function resolveMongoSyncProcessOptions(options, cwd = process.cwd(), deps = {}) {
21160
+ const explicitProject = options.project;
21161
+ if (explicitProject !== void 0 && explicitProject.trim().length === 0) {
21162
+ throw new Error("--project must not be empty");
21163
+ }
21164
+ const explicitLockPath = options.processLockPath;
21165
+ if (explicitLockPath !== void 0 && explicitLockPath.trim().length === 0) {
21166
+ throw new Error("--process-lock-path must not be empty");
21167
+ }
21168
+ const projectPath = explicitProject ?? cwd;
21169
+ const processIntervalMs = parsePositiveIntegerOption2(
21170
+ options.processInterval ?? String(DEFAULT_PROCESS_INTERVAL_MS),
21171
+ "--process-interval"
21172
+ );
21173
+ const getProjectStoragePath2 = deps.getProjectStoragePath ?? getProjectStoragePath;
21174
+ return {
21175
+ projectPath,
21176
+ processAfterSync: options.processAfterSync === true,
21177
+ processIntervalMs,
21178
+ lockPath: explicitLockPath ?? path23.join(getProjectStoragePath2(projectPath), "vector-worker.lock")
21179
+ };
21180
+ }
21181
+ function createMongoSyncPostProcessor(options, deps = {}) {
21182
+ const now = deps.now ?? (() => Date.now());
21183
+ const processOnce = deps.processOnce ?? processProjectEmbeddingsOnce;
21184
+ const log = deps.log ?? (() => void 0);
21185
+ let lastProcessAtMs = null;
21186
+ return {
21187
+ async afterSync(result) {
21188
+ if (!options.processAfterSync)
21189
+ return;
21190
+ if (result.pulled <= 0)
21191
+ return;
21192
+ const currentTimeMs = now();
21193
+ if (lastProcessAtMs !== null && currentTimeMs - lastProcessAtMs < options.processIntervalMs) {
21194
+ return;
21195
+ }
21196
+ lastProcessAtMs = currentTimeMs;
21197
+ log(`[mongo-sync] Processing pending embeddings after pulling ${result.pulled} events...`);
21198
+ let outcome;
21199
+ try {
21200
+ outcome = await processOnce({
21201
+ projectPath: options.projectPath,
21202
+ lockPath: options.lockPath
21203
+ });
21204
+ } catch (error) {
21205
+ const message = error instanceof Error ? error.message : String(error);
21206
+ log(`[mongo-sync] Process-after-sync failed: ${message}`);
21207
+ return;
21208
+ }
21209
+ if (outcome.skipped) {
21210
+ const holder = outcome.holderPid === void 0 || outcome.holderPid === null ? "unknown" : String(outcome.holderPid);
21211
+ log(`[mongo-sync] Skipped embedding processing because another vector worker is running (holderPid=${holder})`);
21212
+ return;
21213
+ }
21214
+ log(`[mongo-sync] Processed ${outcome.processed} embeddings after sync`);
21215
+ }
21216
+ };
21217
+ }
21218
+ async function processProjectEmbeddingsOnce(input, deps = {}) {
21219
+ const createWorkerLock = deps.createWorkerLock ?? ((lockPath) => new WorkerLock(lockPath));
21220
+ const createService = deps.createMemoryService ?? createMemoryService;
21221
+ const getProjectStoragePath2 = deps.getProjectStoragePath ?? getProjectStoragePath;
21222
+ const hashProjectPath2 = deps.hashProjectPath ?? hashProjectPath;
21223
+ const workerLock = createWorkerLock(input.lockPath);
21224
+ const lockResult = workerLock.acquire();
21225
+ if (!lockResult.acquired) {
21226
+ return {
21227
+ skipped: true,
21228
+ processed: 0,
21229
+ holderPid: "holderPid" in lockResult ? lockResult.holderPid : null
21230
+ };
21231
+ }
21232
+ let service;
21233
+ try {
21234
+ service = createService({
21235
+ storagePath: getProjectStoragePath2(input.projectPath),
21236
+ projectHash: hashProjectPath2(input.projectPath),
21237
+ projectPath: input.projectPath,
21238
+ sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG,
21239
+ analyticsEnabled: false
21240
+ });
21241
+ await service.initialize();
21242
+ await service.recoverStuckOutboxItems();
21243
+ const processed = await service.processPendingEmbeddings();
21244
+ return { skipped: false, processed };
21245
+ } finally {
21246
+ workerLock.release();
21247
+ await service?.shutdown().catch(() => void 0);
21248
+ }
21249
+ }
21250
+ function parsePositiveIntegerOption2(raw, flagName) {
21251
+ const value = raw.trim();
21252
+ if (!/^\d+$/.test(value)) {
21253
+ throw new Error(`${flagName} must be a positive integer number of milliseconds`);
21254
+ }
21255
+ const parsed = Number(value);
21256
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) {
21257
+ throw new Error(`${flagName} must be a positive integer number of milliseconds`);
21258
+ }
21259
+ return parsed;
21260
+ }
21261
+
21154
21262
  // src/apps/cli/index.ts
21155
- var CLAUDE_SETTINGS_PATH = path23.join(os13.homedir(), ".claude", "settings.json");
21263
+ var CLAUDE_SETTINGS_PATH = path24.join(os13.homedir(), ".claude", "settings.json");
21156
21264
  function getPluginPath() {
21157
21265
  const possiblePaths = [
21158
- path23.join(__dirname, ".."),
21266
+ path24.join(__dirname, ".."),
21159
21267
  // When running from dist/cli
21160
- path23.join(__dirname, "../..", "dist"),
21268
+ path24.join(__dirname, "../..", "dist"),
21161
21269
  // When running from src
21162
- path23.join(process.cwd(), "dist")
21270
+ path24.join(process.cwd(), "dist")
21163
21271
  // Current working directory
21164
21272
  ];
21165
21273
  for (const p of possiblePaths) {
21166
- const hooksPath = path23.join(p, "hooks", "user-prompt-submit.js");
21274
+ const hooksPath = path24.join(p, "hooks", "user-prompt-submit.js");
21167
21275
  if (fs18.existsSync(hooksPath)) {
21168
21276
  return p;
21169
21277
  }
21170
21278
  }
21171
- return path23.join(os13.homedir(), ".npm-global", "lib", "node_modules", "claude-memory-layer", "dist");
21279
+ return path24.join(os13.homedir(), ".npm-global", "lib", "node_modules", "claude-memory-layer", "dist");
21172
21280
  }
21173
21281
  function loadClaudeSettings() {
21174
21282
  try {
@@ -21182,7 +21290,7 @@ function loadClaudeSettings() {
21182
21290
  return {};
21183
21291
  }
21184
21292
  function saveClaudeSettings(settings) {
21185
- const dir = path23.dirname(CLAUDE_SETTINGS_PATH);
21293
+ const dir = path24.dirname(CLAUDE_SETTINGS_PATH);
21186
21294
  if (!fs18.existsSync(dir)) {
21187
21295
  fs18.mkdirSync(dir, { recursive: true });
21188
21296
  }
@@ -21211,7 +21319,7 @@ function parseMarketProviders(value) {
21211
21319
  }
21212
21320
  return selected;
21213
21321
  }
21214
- function parsePositiveIntegerOption2(value, optionName) {
21322
+ function parsePositiveIntegerOption3(value, optionName) {
21215
21323
  if (value === void 0)
21216
21324
  return void 0;
21217
21325
  const normalized = value.trim();
@@ -21246,14 +21354,14 @@ async function runCodexValidationCommand(options) {
21246
21354
  const report = await validateCodexSessions({
21247
21355
  sessionsDir: options.sessionsDir,
21248
21356
  projectPath: options.project,
21249
- limit: parsePositiveIntegerOption2(options.limit, "limit"),
21357
+ limit: parsePositiveIntegerOption3(options.limit, "limit"),
21250
21358
  anonymizeProjects: options.anonymizeProjects === true
21251
21359
  });
21252
21360
  const rendered = formatCodexValidationReport(report, format);
21253
21361
  process.stdout.write(rendered.endsWith("\n") ? rendered : `${rendered}
21254
21362
  `);
21255
21363
  if (options.output) {
21256
- const outputPath = path23.resolve(options.output);
21364
+ const outputPath = path24.resolve(options.output);
21257
21365
  writeCodexValidationReport(outputPath, report, format);
21258
21366
  console.log(`
21259
21367
  Report written to ${outputPath}`);
@@ -21267,13 +21375,13 @@ async function runHermesValidationCommand(options) {
21267
21375
  const report = await validateHermesSessions({
21268
21376
  stateDbPath: options.stateDb,
21269
21377
  projectPath: options.project,
21270
- limit: parsePositiveIntegerOption2(options.limit, "limit")
21378
+ limit: parsePositiveIntegerOption3(options.limit, "limit")
21271
21379
  });
21272
21380
  const rendered = formatHermesValidationReport(report, format);
21273
21381
  process.stdout.write(rendered.endsWith("\n") ? rendered : `${rendered}
21274
21382
  `);
21275
21383
  if (options.output) {
21276
- const outputPath = path23.resolve(options.output);
21384
+ const outputPath = path24.resolve(options.output);
21277
21385
  writeHermesValidationReport(outputPath, report, format);
21278
21386
  console.log(`
21279
21387
  Report written to ${outputPath}`);
@@ -21307,14 +21415,14 @@ var OPERATION_PRIVACY_CONFIG = {
21307
21415
  }
21308
21416
  };
21309
21417
  function resolveOperationProject(project) {
21310
- const projectPath = path23.resolve(project ?? process.cwd());
21418
+ const projectPath = path24.resolve(project ?? process.cwd());
21311
21419
  const projectHash = hashProjectPath(projectPath);
21312
21420
  const storagePath = getProjectStoragePath(projectPath);
21313
21421
  return {
21314
21422
  projectPath,
21315
21423
  projectHash,
21316
21424
  storagePath,
21317
- dbPath: path23.join(storagePath, "events.sqlite")
21425
+ dbPath: path24.join(storagePath, "events.sqlite")
21318
21426
  };
21319
21427
  }
21320
21428
  function openOperationReadDatabase(context) {
@@ -21544,7 +21652,7 @@ async function runOperationCli(action, label) {
21544
21652
  }
21545
21653
  }
21546
21654
  var program = new Command();
21547
- program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.42");
21655
+ program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.44");
21548
21656
  program.command("market-context").description("Fetch read-only DART/FRED/Finnhub context with structured MarketContextSnapshot bull/bear/risk/catalyst analysis").option("--company <name>", "Company name for DART fallback search and report subject").option("--dart-corp-code <code>", "Exact DART corp_code for issuer-specific filings").option("--symbol <ticker>", "Listed ticker for Finnhub company profile").option("--providers <list>", "Comma-separated providers: dart,fred,finnhub").option("--fred-series <list>", "Comma-separated FRED series IDs").option("--json", "Print structured JSON including analysis.marketSnapshot").option("--no-snapshot", "Disable MarketContextSnapshot and DART company snapshot analysis").action(async (options) => {
21549
21657
  try {
21550
21658
  await runMarketContextCommand(options);
@@ -21557,7 +21665,7 @@ program.command("install").description("Install hooks into Claude Code settings"
21557
21665
  try {
21558
21666
  const pluginPath = options.path || getPluginPath();
21559
21667
  const missingHooks = REQUIRED_HOOK_FILES.filter(
21560
- (file) => !fs18.existsSync(path23.join(pluginPath, "hooks", file))
21668
+ (file) => !fs18.existsSync(path24.join(pluginPath, "hooks", file))
21561
21669
  );
21562
21670
  if (missingHooks.length > 0) {
21563
21671
  console.error(`
@@ -21623,7 +21731,7 @@ program.command("status").description("Check plugin installation status").action
21623
21731
  console.log(` PostToolUse: ${hasPostToolHook ? "\u2705 Installed" : "\u274C Not installed"}`);
21624
21732
  console.log(` Stop: ${hasStopHook ? "\u2705 Installed" : "\u274C Not installed"}`);
21625
21733
  console.log(` SessionEnd: ${hasSessionEndHook ? "\u2705 Installed" : "\u274C Not installed"}`);
21626
- const hooksExist = REQUIRED_HOOK_FILES.every((file) => fs18.existsSync(path23.join(pluginPath, "hooks", file)));
21734
+ const hooksExist = REQUIRED_HOOK_FILES.every((file) => fs18.existsSync(path24.join(pluginPath, "hooks", file)));
21627
21735
  console.log(`
21628
21736
  Plugin files: ${hooksExist ? "\u2705 Found" : "\u274C Not found"}`);
21629
21737
  console.log(` Path: ${pluginPath}`);
@@ -21857,7 +21965,7 @@ repairCommand.command("legacy-project-scope").description("Dry-run or apply proj
21857
21965
  apply: options.apply
21858
21966
  });
21859
21967
  const storagePath = projectPath ? getProjectStoragePath(projectPath) : resolveProjectStoragePath(repairOptions.projectHash);
21860
- const dbPath = path23.join(storagePath, "events.sqlite");
21968
+ const dbPath = path24.join(storagePath, "events.sqlite");
21861
21969
  if (repairOptions.dryRun && !fs18.existsSync(dbPath)) {
21862
21970
  const projectHash = repairOptions.projectHash || hashProjectPath(repairOptions.projectPath);
21863
21971
  console.log(formatLegacyProjectScopeRepairResult({
@@ -22108,7 +22216,7 @@ retentionCommand.command("audit").description("Run a dry-run retention audit for
22108
22216
  });
22109
22217
  const projectHash = auditOptions.projectHash ?? hashProjectPath(auditOptions.projectPath);
22110
22218
  const storagePath = auditOptions.projectPath ? getProjectStoragePath(auditOptions.projectPath) : resolveProjectStoragePath(projectHash);
22111
- const dbPath = path23.join(storagePath, "events.sqlite");
22219
+ const dbPath = path24.join(storagePath, "events.sqlite");
22112
22220
  if (!fs18.existsSync(dbPath)) {
22113
22221
  const emptyReport = emptyRetentionAuditReport(projectHash, auditOptions.limit);
22114
22222
  console.log(formatRetentionAuditReport(emptyReport, { json: auditOptions.json }));
@@ -22132,11 +22240,11 @@ retentionCommand.command("audit").description("Run a dry-run retention audit for
22132
22240
  process.exit(1);
22133
22241
  }
22134
22242
  });
22135
- program.command("mongo-sync").description("Sync events with MongoDB for multi-server collaboration (optional)").option("-p, --project <path>", "Project path (defaults to cwd)").option("--mongo-uri <uri>", "MongoDB connection URI (env: CLAUDE_MEMORY_MONGO_URI)").option("--mongo-db <name>", "MongoDB database name (env: CLAUDE_MEMORY_MONGO_DB)").option("--mongo-project <key>", "Remote project key (env: CLAUDE_MEMORY_MONGO_PROJECT, default: basename(projectPath))").option("--direction <dir>", "push|pull|both", "both").option("--batch-size <n>", "Batch size", "500").option("--interval <ms>", "Watch interval ms", "30000").option("--watch", "Run continuously").action(async (options) => {
22243
+ program.command("mongo-sync").description("Sync events with MongoDB for multi-server collaboration (optional)").option("-p, --project <path>", "Project path (defaults to cwd)").option("--mongo-uri <uri>", "MongoDB connection URI (env: CLAUDE_MEMORY_MONGO_URI)").option("--mongo-db <name>", "MongoDB database name (env: CLAUDE_MEMORY_MONGO_DB)").option("--mongo-project <key>", "Remote project key (env: CLAUDE_MEMORY_MONGO_PROJECT, default: basename(projectPath))").option("--direction <dir>", "push|pull|both", "both").option("--batch-size <n>", "Batch size", "500").option("--interval <ms>", "Watch interval ms", "30000").option("--watch", "Run continuously").option("--process-after-sync", "Process pending embeddings after pull activity").option("--process-interval <ms>", "Minimum ms between process runs when --process-after-sync is enabled", "120000").option("--process-lock-path <path>", "Override process-after-sync lock path (advanced)").action(async (options) => {
22136
22244
  const projectPath = options.project || process.cwd();
22137
22245
  const mongoUri = options.mongoUri || process.env.CLAUDE_MEMORY_MONGO_URI;
22138
22246
  const mongoDb = options.mongoDb || process.env.CLAUDE_MEMORY_MONGO_DB;
22139
- const projectKey = options.mongoProject || process.env.CLAUDE_MEMORY_MONGO_PROJECT || path23.basename(projectPath);
22247
+ const projectKey = options.mongoProject || process.env.CLAUDE_MEMORY_MONGO_PROJECT || path24.basename(projectPath);
22140
22248
  const direction = String(options.direction || "both").toLowerCase();
22141
22249
  if (!mongoUri || !mongoDb) {
22142
22250
  console.error("\n\u274C MongoDB sync is not configured.");
@@ -22147,15 +22255,33 @@ program.command("mongo-sync").description("Sync events with MongoDB for multi-se
22147
22255
  console.error("\n\u274C Invalid --direction. Use: push | pull | both\n");
22148
22256
  process.exit(1);
22149
22257
  }
22150
- const storagePath = getProjectStoragePath(projectPath);
22151
- if (!fs18.existsSync(storagePath)) {
22152
- fs18.mkdirSync(storagePath, { recursive: true });
22153
- }
22154
22258
  const batchSizeParsed = parseInt(options.batchSize, 10);
22155
22259
  const intervalParsed = parseInt(options.interval, 10);
22156
22260
  const batchSize = Number.isFinite(batchSizeParsed) && batchSizeParsed > 0 ? batchSizeParsed : 500;
22157
22261
  const intervalMs = Number.isFinite(intervalParsed) && intervalParsed > 0 ? intervalParsed : 3e4;
22158
- const sqliteStore = new SQLiteEventStore(path23.join(storagePath, "events.sqlite"));
22262
+ const processOptions = (() => {
22263
+ try {
22264
+ return resolveMongoSyncProcessOptions({
22265
+ project: projectPath,
22266
+ processAfterSync: options.processAfterSync,
22267
+ processInterval: options.processInterval,
22268
+ processLockPath: options.processLockPath
22269
+ });
22270
+ } catch (error) {
22271
+ const message = error instanceof Error ? error.message : String(error);
22272
+ console.error(`[mongo-sync] Failed: ${message}`);
22273
+ process.exit(1);
22274
+ }
22275
+ })();
22276
+ const storagePath = getProjectStoragePath(projectPath);
22277
+ if (!fs18.existsSync(storagePath)) {
22278
+ fs18.mkdirSync(storagePath, { recursive: true });
22279
+ }
22280
+ const postProcessor = createMongoSyncPostProcessor(processOptions, {
22281
+ log: (message) => process.stdout.write(`${message}
22282
+ `)
22283
+ });
22284
+ const sqliteStore = new SQLiteEventStore(path24.join(storagePath, "events.sqlite"));
22159
22285
  const worker = new MongoSyncWorker(sqliteStore, {
22160
22286
  uri: mongoUri,
22161
22287
  dbName: mongoDb,
@@ -22169,6 +22295,7 @@ program.command("mongo-sync").description("Sync events with MongoDB for multi-se
22169
22295
  const ts = (/* @__PURE__ */ new Date()).toISOString();
22170
22296
  process.stdout.write(`[mongo-sync] ${ts} project=${projectKey} pushed=${pushed} pulled=${pulled}
22171
22297
  `);
22298
+ await postProcessor.afterSync({ pushed, pulled });
22172
22299
  };
22173
22300
  try {
22174
22301
  if (!options.watch) {
@@ -22214,7 +22341,7 @@ function renderProgress(event) {
22214
22341
  break;
22215
22342
  case "session-start": {
22216
22343
  const pct = Math.round(event.sessionIndex / event.totalSessions * 100);
22217
- const sessionName = path23.basename(event.filePath, ".jsonl").slice(0, 8);
22344
+ const sessionName = path24.basename(event.filePath, ".jsonl").slice(0, 8);
22218
22345
  process.stdout.write(
22219
22346
  `\r \u{1F4C4} [${event.sessionIndex + 1}/${event.totalSessions}] ${pct}% | Session ${sessionName}... `
22220
22347
  );
@@ -22291,7 +22418,7 @@ async function listMarkdownFiles(root) {
22291
22418
  const dir = stack.pop();
22292
22419
  const entries = await fs18.promises.readdir(dir, { withFileTypes: true });
22293
22420
  for (const e of entries) {
22294
- const full = path23.join(dir, e.name);
22421
+ const full = path24.join(dir, e.name);
22295
22422
  if (e.isDirectory())
22296
22423
  stack.push(full);
22297
22424
  else if (e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md")
@@ -22301,8 +22428,8 @@ async function listMarkdownFiles(root) {
22301
22428
  return out.sort();
22302
22429
  }
22303
22430
  function deriveNamespaceCategory(sourceRoot, filePath) {
22304
- const rel = path23.relative(sourceRoot, filePath);
22305
- const dirSeg = path23.dirname(rel).split(path23.sep).filter(Boolean);
22431
+ const rel = path24.relative(sourceRoot, filePath);
22432
+ const dirSeg = path24.dirname(rel).split(path24.sep).filter(Boolean);
22306
22433
  if (dirSeg.length >= 2) {
22307
22434
  const namespace = sanitizeSegment3(dirSeg[0], "default");
22308
22435
  const categoryPath = dirSeg.slice(1).map((s) => sanitizeSegment3(s, "uncategorized"));
@@ -22321,8 +22448,8 @@ function extractImportEvidence(markdown) {
22321
22448
  program.command("organize-import [sourceDir]").description("Import existing markdown memory files, or bootstrap knowledge docs from codebase/git when markdown is missing").option("-p, --project <path>", "Project path (defaults to cwd)").option("--session <id>", "Session id for imported events (default: import:organized)").option("--limit <n>", "Limit number of files to import").option("--dry-run", "Preview mapping without writing").option("--bootstrap", "Force-generate structured markdown from codebase + git history before import").option("--bootstrap-if-empty", "Auto-bootstrap when source has no markdown files (default: true)", true).option("--no-bootstrap-if-empty", "Disable auto-bootstrap when source has no markdown files").option("--force-bootstrap", "Run bootstrap even when markdown files exist").option("--repo <path>", "Repository root for bootstrap analysis (default: project path)").option("--out <path>", "Output directory for generated bootstrap markdown (default: <sourceDir>/bootstrap-kb)").option("--since <range>", 'Git history range for bootstrap (default: "180 days ago")').option("--max-commits <n>", "Max commits to analyze for bootstrap (default: 1000)").option("--incremental", "Use previous bootstrap manifest as baseline for incremental updates (default: true)", true).option("--no-incremental", "Disable incremental bootstrap; regenerate full snapshot").action(async (sourceDir, options) => {
22322
22449
  const projectPath = options.project || process.cwd();
22323
22450
  const sessionId = options.session || "import:organized";
22324
- const sourceRoot = path23.resolve(sourceDir || options.out || projectPath);
22325
- const repoPath = path23.resolve(options.repo || projectPath);
22451
+ const sourceRoot = path24.resolve(sourceDir || options.out || projectPath);
22452
+ const repoPath = path24.resolve(options.repo || projectPath);
22326
22453
  if (!fs18.existsSync(sourceRoot)) {
22327
22454
  fs18.mkdirSync(sourceRoot, { recursive: true });
22328
22455
  }
@@ -22334,7 +22461,7 @@ program.command("organize-import [sourceDir]").description("Import existing mark
22334
22461
  const hasMarkdown = files.length > 0;
22335
22462
  const shouldBootstrap = Boolean(options.forceBootstrap || options.bootstrap || !hasMarkdown && options.bootstrapIfEmpty);
22336
22463
  if (shouldBootstrap) {
22337
- const outDir = path23.resolve(options.out || path23.join(sourceRoot, "bootstrap-kb"));
22464
+ const outDir = path24.resolve(options.out || path24.join(sourceRoot, "bootstrap-kb"));
22338
22465
  const since = options.since || "180 days ago";
22339
22466
  const maxCommits = options.maxCommits ? Math.max(1, parseInt(options.maxCommits, 10)) : 1e3;
22340
22467
  console.log("\n\u{1F9E0} Bootstrapping markdown knowledge base...");
@@ -22379,7 +22506,7 @@ program.command("organize-import [sourceDir]").description("Import existing mark
22379
22506
  continue;
22380
22507
  }
22381
22508
  const { namespace, categoryPath } = deriveNamespaceCategory(activeSourceRoot, file);
22382
- const rel = path23.relative(activeSourceRoot, file);
22509
+ const rel = path24.relative(activeSourceRoot, file);
22383
22510
  const evidence = extractImportEvidence(text);
22384
22511
  if (options.dryRun) {
22385
22512
  console.log(`- ${rel} -> namespace=${namespace} category=${categoryPath.join("/")} confidence=${evidence.confidence || "n/a"} sources=${evidence.sources.length}`);
@@ -22423,8 +22550,8 @@ program.command("import").description("Import existing Claude Code conversation
22423
22550
  const service = getMemoryServiceForProject(targetProjectPath);
22424
22551
  const importer = createSessionHistoryImporter(service);
22425
22552
  const importOpts = {
22426
- limit: parsePositiveIntegerOption2(options.limit, "limit"),
22427
- sessionLimit: parsePositiveIntegerOption2(options.sessionLimit, "session-limit"),
22553
+ limit: parsePositiveIntegerOption3(options.limit, "limit"),
22554
+ sessionLimit: parsePositiveIntegerOption3(options.sessionLimit, "session-limit"),
22428
22555
  force: options.force,
22429
22556
  verbose: options.verbose,
22430
22557
  onProgress: renderProgress