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/README.md +30 -4
- package/dist/cli/index.js +391 -147
- package/dist/cli/index.js.map +4 -4
- package/dist/hooks/codex-session-end.js +124 -0
- package/dist/hooks/codex-session-end.js.map +7 -0
- package/dist/hooks/codex-session-import-worker.js +16798 -0
- package/dist/hooks/codex-session-import-worker.js.map +7 -0
- package/dist/hooks/codex-session-start.js +16660 -0
- package/dist/hooks/codex-session-start.js.map +7 -0
- package/package.json +1 -1
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
|
|
13
|
-
import * as
|
|
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
|
|
1784
|
-
const
|
|
1785
|
-
const modelDir =
|
|
1786
|
-
const resolvedCacheDir =
|
|
1787
|
-
const resolvedModelDir =
|
|
1788
|
-
if (!resolvedModelDir.startsWith(resolvedCacheDir +
|
|
1789
|
-
await
|
|
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(
|
|
2436
|
-
const dir = nodePath.dirname(
|
|
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(
|
|
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,
|
|
3099
|
+
function getNestedRecord(root, path34) {
|
|
3100
3100
|
let cursor = root;
|
|
3101
|
-
for (const key of
|
|
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,
|
|
3107
|
+
function getNestedString(root, path34) {
|
|
3108
3108
|
let cursor = root;
|
|
3109
|
-
for (const key of
|
|
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,
|
|
8966
|
+
function nestedString(root, path34) {
|
|
8967
8967
|
let cursor = root;
|
|
8968
|
-
for (const key of
|
|
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,
|
|
9043
|
+
const paths = Array.from(bestByTarget.entries()).map(([key, path34]) => ({
|
|
9044
9044
|
target: graph.node(nodeFromKey(key)),
|
|
9045
|
-
hops:
|
|
9046
|
-
totalCost:
|
|
9047
|
-
scoreContribution:
|
|
9048
|
-
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,
|
|
9867
|
+
function nestedValue(root, path34) {
|
|
9868
9868
|
let cursor = root;
|
|
9869
|
-
for (const key of
|
|
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,
|
|
9876
|
-
const value = nestedValue(root,
|
|
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
|
|
9932
|
-
const extensionMatch = /\.([A-Za-z0-9]+)$/.exec(
|
|
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 (
|
|
9935
|
+
if (path34.startsWith("tests/")) {
|
|
9936
9936
|
categories.add(`test:${extension}`);
|
|
9937
|
-
} else if (
|
|
9937
|
+
} else if (path34.startsWith("src/")) {
|
|
9938
9938
|
categories.add(`source:${extension}`);
|
|
9939
|
-
} else if (
|
|
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,
|
|
10266
|
+
function nestedValue2(root, path34) {
|
|
10267
10267
|
let cursor = root;
|
|
10268
|
-
for (const key of
|
|
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,
|
|
10275
|
-
const value = nestedValue2(root,
|
|
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((
|
|
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
|
|
14599
|
-
if (
|
|
14600
|
-
const target = await this.eventStore.getEvent(
|
|
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(
|
|
14603
|
-
const score = graphPathScore(
|
|
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-${
|
|
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
|
|
14651
|
-
if (
|
|
14652
|
-
const target = await this.eventStore.getEvent(
|
|
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(
|
|
14655
|
-
const score = graphPathScore(
|
|
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-${
|
|
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(([
|
|
15005
|
-
const actual =
|
|
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(
|
|
15057
|
-
const firstStep =
|
|
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:
|
|
15064
|
-
targetType:
|
|
15065
|
-
hops:
|
|
15066
|
-
relationPath:
|
|
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(
|
|
15070
|
-
const base = Math.min(0.95, Math.max(0,
|
|
15071
|
-
return Math.max(0.05, base - hopPenalty * Math.max(0,
|
|
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
|
|
15080
|
-
const key = [
|
|
15081
|
-
if (!byKey.has(key)) byKey.set(key,
|
|
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(
|
|
15086
|
-
return [
|
|
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((
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 ??
|
|
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
|
|
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 =
|
|
26194
|
+
const globalStoragePath = path30.join((deps.homedir ?? os13.homedir)(), ".claude-code", "memory");
|
|
26048
26195
|
return {
|
|
26049
26196
|
storageScope: "global",
|
|
26050
|
-
lockPath: options.lockPath ??
|
|
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 ??
|
|
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
|
|
26338
|
+
import * as path32 from "node:path";
|
|
26192
26339
|
|
|
26193
26340
|
// src/core/worker-lock.ts
|
|
26194
|
-
import { closeSync, existsSync as
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 || !
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 ??
|
|
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 =
|
|
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
|
-
|
|
26584
|
+
path33.join(__dirname, ".."),
|
|
26437
26585
|
// When running from dist/cli
|
|
26438
|
-
|
|
26586
|
+
path33.join(__dirname, "../..", "dist"),
|
|
26439
26587
|
// When running from src
|
|
26440
|
-
|
|
26588
|
+
path33.join(process.cwd(), "dist")
|
|
26441
26589
|
// Current working directory
|
|
26442
26590
|
];
|
|
26443
26591
|
for (const p of possiblePaths) {
|
|
26444
|
-
const hooksPath =
|
|
26445
|
-
if (
|
|
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
|
|
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 (
|
|
26454
|
-
const content =
|
|
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 =
|
|
26464
|
-
if (!
|
|
26465
|
-
|
|
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
|
-
|
|
26469
|
-
|
|
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) :
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
26538
|
-
if (!
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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:
|
|
26861
|
+
dbPath: path33.join(storagePath, "events.sqlite")
|
|
26692
26862
|
};
|
|
26693
26863
|
}
|
|
26694
26864
|
function openOperationReadDatabase(context) {
|
|
26695
|
-
if (!
|
|
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.
|
|
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) => !
|
|
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) =>
|
|
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 =
|
|
27285
|
-
if (repairOptions.dryRun && !
|
|
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 =
|
|
27319
|
-
if (pruneOptions.dryRun && !
|
|
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(
|
|
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 =
|
|
27349
|
-
if (!
|
|
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(
|
|
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 =
|
|
27403
|
-
if (!
|
|
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 ??
|
|
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(
|
|
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 =
|
|
27697
|
-
if (!
|
|
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 ||
|
|
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 (!
|
|
27755
|
-
|
|
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(
|
|
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 =
|
|
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
|
|
28066
|
+
const entries = await fs23.promises.readdir(dir, { withFileTypes: true });
|
|
27897
28067
|
for (const e of entries) {
|
|
27898
|
-
const full =
|
|
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 =
|
|
27907
|
-
const dirSeg =
|
|
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 =
|
|
27927
|
-
const repoPath =
|
|
27928
|
-
if (!
|
|
27929
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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("
|
|
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);
|