claude-memory-layer 2.2.5 → 2.2.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.
package/dist/cli/index.js CHANGED
@@ -9,8 +9,8 @@ const __dirname = dirname(__filename);
9
9
  // src/apps/cli/index.ts
10
10
  import { Command } from "commander";
11
11
  import { exec } from "child_process";
12
- import * as fs22 from "fs";
13
- import * as path31 from "path";
12
+ import * as fs23 from "fs";
13
+ import * as path33 from "path";
14
14
  import * as os15 from "os";
15
15
 
16
16
  // src/services/memory-service.ts
@@ -1780,13 +1780,13 @@ async function clearModelCacheDirectory(modelName, cacheDir) {
1780
1780
  try {
1781
1781
  const resolvedCacheDirInput = cacheDir ?? await resolveTransformersCacheDir();
1782
1782
  if (!resolvedCacheDirInput) return false;
1783
- const fs23 = await import("node:fs/promises");
1784
- const path32 = await import("node:path");
1785
- const modelDir = path32.join(resolvedCacheDirInput, modelName);
1786
- const resolvedCacheDir = path32.resolve(resolvedCacheDirInput);
1787
- const resolvedModelDir = path32.resolve(modelDir);
1788
- if (!resolvedModelDir.startsWith(resolvedCacheDir + path32.sep)) return false;
1789
- await fs23.rm(resolvedModelDir, { recursive: true, force: true });
1783
+ const fs24 = await import("node:fs/promises");
1784
+ const path34 = await import("node:path");
1785
+ const modelDir = path34.join(resolvedCacheDirInput, modelName);
1786
+ const resolvedCacheDir = path34.resolve(resolvedCacheDirInput);
1787
+ const resolvedModelDir = path34.resolve(modelDir);
1788
+ if (!resolvedModelDir.startsWith(resolvedCacheDir + path34.sep)) return false;
1789
+ await fs24.rm(resolvedModelDir, { recursive: true, force: true });
1790
1790
  return true;
1791
1791
  } catch {
1792
1792
  return false;
@@ -2432,12 +2432,12 @@ import * as nodePath2 from "path";
2432
2432
  import Database from "better-sqlite3";
2433
2433
  import * as fs4 from "fs";
2434
2434
  import * as nodePath from "path";
2435
- function createSQLiteDatabase(path32, options) {
2436
- const dir = nodePath.dirname(path32);
2435
+ function createSQLiteDatabase(path34, options) {
2436
+ const dir = nodePath.dirname(path34);
2437
2437
  if (!fs4.existsSync(dir)) {
2438
2438
  fs4.mkdirSync(dir, { recursive: true });
2439
2439
  }
2440
- const db = new Database(path32, {
2440
+ const db = new Database(path34, {
2441
2441
  readonly: options?.readonly ?? false
2442
2442
  });
2443
2443
  if (!options?.readonly && (options?.walMode ?? true)) {
@@ -3096,17 +3096,17 @@ function emptyOutboxRecoveryResult() {
3096
3096
  function isRecord(value) {
3097
3097
  return typeof value === "object" && value !== null && !Array.isArray(value);
3098
3098
  }
3099
- function getNestedRecord(root, path32) {
3099
+ function getNestedRecord(root, path34) {
3100
3100
  let cursor = root;
3101
- for (const key of path32) {
3101
+ for (const key of path34) {
3102
3102
  if (!isRecord(cursor)) return void 0;
3103
3103
  cursor = cursor[key];
3104
3104
  }
3105
3105
  return isRecord(cursor) ? cursor : void 0;
3106
3106
  }
3107
- function getNestedString(root, path32) {
3107
+ function getNestedString(root, path34) {
3108
3108
  let cursor = root;
3109
- for (const key of path32) {
3109
+ for (const key of path34) {
3110
3110
  if (!isRecord(cursor)) return void 0;
3111
3111
  cursor = cursor[key];
3112
3112
  }
@@ -8963,9 +8963,9 @@ function safeParseObject(value) {
8963
8963
  return void 0;
8964
8964
  }
8965
8965
  }
8966
- function nestedString(root, path32) {
8966
+ function nestedString(root, path34) {
8967
8967
  let cursor = root;
8968
- for (const key of path32) {
8968
+ for (const key of path34) {
8969
8969
  if (!isRecord2(cursor)) return void 0;
8970
8970
  cursor = cursor[key];
8971
8971
  }
@@ -9040,12 +9040,12 @@ var GraphPathService = class {
9040
9040
  }
9041
9041
  }
9042
9042
  }
9043
- const paths = Array.from(bestByTarget.entries()).map(([key, path32]) => ({
9043
+ const paths = Array.from(bestByTarget.entries()).map(([key, path34]) => ({
9044
9044
  target: graph.node(nodeFromKey(key)),
9045
- hops: path32.hops,
9046
- totalCost: path32.totalCost,
9047
- scoreContribution: path32.totalCost > 0 ? 1 / path32.totalCost : 0,
9048
- steps: path32.steps
9045
+ hops: path34.hops,
9046
+ totalCost: path34.totalCost,
9047
+ scoreContribution: path34.totalCost > 0 ? 1 / path34.totalCost : 0,
9048
+ steps: path34.steps
9049
9049
  })).sort((a, b) => b.scoreContribution - a.scoreContribution || a.hops - b.hops || a.target.name.localeCompare(b.target.name)).slice(0, maxResults);
9050
9050
  return { startNodes, effectiveMaxHops, paths };
9051
9051
  }
@@ -9864,16 +9864,16 @@ function parseMetadata(value) {
9864
9864
  return void 0;
9865
9865
  }
9866
9866
  }
9867
- function nestedValue(root, path32) {
9867
+ function nestedValue(root, path34) {
9868
9868
  let cursor = root;
9869
- for (const key of path32) {
9869
+ for (const key of path34) {
9870
9870
  if (!isRecord3(cursor)) return void 0;
9871
9871
  cursor = cursor[key];
9872
9872
  }
9873
9873
  return cursor;
9874
9874
  }
9875
- function nestedString2(root, path32) {
9876
- const value = nestedValue(root, path32);
9875
+ function nestedString2(root, path34) {
9876
+ const value = nestedValue(root, path34);
9877
9877
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
9878
9878
  }
9879
9879
  function projectHashFromMetadata(metadata) {
@@ -9928,15 +9928,15 @@ function extractFileCategories(content) {
9928
9928
  const pathPattern = /(?:^|[\s`"'(])((?:src|tests|specs|docs)\/[A-Za-z0-9._/@-]+(?:\/[A-Za-z0-9._@-]+)*\.(?:ts|tsx|js|jsx|mjs|cjs|md|json|yaml|yml|py))/g;
9929
9929
  let match;
9930
9930
  while ((match = pathPattern.exec(content)) !== null) {
9931
- const path32 = match[1] ?? "";
9932
- const extensionMatch = /\.([A-Za-z0-9]+)$/.exec(path32);
9931
+ const path34 = match[1] ?? "";
9932
+ const extensionMatch = /\.([A-Za-z0-9]+)$/.exec(path34);
9933
9933
  const extension = extensionMatch?.[1]?.toLowerCase();
9934
9934
  if (!extension) continue;
9935
- if (path32.startsWith("tests/")) {
9935
+ if (path34.startsWith("tests/")) {
9936
9936
  categories.add(`test:${extension}`);
9937
- } else if (path32.startsWith("src/")) {
9937
+ } else if (path34.startsWith("src/")) {
9938
9938
  categories.add(`source:${extension}`);
9939
- } else if (path32.startsWith("docs/") || path32.startsWith("specs/")) {
9939
+ } else if (path34.startsWith("docs/") || path34.startsWith("specs/")) {
9940
9940
  categories.add(`docs:${extension}`);
9941
9941
  }
9942
9942
  }
@@ -10263,16 +10263,16 @@ function parseMetadata2(value) {
10263
10263
  return void 0;
10264
10264
  }
10265
10265
  }
10266
- function nestedValue2(root, path32) {
10266
+ function nestedValue2(root, path34) {
10267
10267
  let cursor = root;
10268
- for (const key of path32) {
10268
+ for (const key of path34) {
10269
10269
  if (!isRecord4(cursor)) return void 0;
10270
10270
  cursor = cursor[key];
10271
10271
  }
10272
10272
  return cursor;
10273
10273
  }
10274
- function nestedString3(root, path32) {
10275
- const value = nestedValue2(root, path32);
10274
+ function nestedString3(root, path34) {
10275
+ const value = nestedValue2(root, path34);
10276
10276
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
10277
10277
  }
10278
10278
  function projectHashFromMetadata2(metadata) {
@@ -13184,7 +13184,7 @@ function isLanceCommitConflict(error) {
13184
13184
  return message.includes("commit conflict") && message.includes("concurrent commit") && message.includes("rerun the operation");
13185
13185
  }
13186
13186
  function delay(ms) {
13187
- return new Promise((resolve9) => setTimeout(resolve9, ms));
13187
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
13188
13188
  }
13189
13189
 
13190
13190
  // src/core/ingest-interceptor.ts
@@ -14595,12 +14595,12 @@ var Retriever = class {
14595
14595
  maxResults: opts.limit,
14596
14596
  direction: "both"
14597
14597
  });
14598
- for (const path32 of expansion.paths) {
14599
- if (path32.target.type !== "event") continue;
14600
- const target = await this.eventStore.getEvent(path32.target.id);
14598
+ for (const path34 of expansion.paths) {
14599
+ if (path34.target.type !== "event") continue;
14600
+ const target = await this.eventStore.getEvent(path34.target.id);
14601
14601
  if (!target || target.sessionId === sessionId) continue;
14602
- const graphPath = toRetrievalGraphPathDebug(path32, /* @__PURE__ */ new Map());
14603
- const score = graphPathScore(path32, opts.hopPenalty);
14602
+ const graphPath = toRetrievalGraphPathDebug(path34, /* @__PURE__ */ new Map());
14603
+ const score = graphPathScore(path34, opts.hopPenalty);
14604
14604
  const existing = byId.get(target.id);
14605
14605
  const graphPaths = mergeGraphPaths(existing?.graphPaths ?? [], [graphPath]);
14606
14606
  const graphLane = {
@@ -14609,7 +14609,7 @@ var Retriever = class {
14609
14609
  score
14610
14610
  };
14611
14611
  const row = {
14612
- id: existing?.id ?? `session-file-link-${path32.hops}-${target.id}`,
14612
+ id: existing?.id ?? `session-file-link-${path34.hops}-${target.id}`,
14613
14613
  eventId: target.id,
14614
14614
  content: target.content,
14615
14615
  score: Math.max(existing?.score ?? 0, score),
@@ -14647,12 +14647,12 @@ var Retriever = class {
14647
14647
  direction: "both"
14648
14648
  });
14649
14649
  const titleByEntityId = new Map(startNodes.map((node) => [node.entityId, node.title]));
14650
- for (const path32 of expansion.paths) {
14651
- if (path32.target.type !== "event") continue;
14652
- const target = await this.eventStore.getEvent(path32.target.id);
14650
+ for (const path34 of expansion.paths) {
14651
+ if (path34.target.type !== "event") continue;
14652
+ const target = await this.eventStore.getEvent(path34.target.id);
14653
14653
  if (!target) continue;
14654
- const graphPath = toRetrievalGraphPathDebug(path32, titleByEntityId);
14655
- const score = graphPathScore(path32, opts.hopPenalty);
14654
+ const graphPath = toRetrievalGraphPathDebug(path34, titleByEntityId);
14655
+ const score = graphPathScore(path34, opts.hopPenalty);
14656
14656
  const existing = byId.get(target.id);
14657
14657
  const graphPaths = mergeGraphPaths(existing?.graphPaths ?? [], [graphPath]);
14658
14658
  const graphLane = {
@@ -14661,7 +14661,7 @@ var Retriever = class {
14661
14661
  score
14662
14662
  };
14663
14663
  const row = {
14664
- id: existing?.id ?? `graph-path-${path32.hops}-${target.id}`,
14664
+ id: existing?.id ?? `graph-path-${path34.hops}-${target.id}`,
14665
14665
  eventId: target.id,
14666
14666
  content: target.content,
14667
14667
  score: Math.max(existing?.score ?? 0, score),
@@ -15001,8 +15001,8 @@ _Context:_ ${sessionContext}`;
15001
15001
  }
15002
15002
  matchesMetadataScope(metadata, expected) {
15003
15003
  if (!metadata) return false;
15004
- return Object.entries(expected).every(([path32, value]) => {
15005
- const actual = path32.split(".").reduce((acc, key) => {
15004
+ return Object.entries(expected).every(([path34, value]) => {
15005
+ const actual = path34.split(".").reduce((acc, key) => {
15006
15006
  if (typeof acc !== "object" || acc === null) return void 0;
15007
15007
  return acc[key];
15008
15008
  }, metadata);
@@ -15053,22 +15053,22 @@ function uniqueEntityStartNodes(candidates) {
15053
15053
  }
15054
15054
  return nodes;
15055
15055
  }
15056
- function toRetrievalGraphPathDebug(path32, titleByEntityId) {
15057
- const firstStep = path32.steps[0];
15056
+ function toRetrievalGraphPathDebug(path34, titleByEntityId) {
15057
+ const firstStep = path34.steps[0];
15058
15058
  const startNode = firstStep?.direction === "incoming" ? firstStep.to : firstStep?.from;
15059
15059
  const startEntityId = startNode?.type === "entity" ? startNode.id : "";
15060
15060
  return {
15061
15061
  startEntityId,
15062
15062
  startEntityTitle: titleByEntityId.get(startEntityId) ?? startNode?.name,
15063
- targetId: path32.target.id,
15064
- targetType: path32.target.type,
15065
- hops: path32.hops,
15066
- relationPath: path32.steps.map((step) => step.relationType)
15063
+ targetId: path34.target.id,
15064
+ targetType: path34.target.type,
15065
+ hops: path34.hops,
15066
+ relationPath: path34.steps.map((step) => step.relationType)
15067
15067
  };
15068
15068
  }
15069
- function graphPathScore(path32, hopPenalty) {
15070
- const base = Math.min(0.95, Math.max(0, path32.scoreContribution));
15071
- return Math.max(0.05, base - hopPenalty * Math.max(0, path32.hops - 1));
15069
+ function graphPathScore(path34, hopPenalty) {
15070
+ const base = Math.min(0.95, Math.max(0, path34.scoreContribution));
15071
+ return Math.max(0.05, base - hopPenalty * Math.max(0, path34.hops - 1));
15072
15072
  }
15073
15073
  function clampGraphHops(maxHops) {
15074
15074
  if (!Number.isFinite(maxHops)) return 2;
@@ -15076,14 +15076,14 @@ function clampGraphHops(maxHops) {
15076
15076
  }
15077
15077
  function mergeGraphPaths(existing, incoming) {
15078
15078
  const byKey = /* @__PURE__ */ new Map();
15079
- for (const path32 of [...existing, ...incoming]) {
15080
- const key = [path32.startEntityId, path32.targetType, path32.targetId, path32.hops, ...path32.relationPath].join("\0");
15081
- if (!byKey.has(key)) byKey.set(key, path32);
15079
+ for (const path34 of [...existing, ...incoming]) {
15080
+ const key = [path34.startEntityId, path34.targetType, path34.targetId, path34.hops, ...path34.relationPath].join("\0");
15081
+ if (!byKey.has(key)) byKey.set(key, path34);
15082
15082
  }
15083
15083
  return [...byKey.values()].sort((a, b) => a.hops - b.hops || compareStable(graphPathSignature(a), graphPathSignature(b))).slice(0, 3);
15084
15084
  }
15085
- function graphPathSignature(path32) {
15086
- return [path32.startEntityId, path32.targetType, path32.targetId, path32.hops, ...path32.relationPath].join("|");
15085
+ function graphPathSignature(path34) {
15086
+ return [path34.startEntityId, path34.targetType, path34.targetId, path34.hops, ...path34.relationPath].join("|");
15087
15087
  }
15088
15088
  function compareStable(a, b) {
15089
15089
  if (a < b) return -1;
@@ -23360,7 +23360,7 @@ function buildPrompt(statsContext, memoryContext, history, currentMessage) {
23360
23360
  return parts.join("\n");
23361
23361
  }
23362
23362
  function streamClaudeResponse(prompt, stream) {
23363
- return new Promise((resolve9, reject) => {
23363
+ return new Promise((resolve10, reject) => {
23364
23364
  const proc = spawn("claude", [
23365
23365
  "-p",
23366
23366
  "--output-format",
@@ -23433,7 +23433,7 @@ function streamClaudeResponse(prompt, stream) {
23433
23433
  if (code !== 0 && code !== null) {
23434
23434
  reject(classifyProviderFailure(stderrText || `Claude CLI exited with code ${code}`));
23435
23435
  } else {
23436
- resolve9();
23436
+ resolve10();
23437
23437
  }
23438
23438
  });
23439
23439
  });
@@ -25036,9 +25036,140 @@ function mergePluginHooksIntoSettings(settings, pluginPath) {
25036
25036
  return next;
25037
25037
  }
25038
25038
 
25039
+ // src/apps/cli/codex-hooks-config.ts
25040
+ import * as path25 from "node:path";
25041
+ var REQUIRED_CODEX_HOOK_FILES = [
25042
+ "codex-session-start.js",
25043
+ "codex-session-end.js",
25044
+ "codex-session-import-worker.js"
25045
+ ];
25046
+ var CODEX_HOOK_FILES = {
25047
+ SessionStart: "codex-session-start.js",
25048
+ SessionEnd: "codex-session-end.js"
25049
+ };
25050
+ function buildCodexHookCommand(pluginPath, fileName) {
25051
+ const absolutePluginPath = path25.resolve(pluginPath);
25052
+ return `node ${shellQuotePathForNode(path25.join(absolutePluginPath, "hooks", fileName))}`;
25053
+ }
25054
+ function buildCodexHookWindowsCommand(pluginPath, fileName) {
25055
+ const absolutePluginPath = path25.resolve(pluginPath);
25056
+ return `node "${path25.join(absolutePluginPath, "hooks", fileName).replace(/"/g, '""')}"`;
25057
+ }
25058
+ function getCodexMemoryHooks(pluginPath) {
25059
+ return {
25060
+ SessionStart: [
25061
+ {
25062
+ matcher: "startup|resume|clear|compact",
25063
+ hooks: [
25064
+ {
25065
+ type: "command",
25066
+ command: buildCodexHookCommand(pluginPath, CODEX_HOOK_FILES.SessionStart),
25067
+ commandWindows: buildCodexHookWindowsCommand(pluginPath, CODEX_HOOK_FILES.SessionStart),
25068
+ statusMessage: "Loading project memory",
25069
+ additionalContextLimit: 5e3
25070
+ }
25071
+ ]
25072
+ }
25073
+ ],
25074
+ SessionEnd: [
25075
+ {
25076
+ hooks: [
25077
+ {
25078
+ type: "command",
25079
+ command: buildCodexHookCommand(pluginPath, CODEX_HOOK_FILES.SessionEnd),
25080
+ commandWindows: buildCodexHookWindowsCommand(pluginPath, CODEX_HOOK_FILES.SessionEnd),
25081
+ statusMessage: "Saving session memory",
25082
+ timeout: 3
25083
+ }
25084
+ ]
25085
+ }
25086
+ ]
25087
+ };
25088
+ }
25089
+ function isCodexMemoryHookCommand(command, pluginPath) {
25090
+ if (!command) return false;
25091
+ const normalized = command.replace(/\\/g, "/");
25092
+ const normalizedPluginPath = pluginPath?.replace(/\\/g, "/").replace(/\/$/, "");
25093
+ return Object.values(CODEX_HOOK_FILES).some((fileName) => {
25094
+ if (normalizedPluginPath && normalized.includes(`${normalizedPluginPath}/hooks/${fileName}`)) {
25095
+ return true;
25096
+ }
25097
+ return normalized.includes("claude-memory-layer") && normalized.includes(`/hooks/${fileName}`);
25098
+ });
25099
+ }
25100
+ function isRecord6(value) {
25101
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25102
+ }
25103
+ function parseCodexHooksConfig(value) {
25104
+ if (!isRecord6(value)) {
25105
+ throw new Error("Codex hooks config must be a JSON object");
25106
+ }
25107
+ if (value.hooks === void 0) return value;
25108
+ if (!isRecord6(value.hooks)) {
25109
+ throw new Error('Codex hooks config "hooks" must be a JSON object');
25110
+ }
25111
+ for (const [eventName, entries] of Object.entries(value.hooks)) {
25112
+ if (!Array.isArray(entries)) {
25113
+ throw new Error(`Codex hooks config "hooks.${eventName}" must be an array`);
25114
+ }
25115
+ for (const [entryIndex, entry] of entries.entries()) {
25116
+ if (!isRecord6(entry) || !Array.isArray(entry.hooks)) {
25117
+ throw new Error(`Codex hooks config "hooks.${eventName}[${entryIndex}].hooks" must be an array`);
25118
+ }
25119
+ for (const [hookIndex, hook] of entry.hooks.entries()) {
25120
+ if (!isRecord6(hook)) {
25121
+ throw new Error(`Codex hook "hooks.${eventName}[${entryIndex}].hooks[${hookIndex}]" must be a JSON object`);
25122
+ }
25123
+ if (hook.command !== void 0 && typeof hook.command !== "string") {
25124
+ throw new Error(`Codex hook "hooks.${eventName}[${entryIndex}].hooks[${hookIndex}].command" must be a string`);
25125
+ }
25126
+ }
25127
+ }
25128
+ }
25129
+ return value;
25130
+ }
25131
+ function removeCodexMemoryHooks(config, pluginPath) {
25132
+ const next = { ...config };
25133
+ if (!config.hooks) return next;
25134
+ const hooks = { ...config.hooks };
25135
+ for (const hookName of Object.keys(CODEX_HOOK_FILES)) {
25136
+ const entries = hooks[hookName] ?? [];
25137
+ const cleaned = entries.map((entry) => ({
25138
+ ...entry,
25139
+ hooks: (entry.hooks ?? []).filter((hook) => !isCodexMemoryHookCommand(hook.command, pluginPath) && !isCodexMemoryHookCommand(hook.commandWindows, pluginPath))
25140
+ })).filter((entry) => entry.hooks.length > 0);
25141
+ if (cleaned.length > 0) {
25142
+ hooks[hookName] = cleaned;
25143
+ } else {
25144
+ delete hooks[hookName];
25145
+ }
25146
+ }
25147
+ if (Object.keys(hooks).length > 0) {
25148
+ next.hooks = hooks;
25149
+ } else {
25150
+ delete next.hooks;
25151
+ }
25152
+ return next;
25153
+ }
25154
+ function mergeCodexMemoryHooks(config, pluginPath) {
25155
+ const cleaned = removeCodexMemoryHooks(config, pluginPath);
25156
+ const next = { ...cleaned, hooks: { ...cleaned.hooks ?? {} } };
25157
+ const pluginHooks = getCodexMemoryHooks(pluginPath);
25158
+ for (const hookName of Object.keys(CODEX_HOOK_FILES)) {
25159
+ next.hooks[hookName] = [
25160
+ ...next.hooks[hookName] ?? [],
25161
+ ...pluginHooks[hookName]
25162
+ ];
25163
+ }
25164
+ return next;
25165
+ }
25166
+ function hasCodexMemoryHook(config, hookName, pluginPath) {
25167
+ return (config.hooks?.[hookName] ?? []).some((entry) => entry.hooks.some((hook) => isCodexMemoryHookCommand(hook.command, pluginPath) || isCodexMemoryHookCommand(hook.commandWindows, pluginPath)));
25168
+ }
25169
+
25039
25170
  // src/apps/cli/codex-validation-output.ts
25040
25171
  import * as fs20 from "fs";
25041
- import * as path25 from "path";
25172
+ import * as path26 from "path";
25042
25173
  function formatNumber(value) {
25043
25174
  return Number.isFinite(value) ? value.toLocaleString("en-US") : String(value);
25044
25175
  }
@@ -25112,14 +25243,14 @@ function formatCodexValidationReport(report, format = "markdown") {
25112
25243
  return lines.join("\n");
25113
25244
  }
25114
25245
  function writeCodexValidationReport(outputPath, report, format = "markdown") {
25115
- const dir = path25.dirname(outputPath);
25246
+ const dir = path26.dirname(outputPath);
25116
25247
  fs20.mkdirSync(dir, { recursive: true });
25117
25248
  fs20.writeFileSync(outputPath, formatCodexValidationReport(report, format), "utf8");
25118
25249
  }
25119
25250
 
25120
25251
  // src/apps/cli/hermes-validation-output.ts
25121
25252
  import * as fs21 from "fs";
25122
- import * as path26 from "path";
25253
+ import * as path27 from "path";
25123
25254
  function formatNumber2(value) {
25124
25255
  return Number.isFinite(value) ? value.toLocaleString("en-US") : String(value);
25125
25256
  }
@@ -25189,7 +25320,7 @@ function formatHermesValidationReport(report, format = "markdown") {
25189
25320
  return lines.join("\n");
25190
25321
  }
25191
25322
  function writeHermesValidationReport(outputPath, report, format = "markdown") {
25192
- const dir = path26.dirname(outputPath);
25323
+ const dir = path27.dirname(outputPath);
25193
25324
  fs21.mkdirSync(dir, { recursive: true });
25194
25325
  fs21.writeFileSync(outputPath, formatHermesValidationReport(report, format), "utf8");
25195
25326
  }
@@ -25262,6 +25393,22 @@ async function runCodexImportOnce(options, deps = realDeps) {
25262
25393
  }
25263
25394
  }
25264
25395
 
25396
+ // src/services/codex-session-auto-import.ts
25397
+ import * as fs22 from "node:fs";
25398
+ import * as path28 from "node:path";
25399
+ function getCodexAutoImportStatusPath(projectPath) {
25400
+ return path28.join(getProjectStoragePath(projectPath), "codex-auto-import-status.json");
25401
+ }
25402
+ function readCodexAutoImportStatus(projectPath) {
25403
+ try {
25404
+ const value = JSON.parse(fs22.readFileSync(getCodexAutoImportStatusPath(projectPath), "utf8"));
25405
+ if (value.status !== "success" && value.status !== "failed" || typeof value.updatedAt !== "string") return null;
25406
+ return value;
25407
+ } catch {
25408
+ return null;
25409
+ }
25410
+ }
25411
+
25265
25412
  // src/apps/cli/hermes-import-runner.ts
25266
25413
  var realDeps2 = {
25267
25414
  cwd: () => process.cwd(),
@@ -25953,7 +26100,7 @@ function redactSecrets(input) {
25953
26100
  }
25954
26101
 
25955
26102
  // src/apps/cli/process-command.ts
25956
- import * as path27 from "node:path";
26103
+ import * as path29 from "node:path";
25957
26104
  function resolveProcessCommandOptions(options, cwd = process.cwd(), deps = {}) {
25958
26105
  const explicitProject = options.project;
25959
26106
  if (explicitProject !== void 0 && explicitProject.trim().length === 0) {
@@ -25976,7 +26123,7 @@ function resolveProcessCommandOptions(options, cwd = process.cwd(), deps = {}) {
25976
26123
  recoverStuck,
25977
26124
  dryRunRecovery,
25978
26125
  graduation,
25979
- lockPath: explicitLockPath ?? path27.join(getProjectStoragePath2(projectPath), "vector-worker.lock")
26126
+ lockPath: explicitLockPath ?? path29.join(getProjectStoragePath2(projectPath), "vector-worker.lock")
25980
26127
  };
25981
26128
  }
25982
26129
  function formatProcessLockBusy(input) {
@@ -26037,17 +26184,17 @@ function quoteCliArg(value) {
26037
26184
 
26038
26185
  // src/apps/cli/import-command.ts
26039
26186
  import * as os13 from "node:os";
26040
- import * as path28 from "node:path";
26187
+ import * as path30 from "node:path";
26041
26188
  function resolveImportCommandLockOptions(options, deps = {}) {
26042
26189
  if (options.lockPath !== void 0 && options.lockPath.trim().length === 0) {
26043
26190
  throw new Error("--lock-path must not be empty");
26044
26191
  }
26045
26192
  const useGlobalStorage = options.all === true && !options.project && !options.session;
26046
26193
  if (useGlobalStorage) {
26047
- const globalStoragePath = path28.join((deps.homedir ?? os13.homedir)(), ".claude-code", "memory");
26194
+ const globalStoragePath = path30.join((deps.homedir ?? os13.homedir)(), ".claude-code", "memory");
26048
26195
  return {
26049
26196
  storageScope: "global",
26050
- lockPath: options.lockPath ?? path28.join(globalStoragePath, "vector-worker.lock")
26197
+ lockPath: options.lockPath ?? path30.join(globalStoragePath, "vector-worker.lock")
26051
26198
  };
26052
26199
  }
26053
26200
  const projectPath = options.project ?? (deps.cwd ?? (() => process.cwd()))();
@@ -26055,7 +26202,7 @@ function resolveImportCommandLockOptions(options, deps = {}) {
26055
26202
  return {
26056
26203
  storageScope: "project",
26057
26204
  projectPath,
26058
- lockPath: options.lockPath ?? path28.join(getProjectStoragePath2(projectPath), "vector-worker.lock")
26205
+ lockPath: options.lockPath ?? path30.join(getProjectStoragePath2(projectPath), "vector-worker.lock")
26059
26206
  };
26060
26207
  }
26061
26208
  function formatImportLockBusy(options, holderPid) {
@@ -26188,12 +26335,12 @@ function formatDuration2(ms) {
26188
26335
  }
26189
26336
 
26190
26337
  // src/apps/cli/mongo-sync-command.ts
26191
- import * as path30 from "node:path";
26338
+ import * as path32 from "node:path";
26192
26339
 
26193
26340
  // src/core/worker-lock.ts
26194
- import { closeSync, existsSync as existsSync19, mkdirSync as mkdirSync10, openSync, readFileSync as readFileSync10, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
26341
+ import { closeSync, existsSync as existsSync20, mkdirSync as mkdirSync11, openSync, readFileSync as readFileSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
26195
26342
  import * as os14 from "node:os";
26196
- import * as path29 from "node:path";
26343
+ import * as path31 from "node:path";
26197
26344
  import { randomUUID as randomUUID23 } from "node:crypto";
26198
26345
  var WorkerLock = class {
26199
26346
  lockPath;
@@ -26202,7 +26349,7 @@ var WorkerLock = class {
26202
26349
  now;
26203
26350
  isProcessRunning;
26204
26351
  acquired = false;
26205
- constructor(lockPath = path29.join(os14.tmpdir(), "claude-memory-layer", "vector-worker.lock"), options = {}) {
26352
+ constructor(lockPath = path31.join(os14.tmpdir(), "claude-memory-layer", "vector-worker.lock"), options = {}) {
26206
26353
  this.lockPath = lockPath;
26207
26354
  this.pid = options.pid ?? process.pid;
26208
26355
  this.ownerId = options.ownerId ?? randomUUID23();
@@ -26215,7 +26362,7 @@ var WorkerLock = class {
26215
26362
  acquire() {
26216
26363
  let staleRecovered = false;
26217
26364
  for (; ; ) {
26218
- mkdirSync10(path29.dirname(this.lockPath), { recursive: true });
26365
+ mkdirSync11(path31.dirname(this.lockPath), { recursive: true });
26219
26366
  const createResult = this.tryCreateLockFile();
26220
26367
  if (createResult === "created") {
26221
26368
  this.acquired = true;
@@ -26249,11 +26396,11 @@ var WorkerLock = class {
26249
26396
  }
26250
26397
  }
26251
26398
  release() {
26252
- if (!this.acquired || !existsSync19(this.lockPath)) return false;
26399
+ if (!this.acquired || !existsSync20(this.lockPath)) return false;
26253
26400
  const existing = this.readPayload();
26254
26401
  if (!this.isOwnedPayload(existing)) return false;
26255
26402
  try {
26256
- unlinkSync(this.lockPath);
26403
+ unlinkSync2(this.lockPath);
26257
26404
  this.acquired = false;
26258
26405
  return true;
26259
26406
  } catch {
@@ -26264,7 +26411,7 @@ var WorkerLock = class {
26264
26411
  let fd = null;
26265
26412
  try {
26266
26413
  fd = openSync(this.lockPath, "wx");
26267
- writeFileSync8(fd, JSON.stringify({
26414
+ writeFileSync9(fd, JSON.stringify({
26268
26415
  pid: this.pid,
26269
26416
  ownerId: this.ownerId,
26270
26417
  acquiredAt: this.now().toISOString()
@@ -26280,7 +26427,7 @@ var WorkerLock = class {
26280
26427
  }
26281
26428
  readPayload() {
26282
26429
  try {
26283
- const raw = readFileSync10(this.lockPath, "utf8").trim();
26430
+ const raw = readFileSync11(this.lockPath, "utf8").trim();
26284
26431
  if (!raw) return null;
26285
26432
  if (/^\d+$/.test(raw)) {
26286
26433
  return {
@@ -26307,7 +26454,7 @@ var WorkerLock = class {
26307
26454
  }
26308
26455
  removeExistingLock() {
26309
26456
  try {
26310
- unlinkSync(this.lockPath);
26457
+ unlinkSync2(this.lockPath);
26311
26458
  return true;
26312
26459
  } catch (error) {
26313
26460
  const code = error.code;
@@ -26347,7 +26494,7 @@ function resolveMongoSyncProcessOptions(options, cwd = process.cwd(), deps = {})
26347
26494
  projectPath,
26348
26495
  processAfterSync: options.processAfterSync === true,
26349
26496
  processIntervalMs,
26350
- lockPath: explicitLockPath ?? path30.join(getProjectStoragePath2(projectPath), "vector-worker.lock")
26497
+ lockPath: explicitLockPath ?? path32.join(getProjectStoragePath2(projectPath), "vector-worker.lock")
26351
26498
  };
26352
26499
  }
26353
26500
  function createMongoSyncPostProcessor(options, deps = {}) {
@@ -26430,28 +26577,29 @@ function parsePositiveIntegerOption2(raw, flagName) {
26430
26577
  }
26431
26578
 
26432
26579
  // src/apps/cli/index.ts
26433
- var CLAUDE_SETTINGS_PATH = path31.join(os15.homedir(), ".claude", "settings.json");
26580
+ var CLAUDE_SETTINGS_PATH = path33.join(os15.homedir(), ".claude", "settings.json");
26581
+ var CODEX_HOOKS_PATH = path33.join(os15.homedir(), ".codex", "hooks.json");
26434
26582
  function getPluginPath() {
26435
26583
  const possiblePaths = [
26436
- path31.join(__dirname, ".."),
26584
+ path33.join(__dirname, ".."),
26437
26585
  // When running from dist/cli
26438
- path31.join(__dirname, "../..", "dist"),
26586
+ path33.join(__dirname, "../..", "dist"),
26439
26587
  // When running from src
26440
- path31.join(process.cwd(), "dist")
26588
+ path33.join(process.cwd(), "dist")
26441
26589
  // Current working directory
26442
26590
  ];
26443
26591
  for (const p of possiblePaths) {
26444
- const hooksPath = path31.join(p, "hooks", "user-prompt-submit.js");
26445
- if (fs22.existsSync(hooksPath)) {
26592
+ const hooksPath = path33.join(p, "hooks", "user-prompt-submit.js");
26593
+ if (fs23.existsSync(hooksPath)) {
26446
26594
  return p;
26447
26595
  }
26448
26596
  }
26449
- return path31.join(os15.homedir(), ".npm-global", "lib", "node_modules", "claude-memory-layer", "dist");
26597
+ return path33.join(os15.homedir(), ".npm-global", "lib", "node_modules", "claude-memory-layer", "dist");
26450
26598
  }
26451
26599
  function loadClaudeSettings() {
26452
26600
  try {
26453
- if (fs22.existsSync(CLAUDE_SETTINGS_PATH)) {
26454
- const content = fs22.readFileSync(CLAUDE_SETTINGS_PATH, "utf-8");
26601
+ if (fs23.existsSync(CLAUDE_SETTINGS_PATH)) {
26602
+ const content = fs23.readFileSync(CLAUDE_SETTINGS_PATH, "utf-8");
26455
26603
  return JSON.parse(content);
26456
26604
  }
26457
26605
  } catch (error) {
@@ -26460,13 +26608,35 @@ function loadClaudeSettings() {
26460
26608
  return {};
26461
26609
  }
26462
26610
  function saveClaudeSettings(settings) {
26463
- const dir = path31.dirname(CLAUDE_SETTINGS_PATH);
26464
- if (!fs22.existsSync(dir)) {
26465
- fs22.mkdirSync(dir, { recursive: true });
26611
+ const dir = path33.dirname(CLAUDE_SETTINGS_PATH);
26612
+ if (!fs23.existsSync(dir)) {
26613
+ fs23.mkdirSync(dir, { recursive: true });
26466
26614
  }
26467
26615
  const tempPath = CLAUDE_SETTINGS_PATH + ".tmp";
26468
- fs22.writeFileSync(tempPath, JSON.stringify(settings, null, 2));
26469
- fs22.renameSync(tempPath, CLAUDE_SETTINGS_PATH);
26616
+ fs23.writeFileSync(tempPath, JSON.stringify(settings, null, 2));
26617
+ fs23.renameSync(tempPath, CLAUDE_SETTINGS_PATH);
26618
+ }
26619
+ function loadCodexHooksConfig(configPath = CODEX_HOOKS_PATH) {
26620
+ try {
26621
+ if (fs23.existsSync(configPath)) {
26622
+ return parseCodexHooksConfig(JSON.parse(fs23.readFileSync(configPath, "utf-8")));
26623
+ }
26624
+ } catch (error) {
26625
+ throw new Error(`Could not read Codex hooks config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
26626
+ }
26627
+ return {};
26628
+ }
26629
+ function saveCodexHooksConfig(config, configPath = CODEX_HOOKS_PATH) {
26630
+ const dir = path33.dirname(configPath);
26631
+ fs23.mkdirSync(dir, { recursive: true });
26632
+ const tempPath = `${configPath}.${process.pid}.tmp`;
26633
+ try {
26634
+ fs23.writeFileSync(tempPath, `${JSON.stringify(config, null, 2)}
26635
+ `);
26636
+ fs23.renameSync(tempPath, configPath);
26637
+ } finally {
26638
+ if (fs23.existsSync(tempPath)) fs23.unlinkSync(tempPath);
26639
+ }
26470
26640
  }
26471
26641
  function resolveHealthProjectIdentity(projectPath) {
26472
26642
  if (!projectPath || projectPath.trim().length === 0) return { scope: "global", id: "global" };
@@ -26475,7 +26645,7 @@ function resolveHealthProjectIdentity(projectPath) {
26475
26645
  }
26476
26646
  function resolveHealthStoragePath(projectPathOrHash) {
26477
26647
  const normalized = projectPathOrHash?.trim();
26478
- return normalized && normalized.length > 0 ? resolveProjectStoragePath(normalized) : path31.join(os15.homedir(), ".claude-code", "memory");
26648
+ return normalized && normalized.length > 0 ? resolveProjectStoragePath(normalized) : path33.join(os15.homedir(), ".claude-code", "memory");
26479
26649
  }
26480
26650
  function createHealthMemoryService(projectPathOrHash) {
26481
26651
  const project = resolveHealthProjectIdentity(projectPathOrHash);
@@ -26489,7 +26659,7 @@ function createHealthMemoryService(projectPathOrHash) {
26489
26659
  });
26490
26660
  }
26491
26661
  function healthStoreExists(projectPathOrHash) {
26492
- return fs22.existsSync(path31.join(resolveHealthStoragePath(projectPathOrHash), "events.sqlite"));
26662
+ return fs23.existsSync(path33.join(resolveHealthStoragePath(projectPathOrHash), "events.sqlite"));
26493
26663
  }
26494
26664
  function emptyHealthStats() {
26495
26665
  return { totalEvents: 0, vectorCount: 0, levelStats: [] };
@@ -26522,20 +26692,20 @@ function emptyHealthDerivationLiveness() {
26522
26692
  };
26523
26693
  }
26524
26694
  function scanCanonicalProjectStores(canonicalId) {
26525
- const projectsRoot = path31.join(os15.homedir(), ".claude-code", "memory", "projects");
26695
+ const projectsRoot = path33.join(os15.homedir(), ".claude-code", "memory", "projects");
26526
26696
  let scannedStoreCount = 0;
26527
26697
  let matchingStoreCount = 0;
26528
26698
  let unreadableStoreCount = 0;
26529
26699
  let entries;
26530
26700
  try {
26531
- entries = fs22.readdirSync(projectsRoot, { withFileTypes: true });
26701
+ entries = fs23.readdirSync(projectsRoot, { withFileTypes: true });
26532
26702
  } catch {
26533
26703
  return { scannedStoreCount, matchingStoreCount, unreadableStoreCount };
26534
26704
  }
26535
26705
  for (const entry of entries) {
26536
26706
  if (!entry.isDirectory() || !/^[a-f0-9]{8}$/.test(entry.name)) continue;
26537
- const dbPath = path31.join(projectsRoot, entry.name, "events.sqlite");
26538
- if (!fs22.existsSync(dbPath)) continue;
26707
+ const dbPath = path33.join(projectsRoot, entry.name, "events.sqlite");
26708
+ if (!fs23.existsSync(dbPath)) continue;
26539
26709
  scannedStoreCount++;
26540
26710
  let db;
26541
26711
  try {
@@ -26627,7 +26797,7 @@ async function runCodexValidationCommand(options) {
26627
26797
  process.stdout.write(rendered.endsWith("\n") ? rendered : `${rendered}
26628
26798
  `);
26629
26799
  if (options.output) {
26630
- const outputPath = path31.resolve(options.output);
26800
+ const outputPath = path33.resolve(options.output);
26631
26801
  writeCodexValidationReport(outputPath, report, format);
26632
26802
  console.log(`
26633
26803
  Report written to ${outputPath}`);
@@ -26647,7 +26817,7 @@ async function runHermesValidationCommand(options) {
26647
26817
  process.stdout.write(rendered.endsWith("\n") ? rendered : `${rendered}
26648
26818
  `);
26649
26819
  if (options.output) {
26650
- const outputPath = path31.resolve(options.output);
26820
+ const outputPath = path33.resolve(options.output);
26651
26821
  writeHermesValidationReport(outputPath, report, format);
26652
26822
  console.log(`
26653
26823
  Report written to ${outputPath}`);
@@ -26681,18 +26851,18 @@ var OPERATION_PRIVACY_CONFIG = {
26681
26851
  }
26682
26852
  };
26683
26853
  function resolveOperationProject(project) {
26684
- const projectPath = path31.resolve(project ?? process.cwd());
26854
+ const projectPath = path33.resolve(project ?? process.cwd());
26685
26855
  const projectHash = hashProjectPath(projectPath);
26686
26856
  const storagePath = getProjectStoragePath(projectPath);
26687
26857
  return {
26688
26858
  projectPath,
26689
26859
  projectHash,
26690
26860
  storagePath,
26691
- dbPath: path31.join(storagePath, "events.sqlite")
26861
+ dbPath: path33.join(storagePath, "events.sqlite")
26692
26862
  };
26693
26863
  }
26694
26864
  function openOperationReadDatabase(context) {
26695
- if (!fs22.existsSync(context.dbPath)) return null;
26865
+ if (!fs23.existsSync(context.dbPath)) return null;
26696
26866
  return createSQLiteDatabase(context.dbPath, { readonly: true, walMode: false });
26697
26867
  }
26698
26868
  async function withOperationWriteDatabase(context, callback) {
@@ -26925,7 +27095,7 @@ async function runOperationCli(action, label) {
26925
27095
  }
26926
27096
  }
26927
27097
  var program = new Command();
26928
- program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("2.2.5");
27098
+ program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("2.2.6");
26929
27099
  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) => {
26930
27100
  try {
26931
27101
  await runMarketContextCommand(options);
@@ -26938,7 +27108,7 @@ program.command("install").description("Install hooks into Claude Code settings"
26938
27108
  try {
26939
27109
  const pluginPath = options.path || getPluginPath();
26940
27110
  const missingHooks = REQUIRED_HOOK_FILES.filter(
26941
- (file) => !fs22.existsSync(path31.join(pluginPath, "hooks", file))
27111
+ (file) => !fs23.existsSync(path33.join(pluginPath, "hooks", file))
26942
27112
  );
26943
27113
  if (missingHooks.length > 0) {
26944
27114
  console.error(`
@@ -27004,7 +27174,7 @@ program.command("status").description("Check plugin installation status").action
27004
27174
  console.log(` PostToolUse: ${hasPostToolHook ? "\u2705 Installed" : "\u274C Not installed"}`);
27005
27175
  console.log(` Stop: ${hasStopHook ? "\u2705 Installed" : "\u274C Not installed"}`);
27006
27176
  console.log(` SessionEnd: ${hasSessionEndHook ? "\u2705 Installed" : "\u274C Not installed"}`);
27007
- const hooksExist = REQUIRED_HOOK_FILES.every((file) => fs22.existsSync(path31.join(pluginPath, "hooks", file)));
27177
+ const hooksExist = REQUIRED_HOOK_FILES.every((file) => fs23.existsSync(path33.join(pluginPath, "hooks", file)));
27008
27178
  console.log(`
27009
27179
  Plugin files: ${hooksExist ? "\u2705 Found" : "\u274C Not found"}`);
27010
27180
  console.log(` Path: ${pluginPath}`);
@@ -27281,8 +27451,8 @@ repairCommand.command("legacy-project-scope").description("Dry-run or apply proj
27281
27451
  apply: options.apply
27282
27452
  });
27283
27453
  const storagePath = projectPath ? getProjectStoragePath(projectPath) : resolveProjectStoragePath(repairOptions.projectHash);
27284
- const dbPath = path31.join(storagePath, "events.sqlite");
27285
- if (repairOptions.dryRun && !fs22.existsSync(dbPath)) {
27454
+ const dbPath = path33.join(storagePath, "events.sqlite");
27455
+ if (repairOptions.dryRun && !fs23.existsSync(dbPath)) {
27286
27456
  const projectHash = repairOptions.projectHash || hashProjectPath(repairOptions.projectPath);
27287
27457
  console.log(formatLegacyProjectScopeRepairResult({
27288
27458
  dryRun: true,
@@ -27315,8 +27485,8 @@ repairCommand.command("prune-tool-observation-vectors").description("Delete alre
27315
27485
  try {
27316
27486
  const pruneOptions = resolvePruneToolObservationVectorsOptions(options);
27317
27487
  const storagePath = getProjectStoragePath(pruneOptions.projectPath);
27318
- const dbPath = path31.join(storagePath, "events.sqlite");
27319
- if (pruneOptions.dryRun && !fs22.existsSync(dbPath)) {
27488
+ const dbPath = path33.join(storagePath, "events.sqlite");
27489
+ if (pruneOptions.dryRun && !fs23.existsSync(dbPath)) {
27320
27490
  console.log(formatPruneToolObservationVectorsResult({
27321
27491
  dryRun: true,
27322
27492
  scanned: 0,
@@ -27327,7 +27497,7 @@ repairCommand.command("prune-tool-observation-vectors").description("Delete alre
27327
27497
  return;
27328
27498
  }
27329
27499
  const store = new SQLiteEventStore(dbPath, { readonly: pruneOptions.dryRun });
27330
- const vectorStore = new VectorStore(path31.join(storagePath, "vectors"));
27500
+ const vectorStore = new VectorStore(path33.join(storagePath, "vectors"));
27331
27501
  try {
27332
27502
  const result = await pruneToolObservationVectors(store, vectorStore, { dryRun: pruneOptions.dryRun });
27333
27503
  console.log(formatPruneToolObservationVectorsResult(result));
@@ -27345,8 +27515,8 @@ repairCommand.command("redact-credentials").description("Redact credentials alre
27345
27515
  const projectPath = options.project ?? process.cwd();
27346
27516
  const dryRun = options.apply !== true;
27347
27517
  const storagePath = getProjectStoragePath(projectPath);
27348
- const dbPath = path31.join(storagePath, "events.sqlite");
27349
- if (!fs22.existsSync(dbPath)) {
27518
+ const dbPath = path33.join(storagePath, "events.sqlite");
27519
+ if (!fs23.existsSync(dbPath)) {
27350
27520
  console.log(formatRedactionPlan({
27351
27521
  dryRun,
27352
27522
  rowChanges: [],
@@ -27366,7 +27536,7 @@ repairCommand.command("redact-credentials").description("Redact credentials alre
27366
27536
  const { rowChanges, affectedEventIds } = planSqliteRedaction(db, { apply: !dryRun });
27367
27537
  const fileChanges = planMarkdownRedaction(storagePath, { apply: !dryRun });
27368
27538
  if (!dryRun && affectedEventIds.length > 0) {
27369
- const vectorStore = new VectorStore(path31.join(storagePath, "vectors"));
27539
+ const vectorStore = new VectorStore(path33.join(storagePath, "vectors"));
27370
27540
  for (const eventId of affectedEventIds) {
27371
27541
  try {
27372
27542
  await vectorStore.deleteEventEverywhere(eventId);
@@ -27399,12 +27569,12 @@ repairCommand.command("auto-heal-tool-observation-vectors").description("Interna
27399
27569
  try {
27400
27570
  const healOptions = resolveAutoHealToolObservationVectorsOptions(options);
27401
27571
  const storagePath = getProjectStoragePath(healOptions.projectPath);
27402
- const dbPath = path31.join(storagePath, "events.sqlite");
27403
- if (!fs22.existsSync(dbPath)) {
27572
+ const dbPath = path33.join(storagePath, "events.sqlite");
27573
+ if (!fs23.existsSync(dbPath)) {
27404
27574
  console.log(formatAutoHealToolObservationVectorsResult({ status: "no-store" }));
27405
27575
  return;
27406
27576
  }
27407
- const lockPath = healOptions.lockPath ?? path31.join(storagePath, "auto-heal.lock");
27577
+ const lockPath = healOptions.lockPath ?? path33.join(storagePath, "auto-heal.lock");
27408
27578
  workerLock = new WorkerLock(lockPath);
27409
27579
  const lockResult = workerLock.acquire();
27410
27580
  if (!lockResult.acquired) {
@@ -27417,7 +27587,7 @@ repairCommand.command("auto-heal-tool-observation-vectors").description("Interna
27417
27587
  console.log(formatAutoHealToolObservationVectorsResult({ status: "already-healed" }));
27418
27588
  return;
27419
27589
  }
27420
- const vectorStore = new VectorStore(path31.join(storagePath, "vectors"));
27590
+ const vectorStore = new VectorStore(path33.join(storagePath, "vectors"));
27421
27591
  const result = await runToolObservationVectorAutoHeal(store, vectorStore);
27422
27592
  console.log(formatAutoHealToolObservationVectorsResult({ status: "healed", result }));
27423
27593
  } catch (error) {
@@ -27693,8 +27863,8 @@ retentionCommand.command("audit").description("Run a dry-run retention audit for
27693
27863
  });
27694
27864
  const projectHash = auditOptions.projectHash ?? hashProjectPath(auditOptions.projectPath);
27695
27865
  const storagePath = auditOptions.projectPath ? getProjectStoragePath(auditOptions.projectPath) : resolveProjectStoragePath(projectHash);
27696
- const dbPath = path31.join(storagePath, "events.sqlite");
27697
- if (!fs22.existsSync(dbPath)) {
27866
+ const dbPath = path33.join(storagePath, "events.sqlite");
27867
+ if (!fs23.existsSync(dbPath)) {
27698
27868
  const emptyReport = emptyRetentionAuditReport(projectHash, auditOptions.limit);
27699
27869
  console.log(formatRetentionAuditReport(emptyReport, { json: auditOptions.json }));
27700
27870
  return;
@@ -27721,7 +27891,7 @@ program.command("mongo-sync").description("Sync events with MongoDB for multi-se
27721
27891
  const projectPath = options.project || process.cwd();
27722
27892
  const mongoUri = options.mongoUri || process.env.CLAUDE_MEMORY_MONGO_URI;
27723
27893
  const mongoDb = options.mongoDb || process.env.CLAUDE_MEMORY_MONGO_DB;
27724
- const projectKey = options.mongoProject || process.env.CLAUDE_MEMORY_MONGO_PROJECT || path31.basename(projectPath);
27894
+ const projectKey = options.mongoProject || process.env.CLAUDE_MEMORY_MONGO_PROJECT || path33.basename(projectPath);
27725
27895
  const direction = String(options.direction || "both").toLowerCase();
27726
27896
  if (!mongoUri || !mongoDb) {
27727
27897
  console.error("\n\u274C MongoDB sync is not configured.");
@@ -27751,14 +27921,14 @@ program.command("mongo-sync").description("Sync events with MongoDB for multi-se
27751
27921
  }
27752
27922
  })();
27753
27923
  const storagePath = getProjectStoragePath(projectPath);
27754
- if (!fs22.existsSync(storagePath)) {
27755
- fs22.mkdirSync(storagePath, { recursive: true });
27924
+ if (!fs23.existsSync(storagePath)) {
27925
+ fs23.mkdirSync(storagePath, { recursive: true });
27756
27926
  }
27757
27927
  const postProcessor = createMongoSyncPostProcessor(processOptions, {
27758
27928
  log: (message) => process.stdout.write(`${message}
27759
27929
  `)
27760
27930
  });
27761
- const sqliteStore = new SQLiteEventStore(path31.join(storagePath, "events.sqlite"));
27931
+ const sqliteStore = new SQLiteEventStore(path33.join(storagePath, "events.sqlite"));
27762
27932
  const worker = new MongoSyncWorker(sqliteStore, {
27763
27933
  uri: mongoUri,
27764
27934
  dbName: mongoDb,
@@ -27818,7 +27988,7 @@ function renderProgress(event) {
27818
27988
  break;
27819
27989
  case "session-start": {
27820
27990
  const pct = Math.round(event.sessionIndex / event.totalSessions * 100);
27821
- const sessionName = path31.basename(event.filePath, ".jsonl").slice(0, 8);
27991
+ const sessionName = path33.basename(event.filePath, ".jsonl").slice(0, 8);
27822
27992
  process.stdout.write(
27823
27993
  `\r \u{1F4C4} [${event.sessionIndex + 1}/${event.totalSessions}] ${pct}% | Session ${sessionName}... `
27824
27994
  );
@@ -27893,9 +28063,9 @@ async function listMarkdownFiles(root) {
27893
28063
  const stack = [root];
27894
28064
  while (stack.length > 0) {
27895
28065
  const dir = stack.pop();
27896
- const entries = await fs22.promises.readdir(dir, { withFileTypes: true });
28066
+ const entries = await fs23.promises.readdir(dir, { withFileTypes: true });
27897
28067
  for (const e of entries) {
27898
- const full = path31.join(dir, e.name);
28068
+ const full = path33.join(dir, e.name);
27899
28069
  if (e.isDirectory()) stack.push(full);
27900
28070
  else if (e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md") out.push(full);
27901
28071
  }
@@ -27903,8 +28073,8 @@ async function listMarkdownFiles(root) {
27903
28073
  return out.sort();
27904
28074
  }
27905
28075
  function deriveNamespaceCategory(sourceRoot, filePath) {
27906
- const rel = path31.relative(sourceRoot, filePath);
27907
- const dirSeg = path31.dirname(rel).split(path31.sep).filter(Boolean);
28076
+ const rel = path33.relative(sourceRoot, filePath);
28077
+ const dirSeg = path33.dirname(rel).split(path33.sep).filter(Boolean);
27908
28078
  if (dirSeg.length >= 2) {
27909
28079
  const namespace = sanitizeSegment3(dirSeg[0], "default");
27910
28080
  const categoryPath = dirSeg.slice(1).map((s) => sanitizeSegment3(s, "uncategorized"));
@@ -27923,10 +28093,10 @@ function extractImportEvidence(markdown) {
27923
28093
  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) => {
27924
28094
  const projectPath = options.project || process.cwd();
27925
28095
  const sessionId = options.session || "import:organized";
27926
- const sourceRoot = path31.resolve(sourceDir || options.out || projectPath);
27927
- const repoPath = path31.resolve(options.repo || projectPath);
27928
- if (!fs22.existsSync(sourceRoot)) {
27929
- fs22.mkdirSync(sourceRoot, { recursive: true });
28096
+ const sourceRoot = path33.resolve(sourceDir || options.out || projectPath);
28097
+ const repoPath = path33.resolve(options.repo || projectPath);
28098
+ if (!fs23.existsSync(sourceRoot)) {
28099
+ fs23.mkdirSync(sourceRoot, { recursive: true });
27930
28100
  }
27931
28101
  const service = getMemoryServiceForProject(projectPath);
27932
28102
  try {
@@ -27936,7 +28106,7 @@ program.command("organize-import [sourceDir]").description("Import existing mark
27936
28106
  const hasMarkdown = files.length > 0;
27937
28107
  const shouldBootstrap = Boolean(options.forceBootstrap || options.bootstrap || !hasMarkdown && options.bootstrapIfEmpty);
27938
28108
  if (shouldBootstrap) {
27939
- const outDir = path31.resolve(options.out || path31.join(sourceRoot, "bootstrap-kb"));
28109
+ const outDir = path33.resolve(options.out || path33.join(sourceRoot, "bootstrap-kb"));
27940
28110
  const since = options.since || "180 days ago";
27941
28111
  const maxCommits = options.maxCommits ? Math.max(1, parseInt(options.maxCommits, 10)) : 1e3;
27942
28112
  console.log("\n\u{1F9E0} Bootstrapping markdown knowledge base...");
@@ -27975,13 +28145,13 @@ program.command("organize-import [sourceDir]").description("Import existing mark
27975
28145
  let imported = 0;
27976
28146
  let skipped = 0;
27977
28147
  for (const file of targets) {
27978
- const text = await fs22.promises.readFile(file, "utf8");
28148
+ const text = await fs23.promises.readFile(file, "utf8");
27979
28149
  if (!text.trim()) {
27980
28150
  skipped += 1;
27981
28151
  continue;
27982
28152
  }
27983
28153
  const { namespace, categoryPath } = deriveNamespaceCategory(activeSourceRoot, file);
27984
- const rel = path31.relative(activeSourceRoot, file);
28154
+ const rel = path33.relative(activeSourceRoot, file);
27985
28155
  const evidence = extractImportEvidence(text);
27986
28156
  if (options.dryRun) {
27987
28157
  console.log(`- ${rel} -> namespace=${namespace} category=${categoryPath.join("/")} confidence=${evidence.confidence || "n/a"} sources=${evidence.sources.length}`);
@@ -28136,7 +28306,81 @@ program.command("list").description("List available Claude Code sessions").optio
28136
28306
  process.exit(1);
28137
28307
  }
28138
28308
  });
28139
- var codexCmd = program.command("codex").description("Read-only Codex session scan/replay validation");
28309
+ var codexCmd = program.command("codex").description("Codex session import, validation, and lifecycle hook integration");
28310
+ var codexHooksCmd = codexCmd.command("hooks").description("Manage Codex lifecycle hooks for automatic memory loading and ingestion");
28311
+ codexHooksCmd.command("install").description("Install Codex SessionStart and SessionEnd memory hooks").option("--path <path>", "Custom plugin path (defaults to auto-detect)").option("--config-path <path>", "Codex hooks config path (default: ~/.codex/hooks.json)").action(async (options) => {
28312
+ try {
28313
+ const pluginPath = path33.resolve(options.path || getPluginPath());
28314
+ const configPath = options.configPath || CODEX_HOOKS_PATH;
28315
+ const missingHooks = REQUIRED_CODEX_HOOK_FILES.filter((file) => !fs23.existsSync(path33.join(pluginPath, "hooks", file)));
28316
+ if (missingHooks.length > 0) {
28317
+ throw new Error(`Codex hook files not found at ${pluginPath}; missing: ${missingHooks.join(", ")}`);
28318
+ }
28319
+ const config = loadCodexHooksConfig(configPath);
28320
+ saveCodexHooksConfig(mergeCodexMemoryHooks(config, pluginPath), configPath);
28321
+ console.log("\n\u2705 Codex automatic memory hooks installed!\n");
28322
+ console.log(" SessionStart: Load project memory into Codex context");
28323
+ console.log(" SessionEnd: Import the completed Codex transcript");
28324
+ console.log(` Config: ${configPath}`);
28325
+ console.log(` Plugin path: ${pluginPath}`);
28326
+ console.log("\n\u26A0\uFE0F Restart Codex, open /hooks, and trust the new hook definitions.\n");
28327
+ } catch (error) {
28328
+ console.error("Codex hooks install failed:", error instanceof Error ? error.message : error);
28329
+ process.exitCode = 1;
28330
+ }
28331
+ });
28332
+ codexHooksCmd.command("status").description("Check Codex automatic memory hook status").option("--path <path>", "Custom plugin path (defaults to auto-detect)").option("--config-path <path>", "Codex hooks config path (default: ~/.codex/hooks.json)").option("-p, --project <path>", "Project whose most recent automatic import status should be shown (default: cwd)").action(async (options) => {
28333
+ try {
28334
+ const pluginPath = path33.resolve(options.path || getPluginPath());
28335
+ const configPath = options.configPath || CODEX_HOOKS_PATH;
28336
+ const config = loadCodexHooksConfig(configPath);
28337
+ const sessionStart = hasCodexMemoryHook(config, "SessionStart", pluginPath);
28338
+ const sessionEnd = hasCodexMemoryHook(config, "SessionEnd", pluginPath);
28339
+ const filesExist = REQUIRED_CODEX_HOOK_FILES.every((file) => fs23.existsSync(path33.join(pluginPath, "hooks", file)));
28340
+ console.log("\n\u{1F9E0} Codex Automatic Memory Status\n");
28341
+ console.log(` SessionStart: ${sessionStart ? "\u2705 Installed" : "\u274C Not installed"}`);
28342
+ console.log(` SessionEnd: ${sessionEnd ? "\u2705 Installed" : "\u274C Not installed"}`);
28343
+ console.log(` Hook files: ${filesExist ? "\u2705 Found" : "\u274C Not found"}`);
28344
+ console.log(` Config: ${configPath}`);
28345
+ const importStatus = readCodexAutoImportStatus(path33.resolve(options.project || process.cwd()));
28346
+ if (importStatus) {
28347
+ const counts = importStatus.status === "success" ? ` \xB7 ${importStatus.importedPrompts ?? 0} prompts \xB7 ${importStatus.importedResponses ?? 0} responses \xB7 ${importStatus.skippedDuplicates ?? 0} duplicates` : ` \xB7 ${importStatus.error ?? "unknown error"}`;
28348
+ console.log(` Last auto-import: ${importStatus.status === "success" ? "\u2705" : "\u274C"} ${importStatus.status} at ${importStatus.updatedAt}${counts}`);
28349
+ } else {
28350
+ console.log(" Last auto-import: \u2014 No recorded run for this project");
28351
+ }
28352
+ if (sessionStart && sessionEnd && filesExist) {
28353
+ console.log("\n\u2705 Automatic loading and ingestion are configured. Verify trust with /hooks in Codex.\n");
28354
+ } else {
28355
+ console.log('\n\u{1F4A1} Run "claude-memory-layer codex hooks install" to configure automatic memory.\n');
28356
+ }
28357
+ } catch (error) {
28358
+ console.error("Codex hooks status failed:", error instanceof Error ? error.message : error);
28359
+ process.exitCode = 1;
28360
+ }
28361
+ });
28362
+ codexHooksCmd.command("uninstall").description("Remove only claude-memory-layer hooks from Codex").option("--path <path>", "Custom plugin path (defaults to auto-detect)").option("--config-path <path>", "Codex hooks config path (default: ~/.codex/hooks.json)").action(async (options) => {
28363
+ try {
28364
+ const pluginPath = path33.resolve(options.path || getPluginPath());
28365
+ const configPath = options.configPath || CODEX_HOOKS_PATH;
28366
+ if (!fs23.existsSync(configPath)) {
28367
+ console.log("\n\u{1F4CB} No Codex memory hooks installed.\n");
28368
+ return;
28369
+ }
28370
+ const config = loadCodexHooksConfig(configPath);
28371
+ const nextConfig = removeCodexMemoryHooks(config, pluginPath);
28372
+ if (Object.keys(nextConfig).length === 0) {
28373
+ fs23.unlinkSync(configPath);
28374
+ } else {
28375
+ saveCodexHooksConfig(nextConfig, configPath);
28376
+ }
28377
+ console.log("\n\u2705 Claude Memory Layer hooks removed from Codex.\n");
28378
+ console.log("Memory data was preserved. Restart Codex for the change to take effect.\n");
28379
+ } catch (error) {
28380
+ console.error("Codex hooks uninstall failed:", error instanceof Error ? error.message : error);
28381
+ process.exitCode = 1;
28382
+ }
28383
+ });
28140
28384
  codexCmd.command("validate").description("Dry-run validate Codex JSONL sessions without importing or mutating memory").option("-p, --project <path>", "Filter sessions by session_meta.payload.cwd").option("--sessions-dir <path>", "Codex sessions directory (default: ~/.codex/sessions)").option("-l, --limit <number>", "Limit number of session files to scan").option("--format <format>", "Report format: json or markdown", "markdown").option("-o, --output <path>", "Write report to file").option("--dry-run", "Read-only validation mode (default; no imports or writes)", true).option("--anonymize-projects", "Show hashed project labels instead of raw cwd paths").action(async (options) => {
28141
28385
  try {
28142
28386
  await runCodexValidationCommand(options);