@hasna/mementos 0.11.1 → 0.14.2
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/dashboard/dist/assets/index-DqyMbv89.js +270 -0
- package/dashboard/dist/assets/index-UBCddFo_.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/cli/brains.d.ts +3 -0
- package/dist/cli/brains.d.ts.map +1 -0
- package/dist/cli/index.js +2093 -512
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/memories.d.ts.map +1 -1
- package/dist/db/tool-events.d.ts +27 -0
- package/dist/db/tool-events.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +177 -5
- package/dist/lib/activation-matcher.d.ts +16 -0
- package/dist/lib/activation-matcher.d.ts.map +1 -0
- package/dist/lib/asmr/categorizer.d.ts +31 -0
- package/dist/lib/asmr/categorizer.d.ts.map +1 -0
- package/dist/lib/asmr/context-agent.d.ts +4 -0
- package/dist/lib/asmr/context-agent.d.ts.map +1 -0
- package/dist/lib/asmr/ensemble.d.ts +23 -0
- package/dist/lib/asmr/ensemble.d.ts.map +1 -0
- package/dist/lib/asmr/fact-agent.d.ts +4 -0
- package/dist/lib/asmr/fact-agent.d.ts.map +1 -0
- package/dist/lib/asmr/index.d.ts +7 -0
- package/dist/lib/asmr/index.d.ts.map +1 -0
- package/dist/lib/asmr/orchestrator.d.ts +4 -0
- package/dist/lib/asmr/orchestrator.d.ts.map +1 -0
- package/dist/lib/asmr/temporal-agent.d.ts +4 -0
- package/dist/lib/asmr/temporal-agent.d.ts.map +1 -0
- package/dist/lib/asmr/types.d.ts +27 -0
- package/dist/lib/asmr/types.d.ts.map +1 -0
- package/dist/lib/auto-inject-orchestrator.d.ts +57 -0
- package/dist/lib/auto-inject-orchestrator.d.ts.map +1 -0
- package/dist/lib/built-in-hooks.d.ts.map +1 -1
- package/dist/lib/channel-pusher.d.ts +39 -0
- package/dist/lib/channel-pusher.d.ts.map +1 -0
- package/dist/lib/connectors/files.d.ts +8 -0
- package/dist/lib/connectors/files.d.ts.map +1 -0
- package/dist/lib/connectors/github.d.ts +7 -0
- package/dist/lib/connectors/github.d.ts.map +1 -0
- package/dist/lib/connectors/index.d.ts +12 -0
- package/dist/lib/connectors/index.d.ts.map +1 -0
- package/dist/lib/connectors/notion.d.ts +7 -0
- package/dist/lib/connectors/notion.d.ts.map +1 -0
- package/dist/lib/connectors/types.d.ts +27 -0
- package/dist/lib/connectors/types.d.ts.map +1 -0
- package/dist/lib/context-extractor.d.ts +14 -0
- package/dist/lib/context-extractor.d.ts.map +1 -0
- package/dist/lib/extractors/audio.d.ts +8 -0
- package/dist/lib/extractors/audio.d.ts.map +1 -0
- package/dist/lib/extractors/index.d.ts +12 -0
- package/dist/lib/extractors/index.d.ts.map +1 -0
- package/dist/lib/extractors/ocr.d.ts +7 -0
- package/dist/lib/extractors/ocr.d.ts.map +1 -0
- package/dist/lib/extractors/pdf.d.ts +7 -0
- package/dist/lib/extractors/pdf.d.ts.map +1 -0
- package/dist/lib/extractors/types.d.ts +12 -0
- package/dist/lib/extractors/types.d.ts.map +1 -0
- package/dist/lib/gatherer.d.ts +16 -0
- package/dist/lib/gatherer.d.ts.map +1 -0
- package/dist/lib/injector.d.ts +48 -1
- package/dist/lib/injector.d.ts.map +1 -1
- package/dist/lib/matryoshka.d.ts +50 -0
- package/dist/lib/matryoshka.d.ts.map +1 -0
- package/dist/lib/model-config.d.ts +14 -0
- package/dist/lib/model-config.d.ts.map +1 -0
- package/dist/lib/procedural-extractor.d.ts +21 -0
- package/dist/lib/procedural-extractor.d.ts.map +1 -0
- package/dist/lib/profile-synthesizer.d.ts +20 -0
- package/dist/lib/profile-synthesizer.d.ts.map +1 -0
- package/dist/lib/session-processor.d.ts.map +1 -1
- package/dist/lib/session-registry.d.ts +47 -0
- package/dist/lib/session-registry.d.ts.map +1 -0
- package/dist/lib/session-start-briefing.d.ts +10 -0
- package/dist/lib/session-start-briefing.d.ts.map +1 -0
- package/dist/lib/session-watcher.d.ts +30 -0
- package/dist/lib/session-watcher.d.ts.map +1 -0
- package/dist/lib/tool-lesson-extractor.d.ts +24 -0
- package/dist/lib/tool-lesson-extractor.d.ts.map +1 -0
- package/dist/lib/tool-memory-synthesizer.d.ts +28 -0
- package/dist/lib/tool-memory-synthesizer.d.ts.map +1 -0
- package/dist/lib/topic-clusterer.d.ts +21 -0
- package/dist/lib/topic-clusterer.d.ts.map +1 -0
- package/dist/lib/when-to-use-generator.d.ts +22 -0
- package/dist/lib/when-to-use-generator.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +3 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +3825 -382
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +883 -18
- package/dist/types/index.d.ts +57 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +4 -1
- package/dashboard/dist/assets/index-B1yiOEw3.js +0 -290
- package/dashboard/dist/assets/index-DnpbasSl.css +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -6,39 +6,60 @@ var __defProp = Object.defineProperty;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
function __accessProp(key) {
|
|
10
|
+
return this[key];
|
|
11
|
+
}
|
|
12
|
+
var __toESMCache_node;
|
|
13
|
+
var __toESMCache_esm;
|
|
9
14
|
var __toESM = (mod, isNodeMode, target) => {
|
|
15
|
+
var canCache = mod != null && typeof mod === "object";
|
|
16
|
+
if (canCache) {
|
|
17
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
18
|
+
var cached = cache.get(mod);
|
|
19
|
+
if (cached)
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
10
22
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
11
23
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
12
24
|
for (let key of __getOwnPropNames(mod))
|
|
13
25
|
if (!__hasOwnProp.call(to, key))
|
|
14
26
|
__defProp(to, key, {
|
|
15
|
-
get: (
|
|
27
|
+
get: __accessProp.bind(mod, key),
|
|
16
28
|
enumerable: true
|
|
17
29
|
});
|
|
30
|
+
if (canCache)
|
|
31
|
+
cache.set(mod, to);
|
|
18
32
|
return to;
|
|
19
33
|
};
|
|
20
|
-
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
21
34
|
var __toCommonJS = (from) => {
|
|
22
|
-
var entry = __moduleCache.get(from), desc;
|
|
35
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
23
36
|
if (entry)
|
|
24
37
|
return entry;
|
|
25
38
|
entry = __defProp({}, "__esModule", { value: true });
|
|
26
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
40
|
+
for (var key of __getOwnPropNames(from))
|
|
41
|
+
if (!__hasOwnProp.call(entry, key))
|
|
42
|
+
__defProp(entry, key, {
|
|
43
|
+
get: __accessProp.bind(from, key),
|
|
44
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
45
|
+
});
|
|
46
|
+
}
|
|
31
47
|
__moduleCache.set(from, entry);
|
|
32
48
|
return entry;
|
|
33
49
|
};
|
|
50
|
+
var __moduleCache;
|
|
34
51
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
52
|
+
var __returnValue = (v) => v;
|
|
53
|
+
function __exportSetter(name, newValue) {
|
|
54
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
55
|
+
}
|
|
35
56
|
var __export = (target, all) => {
|
|
36
57
|
for (var name in all)
|
|
37
58
|
__defProp(target, name, {
|
|
38
59
|
get: all[name],
|
|
39
60
|
enumerable: true,
|
|
40
61
|
configurable: true,
|
|
41
|
-
set: (
|
|
62
|
+
set: __exportSetter.bind(all, name)
|
|
42
63
|
});
|
|
43
64
|
};
|
|
44
65
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -2909,6 +2930,44 @@ CREATE INDEX IF NOT EXISTS idx_memory_embeddings_model ON memory_embeddings(mode
|
|
|
2909
2930
|
|
|
2910
2931
|
PRAGMA foreign_keys = ON;
|
|
2911
2932
|
INSERT OR IGNORE INTO _migrations (id) VALUES (29);
|
|
2933
|
+
`,
|
|
2934
|
+
`
|
|
2935
|
+
ALTER TABLE memories ADD COLUMN when_to_use TEXT DEFAULT NULL;
|
|
2936
|
+
CREATE INDEX IF NOT EXISTS idx_memories_when_to_use ON memories(when_to_use) WHERE when_to_use IS NOT NULL;
|
|
2937
|
+
ALTER TABLE memory_versions ADD COLUMN when_to_use TEXT;
|
|
2938
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (30);
|
|
2939
|
+
`,
|
|
2940
|
+
`
|
|
2941
|
+
CREATE TABLE IF NOT EXISTS tool_events (
|
|
2942
|
+
id TEXT PRIMARY KEY,
|
|
2943
|
+
tool_name TEXT NOT NULL,
|
|
2944
|
+
action TEXT,
|
|
2945
|
+
success INTEGER NOT NULL DEFAULT 1,
|
|
2946
|
+
error_type TEXT CHECK(error_type IS NULL OR error_type IN ('timeout', 'permission', 'not_found', 'syntax', 'rate_limit', 'other')),
|
|
2947
|
+
error_message TEXT,
|
|
2948
|
+
tokens_used INTEGER,
|
|
2949
|
+
latency_ms INTEGER,
|
|
2950
|
+
context TEXT,
|
|
2951
|
+
lesson TEXT,
|
|
2952
|
+
when_to_use TEXT,
|
|
2953
|
+
agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
|
|
2954
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2955
|
+
session_id TEXT,
|
|
2956
|
+
metadata TEXT DEFAULT '{}',
|
|
2957
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2958
|
+
);
|
|
2959
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_tool_name ON tool_events(tool_name);
|
|
2960
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_agent ON tool_events(agent_id);
|
|
2961
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_project ON tool_events(project_id);
|
|
2962
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_success ON tool_events(success);
|
|
2963
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_created ON tool_events(created_at);
|
|
2964
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (31);
|
|
2965
|
+
`,
|
|
2966
|
+
`
|
|
2967
|
+
ALTER TABLE memories ADD COLUMN sequence_group TEXT DEFAULT NULL;
|
|
2968
|
+
ALTER TABLE memories ADD COLUMN sequence_order INTEGER DEFAULT NULL;
|
|
2969
|
+
CREATE INDEX IF NOT EXISTS idx_memories_sequence_group ON memories(sequence_group) WHERE sequence_group IS NOT NULL;
|
|
2970
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
2912
2971
|
`
|
|
2913
2972
|
];
|
|
2914
2973
|
});
|
|
@@ -3317,6 +3376,9 @@ function parseMemoryRow(row) {
|
|
|
3317
3376
|
session_id: row["session_id"] || null,
|
|
3318
3377
|
machine_id: row["machine_id"] || null,
|
|
3319
3378
|
flag: row["flag"] || null,
|
|
3379
|
+
when_to_use: row["when_to_use"] || null,
|
|
3380
|
+
sequence_group: row["sequence_group"] || null,
|
|
3381
|
+
sequence_order: row["sequence_order"] ?? null,
|
|
3320
3382
|
content_type: row["content_type"] || "text",
|
|
3321
3383
|
namespace: row["namespace"] || null,
|
|
3322
3384
|
created_by_agent: row["created_by_agent"] || null,
|
|
@@ -3375,6 +3437,7 @@ function createMemory(input, dedupeMode = "merge", db) {
|
|
|
3375
3437
|
d.run(`UPDATE memories SET
|
|
3376
3438
|
value = ?, category = ?, summary = ?, tags = ?,
|
|
3377
3439
|
importance = ?, metadata = ?, expires_at = ?,
|
|
3440
|
+
when_to_use = ?,
|
|
3378
3441
|
pinned = COALESCE(pinned, 0),
|
|
3379
3442
|
version = version + 1, updated_at = ?
|
|
3380
3443
|
WHERE id = ?`, [
|
|
@@ -3385,6 +3448,7 @@ function createMemory(input, dedupeMode = "merge", db) {
|
|
|
3385
3448
|
input.importance ?? 5,
|
|
3386
3449
|
metadataJson,
|
|
3387
3450
|
expiresAt,
|
|
3451
|
+
input.when_to_use || null,
|
|
3388
3452
|
timestamp,
|
|
3389
3453
|
existing.id
|
|
3390
3454
|
]);
|
|
@@ -3409,8 +3473,8 @@ function createMemory(input, dedupeMode = "merge", db) {
|
|
|
3409
3473
|
return merged;
|
|
3410
3474
|
}
|
|
3411
3475
|
}
|
|
3412
|
-
d.run(`INSERT INTO memories (id, key, value, category, scope, summary, tags, importance, source, status, pinned, agent_id, project_id, session_id, machine_id, namespace, created_by_agent, metadata, access_count, version, expires_at, valid_from, valid_until, ingested_at, created_at, updated_at)
|
|
3413
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', 0, ?, ?, ?, ?, ?, ?, ?, 0, 1, ?, ?, ?, ?, ?, ?)`, [
|
|
3476
|
+
d.run(`INSERT INTO memories (id, key, value, category, scope, summary, tags, importance, source, status, pinned, agent_id, project_id, session_id, machine_id, namespace, created_by_agent, when_to_use, sequence_group, sequence_order, metadata, access_count, version, expires_at, valid_from, valid_until, ingested_at, created_at, updated_at)
|
|
3477
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 1, ?, ?, ?, ?, ?, ?)`, [
|
|
3414
3478
|
id,
|
|
3415
3479
|
input.key,
|
|
3416
3480
|
input.value,
|
|
@@ -3426,6 +3490,9 @@ function createMemory(input, dedupeMode = "merge", db) {
|
|
|
3426
3490
|
input.machine_id || null,
|
|
3427
3491
|
input.namespace || null,
|
|
3428
3492
|
input.agent_id || null,
|
|
3493
|
+
input.when_to_use || null,
|
|
3494
|
+
input.sequence_group || null,
|
|
3495
|
+
input.sequence_order ?? null,
|
|
3429
3496
|
metadataJson,
|
|
3430
3497
|
expiresAt,
|
|
3431
3498
|
input.metadata?.valid_from ?? timestamp,
|
|
@@ -3636,8 +3703,8 @@ function updateMemory(id, input, db) {
|
|
|
3636
3703
|
throw new VersionConflictError(id, input.version, existing.version);
|
|
3637
3704
|
}
|
|
3638
3705
|
try {
|
|
3639
|
-
d.run(`INSERT OR IGNORE INTO memory_versions (id, memory_id, version, value, importance, scope, category, tags, summary, pinned, status, created_at)
|
|
3640
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
3706
|
+
d.run(`INSERT OR IGNORE INTO memory_versions (id, memory_id, version, value, importance, scope, category, tags, summary, pinned, status, when_to_use, created_at)
|
|
3707
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
3641
3708
|
uuid(),
|
|
3642
3709
|
existing.id,
|
|
3643
3710
|
existing.version,
|
|
@@ -3649,6 +3716,7 @@ function updateMemory(id, input, db) {
|
|
|
3649
3716
|
existing.summary,
|
|
3650
3717
|
existing.pinned ? 1 : 0,
|
|
3651
3718
|
existing.status,
|
|
3719
|
+
existing.when_to_use || null,
|
|
3652
3720
|
existing.updated_at
|
|
3653
3721
|
]);
|
|
3654
3722
|
} catch {}
|
|
@@ -3694,6 +3762,10 @@ function updateMemory(id, input, db) {
|
|
|
3694
3762
|
sets.push("flag = ?");
|
|
3695
3763
|
params.push(input.flag ?? null);
|
|
3696
3764
|
}
|
|
3765
|
+
if (input.when_to_use !== undefined) {
|
|
3766
|
+
sets.push("when_to_use = ?");
|
|
3767
|
+
params.push(input.when_to_use ?? null);
|
|
3768
|
+
}
|
|
3697
3769
|
if (input.tags !== undefined) {
|
|
3698
3770
|
sets.push("tags = ?");
|
|
3699
3771
|
params.push(JSON.stringify(input.tags));
|
|
@@ -5965,6 +6037,311 @@ var init_synthesis = __esm(() => {
|
|
|
5965
6037
|
init_database();
|
|
5966
6038
|
});
|
|
5967
6039
|
|
|
6040
|
+
// src/lib/when-to-use-generator.ts
|
|
6041
|
+
var exports_when_to_use_generator = {};
|
|
6042
|
+
__export(exports_when_to_use_generator, {
|
|
6043
|
+
generateWhenToUse: () => generateWhenToUse,
|
|
6044
|
+
autoGenerateWhenToUse: () => autoGenerateWhenToUse
|
|
6045
|
+
});
|
|
6046
|
+
async function generateWhenToUse(key, value, category, tags) {
|
|
6047
|
+
if (process.env["MEMENTOS_AUTO_WHEN_TO_USE"] !== "true")
|
|
6048
|
+
return null;
|
|
6049
|
+
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
6050
|
+
if (!apiKey)
|
|
6051
|
+
return null;
|
|
6052
|
+
try {
|
|
6053
|
+
const userMessage = `Key: "${key}"
|
|
6054
|
+
Value: "${value}"
|
|
6055
|
+
Category: ${category}
|
|
6056
|
+
Tags: ${tags.join(", ") || "none"}
|
|
6057
|
+
|
|
6058
|
+
Generate the when_to_use activation context (1-2 sentences):`;
|
|
6059
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
6060
|
+
method: "POST",
|
|
6061
|
+
headers: {
|
|
6062
|
+
"x-api-key": apiKey,
|
|
6063
|
+
"anthropic-version": "2023-06-01",
|
|
6064
|
+
"content-type": "application/json"
|
|
6065
|
+
},
|
|
6066
|
+
body: JSON.stringify({
|
|
6067
|
+
model: "claude-haiku-4-5-20251001",
|
|
6068
|
+
max_tokens: 150,
|
|
6069
|
+
system: SYSTEM_PROMPT2,
|
|
6070
|
+
messages: [{ role: "user", content: userMessage }]
|
|
6071
|
+
})
|
|
6072
|
+
});
|
|
6073
|
+
if (!response.ok)
|
|
6074
|
+
return null;
|
|
6075
|
+
const data = await response.json();
|
|
6076
|
+
const text = data.content?.[0]?.text?.trim();
|
|
6077
|
+
return text || null;
|
|
6078
|
+
} catch {
|
|
6079
|
+
return null;
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
async function autoGenerateWhenToUse(ctx) {
|
|
6083
|
+
if (ctx.memory.when_to_use)
|
|
6084
|
+
return;
|
|
6085
|
+
if (process.env["MEMENTOS_AUTO_WHEN_TO_USE"] !== "true")
|
|
6086
|
+
return;
|
|
6087
|
+
try {
|
|
6088
|
+
const whenToUse = await generateWhenToUse(ctx.memory.key, ctx.memory.value, ctx.memory.category, ctx.memory.tags);
|
|
6089
|
+
if (whenToUse) {
|
|
6090
|
+
const db = getDatabase();
|
|
6091
|
+
db.run("UPDATE memories SET when_to_use = ? WHERE id = ? AND when_to_use IS NULL", [whenToUse, ctx.memory.id]);
|
|
6092
|
+
}
|
|
6093
|
+
} catch {}
|
|
6094
|
+
}
|
|
6095
|
+
var SYSTEM_PROMPT2 = `You generate activation contexts for memory records. Given a memory's key, value, category, and tags, output a 1-2 sentence "when to use" description that describes the SITUATION or CONDITION under which an AI agent should retrieve this memory.
|
|
6096
|
+
|
|
6097
|
+
Rules:
|
|
6098
|
+
- Start with "When" or "If"
|
|
6099
|
+
- Describe the situation, not the content
|
|
6100
|
+
- Be specific enough to avoid false matches but general enough to catch relevant scenarios
|
|
6101
|
+
- Focus on the task/action the agent would be doing, not what the memory contains
|
|
6102
|
+
|
|
6103
|
+
Examples:
|
|
6104
|
+
- Key: "preferred-language", Value: "Always use TypeScript, never JavaScript" \u2192 "When choosing a programming language for a new file or project"
|
|
6105
|
+
- Key: "db-migration-order", Value: "Always run migrations before deploying" \u2192 "When deploying or updating database schema"
|
|
6106
|
+
- Key: "bash-chain-bug", Value: "Bash tool mangles && chains" \u2192 "When chaining commands with && in the Bash tool"`;
|
|
6107
|
+
var init_when_to_use_generator = __esm(() => {
|
|
6108
|
+
init_database();
|
|
6109
|
+
});
|
|
6110
|
+
|
|
6111
|
+
// src/lib/profile-synthesizer.ts
|
|
6112
|
+
var exports_profile_synthesizer = {};
|
|
6113
|
+
__export(exports_profile_synthesizer, {
|
|
6114
|
+
synthesizeProfile: () => synthesizeProfile,
|
|
6115
|
+
markProfileStale: () => markProfileStale,
|
|
6116
|
+
getProfileKey: () => getProfileKey
|
|
6117
|
+
});
|
|
6118
|
+
function getProfileKey(scope, id) {
|
|
6119
|
+
return `_profile_${scope}_${id}`;
|
|
6120
|
+
}
|
|
6121
|
+
async function synthesizeProfile(options) {
|
|
6122
|
+
const scope = options.scope || (options.project_id ? "project" : options.agent_id ? "agent" : "global");
|
|
6123
|
+
const id = options.project_id || options.agent_id || "global";
|
|
6124
|
+
const profileKey = getProfileKey(scope, id);
|
|
6125
|
+
if (!options.force_refresh) {
|
|
6126
|
+
const cached = getMemoryByKey(profileKey, "shared", undefined, options.project_id);
|
|
6127
|
+
if (cached) {
|
|
6128
|
+
const age = Date.now() - new Date(cached.updated_at).getTime();
|
|
6129
|
+
const maxAge = 24 * 60 * 60 * 1000;
|
|
6130
|
+
const isStale = cached.metadata?.stale === true;
|
|
6131
|
+
if (age < maxAge && !isStale) {
|
|
6132
|
+
return { profile: cached.value, memory_count: 0, from_cache: true };
|
|
6133
|
+
}
|
|
6134
|
+
}
|
|
6135
|
+
}
|
|
6136
|
+
const prefMemories = listMemories({
|
|
6137
|
+
category: "preference",
|
|
6138
|
+
project_id: options.project_id,
|
|
6139
|
+
status: "active",
|
|
6140
|
+
limit: 30
|
|
6141
|
+
});
|
|
6142
|
+
const factMemories = listMemories({
|
|
6143
|
+
category: "fact",
|
|
6144
|
+
project_id: options.project_id,
|
|
6145
|
+
status: "active",
|
|
6146
|
+
limit: 30
|
|
6147
|
+
});
|
|
6148
|
+
const allMemories = [...prefMemories, ...factMemories];
|
|
6149
|
+
if (allMemories.length === 0)
|
|
6150
|
+
return null;
|
|
6151
|
+
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
6152
|
+
if (!apiKey) {
|
|
6153
|
+
const lines = allMemories.map((m) => `- ${m.key}: ${m.value}`).join(`
|
|
6154
|
+
`);
|
|
6155
|
+
const fallbackProfile = `## Profile
|
|
6156
|
+
${lines}`;
|
|
6157
|
+
saveProfile(profileKey, fallbackProfile, allMemories.length, options);
|
|
6158
|
+
return { profile: fallbackProfile, memory_count: allMemories.length, from_cache: false };
|
|
6159
|
+
}
|
|
6160
|
+
try {
|
|
6161
|
+
const memoryList = allMemories.sort((a, b) => b.importance - a.importance).map((m) => `[${m.category}] ${m.key}: ${m.value}`).join(`
|
|
6162
|
+
`);
|
|
6163
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
6164
|
+
method: "POST",
|
|
6165
|
+
headers: {
|
|
6166
|
+
"x-api-key": apiKey,
|
|
6167
|
+
"anthropic-version": "2023-06-01",
|
|
6168
|
+
"content-type": "application/json"
|
|
6169
|
+
},
|
|
6170
|
+
body: JSON.stringify({
|
|
6171
|
+
model: "claude-haiku-4-5-20251001",
|
|
6172
|
+
max_tokens: 500,
|
|
6173
|
+
system: PROFILE_PROMPT,
|
|
6174
|
+
messages: [{ role: "user", content: `Synthesize a profile from these ${allMemories.length} memories:
|
|
6175
|
+
|
|
6176
|
+
${memoryList}` }]
|
|
6177
|
+
})
|
|
6178
|
+
});
|
|
6179
|
+
if (!response.ok)
|
|
6180
|
+
return null;
|
|
6181
|
+
const data = await response.json();
|
|
6182
|
+
const profile = data.content?.[0]?.text?.trim();
|
|
6183
|
+
if (!profile)
|
|
6184
|
+
return null;
|
|
6185
|
+
saveProfile(profileKey, profile, allMemories.length, options);
|
|
6186
|
+
return { profile, memory_count: allMemories.length, from_cache: false };
|
|
6187
|
+
} catch {
|
|
6188
|
+
return null;
|
|
6189
|
+
}
|
|
6190
|
+
}
|
|
6191
|
+
function saveProfile(key, value, memoryCount, options) {
|
|
6192
|
+
try {
|
|
6193
|
+
createMemory({
|
|
6194
|
+
key,
|
|
6195
|
+
value,
|
|
6196
|
+
category: "fact",
|
|
6197
|
+
scope: "shared",
|
|
6198
|
+
importance: 10,
|
|
6199
|
+
source: "auto",
|
|
6200
|
+
tags: ["profile", "synthesized"],
|
|
6201
|
+
when_to_use: "When needing to understand this agent's or project's preferences, style, and conventions",
|
|
6202
|
+
metadata: { memory_count: memoryCount, synthesized_at: new Date().toISOString(), stale: false },
|
|
6203
|
+
agent_id: options.agent_id,
|
|
6204
|
+
project_id: options.project_id
|
|
6205
|
+
});
|
|
6206
|
+
} catch {}
|
|
6207
|
+
}
|
|
6208
|
+
function markProfileStale(projectId, _agentId) {
|
|
6209
|
+
try {
|
|
6210
|
+
const { getDatabase: getDatabase2 } = (init_database(), __toCommonJS(exports_database));
|
|
6211
|
+
const db = getDatabase2();
|
|
6212
|
+
db.run(`UPDATE memories SET metadata = json_set(COALESCE(metadata, '{}'), '$.stale', json('true'))
|
|
6213
|
+
WHERE key LIKE '_profile_%' AND COALESCE(project_id, '') = ?`, [projectId || ""]);
|
|
6214
|
+
} catch {}
|
|
6215
|
+
}
|
|
6216
|
+
var PROFILE_PROMPT = `You synthesize a coherent agent/project profile from individual preference and fact memories.
|
|
6217
|
+
|
|
6218
|
+
Output a concise profile (200-300 words max) organized by:
|
|
6219
|
+
- **Stack & Tools**: Languages, frameworks, package managers, etc.
|
|
6220
|
+
- **Code Style**: Formatting, patterns, naming conventions
|
|
6221
|
+
- **Workflow**: Testing, deployment, git practices
|
|
6222
|
+
- **Communication**: Response style, verbosity, formatting preferences
|
|
6223
|
+
- **Key Facts**: Architecture decisions, constraints, team conventions
|
|
6224
|
+
|
|
6225
|
+
Only include sections that have relevant data. Be specific and actionable.
|
|
6226
|
+
Output in markdown format.`;
|
|
6227
|
+
var init_profile_synthesizer = __esm(() => {
|
|
6228
|
+
init_memories();
|
|
6229
|
+
});
|
|
6230
|
+
|
|
6231
|
+
// src/lib/contradiction.ts
|
|
6232
|
+
var exports_contradiction = {};
|
|
6233
|
+
__export(exports_contradiction, {
|
|
6234
|
+
invalidateFact: () => invalidateFact,
|
|
6235
|
+
detectContradiction: () => detectContradiction
|
|
6236
|
+
});
|
|
6237
|
+
function invalidateFact(oldMemoryId, newMemoryId, db) {
|
|
6238
|
+
const d = db || getDatabase();
|
|
6239
|
+
const timestamp = now();
|
|
6240
|
+
d.run("UPDATE memories SET valid_until = ?, updated_at = ? WHERE id = ?", [timestamp, timestamp, oldMemoryId]);
|
|
6241
|
+
if (newMemoryId) {
|
|
6242
|
+
const row = d.query("SELECT metadata FROM memories WHERE id = ?").get(newMemoryId);
|
|
6243
|
+
if (row) {
|
|
6244
|
+
const metadata = JSON.parse(row.metadata || "{}");
|
|
6245
|
+
metadata.supersedes_id = oldMemoryId;
|
|
6246
|
+
d.run("UPDATE memories SET metadata = ?, updated_at = ? WHERE id = ?", [JSON.stringify(metadata), timestamp, newMemoryId]);
|
|
6247
|
+
}
|
|
6248
|
+
}
|
|
6249
|
+
return {
|
|
6250
|
+
invalidated_memory_id: oldMemoryId,
|
|
6251
|
+
new_memory_id: newMemoryId || null,
|
|
6252
|
+
valid_until: timestamp,
|
|
6253
|
+
supersedes_id: oldMemoryId
|
|
6254
|
+
};
|
|
6255
|
+
}
|
|
6256
|
+
function heuristicContradictionScore(newValue, existingValue, newKey, existingKey) {
|
|
6257
|
+
if (newKey !== existingKey)
|
|
6258
|
+
return 0;
|
|
6259
|
+
const newLower = newValue.toLowerCase().trim();
|
|
6260
|
+
const existingLower = existingValue.toLowerCase().trim();
|
|
6261
|
+
if (newLower === existingLower)
|
|
6262
|
+
return 0;
|
|
6263
|
+
const newWords = new Set(newLower.split(/\s+/));
|
|
6264
|
+
const existingWords = new Set(existingLower.split(/\s+/));
|
|
6265
|
+
let overlap = 0;
|
|
6266
|
+
for (const w of newWords) {
|
|
6267
|
+
if (existingWords.has(w))
|
|
6268
|
+
overlap++;
|
|
6269
|
+
}
|
|
6270
|
+
const totalUnique = new Set([...newWords, ...existingWords]).size;
|
|
6271
|
+
const overlapRatio = totalUnique > 0 ? overlap / totalUnique : 0;
|
|
6272
|
+
if (overlapRatio < 0.3)
|
|
6273
|
+
return 0.7;
|
|
6274
|
+
if (overlapRatio < 0.5)
|
|
6275
|
+
return 0.4;
|
|
6276
|
+
return 0.1;
|
|
6277
|
+
}
|
|
6278
|
+
async function llmContradictionCheck(_newValue, _existingValue, _key) {
|
|
6279
|
+
const provider = providerRegistry.getAvailable();
|
|
6280
|
+
if (!provider) {
|
|
6281
|
+
return { contradicts: false, confidence: 0, reasoning: "No LLM provider available" };
|
|
6282
|
+
}
|
|
6283
|
+
try {
|
|
6284
|
+
return { contradicts: false, confidence: 0, reasoning: "LLM check skipped \u2014 using heuristic only" };
|
|
6285
|
+
} catch {
|
|
6286
|
+
return { contradicts: false, confidence: 0, reasoning: "LLM check failed" };
|
|
6287
|
+
}
|
|
6288
|
+
}
|
|
6289
|
+
async function detectContradiction(newKey, newValue, options = {}, db) {
|
|
6290
|
+
const d = db || getDatabase();
|
|
6291
|
+
const { scope, project_id, min_importance = 7, use_llm = false } = options;
|
|
6292
|
+
const conditions = ["key = ?", "status = 'active'", "importance >= ?"];
|
|
6293
|
+
const params = [newKey, min_importance];
|
|
6294
|
+
if (scope) {
|
|
6295
|
+
conditions.push("scope = ?");
|
|
6296
|
+
params.push(scope);
|
|
6297
|
+
}
|
|
6298
|
+
if (project_id) {
|
|
6299
|
+
conditions.push("project_id = ?");
|
|
6300
|
+
params.push(project_id);
|
|
6301
|
+
}
|
|
6302
|
+
conditions.push("(valid_until IS NULL OR valid_until > datetime('now'))");
|
|
6303
|
+
const sql = `SELECT * FROM memories WHERE ${conditions.join(" AND ")} ORDER BY importance DESC LIMIT 10`;
|
|
6304
|
+
const rows = d.query(sql).all(...params);
|
|
6305
|
+
if (rows.length === 0) {
|
|
6306
|
+
return { contradicts: false, conflicting_memory: null, confidence: 0, reasoning: "No existing memories with this key" };
|
|
6307
|
+
}
|
|
6308
|
+
let bestContradiction = {
|
|
6309
|
+
contradicts: false,
|
|
6310
|
+
conflicting_memory: null,
|
|
6311
|
+
confidence: 0,
|
|
6312
|
+
reasoning: "No contradiction detected"
|
|
6313
|
+
};
|
|
6314
|
+
for (const row of rows) {
|
|
6315
|
+
const existing = parseMemoryRow(row);
|
|
6316
|
+
const heuristicScore = heuristicContradictionScore(newValue, existing.value, newKey, existing.key);
|
|
6317
|
+
if (heuristicScore > bestContradiction.confidence) {
|
|
6318
|
+
bestContradiction = {
|
|
6319
|
+
contradicts: heuristicScore >= 0.5,
|
|
6320
|
+
conflicting_memory: existing,
|
|
6321
|
+
confidence: heuristicScore,
|
|
6322
|
+
reasoning: heuristicScore >= 0.7 ? `New value for "${newKey}" significantly differs from existing high-importance memory (importance ${existing.importance})` : heuristicScore >= 0.5 ? `New value for "${newKey}" partially conflicts with existing memory (importance ${existing.importance})` : `Minor difference detected for "${newKey}"`
|
|
6323
|
+
};
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
if (use_llm && bestContradiction.confidence >= 0.3 && bestContradiction.confidence < 0.7 && bestContradiction.conflicting_memory) {
|
|
6327
|
+
const llmResult = await llmContradictionCheck(newValue, bestContradiction.conflicting_memory.value, newKey);
|
|
6328
|
+
if (llmResult.confidence > bestContradiction.confidence) {
|
|
6329
|
+
bestContradiction = {
|
|
6330
|
+
...bestContradiction,
|
|
6331
|
+
contradicts: llmResult.contradicts,
|
|
6332
|
+
confidence: llmResult.confidence,
|
|
6333
|
+
reasoning: llmResult.reasoning
|
|
6334
|
+
};
|
|
6335
|
+
}
|
|
6336
|
+
}
|
|
6337
|
+
return bestContradiction;
|
|
6338
|
+
}
|
|
6339
|
+
var init_contradiction = __esm(() => {
|
|
6340
|
+
init_database();
|
|
6341
|
+
init_memories();
|
|
6342
|
+
init_registry();
|
|
6343
|
+
});
|
|
6344
|
+
|
|
5968
6345
|
// src/lib/built-in-hooks.ts
|
|
5969
6346
|
var exports_built_in_hooks = {};
|
|
5970
6347
|
__export(exports_built_in_hooks, {
|
|
@@ -6091,10 +6468,76 @@ var init_built_in_hooks = __esm(() => {
|
|
|
6091
6468
|
description: "Generate and store vector embedding for semantic memory search",
|
|
6092
6469
|
handler: async (ctx) => {
|
|
6093
6470
|
const { indexMemoryEmbedding: indexMemoryEmbedding2 } = await Promise.resolve().then(() => (init_memories(), exports_memories));
|
|
6094
|
-
const text = [ctx.memory.value, ctx.memory.summary].filter(Boolean).join(" ");
|
|
6471
|
+
const text = ctx.memory.when_to_use || [ctx.memory.value, ctx.memory.summary].filter(Boolean).join(" ");
|
|
6095
6472
|
indexMemoryEmbedding2(ctx.memory.id, text);
|
|
6096
6473
|
}
|
|
6097
6474
|
});
|
|
6475
|
+
hookRegistry.register({
|
|
6476
|
+
type: "PostMemorySave",
|
|
6477
|
+
blocking: false,
|
|
6478
|
+
builtin: true,
|
|
6479
|
+
priority: 60,
|
|
6480
|
+
description: "Auto-generate when_to_use activation context via LLM if missing",
|
|
6481
|
+
handler: async (ctx) => {
|
|
6482
|
+
const { autoGenerateWhenToUse: autoGenerateWhenToUse2 } = await Promise.resolve().then(() => (init_when_to_use_generator(), exports_when_to_use_generator));
|
|
6483
|
+
await autoGenerateWhenToUse2(ctx);
|
|
6484
|
+
}
|
|
6485
|
+
});
|
|
6486
|
+
hookRegistry.register({
|
|
6487
|
+
type: "PostMemorySave",
|
|
6488
|
+
blocking: false,
|
|
6489
|
+
builtin: true,
|
|
6490
|
+
priority: 65,
|
|
6491
|
+
description: "Mark synthesized profile as stale when a preference or fact memory is saved",
|
|
6492
|
+
handler: async (ctx) => {
|
|
6493
|
+
const category = ctx.memory?.category;
|
|
6494
|
+
if (category !== "preference" && category !== "fact")
|
|
6495
|
+
return;
|
|
6496
|
+
try {
|
|
6497
|
+
const { markProfileStale: markProfileStale2 } = await Promise.resolve().then(() => (init_profile_synthesizer(), exports_profile_synthesizer));
|
|
6498
|
+
markProfileStale2(ctx.projectId, ctx.agentId);
|
|
6499
|
+
} catch {}
|
|
6500
|
+
}
|
|
6501
|
+
});
|
|
6502
|
+
hookRegistry.register({
|
|
6503
|
+
type: "PostMemorySave",
|
|
6504
|
+
blocking: false,
|
|
6505
|
+
builtin: true,
|
|
6506
|
+
priority: 70,
|
|
6507
|
+
description: "Auto-decay importance of existing memories contradicted by the newly saved memory",
|
|
6508
|
+
handler: async (ctx) => {
|
|
6509
|
+
if (ctx.wasUpdated)
|
|
6510
|
+
return;
|
|
6511
|
+
const memory = ctx.memory;
|
|
6512
|
+
if (memory.category !== "fact" && memory.category !== "knowledge")
|
|
6513
|
+
return;
|
|
6514
|
+
try {
|
|
6515
|
+
const { detectContradiction: detectContradiction2 } = await Promise.resolve().then(() => (init_contradiction(), exports_contradiction));
|
|
6516
|
+
const { updateMemory: updateMemory2, getMemory: getMemory2 } = await Promise.resolve().then(() => (init_memories(), exports_memories));
|
|
6517
|
+
const result = await detectContradiction2(memory.key, memory.value, {
|
|
6518
|
+
scope: memory.scope,
|
|
6519
|
+
project_id: ctx.projectId,
|
|
6520
|
+
min_importance: 1
|
|
6521
|
+
});
|
|
6522
|
+
if (!result.contradicts || !result.conflicting_memory)
|
|
6523
|
+
return;
|
|
6524
|
+
const conflicting = result.conflicting_memory;
|
|
6525
|
+
if (conflicting.id === memory.id)
|
|
6526
|
+
return;
|
|
6527
|
+
const fresh = getMemory2(conflicting.id);
|
|
6528
|
+
if (!fresh || fresh.status !== "active")
|
|
6529
|
+
return;
|
|
6530
|
+
const halvedImportance = Math.max(1, Math.floor(fresh.importance / 2));
|
|
6531
|
+
const metadata = { ...fresh.metadata || {}, contradicted_by: memory.id };
|
|
6532
|
+
updateMemory2(fresh.id, {
|
|
6533
|
+
importance: halvedImportance,
|
|
6534
|
+
flag: "contradicted",
|
|
6535
|
+
metadata,
|
|
6536
|
+
version: fresh.version
|
|
6537
|
+
});
|
|
6538
|
+
} catch {}
|
|
6539
|
+
}
|
|
6540
|
+
});
|
|
6098
6541
|
hookRegistry.register({
|
|
6099
6542
|
type: "PostMemoryInject",
|
|
6100
6543
|
blocking: false,
|
|
@@ -7163,40 +7606,413 @@ var init_session_jobs = __esm(() => {
|
|
|
7163
7606
|
init_database();
|
|
7164
7607
|
});
|
|
7165
7608
|
|
|
7166
|
-
// src/
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7609
|
+
// src/db/tool-events.ts
|
|
7610
|
+
var exports_tool_events = {};
|
|
7611
|
+
__export(exports_tool_events, {
|
|
7612
|
+
saveToolEvent: () => saveToolEvent,
|
|
7613
|
+
getToolStats: () => getToolStats,
|
|
7614
|
+
getToolLessons: () => getToolLessons,
|
|
7615
|
+
getToolEvents: () => getToolEvents,
|
|
7616
|
+
getToolEvent: () => getToolEvent,
|
|
7617
|
+
deleteToolEvents: () => deleteToolEvents
|
|
7618
|
+
});
|
|
7619
|
+
function parseToolEventRow(row) {
|
|
7620
|
+
return {
|
|
7621
|
+
id: row["id"],
|
|
7622
|
+
tool_name: row["tool_name"],
|
|
7623
|
+
action: row["action"] || null,
|
|
7624
|
+
success: !!row["success"],
|
|
7625
|
+
error_type: row["error_type"] || null,
|
|
7626
|
+
error_message: row["error_message"] || null,
|
|
7627
|
+
tokens_used: row["tokens_used"] ?? null,
|
|
7628
|
+
latency_ms: row["latency_ms"] ?? null,
|
|
7629
|
+
context: row["context"] || null,
|
|
7630
|
+
lesson: row["lesson"] || null,
|
|
7631
|
+
when_to_use: row["when_to_use"] || null,
|
|
7632
|
+
agent_id: row["agent_id"] || null,
|
|
7633
|
+
project_id: row["project_id"] || null,
|
|
7634
|
+
session_id: row["session_id"] || null,
|
|
7635
|
+
metadata: JSON.parse(row["metadata"] || "{}"),
|
|
7636
|
+
created_at: row["created_at"]
|
|
7637
|
+
};
|
|
7638
|
+
}
|
|
7639
|
+
function saveToolEvent(input, db) {
|
|
7640
|
+
const d = db || getDatabase();
|
|
7641
|
+
const id = uuid();
|
|
7642
|
+
const timestamp = now();
|
|
7643
|
+
const metadataJson = JSON.stringify(input.metadata || {});
|
|
7644
|
+
d.run(`INSERT INTO tool_events (id, tool_name, action, success, error_type, error_message, tokens_used, latency_ms, context, lesson, when_to_use, agent_id, project_id, session_id, metadata, created_at)
|
|
7645
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
7646
|
+
id,
|
|
7647
|
+
input.tool_name,
|
|
7648
|
+
input.action || null,
|
|
7649
|
+
input.success ? 1 : 0,
|
|
7650
|
+
input.error_type || null,
|
|
7651
|
+
input.error_message || null,
|
|
7652
|
+
input.tokens_used ?? null,
|
|
7653
|
+
input.latency_ms ?? null,
|
|
7654
|
+
input.context || null,
|
|
7655
|
+
input.lesson || null,
|
|
7656
|
+
input.when_to_use || null,
|
|
7657
|
+
input.agent_id || null,
|
|
7658
|
+
input.project_id || null,
|
|
7659
|
+
input.session_id || null,
|
|
7660
|
+
metadataJson,
|
|
7661
|
+
timestamp
|
|
7662
|
+
]);
|
|
7663
|
+
return getToolEvent(id, d);
|
|
7664
|
+
}
|
|
7665
|
+
function getToolEvent(id, db) {
|
|
7666
|
+
const d = db || getDatabase();
|
|
7667
|
+
const row = d.query("SELECT * FROM tool_events WHERE id = ?").get(id);
|
|
7668
|
+
if (!row)
|
|
7669
|
+
return null;
|
|
7670
|
+
return parseToolEventRow(row);
|
|
7671
|
+
}
|
|
7672
|
+
function getToolEvents(filters, db) {
|
|
7673
|
+
const d = db || getDatabase();
|
|
7674
|
+
const conditions = [];
|
|
7675
|
+
const params = [];
|
|
7676
|
+
if (filters.tool_name) {
|
|
7677
|
+
conditions.push("tool_name = ?");
|
|
7678
|
+
params.push(filters.tool_name);
|
|
7180
7679
|
}
|
|
7181
|
-
|
|
7680
|
+
if (filters.agent_id) {
|
|
7681
|
+
conditions.push("agent_id = ?");
|
|
7682
|
+
params.push(filters.agent_id);
|
|
7683
|
+
}
|
|
7684
|
+
if (filters.project_id) {
|
|
7685
|
+
conditions.push("project_id = ?");
|
|
7686
|
+
params.push(filters.project_id);
|
|
7687
|
+
}
|
|
7688
|
+
if (filters.success !== undefined) {
|
|
7689
|
+
conditions.push("success = ?");
|
|
7690
|
+
params.push(filters.success ? 1 : 0);
|
|
7691
|
+
}
|
|
7692
|
+
if (filters.from_date) {
|
|
7693
|
+
conditions.push("created_at >= ?");
|
|
7694
|
+
params.push(filters.from_date);
|
|
7695
|
+
}
|
|
7696
|
+
if (filters.to_date) {
|
|
7697
|
+
conditions.push("created_at <= ?");
|
|
7698
|
+
params.push(filters.to_date);
|
|
7699
|
+
}
|
|
7700
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
7701
|
+
const limit = filters.limit || 50;
|
|
7702
|
+
const offset = filters.offset || 0;
|
|
7703
|
+
const rows = d.query(`SELECT * FROM tool_events ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
7704
|
+
return rows.map(parseToolEventRow);
|
|
7182
7705
|
}
|
|
7183
|
-
|
|
7184
|
-
const
|
|
7185
|
-
|
|
7706
|
+
function getToolStats(tool_name, project_id, db) {
|
|
7707
|
+
const d = db || getDatabase();
|
|
7708
|
+
let where = "WHERE tool_name = ?";
|
|
7709
|
+
const params = [tool_name];
|
|
7710
|
+
if (project_id) {
|
|
7711
|
+
where += " AND project_id = ?";
|
|
7712
|
+
params.push(project_id);
|
|
7713
|
+
}
|
|
7714
|
+
const stats = d.query(`SELECT
|
|
7715
|
+
COUNT(*) as total_calls,
|
|
7716
|
+
SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as success_count,
|
|
7717
|
+
SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as failure_count,
|
|
7718
|
+
AVG(CASE WHEN tokens_used IS NOT NULL THEN tokens_used END) as avg_tokens,
|
|
7719
|
+
AVG(CASE WHEN latency_ms IS NOT NULL THEN latency_ms END) as avg_latency_ms,
|
|
7720
|
+
MAX(created_at) as last_used
|
|
7721
|
+
FROM tool_events ${where}`).get(...params);
|
|
7722
|
+
const total = stats["total_calls"] || 0;
|
|
7723
|
+
const successCount = stats["success_count"] || 0;
|
|
7724
|
+
const errorRows = d.query(`SELECT error_type, COUNT(*) as count
|
|
7725
|
+
FROM tool_events ${where} AND error_type IS NOT NULL
|
|
7726
|
+
GROUP BY error_type ORDER BY count DESC LIMIT 5`).all(...params);
|
|
7727
|
+
return {
|
|
7728
|
+
tool_name,
|
|
7729
|
+
total_calls: total,
|
|
7730
|
+
success_count: successCount,
|
|
7731
|
+
failure_count: stats["failure_count"] || 0,
|
|
7732
|
+
success_rate: total > 0 ? successCount / total : 0,
|
|
7733
|
+
avg_tokens: stats["avg_tokens"] ?? null,
|
|
7734
|
+
avg_latency_ms: stats["avg_latency_ms"] ?? null,
|
|
7735
|
+
common_errors: errorRows,
|
|
7736
|
+
last_used: stats["last_used"] || ""
|
|
7737
|
+
};
|
|
7738
|
+
}
|
|
7739
|
+
function getToolLessons(tool_name, project_id, limit, db) {
|
|
7740
|
+
const d = db || getDatabase();
|
|
7741
|
+
let where = "WHERE tool_name = ? AND lesson IS NOT NULL";
|
|
7742
|
+
const params = [tool_name];
|
|
7743
|
+
if (project_id) {
|
|
7744
|
+
where += " AND project_id = ?";
|
|
7745
|
+
params.push(project_id);
|
|
7746
|
+
}
|
|
7747
|
+
const rows = d.query(`SELECT lesson, when_to_use, created_at FROM tool_events ${where} ORDER BY created_at DESC LIMIT ?`).all(...params, limit || 20);
|
|
7748
|
+
return rows;
|
|
7749
|
+
}
|
|
7750
|
+
function deleteToolEvents(filters, db) {
|
|
7751
|
+
const d = db || getDatabase();
|
|
7752
|
+
const conditions = [];
|
|
7753
|
+
const params = [];
|
|
7754
|
+
if (filters.tool_name) {
|
|
7755
|
+
conditions.push("tool_name = ?");
|
|
7756
|
+
params.push(filters.tool_name);
|
|
7757
|
+
}
|
|
7758
|
+
if (filters.agent_id) {
|
|
7759
|
+
conditions.push("agent_id = ?");
|
|
7760
|
+
params.push(filters.agent_id);
|
|
7761
|
+
}
|
|
7762
|
+
if (filters.project_id) {
|
|
7763
|
+
conditions.push("project_id = ?");
|
|
7764
|
+
params.push(filters.project_id);
|
|
7765
|
+
}
|
|
7766
|
+
if (filters.before_date) {
|
|
7767
|
+
conditions.push("created_at < ?");
|
|
7768
|
+
params.push(filters.before_date);
|
|
7769
|
+
}
|
|
7770
|
+
if (conditions.length === 0)
|
|
7186
7771
|
return 0;
|
|
7772
|
+
const result = d.run(`DELETE FROM tool_events WHERE ${conditions.join(" AND ")}`, params);
|
|
7773
|
+
return result.changes;
|
|
7774
|
+
}
|
|
7775
|
+
var init_tool_events = __esm(() => {
|
|
7776
|
+
init_database();
|
|
7777
|
+
});
|
|
7778
|
+
|
|
7779
|
+
// src/lib/tool-lesson-extractor.ts
|
|
7780
|
+
async function extractToolLessons(transcript, options) {
|
|
7781
|
+
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
7782
|
+
if (!apiKey)
|
|
7783
|
+
return [];
|
|
7187
7784
|
try {
|
|
7188
|
-
const
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7785
|
+
const truncated = transcript.length > 8000 ? transcript.slice(0, 8000) + `
|
|
7786
|
+
[...truncated]` : transcript;
|
|
7787
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
7788
|
+
method: "POST",
|
|
7789
|
+
headers: {
|
|
7790
|
+
"x-api-key": apiKey,
|
|
7791
|
+
"anthropic-version": "2023-06-01",
|
|
7792
|
+
"content-type": "application/json"
|
|
7793
|
+
},
|
|
7794
|
+
body: JSON.stringify({
|
|
7795
|
+
model: "claude-haiku-4-5-20251001",
|
|
7796
|
+
max_tokens: 1500,
|
|
7797
|
+
system: SYSTEM_PROMPT3,
|
|
7798
|
+
messages: [{ role: "user", content: `Extract tool lessons from this session transcript:
|
|
7799
|
+
|
|
7800
|
+
${truncated}` }]
|
|
7801
|
+
})
|
|
7192
7802
|
});
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7803
|
+
if (!response.ok)
|
|
7804
|
+
return [];
|
|
7805
|
+
const data = await response.json();
|
|
7806
|
+
const text = data.content?.[0]?.text?.trim();
|
|
7807
|
+
if (!text)
|
|
7808
|
+
return [];
|
|
7809
|
+
const lessons = JSON.parse(text);
|
|
7810
|
+
if (!Array.isArray(lessons))
|
|
7811
|
+
return [];
|
|
7812
|
+
for (const lesson of lessons) {
|
|
7813
|
+
if (!lesson.tool_name || !lesson.lesson)
|
|
7814
|
+
continue;
|
|
7815
|
+
try {
|
|
7816
|
+
saveToolEvent({
|
|
7817
|
+
tool_name: lesson.tool_name,
|
|
7818
|
+
success: lesson.success,
|
|
7819
|
+
error_type: lesson.error_type || undefined,
|
|
7820
|
+
lesson: lesson.lesson,
|
|
7821
|
+
when_to_use: lesson.when_to_use,
|
|
7822
|
+
context: "extracted from session transcript",
|
|
7823
|
+
agent_id: options?.agent_id,
|
|
7824
|
+
project_id: options?.project_id,
|
|
7825
|
+
session_id: options?.session_id
|
|
7826
|
+
});
|
|
7827
|
+
} catch {}
|
|
7828
|
+
try {
|
|
7829
|
+
createMemory({
|
|
7830
|
+
key: `tool-lesson-${lesson.tool_name}-${Date.now()}`,
|
|
7831
|
+
value: lesson.lesson,
|
|
7832
|
+
category: "knowledge",
|
|
7833
|
+
scope: "shared",
|
|
7834
|
+
importance: 7,
|
|
7835
|
+
source: "auto",
|
|
7836
|
+
tags: ["tool-memory", lesson.tool_name, "auto-extracted"],
|
|
7837
|
+
when_to_use: lesson.when_to_use,
|
|
7838
|
+
agent_id: options?.agent_id,
|
|
7839
|
+
project_id: options?.project_id,
|
|
7840
|
+
session_id: options?.session_id
|
|
7841
|
+
});
|
|
7842
|
+
} catch {}
|
|
7843
|
+
}
|
|
7844
|
+
return lessons;
|
|
7845
|
+
} catch {
|
|
7846
|
+
return [];
|
|
7847
|
+
}
|
|
7848
|
+
}
|
|
7849
|
+
var SYSTEM_PROMPT3 = `You are a tool usage analyst. Given a session transcript containing tool calls and their results, extract actionable lessons about tool usage.
|
|
7850
|
+
|
|
7851
|
+
For each tool lesson, output a JSON array of objects with these fields:
|
|
7852
|
+
- tool_name: name of the tool
|
|
7853
|
+
- lesson: the insight (1-2 sentences)
|
|
7854
|
+
- when_to_use: activation context \u2014 when should an agent recall this lesson (start with "When" or "If")
|
|
7855
|
+
- success: boolean \u2014 was the tool call that taught this lesson successful?
|
|
7856
|
+
- error_type: if failed, one of: timeout, permission, not_found, syntax, rate_limit, other (or null if success)
|
|
7857
|
+
|
|
7858
|
+
Focus on:
|
|
7859
|
+
1. Successful patterns: what worked and why
|
|
7860
|
+
2. Failure lessons: what went wrong and how to avoid it
|
|
7861
|
+
3. Parameter insights: optimal settings discovered
|
|
7862
|
+
4. Alternative tools: when one tool is better than another
|
|
7863
|
+
5. Error recovery: what to do when a specific error occurs
|
|
7864
|
+
|
|
7865
|
+
Only extract genuinely useful, non-obvious lessons. Skip trivial observations.
|
|
7866
|
+
Output ONLY the JSON array, no markdown or explanation.`;
|
|
7867
|
+
var init_tool_lesson_extractor = __esm(() => {
|
|
7868
|
+
init_tool_events();
|
|
7869
|
+
init_memories();
|
|
7870
|
+
});
|
|
7871
|
+
|
|
7872
|
+
// src/lib/procedural-extractor.ts
|
|
7873
|
+
async function extractProcedures(transcript, options) {
|
|
7874
|
+
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
7875
|
+
if (!apiKey)
|
|
7876
|
+
return [];
|
|
7877
|
+
try {
|
|
7878
|
+
const truncated = transcript.length > 8000 ? transcript.slice(0, 8000) + `
|
|
7879
|
+
[...truncated]` : transcript;
|
|
7880
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
7881
|
+
method: "POST",
|
|
7882
|
+
headers: {
|
|
7883
|
+
"x-api-key": apiKey,
|
|
7884
|
+
"anthropic-version": "2023-06-01",
|
|
7885
|
+
"content-type": "application/json"
|
|
7886
|
+
},
|
|
7887
|
+
body: JSON.stringify({
|
|
7888
|
+
model: "claude-haiku-4-5-20251001",
|
|
7889
|
+
max_tokens: 2000,
|
|
7890
|
+
system: SYSTEM_PROMPT4,
|
|
7891
|
+
messages: [{ role: "user", content: `Extract procedures from this session:
|
|
7892
|
+
|
|
7893
|
+
${truncated}` }]
|
|
7894
|
+
})
|
|
7895
|
+
});
|
|
7896
|
+
if (!response.ok)
|
|
7897
|
+
return [];
|
|
7898
|
+
const data = await response.json();
|
|
7899
|
+
const text = data.content?.[0]?.text?.trim();
|
|
7900
|
+
if (!text)
|
|
7901
|
+
return [];
|
|
7902
|
+
const procedures = JSON.parse(text);
|
|
7903
|
+
if (!Array.isArray(procedures))
|
|
7904
|
+
return [];
|
|
7905
|
+
for (const proc of procedures) {
|
|
7906
|
+
if (!proc.title || !proc.steps?.length)
|
|
7907
|
+
continue;
|
|
7908
|
+
const sequenceGroup = `proc-${shortUuid()}`;
|
|
7909
|
+
for (let i = 0;i < proc.steps.length; i++) {
|
|
7910
|
+
const step = proc.steps[i];
|
|
7911
|
+
if (!step)
|
|
7912
|
+
continue;
|
|
7913
|
+
try {
|
|
7914
|
+
createMemory({
|
|
7915
|
+
key: `${sequenceGroup}-step-${i + 1}`,
|
|
7916
|
+
value: step.action,
|
|
7917
|
+
category: "procedural",
|
|
7918
|
+
scope: "shared",
|
|
7919
|
+
importance: 7,
|
|
7920
|
+
source: "auto",
|
|
7921
|
+
tags: ["procedure", "auto-extracted", proc.title.toLowerCase().replace(/\s+/g, "-")],
|
|
7922
|
+
when_to_use: step.when_to_use || proc.when_to_use,
|
|
7923
|
+
sequence_group: sequenceGroup,
|
|
7924
|
+
sequence_order: i + 1,
|
|
7925
|
+
agent_id: options?.agent_id,
|
|
7926
|
+
project_id: options?.project_id,
|
|
7927
|
+
session_id: options?.session_id
|
|
7928
|
+
});
|
|
7929
|
+
} catch {}
|
|
7930
|
+
}
|
|
7931
|
+
for (const pattern of proc.failure_patterns || []) {
|
|
7932
|
+
try {
|
|
7933
|
+
createMemory({
|
|
7934
|
+
key: `${sequenceGroup}-warning-${shortUuid()}`,
|
|
7935
|
+
value: `WARNING: ${pattern}`,
|
|
7936
|
+
category: "procedural",
|
|
7937
|
+
scope: "shared",
|
|
7938
|
+
importance: 8,
|
|
7939
|
+
source: "auto",
|
|
7940
|
+
tags: ["procedure", "failure-pattern", "auto-extracted"],
|
|
7941
|
+
when_to_use: proc.when_to_use,
|
|
7942
|
+
sequence_group: sequenceGroup,
|
|
7943
|
+
sequence_order: 999,
|
|
7944
|
+
agent_id: options?.agent_id,
|
|
7945
|
+
project_id: options?.project_id,
|
|
7946
|
+
session_id: options?.session_id
|
|
7947
|
+
});
|
|
7948
|
+
} catch {}
|
|
7949
|
+
}
|
|
7950
|
+
}
|
|
7951
|
+
return procedures;
|
|
7952
|
+
} catch {
|
|
7953
|
+
return [];
|
|
7954
|
+
}
|
|
7955
|
+
}
|
|
7956
|
+
var SYSTEM_PROMPT4 = `You extract procedural knowledge from session transcripts \u2014 workflows, step sequences, and problem-solution patterns.
|
|
7957
|
+
|
|
7958
|
+
For each procedure found, output a JSON array of objects:
|
|
7959
|
+
{
|
|
7960
|
+
"title": "short name for the workflow",
|
|
7961
|
+
"steps": [
|
|
7962
|
+
{"action": "what to do", "when_to_use": "activation context for this step"},
|
|
7963
|
+
{"action": "next step", "when_to_use": "activation context"}
|
|
7964
|
+
],
|
|
7965
|
+
"failure_patterns": ["what to avoid and why"],
|
|
7966
|
+
"when_to_use": "overall activation context for the whole procedure"
|
|
7967
|
+
}
|
|
7968
|
+
|
|
7969
|
+
Focus on:
|
|
7970
|
+
1. Multi-step workflows that were completed successfully
|
|
7971
|
+
2. Step sequences where order matters
|
|
7972
|
+
3. Failure \u2192 recovery patterns (what went wrong, how it was fixed)
|
|
7973
|
+
4. Problem-solution pairs (when X happens, do Y)
|
|
7974
|
+
|
|
7975
|
+
Only extract non-trivial procedures (3+ steps or genuinely useful patterns).
|
|
7976
|
+
Output ONLY the JSON array.`;
|
|
7977
|
+
var init_procedural_extractor = __esm(() => {
|
|
7978
|
+
init_memories();
|
|
7979
|
+
init_database();
|
|
7980
|
+
});
|
|
7981
|
+
|
|
7982
|
+
// src/lib/session-processor.ts
|
|
7983
|
+
function chunkTranscript(transcript, chunkSize = 2000, overlap = 200) {
|
|
7984
|
+
if (!transcript || transcript.length === 0)
|
|
7985
|
+
return [];
|
|
7986
|
+
if (transcript.length <= chunkSize)
|
|
7987
|
+
return [transcript];
|
|
7988
|
+
const chunks = [];
|
|
7989
|
+
let start = 0;
|
|
7990
|
+
while (start < transcript.length) {
|
|
7991
|
+
const end = Math.min(start + chunkSize, transcript.length);
|
|
7992
|
+
chunks.push(transcript.slice(start, end));
|
|
7993
|
+
if (end === transcript.length)
|
|
7994
|
+
break;
|
|
7995
|
+
start += chunkSize - overlap;
|
|
7996
|
+
}
|
|
7997
|
+
return chunks;
|
|
7998
|
+
}
|
|
7999
|
+
async function extractMemoriesFromChunk(chunk, context, db) {
|
|
8000
|
+
const provider = providerRegistry.getAvailable();
|
|
8001
|
+
if (!provider)
|
|
8002
|
+
return 0;
|
|
8003
|
+
try {
|
|
8004
|
+
const extracted = await provider.extractMemories(SESSION_EXTRACTION_USER_TEMPLATE(chunk, context.sessionId), {
|
|
8005
|
+
sessionId: context.sessionId,
|
|
8006
|
+
agentId: context.agentId,
|
|
8007
|
+
projectId: context.projectId
|
|
8008
|
+
});
|
|
8009
|
+
let savedCount = 0;
|
|
8010
|
+
const sourceTag = context.source ? `source:${context.source}` : "source:manual";
|
|
8011
|
+
for (const memory of extracted) {
|
|
8012
|
+
if (!memory.content || !memory.content.trim())
|
|
8013
|
+
continue;
|
|
8014
|
+
try {
|
|
8015
|
+
createMemory({
|
|
7200
8016
|
key: memory.content.slice(0, 120).replace(/\s+/g, "-").toLowerCase(),
|
|
7201
8017
|
value: memory.content,
|
|
7202
8018
|
category: memory.category,
|
|
@@ -7320,6 +8136,20 @@ async function processSessionJob(jobId, db) {
|
|
|
7320
8136
|
}
|
|
7321
8137
|
}
|
|
7322
8138
|
result.memoriesExtracted = totalMemories;
|
|
8139
|
+
try {
|
|
8140
|
+
await extractToolLessons(job.transcript, {
|
|
8141
|
+
agent_id: job.agent_id ?? undefined,
|
|
8142
|
+
project_id: job.project_id ?? undefined,
|
|
8143
|
+
session_id: job.session_id
|
|
8144
|
+
});
|
|
8145
|
+
} catch {}
|
|
8146
|
+
try {
|
|
8147
|
+
await extractProcedures(job.transcript, {
|
|
8148
|
+
agent_id: job.agent_id ?? undefined,
|
|
8149
|
+
project_id: job.project_id ?? undefined,
|
|
8150
|
+
session_id: job.session_id
|
|
8151
|
+
});
|
|
8152
|
+
} catch {}
|
|
7323
8153
|
try {
|
|
7324
8154
|
if (result.errors.length > 0 && result.chunksProcessed === 0) {
|
|
7325
8155
|
updateSessionJob(jobId, {
|
|
@@ -7351,6 +8181,8 @@ var init_session_processor = __esm(() => {
|
|
|
7351
8181
|
init_memories();
|
|
7352
8182
|
init_registry();
|
|
7353
8183
|
init_session_jobs();
|
|
8184
|
+
init_tool_lesson_extractor();
|
|
8185
|
+
init_procedural_extractor();
|
|
7354
8186
|
});
|
|
7355
8187
|
|
|
7356
8188
|
// src/lib/session-queue.ts
|
|
@@ -7440,6 +8272,204 @@ var init_session_queue = __esm(() => {
|
|
|
7440
8272
|
_pendingQueue = new Set;
|
|
7441
8273
|
});
|
|
7442
8274
|
|
|
8275
|
+
// src/lib/session-registry.ts
|
|
8276
|
+
var exports_session_registry = {};
|
|
8277
|
+
__export(exports_session_registry, {
|
|
8278
|
+
updateSessionAgent: () => updateSessionAgent,
|
|
8279
|
+
unregisterSession: () => unregisterSession,
|
|
8280
|
+
registerSession: () => registerSession,
|
|
8281
|
+
listSessions: () => listSessions,
|
|
8282
|
+
heartbeatSession: () => heartbeatSession,
|
|
8283
|
+
getSessionsByProject: () => getSessionsByProject,
|
|
8284
|
+
getSessionByAgent: () => getSessionByAgent,
|
|
8285
|
+
getSession: () => getSession,
|
|
8286
|
+
closeRegistry: () => closeRegistry,
|
|
8287
|
+
cleanStaleSessions: () => cleanStaleSessions
|
|
8288
|
+
});
|
|
8289
|
+
import { Database as Database2 } from "bun:sqlite";
|
|
8290
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
8291
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
8292
|
+
function getDb() {
|
|
8293
|
+
if (_db2)
|
|
8294
|
+
return _db2;
|
|
8295
|
+
const dir = dirname3(DB_PATH);
|
|
8296
|
+
if (!existsSync5(dir))
|
|
8297
|
+
mkdirSync5(dir, { recursive: true });
|
|
8298
|
+
_db2 = new Database2(DB_PATH, { create: true });
|
|
8299
|
+
_db2.run("PRAGMA journal_mode = WAL");
|
|
8300
|
+
_db2.run("PRAGMA busy_timeout = 3000");
|
|
8301
|
+
_db2.exec(`
|
|
8302
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
8303
|
+
id TEXT PRIMARY KEY,
|
|
8304
|
+
pid INTEGER NOT NULL,
|
|
8305
|
+
cwd TEXT NOT NULL,
|
|
8306
|
+
git_root TEXT,
|
|
8307
|
+
agent_name TEXT,
|
|
8308
|
+
project_name TEXT,
|
|
8309
|
+
tty TEXT,
|
|
8310
|
+
mcp_server TEXT NOT NULL,
|
|
8311
|
+
metadata TEXT DEFAULT '{}',
|
|
8312
|
+
registered_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
8313
|
+
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
8314
|
+
);
|
|
8315
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_pid_mcp
|
|
8316
|
+
ON sessions(pid, mcp_server);
|
|
8317
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project
|
|
8318
|
+
ON sessions(project_name);
|
|
8319
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_agent
|
|
8320
|
+
ON sessions(agent_name);
|
|
8321
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_git_root
|
|
8322
|
+
ON sessions(git_root);
|
|
8323
|
+
`);
|
|
8324
|
+
return _db2;
|
|
8325
|
+
}
|
|
8326
|
+
function generateId() {
|
|
8327
|
+
return crypto.randomUUID().slice(0, 8);
|
|
8328
|
+
}
|
|
8329
|
+
function now2() {
|
|
8330
|
+
return new Date().toISOString();
|
|
8331
|
+
}
|
|
8332
|
+
function parseRow2(row) {
|
|
8333
|
+
return {
|
|
8334
|
+
id: row["id"],
|
|
8335
|
+
pid: row["pid"],
|
|
8336
|
+
cwd: row["cwd"],
|
|
8337
|
+
git_root: row["git_root"] || null,
|
|
8338
|
+
agent_name: row["agent_name"] || null,
|
|
8339
|
+
project_name: row["project_name"] || null,
|
|
8340
|
+
tty: row["tty"] || null,
|
|
8341
|
+
mcp_server: row["mcp_server"],
|
|
8342
|
+
metadata: JSON.parse(row["metadata"] || "{}"),
|
|
8343
|
+
registered_at: row["registered_at"],
|
|
8344
|
+
last_seen_at: row["last_seen_at"]
|
|
8345
|
+
};
|
|
8346
|
+
}
|
|
8347
|
+
function isProcessAlive(pid) {
|
|
8348
|
+
try {
|
|
8349
|
+
process.kill(pid, 0);
|
|
8350
|
+
return true;
|
|
8351
|
+
} catch {
|
|
8352
|
+
return false;
|
|
8353
|
+
}
|
|
8354
|
+
}
|
|
8355
|
+
function registerSession(opts) {
|
|
8356
|
+
const db = getDb();
|
|
8357
|
+
const pid = process.pid;
|
|
8358
|
+
const cwd = opts.cwd || process.cwd();
|
|
8359
|
+
const id = generateId();
|
|
8360
|
+
const timestamp = now2();
|
|
8361
|
+
const existing = db.query("SELECT id FROM sessions WHERE pid = ? AND mcp_server = ?").get(pid, opts.mcp_server);
|
|
8362
|
+
if (existing) {
|
|
8363
|
+
db.run(`UPDATE sessions SET agent_name = ?, project_name = ?, cwd = ?,
|
|
8364
|
+
git_root = ?, tty = ?, metadata = ?, last_seen_at = ? WHERE id = ?`, [
|
|
8365
|
+
opts.agent_name || null,
|
|
8366
|
+
opts.project_name || null,
|
|
8367
|
+
cwd,
|
|
8368
|
+
opts.git_root || null,
|
|
8369
|
+
opts.tty || null,
|
|
8370
|
+
JSON.stringify(opts.metadata || {}),
|
|
8371
|
+
timestamp,
|
|
8372
|
+
existing.id
|
|
8373
|
+
]);
|
|
8374
|
+
return getSession(existing.id);
|
|
8375
|
+
}
|
|
8376
|
+
db.run(`INSERT INTO sessions (id, pid, cwd, git_root, agent_name, project_name, tty, mcp_server, metadata, registered_at, last_seen_at)
|
|
8377
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
8378
|
+
id,
|
|
8379
|
+
pid,
|
|
8380
|
+
cwd,
|
|
8381
|
+
opts.git_root || null,
|
|
8382
|
+
opts.agent_name || null,
|
|
8383
|
+
opts.project_name || null,
|
|
8384
|
+
opts.tty || null,
|
|
8385
|
+
opts.mcp_server,
|
|
8386
|
+
JSON.stringify(opts.metadata || {}),
|
|
8387
|
+
timestamp,
|
|
8388
|
+
timestamp
|
|
8389
|
+
]);
|
|
8390
|
+
return getSession(id);
|
|
8391
|
+
}
|
|
8392
|
+
function heartbeatSession(id) {
|
|
8393
|
+
const db = getDb();
|
|
8394
|
+
db.run("UPDATE sessions SET last_seen_at = ? WHERE id = ?", [now2(), id]);
|
|
8395
|
+
}
|
|
8396
|
+
function unregisterSession(id) {
|
|
8397
|
+
const db = getDb();
|
|
8398
|
+
db.run("DELETE FROM sessions WHERE id = ?", [id]);
|
|
8399
|
+
}
|
|
8400
|
+
function getSession(id) {
|
|
8401
|
+
const db = getDb();
|
|
8402
|
+
const row = db.query("SELECT * FROM sessions WHERE id = ?").get(id);
|
|
8403
|
+
return row ? parseRow2(row) : null;
|
|
8404
|
+
}
|
|
8405
|
+
function listSessions(filter) {
|
|
8406
|
+
const db = getDb();
|
|
8407
|
+
const conditions = [];
|
|
8408
|
+
const params = [];
|
|
8409
|
+
if (filter?.project_name) {
|
|
8410
|
+
conditions.push("project_name = ?");
|
|
8411
|
+
params.push(filter.project_name);
|
|
8412
|
+
}
|
|
8413
|
+
if (filter?.git_root) {
|
|
8414
|
+
conditions.push("git_root = ?");
|
|
8415
|
+
params.push(filter.git_root);
|
|
8416
|
+
}
|
|
8417
|
+
if (filter?.mcp_server) {
|
|
8418
|
+
conditions.push("mcp_server = ?");
|
|
8419
|
+
params.push(filter.mcp_server);
|
|
8420
|
+
}
|
|
8421
|
+
if (filter?.agent_name) {
|
|
8422
|
+
conditions.push("agent_name = ?");
|
|
8423
|
+
params.push(filter.agent_name);
|
|
8424
|
+
}
|
|
8425
|
+
if (filter?.exclude_pid) {
|
|
8426
|
+
conditions.push("pid != ?");
|
|
8427
|
+
params.push(filter.exclude_pid);
|
|
8428
|
+
}
|
|
8429
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
8430
|
+
const rows = db.query(`SELECT * FROM sessions ${where} ORDER BY last_seen_at DESC`).all(...params);
|
|
8431
|
+
return rows.map(parseRow2).filter((s) => {
|
|
8432
|
+
if (isProcessAlive(s.pid))
|
|
8433
|
+
return true;
|
|
8434
|
+
db.run("DELETE FROM sessions WHERE id = ?", [s.id]);
|
|
8435
|
+
return false;
|
|
8436
|
+
});
|
|
8437
|
+
}
|
|
8438
|
+
function getSessionByAgent(agentName) {
|
|
8439
|
+
const sessions = listSessions({ agent_name: agentName });
|
|
8440
|
+
return sessions[0] || null;
|
|
8441
|
+
}
|
|
8442
|
+
function getSessionsByProject(projectName) {
|
|
8443
|
+
return listSessions({ project_name: projectName });
|
|
8444
|
+
}
|
|
8445
|
+
function cleanStaleSessions() {
|
|
8446
|
+
const db = getDb();
|
|
8447
|
+
const rows = db.query("SELECT id, pid FROM sessions").all();
|
|
8448
|
+
let cleaned = 0;
|
|
8449
|
+
for (const row of rows) {
|
|
8450
|
+
if (!isProcessAlive(row.pid)) {
|
|
8451
|
+
db.run("DELETE FROM sessions WHERE id = ?", [row.id]);
|
|
8452
|
+
cleaned++;
|
|
8453
|
+
}
|
|
8454
|
+
}
|
|
8455
|
+
return cleaned;
|
|
8456
|
+
}
|
|
8457
|
+
function updateSessionAgent(mcpServer, agentName) {
|
|
8458
|
+
const db = getDb();
|
|
8459
|
+
const pid = process.pid;
|
|
8460
|
+
db.run("UPDATE sessions SET agent_name = ?, last_seen_at = ? WHERE pid = ? AND mcp_server = ?", [agentName, now2(), pid, mcpServer]);
|
|
8461
|
+
}
|
|
8462
|
+
function closeRegistry() {
|
|
8463
|
+
if (_db2) {
|
|
8464
|
+
_db2.close();
|
|
8465
|
+
_db2 = null;
|
|
8466
|
+
}
|
|
8467
|
+
}
|
|
8468
|
+
var DB_PATH, _db2 = null;
|
|
8469
|
+
var init_session_registry = __esm(() => {
|
|
8470
|
+
DB_PATH = join5(process.env["HOME"] || process.env["USERPROFILE"] || "~", ".open-sessions-registry.db");
|
|
8471
|
+
});
|
|
8472
|
+
|
|
7443
8473
|
// node_modules/commander/esm.mjs
|
|
7444
8474
|
var import__ = __toESM(require_commander(), 1);
|
|
7445
8475
|
var {
|
|
@@ -7459,10 +8489,10 @@ var {
|
|
|
7459
8489
|
// src/cli/index.tsx
|
|
7460
8490
|
init_database();
|
|
7461
8491
|
init_memories();
|
|
7462
|
-
import
|
|
7463
|
-
import { readFileSync as
|
|
7464
|
-
import { dirname as
|
|
7465
|
-
import { homedir as
|
|
8492
|
+
import chalk2 from "chalk";
|
|
8493
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, existsSync as existsSync6, unlinkSync as unlinkSync2, accessSync, statSync, copyFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync3, constants as fsConstants } from "fs";
|
|
8494
|
+
import { dirname as dirname4, join as join6, resolve as resolve3 } from "path";
|
|
8495
|
+
import { homedir as homedir4 } from "os";
|
|
7466
8496
|
import { fileURLToPath } from "url";
|
|
7467
8497
|
|
|
7468
8498
|
// src/db/agents.ts
|
|
@@ -7945,37 +8975,332 @@ var FORMAT_UNITS = [
|
|
|
7945
8975
|
init_entities();
|
|
7946
8976
|
init_relations();
|
|
7947
8977
|
init_entity_memories();
|
|
8978
|
+
|
|
8979
|
+
// src/cli/brains.ts
|
|
8980
|
+
import {
|
|
8981
|
+
existsSync as existsSync4,
|
|
8982
|
+
mkdirSync as mkdirSync4,
|
|
8983
|
+
writeFileSync as writeFileSync3,
|
|
8984
|
+
readdirSync as readdirSync2
|
|
8985
|
+
} from "fs";
|
|
8986
|
+
import { homedir as homedir3 } from "os";
|
|
8987
|
+
import { join as join4 } from "path";
|
|
8988
|
+
import chalk from "chalk";
|
|
8989
|
+
|
|
8990
|
+
// src/lib/gatherer.ts
|
|
8991
|
+
init_memories();
|
|
8992
|
+
var SYSTEM_PROMPT = "You are an AI assistant with persistent memory that recalls and saves information across sessions.";
|
|
8993
|
+
function memoryToRecallExample(memory) {
|
|
8994
|
+
return {
|
|
8995
|
+
messages: [
|
|
8996
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
8997
|
+
{
|
|
8998
|
+
role: "user",
|
|
8999
|
+
content: `What do you remember about "${memory.key}"?`
|
|
9000
|
+
},
|
|
9001
|
+
{
|
|
9002
|
+
role: "assistant",
|
|
9003
|
+
content: memory.summary ? `${memory.value}
|
|
9004
|
+
|
|
9005
|
+
Summary: ${memory.summary}` : memory.value
|
|
9006
|
+
}
|
|
9007
|
+
]
|
|
9008
|
+
};
|
|
9009
|
+
}
|
|
9010
|
+
function memoryToSaveExample(memory) {
|
|
9011
|
+
const tags = memory.tags ?? [];
|
|
9012
|
+
return {
|
|
9013
|
+
messages: [
|
|
9014
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
9015
|
+
{
|
|
9016
|
+
role: "user",
|
|
9017
|
+
content: `Remember this for me: ${memory.key} = ${memory.value}${tags.length ? ` (tags: ${tags.join(", ")})` : ""}`
|
|
9018
|
+
},
|
|
9019
|
+
{
|
|
9020
|
+
role: "assistant",
|
|
9021
|
+
content: `Saved to memory: "${memory.key}" with ${memory.category} category, importance ${memory.importance}/10, scope: ${memory.scope}.`
|
|
9022
|
+
}
|
|
9023
|
+
]
|
|
9024
|
+
};
|
|
9025
|
+
}
|
|
9026
|
+
function memoryToSearchExample(memories, category) {
|
|
9027
|
+
const matched = memories.filter((m) => m.category === category && m.status === "active").slice(0, 5);
|
|
9028
|
+
return {
|
|
9029
|
+
messages: [
|
|
9030
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
9031
|
+
{ role: "user", content: `What ${category} memories do you have?` },
|
|
9032
|
+
{
|
|
9033
|
+
role: "assistant",
|
|
9034
|
+
content: matched.length > 0 ? `Here are my ${category} memories:
|
|
9035
|
+
${matched.map((m) => `- ${m.key}: ${m.value.slice(0, 120)}${m.value.length > 120 ? "..." : ""}`).join(`
|
|
9036
|
+
`)}` : `I don't have any ${category} memories stored yet.`
|
|
9037
|
+
}
|
|
9038
|
+
]
|
|
9039
|
+
};
|
|
9040
|
+
}
|
|
9041
|
+
var gatherTrainingData = async (options = {}) => {
|
|
9042
|
+
const allMemories = listMemories({ status: "active" });
|
|
9043
|
+
const filtered = options.since ? allMemories.filter((m) => new Date(m.created_at) >= options.since) : allMemories;
|
|
9044
|
+
const sorted = filtered.slice().sort((a, b) => b.importance - a.importance);
|
|
9045
|
+
const fetchSet = options.limit ? sorted.slice(0, options.limit * 3) : sorted;
|
|
9046
|
+
const examples = [];
|
|
9047
|
+
for (const memory of fetchSet) {
|
|
9048
|
+
examples.push(memoryToRecallExample(memory));
|
|
9049
|
+
examples.push(memoryToSaveExample(memory));
|
|
9050
|
+
}
|
|
9051
|
+
const categories = [...new Set(fetchSet.map((m) => m.category))];
|
|
9052
|
+
for (const category of categories) {
|
|
9053
|
+
examples.push(memoryToSearchExample(fetchSet, category));
|
|
9054
|
+
}
|
|
9055
|
+
const finalExamples = options.limit ? examples.slice(0, options.limit) : examples;
|
|
9056
|
+
return {
|
|
9057
|
+
source: "mementos",
|
|
9058
|
+
examples: finalExamples,
|
|
9059
|
+
count: finalExamples.length
|
|
9060
|
+
};
|
|
9061
|
+
};
|
|
9062
|
+
|
|
9063
|
+
// src/lib/model-config.ts
|
|
9064
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
9065
|
+
import { homedir as homedir2 } from "os";
|
|
9066
|
+
import { join as join3 } from "path";
|
|
9067
|
+
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
9068
|
+
var CONFIG_DIR = join3(homedir2(), ".mementos");
|
|
9069
|
+
var CONFIG_PATH = join3(CONFIG_DIR, "config.json");
|
|
9070
|
+
function readConfig() {
|
|
9071
|
+
if (!existsSync3(CONFIG_PATH))
|
|
9072
|
+
return {};
|
|
9073
|
+
try {
|
|
9074
|
+
const raw = readFileSync2(CONFIG_PATH, "utf-8");
|
|
9075
|
+
return JSON.parse(raw);
|
|
9076
|
+
} catch {
|
|
9077
|
+
return {};
|
|
9078
|
+
}
|
|
9079
|
+
}
|
|
9080
|
+
function writeConfig(config) {
|
|
9081
|
+
if (!existsSync3(CONFIG_DIR)) {
|
|
9082
|
+
mkdirSync3(CONFIG_DIR, { recursive: true });
|
|
9083
|
+
}
|
|
9084
|
+
writeFileSync2(CONFIG_PATH, JSON.stringify(config, null, 2) + `
|
|
9085
|
+
`, "utf-8");
|
|
9086
|
+
}
|
|
9087
|
+
function getActiveModel() {
|
|
9088
|
+
const config = readConfig();
|
|
9089
|
+
return config.activeModel ?? DEFAULT_MODEL;
|
|
9090
|
+
}
|
|
9091
|
+
function setActiveModel(modelId) {
|
|
9092
|
+
const config = readConfig();
|
|
9093
|
+
config.activeModel = modelId;
|
|
9094
|
+
writeConfig(config);
|
|
9095
|
+
}
|
|
9096
|
+
function clearActiveModel() {
|
|
9097
|
+
const config = readConfig();
|
|
9098
|
+
delete config.activeModel;
|
|
9099
|
+
writeConfig(config);
|
|
9100
|
+
}
|
|
9101
|
+
|
|
9102
|
+
// src/cli/brains.ts
|
|
9103
|
+
function printSuccess(msg) {
|
|
9104
|
+
console.log(chalk.green("\u2713 " + msg));
|
|
9105
|
+
}
|
|
9106
|
+
function printError(msg) {
|
|
9107
|
+
console.error(chalk.red("\u2717 " + msg));
|
|
9108
|
+
}
|
|
9109
|
+
function printInfo(msg) {
|
|
9110
|
+
console.log(chalk.cyan("\u2139 " + msg));
|
|
9111
|
+
}
|
|
9112
|
+
function makeBrainsCommand() {
|
|
9113
|
+
const brains = new Command("brains");
|
|
9114
|
+
brains.description("Fine-tuned model training and management (via @hasna/brains)");
|
|
9115
|
+
brains.command("gather").description("Gather training data from memories and write to JSONL").option("--limit <n>", "Maximum number of examples to gather", parseInt).option("--since <date>", "Only include memories created since this date (ISO 8601)").option("--output <dir>", "Output directory (default: ~/.mementos/training/)").option("--json", "Output result summary as JSON").action(async (opts) => {
|
|
9116
|
+
try {
|
|
9117
|
+
const since = opts.since ? new Date(opts.since) : undefined;
|
|
9118
|
+
if (since && isNaN(since.getTime())) {
|
|
9119
|
+
printError(`Invalid date: ${opts.since}`);
|
|
9120
|
+
process.exit(1);
|
|
9121
|
+
}
|
|
9122
|
+
if (!opts.json) {
|
|
9123
|
+
printInfo("Gathering training data from memories...");
|
|
9124
|
+
}
|
|
9125
|
+
const result = await gatherTrainingData({
|
|
9126
|
+
limit: opts.limit,
|
|
9127
|
+
since
|
|
9128
|
+
});
|
|
9129
|
+
const outputDir = opts.output ?? join4(homedir3(), ".mementos", "training");
|
|
9130
|
+
if (!existsSync4(outputDir)) {
|
|
9131
|
+
mkdirSync4(outputDir, { recursive: true });
|
|
9132
|
+
}
|
|
9133
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
9134
|
+
const outputPath = join4(outputDir, `mementos-training-${timestamp}.jsonl`);
|
|
9135
|
+
const jsonl = result.examples.map((ex) => JSON.stringify(ex)).join(`
|
|
9136
|
+
`);
|
|
9137
|
+
writeFileSync3(outputPath, jsonl + `
|
|
9138
|
+
`, "utf-8");
|
|
9139
|
+
if (opts.json) {
|
|
9140
|
+
console.log(JSON.stringify({
|
|
9141
|
+
source: result.source,
|
|
9142
|
+
count: result.count,
|
|
9143
|
+
path: outputPath
|
|
9144
|
+
}));
|
|
9145
|
+
} else {
|
|
9146
|
+
printSuccess(`Gathered ${result.count} training examples from memories`);
|
|
9147
|
+
console.log(chalk.dim(` Output: ${outputPath}`));
|
|
9148
|
+
}
|
|
9149
|
+
} catch (err) {
|
|
9150
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9151
|
+
process.exit(1);
|
|
9152
|
+
}
|
|
9153
|
+
});
|
|
9154
|
+
brains.command("train").description("Start a fine-tuning job using gathered memory training data").option("--base-model <model>", "Base model to fine-tune", "gpt-4o-mini-2024-07-18").option("--provider <provider>", "Provider (openai|thinker-labs)", "openai").option("--dataset <path>", "Path to JSONL dataset (auto-detects latest if omitted)").option("--name <name>", "Display name for the fine-tuned model").option("--json", "Output result as JSON").action(async (opts) => {
|
|
9155
|
+
try {
|
|
9156
|
+
let datasetPath = opts.dataset;
|
|
9157
|
+
if (!datasetPath) {
|
|
9158
|
+
const trainingDir = join4(homedir3(), ".mementos", "training");
|
|
9159
|
+
if (!existsSync4(trainingDir)) {
|
|
9160
|
+
printError("No training data found. Run `mementos brains gather` first.");
|
|
9161
|
+
process.exit(1);
|
|
9162
|
+
}
|
|
9163
|
+
const files = readdirSync2(trainingDir).filter((f) => f.endsWith(".jsonl")).sort().reverse();
|
|
9164
|
+
const latestFile = files[0];
|
|
9165
|
+
if (!latestFile) {
|
|
9166
|
+
printError("No JSONL training files found. Run `mementos brains gather` first.");
|
|
9167
|
+
process.exit(1);
|
|
9168
|
+
}
|
|
9169
|
+
datasetPath = join4(trainingDir, latestFile);
|
|
9170
|
+
}
|
|
9171
|
+
if (!datasetPath || !existsSync4(datasetPath)) {
|
|
9172
|
+
printError(`Dataset file not found: ${datasetPath ?? "(unresolved)"}`);
|
|
9173
|
+
process.exit(1);
|
|
9174
|
+
}
|
|
9175
|
+
if (!opts.json) {
|
|
9176
|
+
printInfo(`Starting fine-tuning job with dataset: ${datasetPath}`);
|
|
9177
|
+
}
|
|
9178
|
+
let brainsSDK;
|
|
9179
|
+
try {
|
|
9180
|
+
brainsSDK = await import("@hasna/brains");
|
|
9181
|
+
} catch {
|
|
9182
|
+
printError("@hasna/brains is not installed. Run `bun add @hasna/brains` to enable training.");
|
|
9183
|
+
process.exit(1);
|
|
9184
|
+
}
|
|
9185
|
+
const startFinetune = brainsSDK["startFinetune"];
|
|
9186
|
+
if (typeof startFinetune !== "function") {
|
|
9187
|
+
printError("@hasna/brains does not export startFinetune. Please update @hasna/brains.");
|
|
9188
|
+
process.exit(1);
|
|
9189
|
+
}
|
|
9190
|
+
const modelName = opts.name ?? `mementos-${new Date().toISOString().slice(0, 10)}`;
|
|
9191
|
+
const jobResult = await startFinetune({
|
|
9192
|
+
provider: opts.provider,
|
|
9193
|
+
baseModel: opts.baseModel,
|
|
9194
|
+
datasetPath,
|
|
9195
|
+
name: modelName
|
|
9196
|
+
});
|
|
9197
|
+
if (opts.json) {
|
|
9198
|
+
console.log(JSON.stringify(jobResult));
|
|
9199
|
+
} else {
|
|
9200
|
+
printSuccess(`Fine-tuning job started: ${String(jobResult["jobId"] ?? "(unknown)")}`);
|
|
9201
|
+
console.log(chalk.dim(` Provider: ${opts.provider}`));
|
|
9202
|
+
console.log(chalk.dim(` Base model: ${opts.baseModel}`));
|
|
9203
|
+
console.log(chalk.dim(` Name: ${modelName}`));
|
|
9204
|
+
if (jobResult["jobId"]) {
|
|
9205
|
+
console.log();
|
|
9206
|
+
printInfo(`Use \`mementos brains model set <model-id>\` once training completes.`);
|
|
9207
|
+
}
|
|
9208
|
+
}
|
|
9209
|
+
} catch (err) {
|
|
9210
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9211
|
+
process.exit(1);
|
|
9212
|
+
}
|
|
9213
|
+
});
|
|
9214
|
+
const modelCmd = brains.command("model").description("Manage the active fine-tuned model");
|
|
9215
|
+
modelCmd.command("get").description("Show the currently active fine-tuned model").option("--json", "Output as JSON").action((opts) => {
|
|
9216
|
+
try {
|
|
9217
|
+
const active = getActiveModel();
|
|
9218
|
+
const isDefault = active === DEFAULT_MODEL;
|
|
9219
|
+
if (opts.json) {
|
|
9220
|
+
console.log(JSON.stringify({ activeModel: active, isDefault }));
|
|
9221
|
+
} else {
|
|
9222
|
+
if (isDefault) {
|
|
9223
|
+
console.log(`Active model: ${chalk.cyan(active)} ${chalk.dim("(default)")}`);
|
|
9224
|
+
} else {
|
|
9225
|
+
console.log(`Active model: ${chalk.green(active)}`);
|
|
9226
|
+
}
|
|
9227
|
+
}
|
|
9228
|
+
} catch (err) {
|
|
9229
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9230
|
+
process.exit(1);
|
|
9231
|
+
}
|
|
9232
|
+
});
|
|
9233
|
+
modelCmd.command("set <modelId>").description("Set the active fine-tuned model ID").action((modelId) => {
|
|
9234
|
+
try {
|
|
9235
|
+
setActiveModel(modelId);
|
|
9236
|
+
printSuccess(`Active model set to: ${modelId}`);
|
|
9237
|
+
} catch (err) {
|
|
9238
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9239
|
+
process.exit(1);
|
|
9240
|
+
}
|
|
9241
|
+
});
|
|
9242
|
+
modelCmd.command("clear").description(`Clear the active fine-tuned model (reverts to ${DEFAULT_MODEL})`).action(() => {
|
|
9243
|
+
try {
|
|
9244
|
+
clearActiveModel();
|
|
9245
|
+
printSuccess(`Active model cleared. Using default: ${DEFAULT_MODEL}`);
|
|
9246
|
+
} catch (err) {
|
|
9247
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9248
|
+
process.exit(1);
|
|
9249
|
+
}
|
|
9250
|
+
});
|
|
9251
|
+
modelCmd.action((opts) => {
|
|
9252
|
+
try {
|
|
9253
|
+
const active = getActiveModel();
|
|
9254
|
+
const isDefault = active === DEFAULT_MODEL;
|
|
9255
|
+
if (opts.json) {
|
|
9256
|
+
console.log(JSON.stringify({ activeModel: active, isDefault }));
|
|
9257
|
+
} else {
|
|
9258
|
+
if (isDefault) {
|
|
9259
|
+
console.log(`Active model: ${chalk.cyan(active)} ${chalk.dim("(default)")}`);
|
|
9260
|
+
} else {
|
|
9261
|
+
console.log(`Active model: ${chalk.green(active)}`);
|
|
9262
|
+
}
|
|
9263
|
+
}
|
|
9264
|
+
} catch (err) {
|
|
9265
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9266
|
+
process.exit(1);
|
|
9267
|
+
}
|
|
9268
|
+
});
|
|
9269
|
+
return brains;
|
|
9270
|
+
}
|
|
9271
|
+
|
|
9272
|
+
// src/cli/index.tsx
|
|
7948
9273
|
function getPackageVersion() {
|
|
7949
9274
|
try {
|
|
7950
|
-
const pkgPath =
|
|
7951
|
-
const pkg = JSON.parse(
|
|
9275
|
+
const pkgPath = join6(dirname4(fileURLToPath(import.meta.url)), "..", "..", "package.json");
|
|
9276
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
7952
9277
|
return pkg.version || "0.0.0";
|
|
7953
9278
|
} catch {
|
|
7954
9279
|
return "0.0.0";
|
|
7955
9280
|
}
|
|
7956
9281
|
}
|
|
7957
9282
|
var scopeColor = {
|
|
7958
|
-
global:
|
|
7959
|
-
shared:
|
|
7960
|
-
private:
|
|
7961
|
-
working:
|
|
9283
|
+
global: chalk2.cyan,
|
|
9284
|
+
shared: chalk2.yellow,
|
|
9285
|
+
private: chalk2.magenta,
|
|
9286
|
+
working: chalk2.gray
|
|
7962
9287
|
};
|
|
7963
9288
|
var categoryColor = {
|
|
7964
|
-
preference:
|
|
7965
|
-
fact:
|
|
7966
|
-
knowledge:
|
|
7967
|
-
history:
|
|
7968
|
-
procedural:
|
|
7969
|
-
resource:
|
|
9289
|
+
preference: chalk2.blue,
|
|
9290
|
+
fact: chalk2.green,
|
|
9291
|
+
knowledge: chalk2.yellow,
|
|
9292
|
+
history: chalk2.gray,
|
|
9293
|
+
procedural: chalk2.cyan,
|
|
9294
|
+
resource: chalk2.magenta
|
|
7970
9295
|
};
|
|
7971
9296
|
function importanceColor(importance) {
|
|
7972
9297
|
if (importance >= 9)
|
|
7973
|
-
return
|
|
9298
|
+
return chalk2.red.bold;
|
|
7974
9299
|
if (importance >= 7)
|
|
7975
|
-
return
|
|
9300
|
+
return chalk2.yellow;
|
|
7976
9301
|
if (importance >= 5)
|
|
7977
|
-
return
|
|
7978
|
-
return
|
|
9302
|
+
return chalk2.green;
|
|
9303
|
+
return chalk2.dim;
|
|
7979
9304
|
}
|
|
7980
9305
|
function colorScope(scope) {
|
|
7981
9306
|
return scopeColor[scope](scope);
|
|
@@ -7987,17 +9312,17 @@ function colorImportance(importance) {
|
|
|
7987
9312
|
return importanceColor(importance)(String(importance));
|
|
7988
9313
|
}
|
|
7989
9314
|
var entityTypeColor = {
|
|
7990
|
-
person:
|
|
7991
|
-
project:
|
|
7992
|
-
tool:
|
|
7993
|
-
concept:
|
|
7994
|
-
file:
|
|
7995
|
-
api:
|
|
7996
|
-
pattern:
|
|
7997
|
-
organization:
|
|
9315
|
+
person: chalk2.cyan,
|
|
9316
|
+
project: chalk2.yellow,
|
|
9317
|
+
tool: chalk2.green,
|
|
9318
|
+
concept: chalk2.blue,
|
|
9319
|
+
file: chalk2.magenta,
|
|
9320
|
+
api: chalk2.red,
|
|
9321
|
+
pattern: chalk2.gray,
|
|
9322
|
+
organization: chalk2.white
|
|
7998
9323
|
};
|
|
7999
9324
|
function colorEntityType(type) {
|
|
8000
|
-
const colorFn = entityTypeColor[type] ||
|
|
9325
|
+
const colorFn = entityTypeColor[type] || chalk2.white;
|
|
8001
9326
|
return colorFn(type);
|
|
8002
9327
|
}
|
|
8003
9328
|
function resolveEntityArg(nameOrId, type) {
|
|
@@ -8008,7 +9333,7 @@ function resolveEntityArg(nameOrId, type) {
|
|
|
8008
9333
|
const id = resolvePartialId(db, "entities", nameOrId);
|
|
8009
9334
|
if (id)
|
|
8010
9335
|
return getEntity(id);
|
|
8011
|
-
console.error(
|
|
9336
|
+
console.error(chalk2.red(`Entity not found: ${nameOrId}`));
|
|
8012
9337
|
process.exit(1);
|
|
8013
9338
|
}
|
|
8014
9339
|
function outputJson(data) {
|
|
@@ -8092,44 +9417,44 @@ ${formatObj({ [k]: v }, " ").replace(/^\s*\S+:\n/, "")}`);
|
|
|
8092
9417
|
}
|
|
8093
9418
|
}
|
|
8094
9419
|
function formatMemoryLine(m) {
|
|
8095
|
-
const id =
|
|
9420
|
+
const id = chalk2.dim(m.id.slice(0, 8));
|
|
8096
9421
|
const scope = colorScope(m.scope);
|
|
8097
9422
|
const cat = colorCategory(m.category);
|
|
8098
9423
|
const imp = colorImportance(m.importance);
|
|
8099
|
-
const pin = m.pinned ?
|
|
9424
|
+
const pin = m.pinned ? chalk2.red(" *") : "";
|
|
8100
9425
|
const value = m.value.length > 80 ? m.value.slice(0, 80) + "..." : m.value;
|
|
8101
|
-
return `${id} [${scope}/${cat}] ${
|
|
9426
|
+
return `${id} [${scope}/${cat}] ${chalk2.bold(m.key)} = ${value} (${imp})${pin}`;
|
|
8102
9427
|
}
|
|
8103
9428
|
function formatMemoryDetail(m) {
|
|
8104
9429
|
const lines = [
|
|
8105
|
-
`${
|
|
8106
|
-
`${
|
|
8107
|
-
`${
|
|
8108
|
-
`${
|
|
8109
|
-
`${
|
|
8110
|
-
`${
|
|
8111
|
-
`${
|
|
8112
|
-
`${
|
|
8113
|
-
`${
|
|
9430
|
+
`${chalk2.bold("ID:")} ${m.id}`,
|
|
9431
|
+
`${chalk2.bold("Key:")} ${m.key}`,
|
|
9432
|
+
`${chalk2.bold("Value:")} ${m.value}`,
|
|
9433
|
+
`${chalk2.bold("Scope:")} ${colorScope(m.scope)}`,
|
|
9434
|
+
`${chalk2.bold("Category:")} ${colorCategory(m.category)}`,
|
|
9435
|
+
`${chalk2.bold("Importance:")} ${colorImportance(m.importance)}/10`,
|
|
9436
|
+
`${chalk2.bold("Source:")} ${m.source}`,
|
|
9437
|
+
`${chalk2.bold("Status:")} ${m.status}`,
|
|
9438
|
+
`${chalk2.bold("Pinned:")} ${m.pinned ? chalk2.red("yes") : "no"}`
|
|
8114
9439
|
];
|
|
8115
9440
|
if (m.summary)
|
|
8116
|
-
lines.push(`${
|
|
9441
|
+
lines.push(`${chalk2.bold("Summary:")} ${m.summary}`);
|
|
8117
9442
|
if (m.tags.length > 0)
|
|
8118
|
-
lines.push(`${
|
|
9443
|
+
lines.push(`${chalk2.bold("Tags:")} ${m.tags.join(", ")}`);
|
|
8119
9444
|
if (m.agent_id)
|
|
8120
|
-
lines.push(`${
|
|
9445
|
+
lines.push(`${chalk2.bold("Agent:")} ${m.agent_id}`);
|
|
8121
9446
|
if (m.project_id)
|
|
8122
|
-
lines.push(`${
|
|
9447
|
+
lines.push(`${chalk2.bold("Project:")} ${m.project_id}`);
|
|
8123
9448
|
if (m.session_id)
|
|
8124
|
-
lines.push(`${
|
|
9449
|
+
lines.push(`${chalk2.bold("Session:")} ${m.session_id}`);
|
|
8125
9450
|
if (m.expires_at)
|
|
8126
|
-
lines.push(`${
|
|
8127
|
-
lines.push(`${
|
|
8128
|
-
lines.push(`${
|
|
8129
|
-
lines.push(`${
|
|
8130
|
-
lines.push(`${
|
|
9451
|
+
lines.push(`${chalk2.bold("Expires:")} ${m.expires_at}`);
|
|
9452
|
+
lines.push(`${chalk2.bold("Access:")} ${m.access_count}`);
|
|
9453
|
+
lines.push(`${chalk2.bold("Version:")} ${m.version}`);
|
|
9454
|
+
lines.push(`${chalk2.bold("Created:")} ${m.created_at}`);
|
|
9455
|
+
lines.push(`${chalk2.bold("Updated:")} ${m.updated_at}`);
|
|
8131
9456
|
if (m.accessed_at)
|
|
8132
|
-
lines.push(`${
|
|
9457
|
+
lines.push(`${chalk2.bold("Accessed:")} ${m.accessed_at}`);
|
|
8133
9458
|
return lines.join(`
|
|
8134
9459
|
`);
|
|
8135
9460
|
}
|
|
@@ -8141,7 +9466,7 @@ function handleError(e) {
|
|
|
8141
9466
|
error: e instanceof Error ? e.message : String(e)
|
|
8142
9467
|
});
|
|
8143
9468
|
} else {
|
|
8144
|
-
console.error(
|
|
9469
|
+
console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
|
|
8145
9470
|
}
|
|
8146
9471
|
process.exit(1);
|
|
8147
9472
|
}
|
|
@@ -8149,7 +9474,7 @@ function resolveMemoryId(partialId) {
|
|
|
8149
9474
|
const db = getDatabase();
|
|
8150
9475
|
const id = resolvePartialId(db, "memories", partialId);
|
|
8151
9476
|
if (!id) {
|
|
8152
|
-
console.error(
|
|
9477
|
+
console.error(chalk2.red(`Could not resolve memory ID: ${partialId}`));
|
|
8153
9478
|
process.exit(1);
|
|
8154
9479
|
}
|
|
8155
9480
|
return id;
|
|
@@ -8175,10 +9500,10 @@ function resolveKeyOrId(keyOrId, opts, globalOpts) {
|
|
|
8175
9500
|
program2.name("mementos").description("Universal memory system for AI agents").version(getPackageVersion()).option("--project <path>", "Project path for scoping").option("--json", "Output as JSON").option("--format <fmt>", "Output format: compact, json, csv, yaml").option("--agent <name>", "Agent name or ID").option("--session <id>", "Session ID");
|
|
8176
9501
|
program2.command("init").description("One-command setup: register MCP, install stop hook, configure auto-start").action(async () => {
|
|
8177
9502
|
const { platform } = process;
|
|
8178
|
-
const home =
|
|
9503
|
+
const home = homedir4();
|
|
8179
9504
|
const isMac = platform === "darwin";
|
|
8180
9505
|
console.log("");
|
|
8181
|
-
console.log(
|
|
9506
|
+
console.log(chalk2.bold(" mementos \u2014 setting up your memory layer"));
|
|
8182
9507
|
console.log("");
|
|
8183
9508
|
async function run(cmd) {
|
|
8184
9509
|
try {
|
|
@@ -8223,24 +9548,24 @@ program2.command("init").description("One-command setup: register MCP, install s
|
|
|
8223
9548
|
mcpError = e instanceof Error ? e.message : String(e);
|
|
8224
9549
|
}
|
|
8225
9550
|
if (mcpAlreadyInstalled) {
|
|
8226
|
-
console.log(
|
|
9551
|
+
console.log(chalk2.dim(" \xB7 MCP already registered"));
|
|
8227
9552
|
} else if (mcpError) {
|
|
8228
|
-
console.log(
|
|
8229
|
-
console.log(
|
|
9553
|
+
console.log(chalk2.red(` \u2717 Failed to register MCP: ${mcpError}`));
|
|
9554
|
+
console.log(chalk2.dim(" (Is Claude Code installed? Try: claude mcp add --transport stdio --scope user mementos -- mementos-mcp)"));
|
|
8230
9555
|
} else {
|
|
8231
|
-
console.log(
|
|
9556
|
+
console.log(chalk2.green(" \u2713 MCP server registered with Claude Code"));
|
|
8232
9557
|
}
|
|
8233
|
-
const hooksDir =
|
|
8234
|
-
const hookDest =
|
|
8235
|
-
const settingsPath =
|
|
9558
|
+
const hooksDir = join6(home, ".claude", "hooks");
|
|
9559
|
+
const hookDest = join6(hooksDir, "mementos-stop-hook.ts");
|
|
9560
|
+
const settingsPath = join6(home, ".claude", "settings.json");
|
|
8236
9561
|
const hookCommand = `bun ${hookDest}`;
|
|
8237
9562
|
let hookAlreadyInstalled = false;
|
|
8238
9563
|
let hookError = null;
|
|
8239
9564
|
try {
|
|
8240
9565
|
let settings = {};
|
|
8241
|
-
if (
|
|
9566
|
+
if (existsSync6(settingsPath)) {
|
|
8242
9567
|
try {
|
|
8243
|
-
settings = JSON.parse(
|
|
9568
|
+
settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
|
|
8244
9569
|
} catch {
|
|
8245
9570
|
settings = {};
|
|
8246
9571
|
}
|
|
@@ -8251,19 +9576,19 @@ program2.command("init").description("One-command setup: register MCP, install s
|
|
|
8251
9576
|
if (alreadyHasMementos) {
|
|
8252
9577
|
hookAlreadyInstalled = true;
|
|
8253
9578
|
} else {
|
|
8254
|
-
if (!
|
|
8255
|
-
|
|
9579
|
+
if (!existsSync6(hooksDir)) {
|
|
9580
|
+
mkdirSync6(hooksDir, { recursive: true });
|
|
8256
9581
|
}
|
|
8257
|
-
if (!
|
|
8258
|
-
const packageDir =
|
|
9582
|
+
if (!existsSync6(hookDest)) {
|
|
9583
|
+
const packageDir = dirname4(dirname4(fileURLToPath(import.meta.url)));
|
|
8259
9584
|
const candidatePaths = [
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
9585
|
+
join6(packageDir, "scripts", "hooks", "claude-stop-hook.ts"),
|
|
9586
|
+
join6(packageDir, "..", "scripts", "hooks", "claude-stop-hook.ts"),
|
|
9587
|
+
join6(home, ".bun", "install", "global", "node_modules", "@hasna", "mementos", "scripts", "hooks", "claude-stop-hook.ts")
|
|
8263
9588
|
];
|
|
8264
9589
|
let hookSourceFound = false;
|
|
8265
9590
|
for (const src of candidatePaths) {
|
|
8266
|
-
if (
|
|
9591
|
+
if (existsSync6(src)) {
|
|
8267
9592
|
copyFileSync(src, hookDest);
|
|
8268
9593
|
hookSourceFound = true;
|
|
8269
9594
|
break;
|
|
@@ -8303,7 +9628,7 @@ async function main() {
|
|
|
8303
9628
|
|
|
8304
9629
|
main().catch(() => {});
|
|
8305
9630
|
`;
|
|
8306
|
-
|
|
9631
|
+
writeFileSync4(hookDest, inlineHook, "utf-8");
|
|
8307
9632
|
}
|
|
8308
9633
|
}
|
|
8309
9634
|
const newStopEntry = {
|
|
@@ -8312,24 +9637,24 @@ main().catch(() => {});
|
|
|
8312
9637
|
};
|
|
8313
9638
|
hooksObj["Stop"] = [...stopHooks, newStopEntry];
|
|
8314
9639
|
settings["hooks"] = hooksObj;
|
|
8315
|
-
|
|
9640
|
+
writeFileSync4(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
8316
9641
|
}
|
|
8317
9642
|
} catch (e) {
|
|
8318
9643
|
hookError = e instanceof Error ? e.message : String(e);
|
|
8319
9644
|
}
|
|
8320
9645
|
if (hookAlreadyInstalled) {
|
|
8321
|
-
console.log(
|
|
9646
|
+
console.log(chalk2.dim(" \xB7 Stop hook already installed"));
|
|
8322
9647
|
} else if (hookError) {
|
|
8323
|
-
console.log(
|
|
9648
|
+
console.log(chalk2.red(` \u2717 Failed to install stop hook: ${hookError}`));
|
|
8324
9649
|
} else {
|
|
8325
|
-
console.log(
|
|
9650
|
+
console.log(chalk2.green(" \u2713 Stop hook installed (sessions \u2192 memories)"));
|
|
8326
9651
|
}
|
|
8327
9652
|
let autoStartAlreadyInstalled = false;
|
|
8328
9653
|
let autoStartError = null;
|
|
8329
9654
|
if (!isMac) {
|
|
8330
|
-
console.log(
|
|
9655
|
+
console.log(chalk2.dim(` \xB7 Auto-start skipped (not macOS \u2014 platform: ${platform})`));
|
|
8331
9656
|
} else {
|
|
8332
|
-
const plistPath =
|
|
9657
|
+
const plistPath = join6(home, "Library", "LaunchAgents", "com.hasna.mementos.plist");
|
|
8333
9658
|
const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
8334
9659
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
8335
9660
|
<plist version="1.0">
|
|
@@ -8354,30 +9679,30 @@ main().catch(() => {});
|
|
|
8354
9679
|
</plist>
|
|
8355
9680
|
`;
|
|
8356
9681
|
try {
|
|
8357
|
-
if (
|
|
9682
|
+
if (existsSync6(plistPath)) {
|
|
8358
9683
|
autoStartAlreadyInstalled = true;
|
|
8359
9684
|
} else {
|
|
8360
|
-
const launchAgentsDir =
|
|
8361
|
-
if (!
|
|
8362
|
-
|
|
9685
|
+
const launchAgentsDir = join6(home, "Library", "LaunchAgents");
|
|
9686
|
+
if (!existsSync6(launchAgentsDir)) {
|
|
9687
|
+
mkdirSync6(launchAgentsDir, { recursive: true });
|
|
8363
9688
|
}
|
|
8364
|
-
|
|
9689
|
+
writeFileSync4(plistPath, plistContent, "utf-8");
|
|
8365
9690
|
}
|
|
8366
9691
|
} catch (e) {
|
|
8367
9692
|
autoStartError = e instanceof Error ? e.message : String(e);
|
|
8368
9693
|
}
|
|
8369
9694
|
if (autoStartAlreadyInstalled) {
|
|
8370
|
-
console.log(
|
|
9695
|
+
console.log(chalk2.dim(" \xB7 Auto-start already configured"));
|
|
8371
9696
|
} else if (autoStartError) {
|
|
8372
|
-
console.log(
|
|
9697
|
+
console.log(chalk2.red(` \u2717 Failed to configure auto-start: ${autoStartError}`));
|
|
8373
9698
|
} else {
|
|
8374
|
-
console.log(
|
|
9699
|
+
console.log(chalk2.green(" \u2713 Auto-start configured (starts on login)"));
|
|
8375
9700
|
}
|
|
8376
9701
|
if (!autoStartAlreadyInstalled && !autoStartError) {
|
|
8377
|
-
const plistPath2 =
|
|
9702
|
+
const plistPath2 = join6(home, "Library", "LaunchAgents", "com.hasna.mementos.plist");
|
|
8378
9703
|
const loadResult = await run(["launchctl", "load", plistPath2]);
|
|
8379
9704
|
if (!loadResult.ok) {
|
|
8380
|
-
console.log(
|
|
9705
|
+
console.log(chalk2.dim(` \xB7 launchctl load: ${loadResult.output || "already loaded"}`));
|
|
8381
9706
|
}
|
|
8382
9707
|
}
|
|
8383
9708
|
}
|
|
@@ -8389,18 +9714,18 @@ main().catch(() => {});
|
|
|
8389
9714
|
serverRunning = res.ok;
|
|
8390
9715
|
} catch {}
|
|
8391
9716
|
if (serverRunning) {
|
|
8392
|
-
console.log(
|
|
9717
|
+
console.log(chalk2.green(" \u2713 Server running on http://127.0.0.1:19428"));
|
|
8393
9718
|
} else {
|
|
8394
|
-
console.log(
|
|
8395
|
-
console.log(
|
|
9719
|
+
console.log(chalk2.dim(" \xB7 Server not yet running \u2014 it will start automatically on next login"));
|
|
9720
|
+
console.log(chalk2.dim(" (Or start it now: mementos-serve)"));
|
|
8396
9721
|
}
|
|
8397
9722
|
console.log("");
|
|
8398
|
-
console.log(
|
|
9723
|
+
console.log(chalk2.bold(" You're all set. Restart Claude Code to activate."));
|
|
8399
9724
|
console.log("");
|
|
8400
9725
|
console.log(" Quick start:");
|
|
8401
|
-
console.log(` ${
|
|
8402
|
-
console.log(` ${
|
|
8403
|
-
console.log(` ${
|
|
9726
|
+
console.log(` ${chalk2.cyan('mementos save "my-preference" "I prefer bun over npm"')}`);
|
|
9727
|
+
console.log(` ${chalk2.cyan("mementos list")}`);
|
|
9728
|
+
console.log(` ${chalk2.cyan("mementos doctor")}`);
|
|
8404
9729
|
console.log("");
|
|
8405
9730
|
});
|
|
8406
9731
|
program2.command("save <key> <value>").description("Save a memory (create or upsert)").option("-c, --category <cat>", "Category: preference, fact, knowledge, history").option("-s, --scope <scope>", "Scope: global, shared, private").option("--importance <n>", "Importance 1-10", parseInt).option("--tags <tags>", "Comma-separated tags").option("--summary <text>", "Brief summary").option("--ttl <duration>", "Time-to-live: 30s, 5m, 2h, 1d, 1w, or milliseconds").option("--source <src>", "Source: user, agent, system, auto, imported").option("--template <name>", "Apply a template: correction, preference, decision, learning").action((key, value, opts) => {
|
|
@@ -8436,7 +9761,7 @@ program2.command("save <key> <value>").description("Save a memory (create or ups
|
|
|
8436
9761
|
if (opts.template) {
|
|
8437
9762
|
const tpl = templates[opts.template];
|
|
8438
9763
|
if (!tpl) {
|
|
8439
|
-
console.error(
|
|
9764
|
+
console.error(chalk2.red(`Unknown template: ${opts.template}. Valid templates: ${Object.keys(templates).join(", ")}`));
|
|
8440
9765
|
process.exit(1);
|
|
8441
9766
|
}
|
|
8442
9767
|
templateDefaults = tpl;
|
|
@@ -8465,7 +9790,7 @@ program2.command("save <key> <value>").description("Save a memory (create or ups
|
|
|
8465
9790
|
if (globalOpts.json) {
|
|
8466
9791
|
outputJson(memory);
|
|
8467
9792
|
} else {
|
|
8468
|
-
console.log(
|
|
9793
|
+
console.log(chalk2.green(`Saved: ${memory.key} (${memory.id.slice(0, 8)})`));
|
|
8469
9794
|
}
|
|
8470
9795
|
} catch (e) {
|
|
8471
9796
|
handleError(e);
|
|
@@ -8504,7 +9829,7 @@ program2.command("recall <key>").description("Recall a memory by key").option("-
|
|
|
8504
9829
|
if (globalOpts.json) {
|
|
8505
9830
|
outputJson({ fuzzy_match: true, score: best.score, match_type: best.match_type, memory: best.memory });
|
|
8506
9831
|
} else {
|
|
8507
|
-
console.log(
|
|
9832
|
+
console.log(chalk2.yellow(`No exact match, showing best result (score: ${best.score.toFixed(2)}, match: ${best.match_type}):`));
|
|
8508
9833
|
console.log(formatMemoryDetail(best.memory));
|
|
8509
9834
|
}
|
|
8510
9835
|
return;
|
|
@@ -8512,7 +9837,7 @@ program2.command("recall <key>").description("Recall a memory by key").option("-
|
|
|
8512
9837
|
if (globalOpts.json) {
|
|
8513
9838
|
outputJson({ error: `No memory found for key: ${key}` });
|
|
8514
9839
|
} else {
|
|
8515
|
-
console.error(
|
|
9840
|
+
console.error(chalk2.yellow(`No memory found for key: ${key}`));
|
|
8516
9841
|
}
|
|
8517
9842
|
process.exit(1);
|
|
8518
9843
|
} catch (e) {
|
|
@@ -8562,10 +9887,10 @@ program2.command("list").description("List memories with optional filters").opti
|
|
|
8562
9887
|
return;
|
|
8563
9888
|
}
|
|
8564
9889
|
if (memories.length === 0) {
|
|
8565
|
-
console.log(
|
|
9890
|
+
console.log(chalk2.yellow("No memories found."));
|
|
8566
9891
|
return;
|
|
8567
9892
|
}
|
|
8568
|
-
console.log(
|
|
9893
|
+
console.log(chalk2.bold(`${memories.length} memor${memories.length === 1 ? "y" : "ies"}:`));
|
|
8569
9894
|
for (const m of memories) {
|
|
8570
9895
|
console.log(formatMemoryLine(m));
|
|
8571
9896
|
}
|
|
@@ -8582,7 +9907,7 @@ program2.command("update <id>").description("Update a memory by ID").option("--v
|
|
|
8582
9907
|
if (globalOpts.json) {
|
|
8583
9908
|
outputJson({ error: `Memory not found: ${id}` });
|
|
8584
9909
|
} else {
|
|
8585
|
-
console.error(
|
|
9910
|
+
console.error(chalk2.red(`Memory not found: ${id}`));
|
|
8586
9911
|
}
|
|
8587
9912
|
process.exit(1);
|
|
8588
9913
|
}
|
|
@@ -8611,7 +9936,7 @@ program2.command("update <id>").description("Update a memory by ID").option("--v
|
|
|
8611
9936
|
if (globalOpts.json) {
|
|
8612
9937
|
outputJson(updated);
|
|
8613
9938
|
} else {
|
|
8614
|
-
console.log(
|
|
9939
|
+
console.log(chalk2.green(`Updated: ${updated.key} (${updated.id.slice(0, 8)})`));
|
|
8615
9940
|
}
|
|
8616
9941
|
} catch (e) {
|
|
8617
9942
|
handleError(e);
|
|
@@ -8627,7 +9952,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8627
9952
|
if (globalOpts.json) {
|
|
8628
9953
|
outputJson({ deleted: idMatch });
|
|
8629
9954
|
} else {
|
|
8630
|
-
console.log(
|
|
9955
|
+
console.log(chalk2.green(`Memory ${idMatch} deleted.`));
|
|
8631
9956
|
}
|
|
8632
9957
|
return;
|
|
8633
9958
|
}
|
|
@@ -8636,7 +9961,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8636
9961
|
if (globalOpts.json) {
|
|
8637
9962
|
outputJson({ error: `No memory found: ${keyOrId}` });
|
|
8638
9963
|
} else {
|
|
8639
|
-
console.error(
|
|
9964
|
+
console.error(chalk2.red(`No memory found: ${keyOrId}`));
|
|
8640
9965
|
}
|
|
8641
9966
|
process.exit(1);
|
|
8642
9967
|
}
|
|
@@ -8645,7 +9970,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8645
9970
|
if (globalOpts.json) {
|
|
8646
9971
|
outputJson({ deleted: matches[0].id, key: keyOrId });
|
|
8647
9972
|
} else {
|
|
8648
|
-
console.log(
|
|
9973
|
+
console.log(chalk2.green(`Memory "${keyOrId}" (${matches[0].id}) deleted.`));
|
|
8649
9974
|
}
|
|
8650
9975
|
return;
|
|
8651
9976
|
}
|
|
@@ -8656,7 +9981,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8656
9981
|
if (globalOpts.json) {
|
|
8657
9982
|
outputJson({ deleted: ids, key: keyOrId, count: ids.length });
|
|
8658
9983
|
} else {
|
|
8659
|
-
console.log(
|
|
9984
|
+
console.log(chalk2.green(`Deleted ${ids.length} memories with key "${keyOrId}".`));
|
|
8660
9985
|
}
|
|
8661
9986
|
return;
|
|
8662
9987
|
}
|
|
@@ -8672,7 +9997,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8672
9997
|
}))
|
|
8673
9998
|
});
|
|
8674
9999
|
} else {
|
|
8675
|
-
console.error(
|
|
10000
|
+
console.error(chalk2.yellow(`Ambiguous: ${matches.length} memories match key "${keyOrId}":
|
|
8676
10001
|
`));
|
|
8677
10002
|
for (const m of matches) {
|
|
8678
10003
|
const parts = [
|
|
@@ -8684,7 +10009,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8684
10009
|
].filter(Boolean).join(" ");
|
|
8685
10010
|
console.error(parts);
|
|
8686
10011
|
}
|
|
8687
|
-
console.error(
|
|
10012
|
+
console.error(chalk2.cyan(`
|
|
8688
10013
|
Use the full ID, or narrow with --scope, --agent, --project, or --all.`));
|
|
8689
10014
|
}
|
|
8690
10015
|
process.exit(1);
|
|
@@ -8702,12 +10027,12 @@ program2.command("search <query>").description("Full-text search across memories
|
|
|
8702
10027
|
return;
|
|
8703
10028
|
}
|
|
8704
10029
|
if (history.length === 0) {
|
|
8705
|
-
console.log(
|
|
10030
|
+
console.log(chalk2.yellow("No search history."));
|
|
8706
10031
|
return;
|
|
8707
10032
|
}
|
|
8708
|
-
console.log(
|
|
10033
|
+
console.log(chalk2.bold("Recent searches:"));
|
|
8709
10034
|
for (const h of history) {
|
|
8710
|
-
console.log(` ${
|
|
10035
|
+
console.log(` ${chalk2.cyan(h.query)} ${chalk2.dim(`(${h.result_count} results, ${h.created_at})`)}`);
|
|
8711
10036
|
}
|
|
8712
10037
|
return;
|
|
8713
10038
|
}
|
|
@@ -8719,12 +10044,12 @@ program2.command("search <query>").description("Full-text search across memories
|
|
|
8719
10044
|
return;
|
|
8720
10045
|
}
|
|
8721
10046
|
if (popular.length === 0) {
|
|
8722
|
-
console.log(
|
|
10047
|
+
console.log(chalk2.yellow("No search history."));
|
|
8723
10048
|
return;
|
|
8724
10049
|
}
|
|
8725
|
-
console.log(
|
|
10050
|
+
console.log(chalk2.bold("Popular searches:"));
|
|
8726
10051
|
for (const p of popular) {
|
|
8727
|
-
console.log(` ${
|
|
10052
|
+
console.log(` ${chalk2.cyan(p.query)} ${chalk2.dim(`(${p.count} times)`)}`);
|
|
8728
10053
|
}
|
|
8729
10054
|
return;
|
|
8730
10055
|
}
|
|
@@ -8771,16 +10096,16 @@ program2.command("search <query>").description("Full-text search across memories
|
|
|
8771
10096
|
return;
|
|
8772
10097
|
}
|
|
8773
10098
|
if (results.length === 0) {
|
|
8774
|
-
console.log(
|
|
10099
|
+
console.log(chalk2.yellow(`No memories found matching "${query}".`));
|
|
8775
10100
|
return;
|
|
8776
10101
|
}
|
|
8777
|
-
console.log(
|
|
10102
|
+
console.log(chalk2.bold(`${results.length} result${results.length === 1 ? "" : "s"} for "${query}":`));
|
|
8778
10103
|
for (const r of results) {
|
|
8779
|
-
const score =
|
|
10104
|
+
const score = chalk2.dim(`(score: ${r.score.toFixed(1)})`);
|
|
8780
10105
|
console.log(`${formatMemoryLine(r.memory)} ${score}`);
|
|
8781
10106
|
if (r.highlights && r.highlights.length > 0) {
|
|
8782
10107
|
for (const h of r.highlights) {
|
|
8783
|
-
console.log(
|
|
10108
|
+
console.log(chalk2.dim(` ${h.field}: ${h.snippet}`));
|
|
8784
10109
|
}
|
|
8785
10110
|
}
|
|
8786
10111
|
}
|
|
@@ -8849,16 +10174,16 @@ program2.command("stats").description("Show memory statistics").option("--format
|
|
|
8849
10174
|
console.log(`by_agent,${k},${v}`);
|
|
8850
10175
|
return;
|
|
8851
10176
|
}
|
|
8852
|
-
console.log(
|
|
8853
|
-
console.log(`${
|
|
8854
|
-
console.log(`${
|
|
8855
|
-
console.log(`${
|
|
8856
|
-
console.log(`${
|
|
8857
|
-
console.log(`${
|
|
8858
|
-
console.log(`${
|
|
10177
|
+
console.log(chalk2.bold("Memory Statistics"));
|
|
10178
|
+
console.log(`${chalk2.bold("Total active:")} ${chalk2.white(String(total))}`);
|
|
10179
|
+
console.log(`${chalk2.bold("By scope:")} ${chalk2.cyan("global")}=${stats.by_scope.global} ${chalk2.yellow("shared")}=${stats.by_scope.shared} ${chalk2.magenta("private")}=${stats.by_scope.private} ${chalk2.dim("working")}=${stats.by_scope.working}`);
|
|
10180
|
+
console.log(`${chalk2.bold("By category:")} ${chalk2.blue("preference")}=${stats.by_category.preference} ${chalk2.green("fact")}=${stats.by_category.fact} ${chalk2.yellow("knowledge")}=${stats.by_category.knowledge} ${chalk2.gray("history")}=${stats.by_category.history}`);
|
|
10181
|
+
console.log(`${chalk2.bold("By status:")} active=${stats.by_status.active} archived=${stats.by_status.archived} expired=${stats.by_status.expired}`);
|
|
10182
|
+
console.log(`${chalk2.bold("Pinned:")} ${stats.pinned_count}`);
|
|
10183
|
+
console.log(`${chalk2.bold("Expired:")} ${stats.expired_count}`);
|
|
8859
10184
|
if (Object.keys(stats.by_agent).length > 0) {
|
|
8860
10185
|
const agentParts = Object.entries(stats.by_agent).map(([k, v]) => `${k}=${v}`).join(" ");
|
|
8861
|
-
console.log(`${
|
|
10186
|
+
console.log(`${chalk2.bold("By agent:")} ${agentParts}`);
|
|
8862
10187
|
}
|
|
8863
10188
|
} catch (e) {
|
|
8864
10189
|
handleError(e);
|
|
@@ -8923,30 +10248,30 @@ program2.command("report").description("Rich summary of memory activity and top
|
|
|
8923
10248
|
const maxC = Math.max(...activityRows.map((x) => x.cnt), 1);
|
|
8924
10249
|
return bars[Math.round(r.cnt / maxC * 7)] || "\u2581";
|
|
8925
10250
|
}).join("");
|
|
8926
|
-
console.log(
|
|
10251
|
+
console.log(chalk2.bold(`
|
|
8927
10252
|
mementos report \u2014 last ${days} days
|
|
8928
10253
|
`));
|
|
8929
|
-
console.log(` ${
|
|
8930
|
-
console.log(` ${
|
|
8931
|
-
console.log(` ${
|
|
8932
|
-
console.log(` ${
|
|
8933
|
-
console.log(` ${
|
|
10254
|
+
console.log(` ${chalk2.cyan("Total:")} ${total} memories (${chalk2.yellow(String(pinned))} pinned)`);
|
|
10255
|
+
console.log(` ${chalk2.cyan("Recent:")} ${recentTotal} new \xB7 ${chalk2.dim(`~${avgPerDay}/day`)}`);
|
|
10256
|
+
console.log(` ${chalk2.cyan("Activity:")} ${sparkline || chalk2.dim("no activity")}`);
|
|
10257
|
+
console.log(` ${chalk2.cyan("Scopes:")} global=${byScope["global"] || 0} shared=${byScope["shared"] || 0} private=${byScope["private"] || 0}`);
|
|
10258
|
+
console.log(` ${chalk2.cyan("Categories:")} knowledge=${byCat["knowledge"] || 0} fact=${byCat["fact"] || 0} preference=${byCat["preference"] || 0} history=${byCat["history"] || 0}`);
|
|
8934
10259
|
if (topMems.length > 0) {
|
|
8935
10260
|
console.log(`
|
|
8936
|
-
${
|
|
10261
|
+
${chalk2.bold("Top memories by importance:")}`);
|
|
8937
10262
|
topMems.forEach((m) => {
|
|
8938
|
-
console.log(` ${
|
|
10263
|
+
console.log(` ${chalk2.green(`[${m.importance}]`)} ${chalk2.bold(m.key)} ${chalk2.dim(`(${m.scope}/${m.category})`)}`);
|
|
8939
10264
|
console.log(` ${m.value.slice(0, 90)}${m.value.length > 90 ? "..." : ""}`);
|
|
8940
10265
|
});
|
|
8941
10266
|
}
|
|
8942
10267
|
if (topAgents.length > 0) {
|
|
8943
10268
|
console.log(`
|
|
8944
|
-
${
|
|
10269
|
+
${chalk2.bold("Top agents:")}`);
|
|
8945
10270
|
topAgents.forEach((a) => console.log(` ${a.agent_id}: ${a.c} memories`));
|
|
8946
10271
|
}
|
|
8947
10272
|
console.log("");
|
|
8948
10273
|
} catch (e) {
|
|
8949
|
-
console.error(
|
|
10274
|
+
console.error(chalk2.red(`report failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
8950
10275
|
process.exit(1);
|
|
8951
10276
|
}
|
|
8952
10277
|
});
|
|
@@ -8991,21 +10316,21 @@ program2.command("stale").description("Find memories not accessed recently (for
|
|
|
8991
10316
|
return;
|
|
8992
10317
|
}
|
|
8993
10318
|
if (rows.length === 0) {
|
|
8994
|
-
console.log(
|
|
10319
|
+
console.log(chalk2.green(`No stale memories (not accessed in ${days}+ days).`));
|
|
8995
10320
|
return;
|
|
8996
10321
|
}
|
|
8997
|
-
console.log(
|
|
10322
|
+
console.log(chalk2.bold(`
|
|
8998
10323
|
Stale memories (not accessed in ${days}+ days):
|
|
8999
10324
|
`));
|
|
9000
10325
|
for (const m of rows) {
|
|
9001
|
-
const lastAccess = m.accessed_at ? m.accessed_at.slice(0, 10) :
|
|
9002
|
-
console.log(` ${
|
|
10326
|
+
const lastAccess = m.accessed_at ? m.accessed_at.slice(0, 10) : chalk2.dim("never");
|
|
10327
|
+
console.log(` ${chalk2.dim(`[${m.importance}]`)} ${chalk2.bold(m.key)} ${chalk2.dim(`(${m.scope}/${m.category})`)}`);
|
|
9003
10328
|
console.log(` Last accessed: ${lastAccess} \xB7 ${m.access_count} reads \xB7 ${m.value.slice(0, 80)}${m.value.length > 80 ? "..." : ""}`);
|
|
9004
10329
|
}
|
|
9005
10330
|
console.log(`
|
|
9006
|
-
${
|
|
10331
|
+
${chalk2.dim(`${rows.length} result(s). Run 'mementos archive <key>' or 'mementos forget <key>' to clean up.`)}`);
|
|
9007
10332
|
} catch (e) {
|
|
9008
|
-
console.error(
|
|
10333
|
+
console.error(chalk2.red(`stale failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
9009
10334
|
process.exit(1);
|
|
9010
10335
|
}
|
|
9011
10336
|
});
|
|
@@ -9040,9 +10365,9 @@ program2.command("import [file]").description("Import memories from a JSON file
|
|
|
9040
10365
|
if (file === "-" || !file && !process.stdin.isTTY) {
|
|
9041
10366
|
raw = await Bun.stdin.text();
|
|
9042
10367
|
} else if (file) {
|
|
9043
|
-
raw =
|
|
10368
|
+
raw = readFileSync3(resolve3(file), "utf-8");
|
|
9044
10369
|
} else {
|
|
9045
|
-
console.error(
|
|
10370
|
+
console.error(chalk2.red("No input: provide a file path, use '-' for stdin, or pipe data."));
|
|
9046
10371
|
process.exit(1);
|
|
9047
10372
|
}
|
|
9048
10373
|
const memories = JSON.parse(raw);
|
|
@@ -9058,7 +10383,7 @@ program2.command("import [file]").description("Import memories from a JSON file
|
|
|
9058
10383
|
if (globalOpts.json) {
|
|
9059
10384
|
outputJson({ imported });
|
|
9060
10385
|
} else {
|
|
9061
|
-
console.log(
|
|
10386
|
+
console.log(chalk2.green(`Imported ${imported} memor${imported === 1 ? "y" : "ies"}.`));
|
|
9062
10387
|
}
|
|
9063
10388
|
} catch (e) {
|
|
9064
10389
|
handleError(e);
|
|
@@ -9072,12 +10397,12 @@ program2.command("clean").description("Remove expired memories and enforce quota
|
|
|
9072
10397
|
if (globalOpts.json) {
|
|
9073
10398
|
outputJson(result);
|
|
9074
10399
|
} else {
|
|
9075
|
-
console.log(
|
|
9076
|
-
console.log(` Expired removed: ${
|
|
9077
|
-
console.log(` Evicted (quota): ${
|
|
9078
|
-
console.log(` Archived (stale): ${
|
|
9079
|
-
console.log(` Archived (unused): ${
|
|
9080
|
-
console.log(` Deprioritized: ${
|
|
10400
|
+
console.log(chalk2.bold("Cleanup complete:"));
|
|
10401
|
+
console.log(` Expired removed: ${chalk2.red(String(result.expired))}`);
|
|
10402
|
+
console.log(` Evicted (quota): ${chalk2.yellow(String(result.evicted))}`);
|
|
10403
|
+
console.log(` Archived (stale): ${chalk2.gray(String(result.archived))}`);
|
|
10404
|
+
console.log(` Archived (unused): ${chalk2.gray(String(result.unused_archived))}`);
|
|
10405
|
+
console.log(` Deprioritized: ${chalk2.blue(String(result.deprioritized))}`);
|
|
9081
10406
|
}
|
|
9082
10407
|
} catch (e) {
|
|
9083
10408
|
handleError(e);
|
|
@@ -9090,11 +10415,11 @@ program2.command("register-agent <name>").alias("init-agent").description("Regis
|
|
|
9090
10415
|
if (globalOpts.json) {
|
|
9091
10416
|
outputJson(agent);
|
|
9092
10417
|
} else {
|
|
9093
|
-
console.log(
|
|
9094
|
-
console.log(` ${
|
|
9095
|
-
console.log(` ${
|
|
9096
|
-
console.log(` ${
|
|
9097
|
-
console.log(` ${
|
|
10418
|
+
console.log(chalk2.green("Agent registered:"));
|
|
10419
|
+
console.log(` ${chalk2.bold("ID:")} ${agent.id}`);
|
|
10420
|
+
console.log(` ${chalk2.bold("Name:")} ${agent.name}`);
|
|
10421
|
+
console.log(` ${chalk2.bold("Role:")} ${agent.role || "agent"}`);
|
|
10422
|
+
console.log(` ${chalk2.bold("Created:")} ${agent.created_at}`);
|
|
9098
10423
|
}
|
|
9099
10424
|
} catch (e) {
|
|
9100
10425
|
handleError(e);
|
|
@@ -9109,12 +10434,12 @@ program2.command("agents").description("List all registered agents").action(() =
|
|
|
9109
10434
|
return;
|
|
9110
10435
|
}
|
|
9111
10436
|
if (agents.length === 0) {
|
|
9112
|
-
console.log(
|
|
10437
|
+
console.log(chalk2.yellow("No agents registered."));
|
|
9113
10438
|
return;
|
|
9114
10439
|
}
|
|
9115
|
-
console.log(
|
|
10440
|
+
console.log(chalk2.bold(`${agents.length} agent${agents.length === 1 ? "" : "s"}:`));
|
|
9116
10441
|
for (const a of agents) {
|
|
9117
|
-
console.log(` ${
|
|
10442
|
+
console.log(` ${chalk2.dim(a.id)} ${chalk2.bold(a.name)} ${chalk2.gray(a.role || "agent")} ${chalk2.dim(`last seen: ${a.last_seen_at}`)}`);
|
|
9118
10443
|
}
|
|
9119
10444
|
} catch (e) {
|
|
9120
10445
|
handleError(e);
|
|
@@ -9134,7 +10459,7 @@ program2.command("agent-update <id>").description("Update an agent's name, descr
|
|
|
9134
10459
|
if (globalOpts.json) {
|
|
9135
10460
|
outputJson({ error: "No updates provided. Use --name, --description, or --role." });
|
|
9136
10461
|
} else {
|
|
9137
|
-
console.error(
|
|
10462
|
+
console.error(chalk2.red("No updates provided. Use --name, --description, or --role."));
|
|
9138
10463
|
}
|
|
9139
10464
|
process.exit(1);
|
|
9140
10465
|
}
|
|
@@ -9143,19 +10468,19 @@ program2.command("agent-update <id>").description("Update an agent's name, descr
|
|
|
9143
10468
|
if (globalOpts.json) {
|
|
9144
10469
|
outputJson({ error: `Agent not found: ${id}` });
|
|
9145
10470
|
} else {
|
|
9146
|
-
console.error(
|
|
10471
|
+
console.error(chalk2.red(`Agent not found: ${id}`));
|
|
9147
10472
|
}
|
|
9148
10473
|
process.exit(1);
|
|
9149
10474
|
}
|
|
9150
10475
|
if (globalOpts.json) {
|
|
9151
10476
|
outputJson(agent);
|
|
9152
10477
|
} else {
|
|
9153
|
-
console.log(
|
|
9154
|
-
console.log(` ${
|
|
9155
|
-
console.log(` ${
|
|
9156
|
-
console.log(` ${
|
|
9157
|
-
console.log(` ${
|
|
9158
|
-
console.log(` ${
|
|
10478
|
+
console.log(chalk2.green("Agent updated:"));
|
|
10479
|
+
console.log(` ${chalk2.bold("ID:")} ${agent.id}`);
|
|
10480
|
+
console.log(` ${chalk2.bold("Name:")} ${agent.name}`);
|
|
10481
|
+
console.log(` ${chalk2.bold("Description:")} ${agent.description || "-"}`);
|
|
10482
|
+
console.log(` ${chalk2.bold("Role:")} ${agent.role || "agent"}`);
|
|
10483
|
+
console.log(` ${chalk2.bold("Last seen:")} ${agent.last_seen_at}`);
|
|
9159
10484
|
}
|
|
9160
10485
|
} catch (e) {
|
|
9161
10486
|
handleError(e);
|
|
@@ -9168,17 +10493,17 @@ program2.command("projects").description("Manage projects").option("--add", "Add
|
|
|
9168
10493
|
const name = opts.name;
|
|
9169
10494
|
const path = opts.path;
|
|
9170
10495
|
if (!name || !path) {
|
|
9171
|
-
console.error(
|
|
10496
|
+
console.error(chalk2.red("--name and --path are required when adding a project"));
|
|
9172
10497
|
process.exit(1);
|
|
9173
10498
|
}
|
|
9174
10499
|
const project = registerProject(name, resolve3(path), opts.description);
|
|
9175
10500
|
if (globalOpts.json) {
|
|
9176
10501
|
outputJson(project);
|
|
9177
10502
|
} else {
|
|
9178
|
-
console.log(
|
|
9179
|
-
console.log(` ${
|
|
9180
|
-
console.log(` ${
|
|
9181
|
-
console.log(` ${
|
|
10503
|
+
console.log(chalk2.green("Project registered:"));
|
|
10504
|
+
console.log(` ${chalk2.bold("ID:")} ${project.id}`);
|
|
10505
|
+
console.log(` ${chalk2.bold("Name:")} ${project.name}`);
|
|
10506
|
+
console.log(` ${chalk2.bold("Path:")} ${project.path}`);
|
|
9182
10507
|
}
|
|
9183
10508
|
return;
|
|
9184
10509
|
}
|
|
@@ -9188,12 +10513,12 @@ program2.command("projects").description("Manage projects").option("--add", "Add
|
|
|
9188
10513
|
return;
|
|
9189
10514
|
}
|
|
9190
10515
|
if (projects.length === 0) {
|
|
9191
|
-
console.log(
|
|
10516
|
+
console.log(chalk2.yellow("No projects registered."));
|
|
9192
10517
|
return;
|
|
9193
10518
|
}
|
|
9194
|
-
console.log(
|
|
10519
|
+
console.log(chalk2.bold(`${projects.length} project${projects.length === 1 ? "" : "s"}:`));
|
|
9195
10520
|
for (const p of projects) {
|
|
9196
|
-
console.log(` ${
|
|
10521
|
+
console.log(` ${chalk2.dim(p.id.slice(0, 8))} ${chalk2.bold(p.name)} ${chalk2.gray(p.path)}${p.description ? chalk2.dim(` \u2014 ${p.description}`) : ""}`);
|
|
9197
10522
|
}
|
|
9198
10523
|
} catch (e) {
|
|
9199
10524
|
handleError(e);
|
|
@@ -9283,7 +10608,7 @@ program2.command("inject").description("Output injection context for agent syste
|
|
|
9283
10608
|
if (globalOpts.json) {
|
|
9284
10609
|
outputJson({ context: "", count: 0 });
|
|
9285
10610
|
} else {
|
|
9286
|
-
console.log(
|
|
10611
|
+
console.log(chalk2.yellow("No relevant memories found for injection."));
|
|
9287
10612
|
}
|
|
9288
10613
|
return;
|
|
9289
10614
|
}
|
|
@@ -9323,7 +10648,7 @@ program2.command("bulk <action> <ids...>").description("Batch operations: forget
|
|
|
9323
10648
|
"unpin"
|
|
9324
10649
|
];
|
|
9325
10650
|
if (!validActions.includes(action)) {
|
|
9326
|
-
console.error(
|
|
10651
|
+
console.error(chalk2.red(`Invalid action: ${action}. Valid: ${validActions.join(", ")}`));
|
|
9327
10652
|
process.exit(1);
|
|
9328
10653
|
}
|
|
9329
10654
|
const resolvedIds = ids.map((id) => resolveMemoryId(id));
|
|
@@ -9376,7 +10701,7 @@ program2.command("bulk <action> <ids...>").description("Batch operations: forget
|
|
|
9376
10701
|
if (globalOpts.json) {
|
|
9377
10702
|
outputJson({ action, affected, ids: resolvedIds });
|
|
9378
10703
|
} else {
|
|
9379
|
-
console.log(
|
|
10704
|
+
console.log(chalk2.green(`${action}: ${affected} memor${affected === 1 ? "y" : "ies"} affected.`));
|
|
9380
10705
|
}
|
|
9381
10706
|
} catch (e) {
|
|
9382
10707
|
handleError(e);
|
|
@@ -9389,7 +10714,7 @@ program2.command("doctor").description("Diagnose common issues with the mementos
|
|
|
9389
10714
|
checks.push({ name: "Version", status: "ok", detail: version });
|
|
9390
10715
|
const dbPath = getDbPath();
|
|
9391
10716
|
let db = null;
|
|
9392
|
-
if (dbPath !== ":memory:" &&
|
|
10717
|
+
if (dbPath !== ":memory:" && existsSync6(dbPath)) {
|
|
9393
10718
|
try {
|
|
9394
10719
|
accessSync(dbPath, fsConstants.R_OK | fsConstants.W_OK);
|
|
9395
10720
|
checks.push({ name: "Database file", status: "ok", detail: dbPath });
|
|
@@ -9408,7 +10733,7 @@ program2.command("doctor").description("Diagnose common issues with the mementos
|
|
|
9408
10733
|
checks.push({ name: "Database connection", status: "fail", detail: e instanceof Error ? e.message : String(e) });
|
|
9409
10734
|
}
|
|
9410
10735
|
try {
|
|
9411
|
-
if (dbPath !== ":memory:" &&
|
|
10736
|
+
if (dbPath !== ":memory:" && existsSync6(dbPath)) {
|
|
9412
10737
|
const stats = statSync(dbPath);
|
|
9413
10738
|
const sizeKb = (stats.size / 1024).toFixed(1);
|
|
9414
10739
|
const sizeMb = (stats.size / (1024 * 1024)).toFixed(2);
|
|
@@ -9446,14 +10771,14 @@ program2.command("doctor").description("Diagnose common issues with the mementos
|
|
|
9446
10771
|
const byScope = {};
|
|
9447
10772
|
let expiredCount = 0;
|
|
9448
10773
|
let staleCount = 0;
|
|
9449
|
-
const
|
|
10774
|
+
const now3 = Date.now();
|
|
9450
10775
|
const staleThreshold = 14 * 24 * 60 * 60 * 1000;
|
|
9451
10776
|
for (const m of all) {
|
|
9452
10777
|
byScope[m.scope] = (byScope[m.scope] || 0) + 1;
|
|
9453
10778
|
if (m.status === "expired")
|
|
9454
10779
|
expiredCount++;
|
|
9455
10780
|
const lastAccess = m.accessed_at ? new Date(m.accessed_at).getTime() : new Date(m.created_at).getTime();
|
|
9456
|
-
if (
|
|
10781
|
+
if (now3 - lastAccess > staleThreshold && m.status === "active") {
|
|
9457
10782
|
staleCount++;
|
|
9458
10783
|
}
|
|
9459
10784
|
}
|
|
@@ -9544,9 +10869,9 @@ program2.command("doctor").description("Diagnose common issues with the mementos
|
|
|
9544
10869
|
checks.push({ name: "MCP server", status: "warn", detail: "could not check (is claude CLI installed?)" });
|
|
9545
10870
|
}
|
|
9546
10871
|
try {
|
|
9547
|
-
const settingsFilePath =
|
|
9548
|
-
if (
|
|
9549
|
-
const settings = JSON.parse(
|
|
10872
|
+
const settingsFilePath = join6(homedir4(), ".claude", "settings.json");
|
|
10873
|
+
if (existsSync6(settingsFilePath)) {
|
|
10874
|
+
const settings = JSON.parse(readFileSync3(settingsFilePath, "utf-8"));
|
|
9550
10875
|
const hooksObj = settings["hooks"] || {};
|
|
9551
10876
|
const stopHooks = hooksObj["Stop"] || [];
|
|
9552
10877
|
const hasMementos = stopHooks.some((e) => e.hooks?.some((h) => h.command && h.command.includes("mementos")));
|
|
@@ -9562,11 +10887,11 @@ program2.command("doctor").description("Diagnose common issues with the mementos
|
|
|
9562
10887
|
checks.push({ name: "Stop hook", status: "warn", detail: "could not check stop hook" });
|
|
9563
10888
|
}
|
|
9564
10889
|
if (process.platform === "darwin") {
|
|
9565
|
-
const plistFilePath =
|
|
10890
|
+
const plistFilePath = join6(homedir4(), "Library", "LaunchAgents", "com.hasna.mementos.plist");
|
|
9566
10891
|
checks.push({
|
|
9567
10892
|
name: "Auto-start",
|
|
9568
|
-
status:
|
|
9569
|
-
detail:
|
|
10893
|
+
status: existsSync6(plistFilePath) ? "ok" : "warn",
|
|
10894
|
+
detail: existsSync6(plistFilePath) ? "configured (starts on login)" : "not configured \u2192 run: mementos init"
|
|
9570
10895
|
});
|
|
9571
10896
|
} else {
|
|
9572
10897
|
checks.push({ name: "Auto-start", status: "ok", detail: `n/a on ${process.platform}` });
|
|
@@ -9577,24 +10902,24 @@ function outputDoctorResults(globalOpts, checks) {
|
|
|
9577
10902
|
if (globalOpts.json) {
|
|
9578
10903
|
outputJson({ checks, healthy: checks.every((c) => c.status === "ok") });
|
|
9579
10904
|
} else {
|
|
9580
|
-
console.log(
|
|
10905
|
+
console.log(chalk2.bold(`
|
|
9581
10906
|
mementos doctor
|
|
9582
10907
|
`));
|
|
9583
10908
|
for (const check of checks) {
|
|
9584
|
-
const icon = check.status === "ok" ?
|
|
9585
|
-
console.log(` ${icon} ${
|
|
10909
|
+
const icon = check.status === "ok" ? chalk2.green("\u2713") : check.status === "warn" ? chalk2.yellow("\u26A0") : chalk2.red("\u2717");
|
|
10910
|
+
console.log(` ${icon} ${chalk2.bold(check.name)}: ${check.detail}`);
|
|
9586
10911
|
}
|
|
9587
10912
|
const healthy = checks.every((c) => c.status === "ok");
|
|
9588
10913
|
const warnings = checks.filter((c) => c.status === "warn").length;
|
|
9589
10914
|
const failures = checks.filter((c) => c.status === "fail").length;
|
|
9590
10915
|
console.log("");
|
|
9591
10916
|
if (healthy) {
|
|
9592
|
-
console.log(
|
|
10917
|
+
console.log(chalk2.green(" All checks passed."));
|
|
9593
10918
|
} else {
|
|
9594
10919
|
if (failures > 0)
|
|
9595
|
-
console.log(
|
|
10920
|
+
console.log(chalk2.red(` ${failures} check(s) failed.`));
|
|
9596
10921
|
if (warnings > 0)
|
|
9597
|
-
console.log(
|
|
10922
|
+
console.log(chalk2.yellow(` ${warnings} warning(s).`));
|
|
9598
10923
|
}
|
|
9599
10924
|
console.log("");
|
|
9600
10925
|
}
|
|
@@ -9608,7 +10933,7 @@ program2.command("show <id>").description("Show full detail of a memory by ID (s
|
|
|
9608
10933
|
if (globalOpts.json) {
|
|
9609
10934
|
outputJson({ error: `Memory not found: ${id}` });
|
|
9610
10935
|
} else {
|
|
9611
|
-
console.error(
|
|
10936
|
+
console.error(chalk2.red(`Memory not found: ${id}`));
|
|
9612
10937
|
}
|
|
9613
10938
|
process.exit(1);
|
|
9614
10939
|
}
|
|
@@ -9634,24 +10959,24 @@ program2.command("history").description("List memories sorted by most recently a
|
|
|
9634
10959
|
return;
|
|
9635
10960
|
}
|
|
9636
10961
|
if (memories.length === 0) {
|
|
9637
|
-
console.log(
|
|
10962
|
+
console.log(chalk2.yellow("No recently accessed memories."));
|
|
9638
10963
|
return;
|
|
9639
10964
|
}
|
|
9640
|
-
console.log(
|
|
10965
|
+
console.log(chalk2.bold(`${memories.length} recently accessed memor${memories.length === 1 ? "y" : "ies"}:`));
|
|
9641
10966
|
for (const m of memories) {
|
|
9642
|
-
const id =
|
|
10967
|
+
const id = chalk2.dim(m.id.slice(0, 8));
|
|
9643
10968
|
const scope = colorScope(m.scope);
|
|
9644
10969
|
const cat = colorCategory(m.category);
|
|
9645
10970
|
const value = m.value.length > 60 ? m.value.slice(0, 60) + "..." : m.value;
|
|
9646
|
-
const accessed = m.accessed_at ?
|
|
9647
|
-
console.log(`${id} [${scope}/${cat}] ${
|
|
10971
|
+
const accessed = m.accessed_at ? chalk2.dim(m.accessed_at) : chalk2.dim("never");
|
|
10972
|
+
console.log(`${id} [${scope}/${cat}] ${chalk2.bold(m.key)} = ${value} ${accessed}`);
|
|
9648
10973
|
}
|
|
9649
10974
|
} catch (e) {
|
|
9650
10975
|
handleError(e);
|
|
9651
10976
|
}
|
|
9652
10977
|
});
|
|
9653
10978
|
program2.command("mcp").description("Install mementos MCP server into Claude Code, Codex, or Gemini").option("--claude", "Install into Claude Code (~/.claude/.mcp.json)").option("--codex", "Install into Codex (~/.codex/config.toml)").option("--gemini", "Install into Gemini (~/.gemini/settings.json)").option("--all", "Install into all supported agents").option("--uninstall", "Remove mementos MCP from config").action((opts) => {
|
|
9654
|
-
const { readFileSync:
|
|
10979
|
+
const { readFileSync: readFileSync4, writeFileSync: writeFileSync5, existsSync: fileExists } = __require("fs");
|
|
9655
10980
|
const { join: pathJoin } = __require("path");
|
|
9656
10981
|
const { homedir: getHome } = __require("os");
|
|
9657
10982
|
const home = getHome();
|
|
@@ -9662,8 +10987,8 @@ program2.command("mcp").description("Install mementos MCP server into Claude Cod
|
|
|
9662
10987
|
opts.gemini ? "gemini" : null
|
|
9663
10988
|
].filter(Boolean);
|
|
9664
10989
|
if (targets.length === 0) {
|
|
9665
|
-
console.log(
|
|
9666
|
-
console.log(
|
|
10990
|
+
console.log(chalk2.yellow("Specify a target: --claude, --codex, --gemini, or --all"));
|
|
10991
|
+
console.log(chalk2.gray("Example: mementos mcp --all"));
|
|
9667
10992
|
return;
|
|
9668
10993
|
}
|
|
9669
10994
|
for (const target of targets) {
|
|
@@ -9673,25 +10998,25 @@ program2.command("mcp").description("Install mementos MCP server into Claude Cod
|
|
|
9673
10998
|
if (opts.uninstall) {
|
|
9674
10999
|
try {
|
|
9675
11000
|
execSync(`claude mcp remove mementos`, { stdio: "pipe" });
|
|
9676
|
-
console.log(
|
|
11001
|
+
console.log(chalk2.green("Removed mementos from Claude Code MCP"));
|
|
9677
11002
|
} catch {
|
|
9678
|
-
console.log(
|
|
11003
|
+
console.log(chalk2.yellow("mementos was not installed in Claude Code (or claude CLI not found)"));
|
|
9679
11004
|
}
|
|
9680
11005
|
} else {
|
|
9681
11006
|
try {
|
|
9682
11007
|
execSync(`claude mcp add --transport stdio --scope user mementos -- ${mementosCmd}`, { stdio: "pipe" });
|
|
9683
|
-
console.log(
|
|
9684
|
-
console.log(
|
|
11008
|
+
console.log(chalk2.green(`Installed mementos into Claude Code (user scope)`));
|
|
11009
|
+
console.log(chalk2.gray(" Restart Claude Code for the change to take effect."));
|
|
9685
11010
|
} catch (e) {
|
|
9686
|
-
console.log(
|
|
9687
|
-
console.log(
|
|
11011
|
+
console.log(chalk2.yellow("claude CLI not found. Run this manually:"));
|
|
11012
|
+
console.log(chalk2.white(` claude mcp add --transport stdio --scope user mementos -- ${mementosCmd}`));
|
|
9688
11013
|
}
|
|
9689
11014
|
}
|
|
9690
11015
|
}
|
|
9691
11016
|
if (target === "codex") {
|
|
9692
11017
|
const configPath = pathJoin(home, ".codex", "config.toml");
|
|
9693
11018
|
if (fileExists(configPath)) {
|
|
9694
|
-
let content =
|
|
11019
|
+
let content = readFileSync4(configPath, "utf-8");
|
|
9695
11020
|
if (opts.uninstall) {
|
|
9696
11021
|
content = content.replace(/\n\[mcp_servers\.mementos\]\ncommand = "[^"]*"\nargs = \[\]\n?/g, `
|
|
9697
11022
|
`);
|
|
@@ -9702,17 +11027,17 @@ command = "${mementosCmd}"
|
|
|
9702
11027
|
args = []
|
|
9703
11028
|
`;
|
|
9704
11029
|
}
|
|
9705
|
-
|
|
9706
|
-
console.log(
|
|
11030
|
+
writeFileSync5(configPath, content, "utf-8");
|
|
11031
|
+
console.log(chalk2.green(`${opts.uninstall ? "Removed from" : "Installed into"} Codex: ${configPath}`));
|
|
9707
11032
|
} else {
|
|
9708
|
-
console.log(
|
|
11033
|
+
console.log(chalk2.yellow(`Codex config not found: ${configPath}`));
|
|
9709
11034
|
}
|
|
9710
11035
|
}
|
|
9711
11036
|
if (target === "gemini") {
|
|
9712
11037
|
const configPath = pathJoin(home, ".gemini", "settings.json");
|
|
9713
11038
|
let config = {};
|
|
9714
11039
|
if (fileExists(configPath)) {
|
|
9715
|
-
config = JSON.parse(
|
|
11040
|
+
config = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
9716
11041
|
}
|
|
9717
11042
|
const servers = config["mcpServers"] || {};
|
|
9718
11043
|
if (opts.uninstall) {
|
|
@@ -9721,12 +11046,12 @@ args = []
|
|
|
9721
11046
|
servers["mementos"] = { command: mementosCmd, args: [] };
|
|
9722
11047
|
}
|
|
9723
11048
|
config["mcpServers"] = servers;
|
|
9724
|
-
|
|
11049
|
+
writeFileSync5(configPath, JSON.stringify(config, null, 2) + `
|
|
9725
11050
|
`, "utf-8");
|
|
9726
|
-
console.log(
|
|
11051
|
+
console.log(chalk2.green(`${opts.uninstall ? "Removed from" : "Installed into"} Gemini: ${configPath}`));
|
|
9727
11052
|
}
|
|
9728
11053
|
} catch (e) {
|
|
9729
|
-
console.error(
|
|
11054
|
+
console.error(chalk2.red(`Failed for ${target}: ${e instanceof Error ? e.message : String(e)}`));
|
|
9730
11055
|
}
|
|
9731
11056
|
}
|
|
9732
11057
|
});
|
|
@@ -9742,20 +11067,20 @@ program2.command("watch").description("Watch for new and changed memories in rea
|
|
|
9742
11067
|
projectId = project.id;
|
|
9743
11068
|
}
|
|
9744
11069
|
const intervalMs = opts.interval || 500;
|
|
9745
|
-
console.log(
|
|
11070
|
+
console.log(chalk2.bold.cyan("Watching memories...") + chalk2.dim(" (Ctrl+C to stop)"));
|
|
9746
11071
|
const filters = [];
|
|
9747
11072
|
if (opts.scope)
|
|
9748
11073
|
filters.push(`scope=${colorScope(opts.scope)}`);
|
|
9749
11074
|
if (opts.category)
|
|
9750
11075
|
filters.push(`category=${colorCategory(opts.category)}`);
|
|
9751
11076
|
if (agentId)
|
|
9752
|
-
filters.push(`agent=${
|
|
11077
|
+
filters.push(`agent=${chalk2.dim(agentId)}`);
|
|
9753
11078
|
if (projectId)
|
|
9754
|
-
filters.push(`project=${
|
|
11079
|
+
filters.push(`project=${chalk2.dim(projectId)}`);
|
|
9755
11080
|
if (filters.length > 0) {
|
|
9756
|
-
console.log(
|
|
11081
|
+
console.log(chalk2.dim("Filters: ") + filters.join(chalk2.dim(" | ")));
|
|
9757
11082
|
}
|
|
9758
|
-
console.log(
|
|
11083
|
+
console.log(chalk2.dim(`Poll interval: ${intervalMs}ms`));
|
|
9759
11084
|
console.log();
|
|
9760
11085
|
const filter = {
|
|
9761
11086
|
scope: opts.scope,
|
|
@@ -9766,14 +11091,14 @@ program2.command("watch").description("Watch for new and changed memories in rea
|
|
|
9766
11091
|
};
|
|
9767
11092
|
const recent = listMemories(filter);
|
|
9768
11093
|
if (recent.length > 0) {
|
|
9769
|
-
console.log(
|
|
11094
|
+
console.log(chalk2.bold.dim(`Recent (${recent.length}):`));
|
|
9770
11095
|
for (const m of recent.reverse()) {
|
|
9771
11096
|
console.log(formatWatchLine(m));
|
|
9772
11097
|
}
|
|
9773
11098
|
} else {
|
|
9774
|
-
console.log(
|
|
11099
|
+
console.log(chalk2.dim("No recent memories."));
|
|
9775
11100
|
}
|
|
9776
|
-
console.log(
|
|
11101
|
+
console.log(chalk2.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Live \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
9777
11102
|
console.log();
|
|
9778
11103
|
const { startPolling: startPolling2 } = (init_poll(), __toCommonJS(exports_poll));
|
|
9779
11104
|
const handle = startPolling2({
|
|
@@ -9789,13 +11114,13 @@ program2.command("watch").description("Watch for new and changed memories in rea
|
|
|
9789
11114
|
}
|
|
9790
11115
|
},
|
|
9791
11116
|
on_error: (err) => {
|
|
9792
|
-
console.error(
|
|
11117
|
+
console.error(chalk2.red(`Poll error: ${err.message}`));
|
|
9793
11118
|
}
|
|
9794
11119
|
});
|
|
9795
11120
|
const cleanup = () => {
|
|
9796
11121
|
handle.stop();
|
|
9797
11122
|
console.log();
|
|
9798
|
-
console.log(
|
|
11123
|
+
console.log(chalk2.dim("Stopped watching."));
|
|
9799
11124
|
process.exit(0);
|
|
9800
11125
|
};
|
|
9801
11126
|
process.on("SIGINT", cleanup);
|
|
@@ -9812,7 +11137,7 @@ program2.command("pin <keyOrId>").description("Pin a memory by key or partial ID
|
|
|
9812
11137
|
if (globalOpts.json) {
|
|
9813
11138
|
outputJson({ error: `No memory found: ${keyOrId}` });
|
|
9814
11139
|
} else {
|
|
9815
|
-
console.error(
|
|
11140
|
+
console.error(chalk2.red(`No memory found: ${keyOrId}`));
|
|
9816
11141
|
}
|
|
9817
11142
|
process.exit(1);
|
|
9818
11143
|
}
|
|
@@ -9823,7 +11148,7 @@ program2.command("pin <keyOrId>").description("Pin a memory by key or partial ID
|
|
|
9823
11148
|
if (globalOpts.json) {
|
|
9824
11149
|
outputJson(updated);
|
|
9825
11150
|
} else {
|
|
9826
|
-
console.log(
|
|
11151
|
+
console.log(chalk2.green(`Pinned: ${updated.key} (${updated.id.slice(0, 8)})`));
|
|
9827
11152
|
}
|
|
9828
11153
|
} catch (e) {
|
|
9829
11154
|
handleError(e);
|
|
@@ -9837,7 +11162,7 @@ program2.command("unpin <keyOrId>").description("Unpin a memory by key or partia
|
|
|
9837
11162
|
if (globalOpts.json) {
|
|
9838
11163
|
outputJson({ error: `No memory found: ${keyOrId}` });
|
|
9839
11164
|
} else {
|
|
9840
|
-
console.error(
|
|
11165
|
+
console.error(chalk2.red(`No memory found: ${keyOrId}`));
|
|
9841
11166
|
}
|
|
9842
11167
|
process.exit(1);
|
|
9843
11168
|
}
|
|
@@ -9848,7 +11173,7 @@ program2.command("unpin <keyOrId>").description("Unpin a memory by key or partia
|
|
|
9848
11173
|
if (globalOpts.json) {
|
|
9849
11174
|
outputJson(updated);
|
|
9850
11175
|
} else {
|
|
9851
|
-
console.log(
|
|
11176
|
+
console.log(chalk2.green(`Unpinned: ${updated.key} (${updated.id.slice(0, 8)})`));
|
|
9852
11177
|
}
|
|
9853
11178
|
} catch (e) {
|
|
9854
11179
|
handleError(e);
|
|
@@ -9859,17 +11184,17 @@ program2.command("archive <keyOrId>").description("Archive a memory by key or ID
|
|
|
9859
11184
|
const globalOpts = program2.opts();
|
|
9860
11185
|
let memory = getMemory(resolvePartialId(getDatabase(), "memories", keyOrId) || keyOrId) || getMemoryByKey(keyOrId, opts.scope, globalOpts.agent);
|
|
9861
11186
|
if (!memory) {
|
|
9862
|
-
console.error(
|
|
11187
|
+
console.error(chalk2.red(`No memory found: ${keyOrId}`));
|
|
9863
11188
|
process.exit(1);
|
|
9864
11189
|
}
|
|
9865
11190
|
updateMemory(memory.id, { status: "archived", version: memory.version });
|
|
9866
11191
|
if (globalOpts.json) {
|
|
9867
11192
|
outputJson({ archived: true, id: memory.id, key: memory.key });
|
|
9868
11193
|
} else {
|
|
9869
|
-
console.log(
|
|
11194
|
+
console.log(chalk2.green(`\u2713 Archived: ${chalk2.bold(memory.key)} (${memory.id.slice(0, 8)})`));
|
|
9870
11195
|
}
|
|
9871
11196
|
} catch (e) {
|
|
9872
|
-
console.error(
|
|
11197
|
+
console.error(chalk2.red(`archive failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
9873
11198
|
process.exit(1);
|
|
9874
11199
|
}
|
|
9875
11200
|
});
|
|
@@ -9878,7 +11203,7 @@ program2.command("versions <keyOrId>").description("Show version history for a m
|
|
|
9878
11203
|
const globalOpts = program2.opts();
|
|
9879
11204
|
let memory = getMemory(resolvePartialId(getDatabase(), "memories", keyOrId) || keyOrId) || getMemoryByKey(keyOrId, opts.scope, globalOpts.agent);
|
|
9880
11205
|
if (!memory) {
|
|
9881
|
-
console.error(
|
|
11206
|
+
console.error(chalk2.red(`No memory found: ${keyOrId}`));
|
|
9882
11207
|
process.exit(1);
|
|
9883
11208
|
}
|
|
9884
11209
|
const versions = getMemoryVersions(memory.id);
|
|
@@ -9886,20 +11211,20 @@ program2.command("versions <keyOrId>").description("Show version history for a m
|
|
|
9886
11211
|
outputJson({ memory: { id: memory.id, key: memory.key, current_version: memory.version }, versions });
|
|
9887
11212
|
return;
|
|
9888
11213
|
}
|
|
9889
|
-
console.log(
|
|
11214
|
+
console.log(chalk2.bold(`
|
|
9890
11215
|
Version history: ${memory.key} (current: v${memory.version})
|
|
9891
11216
|
`));
|
|
9892
11217
|
if (versions.length === 0) {
|
|
9893
|
-
console.log(
|
|
11218
|
+
console.log(chalk2.dim(" No previous versions."));
|
|
9894
11219
|
return;
|
|
9895
11220
|
}
|
|
9896
11221
|
for (const v of versions) {
|
|
9897
|
-
console.log(` ${
|
|
11222
|
+
console.log(` ${chalk2.cyan(`v${v.version}`)} ${chalk2.dim(v.created_at.slice(0, 16))} scope=${v.scope} imp=${v.importance}`);
|
|
9898
11223
|
console.log(` ${v.value.slice(0, 120)}${v.value.length > 120 ? "..." : ""}`);
|
|
9899
11224
|
}
|
|
9900
11225
|
console.log("");
|
|
9901
11226
|
} catch (e) {
|
|
9902
|
-
console.error(
|
|
11227
|
+
console.error(chalk2.red(`versions failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
9903
11228
|
process.exit(1);
|
|
9904
11229
|
}
|
|
9905
11230
|
});
|
|
@@ -9919,20 +11244,20 @@ program2.command("tail").description("Watch for new/updated memories in real-tim
|
|
|
9919
11244
|
const notifyEnabled = !!opts.notify;
|
|
9920
11245
|
const startTime = new Date().toISOString();
|
|
9921
11246
|
if (!jsonMode) {
|
|
9922
|
-
console.log(
|
|
11247
|
+
console.log(chalk2.bold.cyan("Watching for memory changes...") + chalk2.dim(" (Ctrl+C to stop)"));
|
|
9923
11248
|
const filters = [];
|
|
9924
11249
|
if (opts.scope)
|
|
9925
11250
|
filters.push(`scope=${colorScope(opts.scope)}`);
|
|
9926
11251
|
if (opts.category)
|
|
9927
11252
|
filters.push(`category=${colorCategory(opts.category)}`);
|
|
9928
11253
|
if (agentId)
|
|
9929
|
-
filters.push(`agent=${
|
|
11254
|
+
filters.push(`agent=${chalk2.dim(agentId)}`);
|
|
9930
11255
|
if (projectId)
|
|
9931
|
-
filters.push(`project=${
|
|
11256
|
+
filters.push(`project=${chalk2.dim(projectId)}`);
|
|
9932
11257
|
if (filters.length > 0) {
|
|
9933
|
-
console.log(
|
|
11258
|
+
console.log(chalk2.dim("Filters: ") + filters.join(chalk2.dim(" | ")));
|
|
9934
11259
|
}
|
|
9935
|
-
console.log(
|
|
11260
|
+
console.log(chalk2.dim(`Poll interval: ${intervalMs}ms`));
|
|
9936
11261
|
console.log();
|
|
9937
11262
|
}
|
|
9938
11263
|
const { startPolling: startPolling2 } = (init_poll(), __toCommonJS(exports_poll));
|
|
@@ -9948,7 +11273,7 @@ program2.command("tail").description("Watch for new/updated memories in real-tim
|
|
|
9948
11273
|
if (jsonMode) {
|
|
9949
11274
|
console.log(JSON.stringify({ event: isNew ? "new" : "updated", memory: m }));
|
|
9950
11275
|
} else {
|
|
9951
|
-
const prefix = isNew ?
|
|
11276
|
+
const prefix = isNew ? chalk2.green.bold("+ ") : chalk2.yellow.bold("~ ");
|
|
9952
11277
|
console.log(prefix + formatWatchLine(m));
|
|
9953
11278
|
}
|
|
9954
11279
|
if (notifyEnabled)
|
|
@@ -9959,7 +11284,7 @@ program2.command("tail").description("Watch for new/updated memories in real-tim
|
|
|
9959
11284
|
if (jsonMode) {
|
|
9960
11285
|
console.error(JSON.stringify({ event: "error", message: err.message }));
|
|
9961
11286
|
} else {
|
|
9962
|
-
console.error(
|
|
11287
|
+
console.error(chalk2.red(`Poll error: ${err.message}`));
|
|
9963
11288
|
}
|
|
9964
11289
|
}
|
|
9965
11290
|
});
|
|
@@ -9967,7 +11292,7 @@ program2.command("tail").description("Watch for new/updated memories in real-tim
|
|
|
9967
11292
|
handle.stop();
|
|
9968
11293
|
if (!jsonMode) {
|
|
9969
11294
|
console.log();
|
|
9970
|
-
console.log(
|
|
11295
|
+
console.log(chalk2.dim("Stopped watching."));
|
|
9971
11296
|
}
|
|
9972
11297
|
process.exit(0);
|
|
9973
11298
|
};
|
|
@@ -10059,7 +11384,7 @@ ${lines.join(`
|
|
|
10059
11384
|
}
|
|
10060
11385
|
if (lines.length === 0) {
|
|
10061
11386
|
if (process.stdout.isTTY) {
|
|
10062
|
-
console.log(
|
|
11387
|
+
console.log(chalk2.yellow("No relevant memories found."));
|
|
10063
11388
|
}
|
|
10064
11389
|
return;
|
|
10065
11390
|
}
|
|
@@ -10075,18 +11400,18 @@ program2.command("backup [path]").description("Backup the SQLite database to a f
|
|
|
10075
11400
|
try {
|
|
10076
11401
|
const globalOpts = program2.opts();
|
|
10077
11402
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
10078
|
-
const backupsDir =
|
|
11403
|
+
const backupsDir = join6(home, ".mementos", "backups");
|
|
10079
11404
|
if (opts.list) {
|
|
10080
|
-
if (!
|
|
11405
|
+
if (!existsSync6(backupsDir)) {
|
|
10081
11406
|
if (globalOpts.json) {
|
|
10082
11407
|
outputJson({ backups: [] });
|
|
10083
11408
|
return;
|
|
10084
11409
|
}
|
|
10085
|
-
console.log(
|
|
11410
|
+
console.log(chalk2.yellow("No backups directory found."));
|
|
10086
11411
|
return;
|
|
10087
11412
|
}
|
|
10088
|
-
const files =
|
|
10089
|
-
const filePath =
|
|
11413
|
+
const files = readdirSync3(backupsDir).filter((f) => f.endsWith(".db")).map((f) => {
|
|
11414
|
+
const filePath = join6(backupsDir, f);
|
|
10090
11415
|
const st2 = statSync(filePath);
|
|
10091
11416
|
return { name: f, path: filePath, size: st2.size, mtime: st2.mtime };
|
|
10092
11417
|
}).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
@@ -10095,7 +11420,7 @@ program2.command("backup [path]").description("Backup the SQLite database to a f
|
|
|
10095
11420
|
outputJson({ backups: [] });
|
|
10096
11421
|
return;
|
|
10097
11422
|
}
|
|
10098
|
-
console.log(
|
|
11423
|
+
console.log(chalk2.yellow("No backups found."));
|
|
10099
11424
|
return;
|
|
10100
11425
|
}
|
|
10101
11426
|
if (globalOpts.json) {
|
|
@@ -10109,34 +11434,34 @@ program2.command("backup [path]").description("Backup the SQLite database to a f
|
|
|
10109
11434
|
});
|
|
10110
11435
|
return;
|
|
10111
11436
|
}
|
|
10112
|
-
console.log(
|
|
11437
|
+
console.log(chalk2.bold(`Backups in ${backupsDir}:`));
|
|
10113
11438
|
for (const f of files) {
|
|
10114
11439
|
const date = f.mtime.toISOString().replace("T", " ").slice(0, 19);
|
|
10115
11440
|
const sizeMB2 = (f.size / (1024 * 1024)).toFixed(1);
|
|
10116
11441
|
const sizeStr2 = f.size >= 1024 * 1024 ? `${sizeMB2} MB` : `${(f.size / 1024).toFixed(1)} KB`;
|
|
10117
|
-
console.log(` ${
|
|
11442
|
+
console.log(` ${chalk2.dim(date)} ${chalk2.cyan(sizeStr2.padStart(8))} ${f.name}`);
|
|
10118
11443
|
}
|
|
10119
11444
|
return;
|
|
10120
11445
|
}
|
|
10121
11446
|
const dbPath = getDbPath();
|
|
10122
|
-
if (!
|
|
10123
|
-
console.error(
|
|
11447
|
+
if (!existsSync6(dbPath)) {
|
|
11448
|
+
console.error(chalk2.red(`Database not found at ${dbPath}`));
|
|
10124
11449
|
process.exit(1);
|
|
10125
11450
|
}
|
|
10126
11451
|
let dest;
|
|
10127
11452
|
if (targetPath) {
|
|
10128
11453
|
dest = resolve3(targetPath);
|
|
10129
11454
|
} else {
|
|
10130
|
-
if (!
|
|
10131
|
-
|
|
11455
|
+
if (!existsSync6(backupsDir)) {
|
|
11456
|
+
mkdirSync6(backupsDir, { recursive: true });
|
|
10132
11457
|
}
|
|
10133
|
-
const
|
|
10134
|
-
const ts =
|
|
10135
|
-
dest =
|
|
11458
|
+
const now3 = new Date;
|
|
11459
|
+
const ts = now3.toISOString().replace(/[-:T]/g, "").replace(/\..+/, "").slice(0, 15);
|
|
11460
|
+
dest = join6(backupsDir, `mementos-${ts}.db`);
|
|
10136
11461
|
}
|
|
10137
|
-
const destDir =
|
|
10138
|
-
if (!
|
|
10139
|
-
|
|
11462
|
+
const destDir = dirname4(dest);
|
|
11463
|
+
if (!existsSync6(destDir)) {
|
|
11464
|
+
mkdirSync6(destDir, { recursive: true });
|
|
10140
11465
|
}
|
|
10141
11466
|
copyFileSync(dbPath, dest);
|
|
10142
11467
|
const st = statSync(dest);
|
|
@@ -10146,7 +11471,7 @@ program2.command("backup [path]").description("Backup the SQLite database to a f
|
|
|
10146
11471
|
outputJson({ backed_up_to: dest, size: st.size, source: dbPath });
|
|
10147
11472
|
return;
|
|
10148
11473
|
}
|
|
10149
|
-
console.log(`Backed up to: ${
|
|
11474
|
+
console.log(`Backed up to: ${chalk2.green(dest)} (size: ${sizeStr})`);
|
|
10150
11475
|
} catch (e) {
|
|
10151
11476
|
handleError(e);
|
|
10152
11477
|
}
|
|
@@ -10155,31 +11480,31 @@ program2.command("restore [file]").description("Restore the database from a back
|
|
|
10155
11480
|
try {
|
|
10156
11481
|
const globalOpts = program2.opts();
|
|
10157
11482
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
10158
|
-
const backupsDir =
|
|
11483
|
+
const backupsDir = join6(home, ".mementos", "backups");
|
|
10159
11484
|
let source;
|
|
10160
11485
|
if (opts.latest) {
|
|
10161
|
-
if (!
|
|
10162
|
-
console.error(
|
|
11486
|
+
if (!existsSync6(backupsDir)) {
|
|
11487
|
+
console.error(chalk2.red("No backups directory found."));
|
|
10163
11488
|
process.exit(1);
|
|
10164
11489
|
}
|
|
10165
|
-
const files =
|
|
10166
|
-
const fp =
|
|
11490
|
+
const files = readdirSync3(backupsDir).filter((f) => f.endsWith(".db")).map((f) => {
|
|
11491
|
+
const fp = join6(backupsDir, f);
|
|
10167
11492
|
const st = statSync(fp);
|
|
10168
11493
|
return { path: fp, mtime: st.mtime };
|
|
10169
11494
|
}).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
10170
11495
|
if (files.length === 0) {
|
|
10171
|
-
console.error(
|
|
11496
|
+
console.error(chalk2.red("No backups found in " + backupsDir));
|
|
10172
11497
|
process.exit(1);
|
|
10173
11498
|
}
|
|
10174
11499
|
source = files[0].path;
|
|
10175
11500
|
} else if (filePath) {
|
|
10176
11501
|
source = resolve3(filePath);
|
|
10177
11502
|
} else {
|
|
10178
|
-
console.error(
|
|
11503
|
+
console.error(chalk2.red("Provide a backup file path or use --latest"));
|
|
10179
11504
|
process.exit(1);
|
|
10180
11505
|
}
|
|
10181
|
-
if (!
|
|
10182
|
-
console.error(
|
|
11506
|
+
if (!existsSync6(source)) {
|
|
11507
|
+
console.error(chalk2.red(`Backup file not found: ${source}`));
|
|
10183
11508
|
process.exit(1);
|
|
10184
11509
|
}
|
|
10185
11510
|
const dbPath = getDbPath();
|
|
@@ -10187,7 +11512,7 @@ program2.command("restore [file]").description("Restore the database from a back
|
|
|
10187
11512
|
const backupSizeMB = (backupStat.size / (1024 * 1024)).toFixed(1);
|
|
10188
11513
|
const backupSizeStr = backupStat.size >= 1024 * 1024 ? `${backupSizeMB} MB` : `${(backupStat.size / 1024).toFixed(1)} KB`;
|
|
10189
11514
|
let currentCount = 0;
|
|
10190
|
-
if (
|
|
11515
|
+
if (existsSync6(dbPath)) {
|
|
10191
11516
|
try {
|
|
10192
11517
|
const db = getDatabase();
|
|
10193
11518
|
const row = db.query("SELECT COUNT(*) as count FROM memories").get();
|
|
@@ -10196,8 +11521,8 @@ program2.command("restore [file]").description("Restore the database from a back
|
|
|
10196
11521
|
}
|
|
10197
11522
|
let backupCount = 0;
|
|
10198
11523
|
try {
|
|
10199
|
-
const { Database:
|
|
10200
|
-
const backupDb = new
|
|
11524
|
+
const { Database: Database3 } = __require("bun:sqlite");
|
|
11525
|
+
const backupDb = new Database3(source, { readonly: true });
|
|
10201
11526
|
const row = backupDb.query("SELECT COUNT(*) as count FROM memories").get();
|
|
10202
11527
|
backupCount = row?.count ?? 0;
|
|
10203
11528
|
backupDb.close();
|
|
@@ -10216,13 +11541,13 @@ program2.command("restore [file]").description("Restore the database from a back
|
|
|
10216
11541
|
});
|
|
10217
11542
|
return;
|
|
10218
11543
|
}
|
|
10219
|
-
console.log(
|
|
10220
|
-
console.log(` Source: ${
|
|
10221
|
-
console.log(` Target: ${
|
|
10222
|
-
console.log(` Current memories: ${
|
|
10223
|
-
console.log(` Backup memories: ${
|
|
11544
|
+
console.log(chalk2.bold("Restore preview:"));
|
|
11545
|
+
console.log(` Source: ${chalk2.cyan(source)} (${backupSizeStr})`);
|
|
11546
|
+
console.log(` Target: ${chalk2.cyan(dbPath)}`);
|
|
11547
|
+
console.log(` Current memories: ${chalk2.yellow(String(currentCount))}`);
|
|
11548
|
+
console.log(` Backup memories: ${chalk2.green(String(backupCount))}`);
|
|
10224
11549
|
console.log();
|
|
10225
|
-
console.log(
|
|
11550
|
+
console.log(chalk2.yellow("Use --force to confirm restore"));
|
|
10226
11551
|
return;
|
|
10227
11552
|
}
|
|
10228
11553
|
copyFileSync(source, dbPath);
|
|
@@ -10245,9 +11570,9 @@ program2.command("restore [file]").description("Restore the database from a back
|
|
|
10245
11570
|
});
|
|
10246
11571
|
return;
|
|
10247
11572
|
}
|
|
10248
|
-
console.log(`Restored from: ${
|
|
10249
|
-
console.log(` Previous memories: ${
|
|
10250
|
-
console.log(` Restored memories: ${
|
|
11573
|
+
console.log(`Restored from: ${chalk2.green(source)}`);
|
|
11574
|
+
console.log(` Previous memories: ${chalk2.yellow(String(currentCount))}`);
|
|
11575
|
+
console.log(` Restored memories: ${chalk2.green(String(newCount))}`);
|
|
10251
11576
|
} catch (e) {
|
|
10252
11577
|
handleError(e);
|
|
10253
11578
|
}
|
|
@@ -10260,7 +11585,7 @@ program2.command("diff <id>").description("Show diff between memory versions").o
|
|
|
10260
11585
|
if (!memoryId) {
|
|
10261
11586
|
const mem = resolveKeyOrId(idArg, {}, globalOpts);
|
|
10262
11587
|
if (!mem) {
|
|
10263
|
-
console.error(
|
|
11588
|
+
console.error(chalk2.red(`Memory not found: ${idArg}`));
|
|
10264
11589
|
process.exit(1);
|
|
10265
11590
|
}
|
|
10266
11591
|
memoryId = mem.id;
|
|
@@ -10273,7 +11598,7 @@ program2.command("diff <id>").description("Show diff between memory versions").o
|
|
|
10273
11598
|
function diffMemory(memoryId, opts, globalOpts) {
|
|
10274
11599
|
const current = getMemory(memoryId);
|
|
10275
11600
|
if (!current) {
|
|
10276
|
-
console.error(
|
|
11601
|
+
console.error(chalk2.red(`Memory not found: ${memoryId}`));
|
|
10277
11602
|
process.exit(1);
|
|
10278
11603
|
}
|
|
10279
11604
|
const versions = getMemoryVersions(memoryId);
|
|
@@ -10281,8 +11606,8 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10281
11606
|
if (globalOpts.json) {
|
|
10282
11607
|
outputJson({ error: "No version history available", memory_id: memoryId });
|
|
10283
11608
|
} else {
|
|
10284
|
-
console.log(
|
|
10285
|
-
console.log(
|
|
11609
|
+
console.log(chalk2.yellow("No version history available."));
|
|
11610
|
+
console.log(chalk2.dim(`Memory "${current.key}" is at version ${current.version} but has no prior snapshots.`));
|
|
10286
11611
|
}
|
|
10287
11612
|
return;
|
|
10288
11613
|
}
|
|
@@ -10293,13 +11618,13 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10293
11618
|
if (opts.version) {
|
|
10294
11619
|
const targetVersion = parseInt(opts.version, 10);
|
|
10295
11620
|
if (isNaN(targetVersion) || targetVersion < 1) {
|
|
10296
|
-
console.error(
|
|
11621
|
+
console.error(chalk2.red("Version must be a positive integer."));
|
|
10297
11622
|
process.exit(1);
|
|
10298
11623
|
}
|
|
10299
11624
|
if (targetVersion === current.version) {
|
|
10300
11625
|
const prev = versions.find((v) => v.version === targetVersion - 1);
|
|
10301
11626
|
if (!prev) {
|
|
10302
|
-
console.error(
|
|
11627
|
+
console.error(chalk2.red(`No version ${targetVersion - 1} found to compare against.`));
|
|
10303
11628
|
process.exit(1);
|
|
10304
11629
|
}
|
|
10305
11630
|
older = prev;
|
|
@@ -10315,10 +11640,10 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10315
11640
|
olderVersion = prevSnap.version;
|
|
10316
11641
|
newerVersion = targetSnap.version;
|
|
10317
11642
|
} else if (targetSnap && !prevSnap) {
|
|
10318
|
-
console.error(
|
|
11643
|
+
console.error(chalk2.red(`No version ${targetVersion - 1} found to compare against.`));
|
|
10319
11644
|
process.exit(1);
|
|
10320
11645
|
} else {
|
|
10321
|
-
console.error(
|
|
11646
|
+
console.error(chalk2.red(`Version ${targetVersion} not found.`));
|
|
10322
11647
|
process.exit(1);
|
|
10323
11648
|
}
|
|
10324
11649
|
}
|
|
@@ -10357,8 +11682,8 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10357
11682
|
});
|
|
10358
11683
|
return;
|
|
10359
11684
|
}
|
|
10360
|
-
console.log(
|
|
10361
|
-
console.log(
|
|
11685
|
+
console.log(chalk2.bold(`Diff for "${current.key}" (${memoryId.slice(0, 8)})`));
|
|
11686
|
+
console.log(chalk2.dim(`Version ${olderVersion} \u2192 ${newerVersion}`));
|
|
10362
11687
|
console.log();
|
|
10363
11688
|
let hasChanges = false;
|
|
10364
11689
|
const n = newer;
|
|
@@ -10366,7 +11691,7 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10366
11691
|
const newValue = n.value;
|
|
10367
11692
|
if (oldValue !== newValue) {
|
|
10368
11693
|
hasChanges = true;
|
|
10369
|
-
console.log(
|
|
11694
|
+
console.log(chalk2.bold(" value:"));
|
|
10370
11695
|
diffLines(oldValue, newValue);
|
|
10371
11696
|
}
|
|
10372
11697
|
const scalarFields = [
|
|
@@ -10380,7 +11705,7 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10380
11705
|
for (const field of scalarFields) {
|
|
10381
11706
|
if (String(field.oldVal) !== String(field.newVal)) {
|
|
10382
11707
|
hasChanges = true;
|
|
10383
|
-
console.log(` ${
|
|
11708
|
+
console.log(` ${chalk2.bold(field.name + ":")} ${chalk2.red(String(field.oldVal))} \u2192 ${chalk2.green(String(field.newVal))}`);
|
|
10384
11709
|
}
|
|
10385
11710
|
}
|
|
10386
11711
|
const oldTags = older.tags;
|
|
@@ -10389,14 +11714,14 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10389
11714
|
hasChanges = true;
|
|
10390
11715
|
const removed = oldTags.filter((t) => !newTags.includes(t));
|
|
10391
11716
|
const added = newTags.filter((t) => !oldTags.includes(t));
|
|
10392
|
-
console.log(` ${
|
|
11717
|
+
console.log(` ${chalk2.bold("tags:")}`);
|
|
10393
11718
|
for (const t of removed)
|
|
10394
|
-
console.log(
|
|
11719
|
+
console.log(chalk2.red(` - ${t}`));
|
|
10395
11720
|
for (const t of added)
|
|
10396
|
-
console.log(
|
|
11721
|
+
console.log(chalk2.green(` + ${t}`));
|
|
10397
11722
|
}
|
|
10398
11723
|
if (!hasChanges) {
|
|
10399
|
-
console.log(
|
|
11724
|
+
console.log(chalk2.dim(" No changes between these versions."));
|
|
10400
11725
|
}
|
|
10401
11726
|
}
|
|
10402
11727
|
function diffLines(oldText, newText) {
|
|
@@ -10405,22 +11730,22 @@ function diffLines(oldText, newText) {
|
|
|
10405
11730
|
const newLines = newText.split(`
|
|
10406
11731
|
`);
|
|
10407
11732
|
if (oldLines.length === 1 && newLines.length === 1) {
|
|
10408
|
-
console.log(
|
|
10409
|
-
console.log(
|
|
11733
|
+
console.log(chalk2.red(` - ${oldLines[0]}`));
|
|
11734
|
+
console.log(chalk2.green(` + ${newLines[0]}`));
|
|
10410
11735
|
return;
|
|
10411
11736
|
}
|
|
10412
11737
|
const oldSet = new Set(oldLines);
|
|
10413
11738
|
const newSet = new Set(newLines);
|
|
10414
11739
|
for (const line of oldLines) {
|
|
10415
11740
|
if (!newSet.has(line)) {
|
|
10416
|
-
console.log(
|
|
11741
|
+
console.log(chalk2.red(` - ${line}`));
|
|
10417
11742
|
} else {
|
|
10418
|
-
console.log(
|
|
11743
|
+
console.log(chalk2.dim(` ${line}`));
|
|
10419
11744
|
}
|
|
10420
11745
|
}
|
|
10421
11746
|
for (const line of newLines) {
|
|
10422
11747
|
if (!oldSet.has(line)) {
|
|
10423
|
-
console.log(
|
|
11748
|
+
console.log(chalk2.green(` + ${line}`));
|
|
10424
11749
|
}
|
|
10425
11750
|
}
|
|
10426
11751
|
}
|
|
@@ -10576,24 +11901,24 @@ function validateConfigKeyValue(key, value) {
|
|
|
10576
11901
|
return null;
|
|
10577
11902
|
}
|
|
10578
11903
|
function getConfigPath() {
|
|
10579
|
-
return
|
|
11904
|
+
return join6(homedir4(), ".mementos", "config.json");
|
|
10580
11905
|
}
|
|
10581
11906
|
function readFileConfig() {
|
|
10582
11907
|
const configPath = getConfigPath();
|
|
10583
|
-
if (!
|
|
11908
|
+
if (!existsSync6(configPath))
|
|
10584
11909
|
return {};
|
|
10585
11910
|
try {
|
|
10586
|
-
return JSON.parse(
|
|
11911
|
+
return JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
10587
11912
|
} catch {
|
|
10588
11913
|
return {};
|
|
10589
11914
|
}
|
|
10590
11915
|
}
|
|
10591
11916
|
function writeFileConfig(data) {
|
|
10592
11917
|
const configPath = getConfigPath();
|
|
10593
|
-
const dir =
|
|
10594
|
-
if (!
|
|
10595
|
-
|
|
10596
|
-
|
|
11918
|
+
const dir = dirname4(configPath);
|
|
11919
|
+
if (!existsSync6(dir))
|
|
11920
|
+
mkdirSync6(dir, { recursive: true });
|
|
11921
|
+
writeFileSync4(configPath, JSON.stringify(data, null, 2) + `
|
|
10597
11922
|
`, "utf-8");
|
|
10598
11923
|
}
|
|
10599
11924
|
program2.command("config [subcommand] [args...]").description("View or modify configuration. Subcommands: get <key>, set <key> <value>, reset [key], path").action((subcommand, args) => {
|
|
@@ -10621,13 +11946,13 @@ program2.command("config [subcommand] [args...]").description("View or modify co
|
|
|
10621
11946
|
if (subcommand === "get") {
|
|
10622
11947
|
const key = args[0];
|
|
10623
11948
|
if (!key) {
|
|
10624
|
-
console.error(
|
|
11949
|
+
console.error(chalk2.red("Usage: mementos config get <key>"));
|
|
10625
11950
|
process.exit(1);
|
|
10626
11951
|
}
|
|
10627
11952
|
const config = loadConfig();
|
|
10628
11953
|
const value = getNestedValue(config, key);
|
|
10629
11954
|
if (value === undefined) {
|
|
10630
|
-
console.error(
|
|
11955
|
+
console.error(chalk2.red(`Unknown config key: ${key}`));
|
|
10631
11956
|
process.exit(1);
|
|
10632
11957
|
}
|
|
10633
11958
|
if (useJson) {
|
|
@@ -10643,13 +11968,13 @@ program2.command("config [subcommand] [args...]").description("View or modify co
|
|
|
10643
11968
|
const key = args[0];
|
|
10644
11969
|
const rawValue = args[1];
|
|
10645
11970
|
if (!key || rawValue === undefined) {
|
|
10646
|
-
console.error(
|
|
11971
|
+
console.error(chalk2.red("Usage: mementos config set <key> <value>"));
|
|
10647
11972
|
process.exit(1);
|
|
10648
11973
|
}
|
|
10649
11974
|
const value = parseConfigValue(rawValue);
|
|
10650
11975
|
const err = validateConfigKeyValue(key, value);
|
|
10651
11976
|
if (err) {
|
|
10652
|
-
console.error(
|
|
11977
|
+
console.error(chalk2.red(err));
|
|
10653
11978
|
process.exit(1);
|
|
10654
11979
|
}
|
|
10655
11980
|
const fileConfig = readFileConfig();
|
|
@@ -10658,7 +11983,7 @@ program2.command("config [subcommand] [args...]").description("View or modify co
|
|
|
10658
11983
|
if (useJson) {
|
|
10659
11984
|
outputJson({ key, value, saved: true });
|
|
10660
11985
|
} else {
|
|
10661
|
-
console.log(
|
|
11986
|
+
console.log(chalk2.green(`Set ${key} = ${JSON.stringify(value)}`));
|
|
10662
11987
|
}
|
|
10663
11988
|
return;
|
|
10664
11989
|
}
|
|
@@ -10667,7 +11992,7 @@ program2.command("config [subcommand] [args...]").description("View or modify co
|
|
|
10667
11992
|
if (key) {
|
|
10668
11993
|
const defaultVal = getNestedValue(DEFAULT_CONFIG, key);
|
|
10669
11994
|
if (defaultVal === undefined) {
|
|
10670
|
-
console.error(
|
|
11995
|
+
console.error(chalk2.red(`Unknown config key: ${key}`));
|
|
10671
11996
|
process.exit(1);
|
|
10672
11997
|
}
|
|
10673
11998
|
const fileConfig = readFileConfig();
|
|
@@ -10676,22 +12001,22 @@ program2.command("config [subcommand] [args...]").description("View or modify co
|
|
|
10676
12001
|
if (useJson) {
|
|
10677
12002
|
outputJson({ key, reset: true, default_value: defaultVal });
|
|
10678
12003
|
} else {
|
|
10679
|
-
console.log(
|
|
12004
|
+
console.log(chalk2.green(`Reset ${key} to default (${JSON.stringify(defaultVal)})`));
|
|
10680
12005
|
}
|
|
10681
12006
|
} else {
|
|
10682
12007
|
const configPath = getConfigPath();
|
|
10683
|
-
if (
|
|
12008
|
+
if (existsSync6(configPath)) {
|
|
10684
12009
|
unlinkSync2(configPath);
|
|
10685
12010
|
}
|
|
10686
12011
|
if (useJson) {
|
|
10687
12012
|
outputJson({ reset: true, all: true });
|
|
10688
12013
|
} else {
|
|
10689
|
-
console.log(
|
|
12014
|
+
console.log(chalk2.green("Config reset to defaults (file removed)"));
|
|
10690
12015
|
}
|
|
10691
12016
|
}
|
|
10692
12017
|
return;
|
|
10693
12018
|
}
|
|
10694
|
-
console.error(
|
|
12019
|
+
console.error(chalk2.red(`Unknown config subcommand: ${subcommand}`));
|
|
10695
12020
|
console.error("Usage: mementos config [get|set|reset|path]");
|
|
10696
12021
|
process.exit(1);
|
|
10697
12022
|
} catch (e) {
|
|
@@ -10718,7 +12043,7 @@ entityCmd.command("create <name>").description("Create a knowledge graph entity"
|
|
|
10718
12043
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10719
12044
|
outputJson(entity);
|
|
10720
12045
|
} else {
|
|
10721
|
-
console.log(
|
|
12046
|
+
console.log(chalk2.green(`Entity: ${entity.name} (${entity.id.slice(0, 8)})`));
|
|
10722
12047
|
}
|
|
10723
12048
|
} catch (e) {
|
|
10724
12049
|
handleError(e);
|
|
@@ -10734,28 +12059,28 @@ entityCmd.command("show <nameOrId>").description("Show entity details with relat
|
|
|
10734
12059
|
outputJson({ ...entity, related, memories });
|
|
10735
12060
|
return;
|
|
10736
12061
|
}
|
|
10737
|
-
console.log(`${
|
|
10738
|
-
console.log(`${
|
|
10739
|
-
console.log(`${
|
|
12062
|
+
console.log(`${chalk2.bold("ID:")} ${entity.id}`);
|
|
12063
|
+
console.log(`${chalk2.bold("Name:")} ${entity.name}`);
|
|
12064
|
+
console.log(`${chalk2.bold("Type:")} ${colorEntityType(entity.type)}`);
|
|
10740
12065
|
if (entity.description)
|
|
10741
|
-
console.log(`${
|
|
12066
|
+
console.log(`${chalk2.bold("Description:")} ${entity.description}`);
|
|
10742
12067
|
if (entity.project_id)
|
|
10743
|
-
console.log(`${
|
|
10744
|
-
console.log(`${
|
|
10745
|
-
console.log(`${
|
|
12068
|
+
console.log(`${chalk2.bold("Project:")} ${entity.project_id}`);
|
|
12069
|
+
console.log(`${chalk2.bold("Created:")} ${entity.created_at}`);
|
|
12070
|
+
console.log(`${chalk2.bold("Updated:")} ${entity.updated_at}`);
|
|
10746
12071
|
if (related.length > 0) {
|
|
10747
12072
|
console.log(`
|
|
10748
|
-
${
|
|
12073
|
+
${chalk2.bold("Related entities:")}`);
|
|
10749
12074
|
for (const r of related) {
|
|
10750
|
-
console.log(` ${
|
|
12075
|
+
console.log(` ${chalk2.dim(r.id.slice(0, 8))} [${colorEntityType(r.type)}] ${r.name}${r.description ? chalk2.dim(` \u2014 ${r.description}`) : ""}`);
|
|
10751
12076
|
}
|
|
10752
12077
|
}
|
|
10753
12078
|
if (memories.length > 0) {
|
|
10754
12079
|
console.log(`
|
|
10755
|
-
${
|
|
12080
|
+
${chalk2.bold("Linked memories:")}`);
|
|
10756
12081
|
for (const m of memories) {
|
|
10757
12082
|
const value = m.value.length > 60 ? m.value.slice(0, 60) + "..." : m.value;
|
|
10758
|
-
console.log(` ${
|
|
12083
|
+
console.log(` ${chalk2.dim(m.id.slice(0, 8))} ${chalk2.bold(m.key)} = ${value}`);
|
|
10759
12084
|
}
|
|
10760
12085
|
}
|
|
10761
12086
|
} catch (e) {
|
|
@@ -10796,13 +12121,13 @@ entityCmd.command("list").description("List entities with optional filters").opt
|
|
|
10796
12121
|
return;
|
|
10797
12122
|
}
|
|
10798
12123
|
if (entities.length === 0) {
|
|
10799
|
-
console.log(
|
|
12124
|
+
console.log(chalk2.yellow("No entities found."));
|
|
10800
12125
|
return;
|
|
10801
12126
|
}
|
|
10802
|
-
console.log(
|
|
12127
|
+
console.log(chalk2.bold(`${entities.length} entit${entities.length === 1 ? "y" : "ies"}:`));
|
|
10803
12128
|
for (const e of entities) {
|
|
10804
|
-
const id =
|
|
10805
|
-
const desc = e.description ?
|
|
12129
|
+
const id = chalk2.dim(e.id.slice(0, 8));
|
|
12130
|
+
const desc = e.description ? chalk2.dim(` (${e.description})`) : "";
|
|
10806
12131
|
console.log(`${id} ${colorEntityType(e.type)} ${e.name}${desc}`);
|
|
10807
12132
|
}
|
|
10808
12133
|
} catch (e) {
|
|
@@ -10817,7 +12142,7 @@ entityCmd.command("delete <nameOrId>").description("Delete an entity and cascade
|
|
|
10817
12142
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10818
12143
|
outputJson({ deleted: entity.id, name: entity.name });
|
|
10819
12144
|
} else {
|
|
10820
|
-
console.log(
|
|
12145
|
+
console.log(chalk2.green(`Deleted entity: ${entity.name} (${entity.id.slice(0, 8)}) \u2014 relations and memory links cascaded`));
|
|
10821
12146
|
}
|
|
10822
12147
|
} catch (e) {
|
|
10823
12148
|
handleError(e);
|
|
@@ -10832,7 +12157,7 @@ entityCmd.command("merge <source> <target>").description("Merge source entity in
|
|
|
10832
12157
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10833
12158
|
outputJson(merged);
|
|
10834
12159
|
} else {
|
|
10835
|
-
console.log(
|
|
12160
|
+
console.log(chalk2.green(`Merged: ${srcEntity.name} \u2192 ${merged.name} (${merged.id.slice(0, 8)})`));
|
|
10836
12161
|
}
|
|
10837
12162
|
} catch (e) {
|
|
10838
12163
|
handleError(e);
|
|
@@ -10844,14 +12169,14 @@ entityCmd.command("link <entity> <memoryKeyOrId>").description("Link an entity t
|
|
|
10844
12169
|
const entity = resolveEntityArg(entityArg, opts.type);
|
|
10845
12170
|
const memory = resolveKeyOrId(memoryKeyOrId, {}, globalOpts);
|
|
10846
12171
|
if (!memory) {
|
|
10847
|
-
console.error(
|
|
12172
|
+
console.error(chalk2.red(`Memory not found: ${memoryKeyOrId}`));
|
|
10848
12173
|
process.exit(1);
|
|
10849
12174
|
}
|
|
10850
12175
|
const link = linkEntityToMemory(entity.id, memory.id, opts.role);
|
|
10851
12176
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10852
12177
|
outputJson(link);
|
|
10853
12178
|
} else {
|
|
10854
|
-
console.log(
|
|
12179
|
+
console.log(chalk2.green(`Linked: ${entity.name} \u2194 ${memory.key} (role: ${link.role})`));
|
|
10855
12180
|
}
|
|
10856
12181
|
} catch (e) {
|
|
10857
12182
|
handleError(e);
|
|
@@ -10872,7 +12197,7 @@ relationCmd.command("create <source> <target>").description("Create a relation b
|
|
|
10872
12197
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10873
12198
|
outputJson(relation);
|
|
10874
12199
|
} else {
|
|
10875
|
-
console.log(
|
|
12200
|
+
console.log(chalk2.green(`Relation: ${srcEntity.name} \u2014[${relation.relation_type}]\u2192 ${tgtEntity.name} (${relation.id.slice(0, 8)})`));
|
|
10876
12201
|
}
|
|
10877
12202
|
} catch (e) {
|
|
10878
12203
|
handleError(e);
|
|
@@ -10903,7 +12228,7 @@ relationCmd.command("list <entityNameOrId>").description("List relations for an
|
|
|
10903
12228
|
return;
|
|
10904
12229
|
}
|
|
10905
12230
|
if (relations.length === 0) {
|
|
10906
|
-
console.log(
|
|
12231
|
+
console.log(chalk2.yellow(`No relations found for: ${entity.name}`));
|
|
10907
12232
|
return;
|
|
10908
12233
|
}
|
|
10909
12234
|
const entityCache = new Map;
|
|
@@ -10919,13 +12244,13 @@ relationCmd.command("list <entityNameOrId>").description("List relations for an
|
|
|
10919
12244
|
return id.slice(0, 8);
|
|
10920
12245
|
}
|
|
10921
12246
|
};
|
|
10922
|
-
console.log(
|
|
12247
|
+
console.log(chalk2.bold(`${relations.length} relation${relations.length === 1 ? "" : "s"} for ${entity.name}:`));
|
|
10923
12248
|
for (const r of relations) {
|
|
10924
12249
|
const src = resolveName(r.source_entity_id);
|
|
10925
12250
|
const tgt = resolveName(r.target_entity_id);
|
|
10926
|
-
const id =
|
|
10927
|
-
const weight = r.weight !== 1 ?
|
|
10928
|
-
console.log(`${id} ${src} \u2014[${
|
|
12251
|
+
const id = chalk2.dim(r.id.slice(0, 8));
|
|
12252
|
+
const weight = r.weight !== 1 ? chalk2.dim(` w:${r.weight}`) : "";
|
|
12253
|
+
console.log(`${id} ${src} \u2014[${chalk2.cyan(r.relation_type)}]\u2192 ${tgt}${weight}`);
|
|
10929
12254
|
}
|
|
10930
12255
|
} catch (e) {
|
|
10931
12256
|
handleError(e);
|
|
@@ -10937,14 +12262,14 @@ relationCmd.command("delete <id>").description("Delete a relation by ID").action
|
|
|
10937
12262
|
const db = getDatabase();
|
|
10938
12263
|
const resolvedId = resolvePartialId(db, "relations", id);
|
|
10939
12264
|
if (!resolvedId) {
|
|
10940
|
-
console.error(
|
|
12265
|
+
console.error(chalk2.red(`Relation not found: ${id}`));
|
|
10941
12266
|
process.exit(1);
|
|
10942
12267
|
}
|
|
10943
12268
|
deleteRelation(resolvedId);
|
|
10944
12269
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10945
12270
|
outputJson({ deleted: resolvedId });
|
|
10946
12271
|
} else {
|
|
10947
|
-
console.log(
|
|
12272
|
+
console.log(chalk2.green(`Deleted relation: ${resolvedId.slice(0, 8)}`));
|
|
10948
12273
|
}
|
|
10949
12274
|
} catch (e) {
|
|
10950
12275
|
handleError(e);
|
|
@@ -10962,7 +12287,7 @@ graphCmd.command("show <entityNameOrId>").description("Show connected entities a
|
|
|
10962
12287
|
return;
|
|
10963
12288
|
}
|
|
10964
12289
|
if (graph.entities.length === 0) {
|
|
10965
|
-
console.log(
|
|
12290
|
+
console.log(chalk2.yellow(`No graph data for: ${entity.name}`));
|
|
10966
12291
|
return;
|
|
10967
12292
|
}
|
|
10968
12293
|
const adj = new Map;
|
|
@@ -10994,7 +12319,7 @@ graphCmd.command("show <entityNameOrId>").description("Show connected entities a
|
|
|
10994
12319
|
for (let i = 0;i < children.length; i++) {
|
|
10995
12320
|
const child = children[i];
|
|
10996
12321
|
const childIndent = indent + (indent === "" ? "" : isLast ? " " : "\u2502 ");
|
|
10997
|
-
const relLabel =
|
|
12322
|
+
const relLabel = chalk2.dim(` (${child.relation})`);
|
|
10998
12323
|
const childPrefix = i === children.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
10999
12324
|
visited.add(child.entity.id);
|
|
11000
12325
|
console.log(`${childIndent}${childPrefix}[${colorEntityType(child.entity.type)}] ${child.entity.name}${relLabel}`);
|
|
@@ -11003,7 +12328,7 @@ graphCmd.command("show <entityNameOrId>").description("Show connected entities a
|
|
|
11003
12328
|
const gc = grandChildren[j];
|
|
11004
12329
|
const gcIndent = childIndent + (i === children.length - 1 ? " " : "\u2502 ");
|
|
11005
12330
|
const gcPrefix = j === grandChildren.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
11006
|
-
const gcRelLabel =
|
|
12331
|
+
const gcRelLabel = chalk2.dim(` (${gc.relation})`);
|
|
11007
12332
|
visited.add(gc.entity.id);
|
|
11008
12333
|
console.log(`${gcIndent}${gcPrefix}[${colorEntityType(gc.entity.type)}] ${gc.entity.name}${gcRelLabel}`);
|
|
11009
12334
|
}
|
|
@@ -11025,10 +12350,10 @@ graphCmd.command("path <from> <to>").description("Show shortest path between two
|
|
|
11025
12350
|
return;
|
|
11026
12351
|
}
|
|
11027
12352
|
if (!path) {
|
|
11028
|
-
console.log(
|
|
12353
|
+
console.log(chalk2.yellow(`No path found between ${fromEntity.name} and ${toEntity.name}`));
|
|
11029
12354
|
return;
|
|
11030
12355
|
}
|
|
11031
|
-
console.log(
|
|
12356
|
+
console.log(chalk2.bold(`Path (${path.length} hop${path.length === 1 ? "" : "s"}):`));
|
|
11032
12357
|
for (let i = 0;i < path.length; i++) {
|
|
11033
12358
|
const e = path[i];
|
|
11034
12359
|
const arrow = i < path.length - 1 ? " \u2192" : "";
|
|
@@ -11055,19 +12380,19 @@ graphCmd.command("stats").description("Show knowledge graph statistics").action(
|
|
|
11055
12380
|
});
|
|
11056
12381
|
return;
|
|
11057
12382
|
}
|
|
11058
|
-
console.log(
|
|
12383
|
+
console.log(chalk2.bold("Knowledge Graph Stats"));
|
|
11059
12384
|
console.log();
|
|
11060
|
-
console.log(
|
|
12385
|
+
console.log(chalk2.bold(`Entities: ${totalEntities}`));
|
|
11061
12386
|
for (const r of entityRows) {
|
|
11062
12387
|
console.log(` ${colorEntityType(r.type)}: ${r.count}`);
|
|
11063
12388
|
}
|
|
11064
12389
|
console.log();
|
|
11065
|
-
console.log(
|
|
12390
|
+
console.log(chalk2.bold(`Relations: ${totalRelations}`));
|
|
11066
12391
|
for (const r of relationRows) {
|
|
11067
|
-
console.log(` ${
|
|
12392
|
+
console.log(` ${chalk2.cyan(r.relation_type)}: ${r.count}`);
|
|
11068
12393
|
}
|
|
11069
12394
|
console.log();
|
|
11070
|
-
console.log(
|
|
12395
|
+
console.log(chalk2.bold(`Memory links: ${linkCount.count}`));
|
|
11071
12396
|
} catch (e) {
|
|
11072
12397
|
handleError(e);
|
|
11073
12398
|
}
|
|
@@ -11077,14 +12402,14 @@ function formatWatchLine(m) {
|
|
|
11077
12402
|
const cat = colorCategory(m.category);
|
|
11078
12403
|
const imp = colorImportance(m.importance);
|
|
11079
12404
|
const value = m.value.length > 100 ? m.value.slice(0, 100) + "..." : m.value;
|
|
11080
|
-
const main = ` [${scope}/${cat}] ${
|
|
12405
|
+
const main = ` [${scope}/${cat}] ${chalk2.bold(m.key)} = ${value} ${chalk2.dim(`(importance: ${imp})`)}`;
|
|
11081
12406
|
const parts = [];
|
|
11082
12407
|
if (m.tags.length > 0)
|
|
11083
12408
|
parts.push(`Tags: ${m.tags.join(", ")}`);
|
|
11084
12409
|
if (m.agent_id)
|
|
11085
12410
|
parts.push(`Agent: ${m.agent_id}`);
|
|
11086
12411
|
parts.push(`Updated: ${m.updated_at}`);
|
|
11087
|
-
const detail =
|
|
12412
|
+
const detail = chalk2.dim(` ${parts.join(" | ")}`);
|
|
11088
12413
|
return `${main}
|
|
11089
12414
|
${detail}`;
|
|
11090
12415
|
}
|
|
@@ -11104,73 +12429,73 @@ profileCmd.command("list").description("List all available profiles").action(()
|
|
|
11104
12429
|
const profiles = listProfiles();
|
|
11105
12430
|
const active = getActiveProfile();
|
|
11106
12431
|
if (profiles.length === 0) {
|
|
11107
|
-
console.log(
|
|
12432
|
+
console.log(chalk2.dim("No profiles yet. Create one with: mementos profile set <name>"));
|
|
11108
12433
|
return;
|
|
11109
12434
|
}
|
|
11110
|
-
console.log(
|
|
12435
|
+
console.log(chalk2.bold("Profiles:"));
|
|
11111
12436
|
for (const p of profiles) {
|
|
11112
|
-
const marker = p === active ?
|
|
12437
|
+
const marker = p === active ? chalk2.green(" \u2713 (active)") : "";
|
|
11113
12438
|
console.log(` ${p}${marker}`);
|
|
11114
12439
|
}
|
|
11115
12440
|
if (!active) {
|
|
11116
|
-
console.log(
|
|
12441
|
+
console.log(chalk2.dim(`
|
|
11117
12442
|
(no active profile \u2014 using default DB)`));
|
|
11118
12443
|
}
|
|
11119
12444
|
});
|
|
11120
12445
|
profileCmd.command("get").description("Show the currently active profile").action(() => {
|
|
11121
12446
|
const active = getActiveProfile();
|
|
11122
12447
|
if (active) {
|
|
11123
|
-
console.log(
|
|
12448
|
+
console.log(chalk2.green(`Active profile: ${active}`));
|
|
11124
12449
|
if (!process.env["MEMENTOS_PROFILE"]) {
|
|
11125
|
-
console.log(
|
|
12450
|
+
console.log(chalk2.dim("(persisted in ~/.mementos/config.json)"));
|
|
11126
12451
|
} else {
|
|
11127
|
-
console.log(
|
|
12452
|
+
console.log(chalk2.dim("(from MEMENTOS_PROFILE env var)"));
|
|
11128
12453
|
}
|
|
11129
12454
|
} else {
|
|
11130
|
-
console.log(
|
|
12455
|
+
console.log(chalk2.dim("No active profile \u2014 using default DB (~/.mementos/mementos.db)"));
|
|
11131
12456
|
}
|
|
11132
12457
|
});
|
|
11133
12458
|
profileCmd.command("set <name>").description("Switch to a named profile (creates the DB on first use)").action((name) => {
|
|
11134
12459
|
const clean = name.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-");
|
|
11135
12460
|
if (!clean) {
|
|
11136
|
-
console.error(
|
|
12461
|
+
console.error(chalk2.red("Invalid profile name. Use letters, numbers, hyphens, underscores."));
|
|
11137
12462
|
process.exit(1);
|
|
11138
12463
|
}
|
|
11139
12464
|
setActiveProfile(clean);
|
|
11140
|
-
console.log(
|
|
11141
|
-
console.log(
|
|
12465
|
+
console.log(chalk2.green(`\u2713 Switched to profile: ${clean}`));
|
|
12466
|
+
console.log(chalk2.dim(` DB: ~/.mementos/profiles/${clean}.db (created on first use)`));
|
|
11142
12467
|
});
|
|
11143
12468
|
profileCmd.command("unset").description("Clear the active profile (revert to default DB)").action(() => {
|
|
11144
12469
|
const was = getActiveProfile();
|
|
11145
12470
|
setActiveProfile(null);
|
|
11146
12471
|
if (was) {
|
|
11147
|
-
console.log(
|
|
12472
|
+
console.log(chalk2.green(`\u2713 Cleared profile (was: ${was})`));
|
|
11148
12473
|
} else {
|
|
11149
|
-
console.log(
|
|
12474
|
+
console.log(chalk2.dim("No active profile was set."));
|
|
11150
12475
|
}
|
|
11151
|
-
console.log(
|
|
12476
|
+
console.log(chalk2.dim(" Now using default DB: ~/.mementos/mementos.db"));
|
|
11152
12477
|
});
|
|
11153
12478
|
profileCmd.command("delete <name>").description("Delete a profile and its DB file (irreversible)").option("-y, --yes", "Skip confirmation prompt").action(async (name, opts) => {
|
|
11154
12479
|
if (!opts.yes) {
|
|
11155
12480
|
const profiles = listProfiles();
|
|
11156
12481
|
if (!profiles.includes(name)) {
|
|
11157
|
-
console.error(
|
|
12482
|
+
console.error(chalk2.red(`Profile not found: ${name}`));
|
|
11158
12483
|
process.exit(1);
|
|
11159
12484
|
}
|
|
11160
|
-
process.stdout.write(
|
|
12485
|
+
process.stdout.write(chalk2.yellow(`Delete profile "${name}" and its DB? This cannot be undone. [y/N] `));
|
|
11161
12486
|
const answer = await new Promise((resolve4) => {
|
|
11162
12487
|
process.stdin.once("data", (d) => resolve4(d.toString().trim().toLowerCase()));
|
|
11163
12488
|
});
|
|
11164
12489
|
if (answer !== "y" && answer !== "yes") {
|
|
11165
|
-
console.log(
|
|
12490
|
+
console.log(chalk2.dim("Cancelled."));
|
|
11166
12491
|
return;
|
|
11167
12492
|
}
|
|
11168
12493
|
}
|
|
11169
12494
|
const deleted = deleteProfile(name);
|
|
11170
12495
|
if (deleted) {
|
|
11171
|
-
console.log(
|
|
12496
|
+
console.log(chalk2.green(`\u2713 Profile "${name}" deleted.`));
|
|
11172
12497
|
} else {
|
|
11173
|
-
console.error(
|
|
12498
|
+
console.error(chalk2.red(`Profile not found: ${name}`));
|
|
11174
12499
|
process.exit(1);
|
|
11175
12500
|
}
|
|
11176
12501
|
});
|
|
@@ -11181,23 +12506,23 @@ autoMemory.command("process <turn>").description("Enqueue text for async LLM mem
|
|
|
11181
12506
|
if (opts.sync) {
|
|
11182
12507
|
const provider = providerRegistry2.getAvailable();
|
|
11183
12508
|
if (!provider) {
|
|
11184
|
-
console.error(
|
|
12509
|
+
console.error(chalk2.red("No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, CEREBRAS_API_KEY, or XAI_API_KEY."));
|
|
11185
12510
|
process.exit(1);
|
|
11186
12511
|
}
|
|
11187
|
-
console.log(
|
|
12512
|
+
console.log(chalk2.dim(`Using ${provider.name} / ${provider.config.model}...`));
|
|
11188
12513
|
const memories = await provider.extractMemories(turn, {
|
|
11189
12514
|
agentId: opts.agent,
|
|
11190
12515
|
projectId: opts.project
|
|
11191
12516
|
});
|
|
11192
12517
|
if (memories.length === 0) {
|
|
11193
|
-
console.log(
|
|
12518
|
+
console.log(chalk2.dim("Nothing worth remembering extracted."));
|
|
11194
12519
|
} else {
|
|
11195
12520
|
memories.forEach((m, i) => {
|
|
11196
|
-
console.log(
|
|
12521
|
+
console.log(chalk2.bold(`
|
|
11197
12522
|
[${i + 1}] ${m.category} \xB7 importance ${m.importance}/10 \xB7 ${m.suggestedScope}`));
|
|
11198
12523
|
console.log(` ${m.content}`);
|
|
11199
12524
|
if (m.tags.length)
|
|
11200
|
-
console.log(
|
|
12525
|
+
console.log(chalk2.dim(` tags: ${m.tags.join(", ")}`));
|
|
11201
12526
|
});
|
|
11202
12527
|
}
|
|
11203
12528
|
} else {
|
|
@@ -11207,8 +12532,8 @@ autoMemory.command("process <turn>").description("Enqueue text for async LLM mem
|
|
|
11207
12532
|
sessionId: opts.session
|
|
11208
12533
|
});
|
|
11209
12534
|
const stats = getAutoMemoryStats2();
|
|
11210
|
-
console.log(
|
|
11211
|
-
console.log(
|
|
12535
|
+
console.log(chalk2.green("\u2713 Queued for extraction"));
|
|
12536
|
+
console.log(chalk2.dim(`Queue: ${stats.pending} pending \xB7 ${stats.processed} processed \xB7 ${stats.failed} failed`));
|
|
11212
12537
|
}
|
|
11213
12538
|
});
|
|
11214
12539
|
autoMemory.command("status").description("Show auto-memory queue stats and provider health").action(async () => {
|
|
@@ -11217,14 +12542,14 @@ autoMemory.command("status").description("Show auto-memory queue stats and provi
|
|
|
11217
12542
|
const stats = getAutoMemoryStats2();
|
|
11218
12543
|
const config = providerRegistry2.getConfig();
|
|
11219
12544
|
const health = providerRegistry2.health();
|
|
11220
|
-
console.log(
|
|
11221
|
-
console.log(` Provider: ${config.enabled ?
|
|
12545
|
+
console.log(chalk2.bold("Auto-Memory Status"));
|
|
12546
|
+
console.log(` Provider: ${config.enabled ? chalk2.green(config.provider) : chalk2.red("disabled")} / ${config.model ?? "default"}`);
|
|
11222
12547
|
console.log(` Queue: ${stats.pending} pending \xB7 ${stats.processing} processing \xB7 ${stats.processed} processed`);
|
|
11223
12548
|
console.log(` Errors: ${stats.failed} failed \xB7 ${stats.dropped} dropped`);
|
|
11224
|
-
console.log(
|
|
12549
|
+
console.log(chalk2.bold(`
|
|
11225
12550
|
Provider Health`));
|
|
11226
12551
|
for (const [name, info] of Object.entries(health)) {
|
|
11227
|
-
const icon = info.available ?
|
|
12552
|
+
const icon = info.available ? chalk2.green("\u2713") : chalk2.red("\u2717");
|
|
11228
12553
|
console.log(` ${icon} ${name.padEnd(12)} ${info.model}`);
|
|
11229
12554
|
}
|
|
11230
12555
|
});
|
|
@@ -11237,35 +12562,35 @@ autoMemory.command("config").description("Show or update auto-memory provider co
|
|
|
11237
12562
|
...opts.model && { model: opts.model },
|
|
11238
12563
|
...opts.minImportance && { minImportance: Number(opts.minImportance) }
|
|
11239
12564
|
});
|
|
11240
|
-
console.log(
|
|
12565
|
+
console.log(chalk2.green("\u2713 Config updated"));
|
|
11241
12566
|
}
|
|
11242
12567
|
const config = providerRegistry2.getConfig();
|
|
11243
|
-
console.log(
|
|
12568
|
+
console.log(chalk2.bold("Auto-Memory Config"));
|
|
11244
12569
|
console.log(JSON.stringify(config, null, 2));
|
|
11245
12570
|
});
|
|
11246
12571
|
autoMemory.command("test <turn>").description("Test extraction without saving (dry run)").option("--provider <name>", "force a specific provider").option("--agent <id>", "agent ID for context").option("--project <id>", "project ID for context").action(async (turn, opts) => {
|
|
11247
12572
|
const { providerRegistry: providerRegistry2 } = await Promise.resolve().then(() => (init_registry(), exports_registry));
|
|
11248
12573
|
const provider = opts.provider ? providerRegistry2.getProvider(opts.provider) : providerRegistry2.getAvailable();
|
|
11249
12574
|
if (!provider) {
|
|
11250
|
-
console.error(
|
|
12575
|
+
console.error(chalk2.red("No LLM provider configured."));
|
|
11251
12576
|
process.exit(1);
|
|
11252
12577
|
}
|
|
11253
|
-
console.log(
|
|
12578
|
+
console.log(chalk2.dim(`DRY RUN \u2014 ${provider.name} / ${provider.config.model} \u2014 nothing will be saved
|
|
11254
12579
|
`));
|
|
11255
12580
|
const memories = await provider.extractMemories(turn, {
|
|
11256
12581
|
agentId: opts.agent,
|
|
11257
12582
|
projectId: opts.project
|
|
11258
12583
|
});
|
|
11259
12584
|
if (memories.length === 0) {
|
|
11260
|
-
console.log(
|
|
12585
|
+
console.log(chalk2.dim("Nothing extracted."));
|
|
11261
12586
|
} else {
|
|
11262
12587
|
memories.forEach((m, i) => {
|
|
11263
|
-
console.log(
|
|
11264
|
-
console.log(` ${
|
|
12588
|
+
console.log(chalk2.bold(`[${i + 1}] ${m.category.toUpperCase()} \xB7 importance ${m.importance}/10 \xB7 ${m.suggestedScope}`));
|
|
12589
|
+
console.log(` ${chalk2.white(m.content)}`);
|
|
11265
12590
|
if (m.tags.length)
|
|
11266
|
-
console.log(
|
|
12591
|
+
console.log(chalk2.dim(` tags: ${m.tags.join(", ")}`));
|
|
11267
12592
|
if (m.reasoning)
|
|
11268
|
-
console.log(
|
|
12593
|
+
console.log(chalk2.dim(` why: ${m.reasoning}`));
|
|
11269
12594
|
console.log();
|
|
11270
12595
|
});
|
|
11271
12596
|
}
|
|
@@ -11273,38 +12598,38 @@ autoMemory.command("test <turn>").description("Test extraction without saving (d
|
|
|
11273
12598
|
autoMemory.command("enable").description("Enable auto-memory extraction").action(async () => {
|
|
11274
12599
|
const { configureAutoMemory: configureAutoMemory2 } = await Promise.resolve().then(() => (init_auto_memory(), exports_auto_memory));
|
|
11275
12600
|
configureAutoMemory2({ enabled: true });
|
|
11276
|
-
console.log(
|
|
12601
|
+
console.log(chalk2.green("\u2713 Auto-memory enabled"));
|
|
11277
12602
|
});
|
|
11278
12603
|
autoMemory.command("disable").description("Disable auto-memory extraction").action(async () => {
|
|
11279
12604
|
const { configureAutoMemory: configureAutoMemory2 } = await Promise.resolve().then(() => (init_auto_memory(), exports_auto_memory));
|
|
11280
12605
|
configureAutoMemory2({ enabled: false });
|
|
11281
|
-
console.log(
|
|
12606
|
+
console.log(chalk2.yellow("\u26A0 Auto-memory disabled"));
|
|
11282
12607
|
});
|
|
11283
12608
|
var hooksCmd = program2.command("hooks").description("Hook registry and webhook management");
|
|
11284
12609
|
hooksCmd.command("list").description("List registered hooks in the in-memory registry").option("--type <type>", "Filter by hook type").action(async (opts) => {
|
|
11285
12610
|
const { hookRegistry: hookRegistry2 } = await Promise.resolve().then(() => (init_hooks(), exports_hooks));
|
|
11286
12611
|
const hooks = hookRegistry2.list(opts.type);
|
|
11287
12612
|
if (hooks.length === 0) {
|
|
11288
|
-
console.log(
|
|
12613
|
+
console.log(chalk2.gray("No hooks registered."));
|
|
11289
12614
|
return;
|
|
11290
12615
|
}
|
|
11291
12616
|
for (const h of hooks) {
|
|
11292
|
-
const builtinTag = h.builtin ?
|
|
11293
|
-
const blockingTag = h.blocking ?
|
|
11294
|
-
console.log(`${
|
|
12617
|
+
const builtinTag = h.builtin ? chalk2.blue(" [builtin]") : "";
|
|
12618
|
+
const blockingTag = h.blocking ? chalk2.red(" [blocking]") : chalk2.gray(" [non-blocking]");
|
|
12619
|
+
console.log(`${chalk2.cyan(h.id)} ${chalk2.bold(h.type)}${builtinTag}${blockingTag} priority=${h.priority}`);
|
|
11295
12620
|
if (h.description)
|
|
11296
|
-
console.log(` ${
|
|
12621
|
+
console.log(` ${chalk2.gray(h.description)}`);
|
|
11297
12622
|
}
|
|
11298
12623
|
});
|
|
11299
12624
|
hooksCmd.command("stats").description("Show hook registry statistics").action(async () => {
|
|
11300
12625
|
const { hookRegistry: hookRegistry2 } = await Promise.resolve().then(() => (init_hooks(), exports_hooks));
|
|
11301
12626
|
const stats = hookRegistry2.stats();
|
|
11302
|
-
console.log(
|
|
11303
|
-
console.log(` Total: ${
|
|
11304
|
-
console.log(` Blocking: ${
|
|
11305
|
-
console.log(` Non-blocking:${
|
|
12627
|
+
console.log(chalk2.bold("Hook Registry Stats"));
|
|
12628
|
+
console.log(` Total: ${chalk2.cyan(stats.total)}`);
|
|
12629
|
+
console.log(` Blocking: ${chalk2.red(stats.blocking)}`);
|
|
12630
|
+
console.log(` Non-blocking:${chalk2.green(stats.nonBlocking)}`);
|
|
11306
12631
|
if (Object.keys(stats.byType).length > 0) {
|
|
11307
|
-
console.log(
|
|
12632
|
+
console.log(chalk2.bold(`
|
|
11308
12633
|
By type:`));
|
|
11309
12634
|
for (const [type, count] of Object.entries(stats.byType)) {
|
|
11310
12635
|
console.log(` ${type}: ${count}`);
|
|
@@ -11319,16 +12644,16 @@ webhooksCmd.command("list").description("List all persisted webhook hooks").opti
|
|
|
11319
12644
|
enabled: opts.disabled ? false : undefined
|
|
11320
12645
|
});
|
|
11321
12646
|
if (webhooks.length === 0) {
|
|
11322
|
-
console.log(
|
|
12647
|
+
console.log(chalk2.gray("No webhooks registered."));
|
|
11323
12648
|
return;
|
|
11324
12649
|
}
|
|
11325
12650
|
for (const wh of webhooks) {
|
|
11326
|
-
const enabledTag = wh.enabled ?
|
|
11327
|
-
const blockingTag = wh.blocking ?
|
|
11328
|
-
console.log(`${
|
|
12651
|
+
const enabledTag = wh.enabled ? chalk2.green("enabled") : chalk2.red("disabled");
|
|
12652
|
+
const blockingTag = wh.blocking ? chalk2.red("blocking") : chalk2.gray("non-blocking");
|
|
12653
|
+
console.log(`${chalk2.cyan(wh.id)} [${enabledTag}] ${chalk2.bold(wh.type)} \u2192 ${wh.handlerUrl}`);
|
|
11329
12654
|
console.log(` ${blockingTag} | priority=${wh.priority} | invocations=${wh.invocationCount} failures=${wh.failureCount}`);
|
|
11330
12655
|
if (wh.description)
|
|
11331
|
-
console.log(` ${
|
|
12656
|
+
console.log(` ${chalk2.gray(wh.description)}`);
|
|
11332
12657
|
}
|
|
11333
12658
|
});
|
|
11334
12659
|
webhooksCmd.command("create <type> <url>").description("Create a persistent webhook hook").option("--blocking", "Block the operation until the webhook responds").option("--priority <n>", "Hook priority (default 50)", "50").option("--agent <id>", "Scope to specific agent").option("--project <id>", "Scope to specific project").option("--description <text>", "Human-readable description").action(async (type, url, opts) => {
|
|
@@ -11344,8 +12669,8 @@ webhooksCmd.command("create <type> <url>").description("Create a persistent webh
|
|
|
11344
12669
|
description: opts.description
|
|
11345
12670
|
});
|
|
11346
12671
|
reloadWebhooks2();
|
|
11347
|
-
console.log(
|
|
11348
|
-
console.log(` ID: ${
|
|
12672
|
+
console.log(chalk2.green("\u2713 Webhook created"));
|
|
12673
|
+
console.log(` ID: ${chalk2.cyan(wh.id)}`);
|
|
11349
12674
|
console.log(` Type: ${wh.type}`);
|
|
11350
12675
|
console.log(` URL: ${wh.handlerUrl}`);
|
|
11351
12676
|
});
|
|
@@ -11353,9 +12678,9 @@ webhooksCmd.command("delete <id>").description("Delete a webhook by ID").action(
|
|
|
11353
12678
|
const { deleteWebhookHook: deleteWebhookHook2 } = await Promise.resolve().then(() => (init_webhook_hooks(), exports_webhook_hooks));
|
|
11354
12679
|
const deleted = deleteWebhookHook2(id);
|
|
11355
12680
|
if (deleted) {
|
|
11356
|
-
console.log(
|
|
12681
|
+
console.log(chalk2.green(`\u2713 Webhook ${id} deleted`));
|
|
11357
12682
|
} else {
|
|
11358
|
-
console.error(
|
|
12683
|
+
console.error(chalk2.red(`Webhook not found: ${id}`));
|
|
11359
12684
|
process.exit(1);
|
|
11360
12685
|
}
|
|
11361
12686
|
});
|
|
@@ -11365,9 +12690,9 @@ webhooksCmd.command("enable <id>").description("Enable a webhook").action(async
|
|
|
11365
12690
|
const updated = updateWebhookHook2(id, { enabled: true });
|
|
11366
12691
|
if (updated) {
|
|
11367
12692
|
reloadWebhooks2();
|
|
11368
|
-
console.log(
|
|
12693
|
+
console.log(chalk2.green(`\u2713 Webhook ${id} enabled`));
|
|
11369
12694
|
} else {
|
|
11370
|
-
console.error(
|
|
12695
|
+
console.error(chalk2.red(`Webhook not found: ${id}`));
|
|
11371
12696
|
process.exit(1);
|
|
11372
12697
|
}
|
|
11373
12698
|
});
|
|
@@ -11377,16 +12702,16 @@ webhooksCmd.command("disable <id>").description("Disable a webhook (without dele
|
|
|
11377
12702
|
const updated = updateWebhookHook2(id, { enabled: false });
|
|
11378
12703
|
if (updated) {
|
|
11379
12704
|
reloadWebhooks2();
|
|
11380
|
-
console.log(
|
|
12705
|
+
console.log(chalk2.yellow(`\u2298 Webhook ${id} disabled`));
|
|
11381
12706
|
} else {
|
|
11382
|
-
console.error(
|
|
12707
|
+
console.error(chalk2.red(`Webhook not found: ${id}`));
|
|
11383
12708
|
process.exit(1);
|
|
11384
12709
|
}
|
|
11385
12710
|
});
|
|
11386
12711
|
var synthesisCmd = program2.command("synthesis").alias("synth").description("ALMA memory synthesis \u2014 analyze and consolidate memories");
|
|
11387
12712
|
synthesisCmd.command("run").description("Run memory synthesis on the corpus").option("--project <id>", "Project ID to synthesize").option("--dry-run", "Preview proposals without applying them").option("--max-proposals <n>", "Maximum proposals to generate", "20").option("--provider <name>", "LLM provider (anthropic, openai, cerebras, grok)").action(async (opts) => {
|
|
11388
12713
|
const { runSynthesis: runSynthesis2 } = await Promise.resolve().then(() => (init_synthesis2(), exports_synthesis2));
|
|
11389
|
-
console.log(
|
|
12714
|
+
console.log(chalk2.blue("Running memory synthesis..."));
|
|
11390
12715
|
const result = await runSynthesis2({
|
|
11391
12716
|
projectId: opts.project,
|
|
11392
12717
|
dryRun: opts.dryRun ?? false,
|
|
@@ -11394,44 +12719,44 @@ synthesisCmd.command("run").description("Run memory synthesis on the corpus").op
|
|
|
11394
12719
|
provider: opts.provider
|
|
11395
12720
|
});
|
|
11396
12721
|
if (result.dryRun) {
|
|
11397
|
-
console.log(
|
|
12722
|
+
console.log(chalk2.yellow(`DRY RUN \u2014 ${result.proposals.length} proposals generated (not applied)`));
|
|
11398
12723
|
} else {
|
|
11399
|
-
console.log(
|
|
12724
|
+
console.log(chalk2.green(`\u2713 Synthesis complete`));
|
|
11400
12725
|
console.log(` Corpus: ${result.run.corpus_size} memories`);
|
|
11401
12726
|
console.log(` Proposals: ${result.run.proposals_generated} generated, ${result.run.proposals_accepted} applied`);
|
|
11402
12727
|
}
|
|
11403
12728
|
if (result.metrics) {
|
|
11404
12729
|
console.log(` Reduction: ${(result.metrics.corpusReduction * 100).toFixed(1)}%`);
|
|
11405
12730
|
}
|
|
11406
|
-
console.log(` Run ID: ${
|
|
12731
|
+
console.log(` Run ID: ${chalk2.cyan(result.run.id)}`);
|
|
11407
12732
|
});
|
|
11408
12733
|
synthesisCmd.command("status").description("Show recent synthesis runs").option("--project <id>", "Filter by project").action(async (opts) => {
|
|
11409
12734
|
const { listSynthesisRuns: listSynthesisRuns2 } = await Promise.resolve().then(() => (init_synthesis(), exports_synthesis));
|
|
11410
12735
|
const runs = listSynthesisRuns2({ project_id: opts.project, limit: 10 });
|
|
11411
12736
|
if (runs.length === 0) {
|
|
11412
|
-
console.log(
|
|
12737
|
+
console.log(chalk2.gray("No synthesis runs found."));
|
|
11413
12738
|
return;
|
|
11414
12739
|
}
|
|
11415
12740
|
for (const run of runs) {
|
|
11416
|
-
const statusColor = run.status === "completed" ?
|
|
11417
|
-
console.log(`${
|
|
12741
|
+
const statusColor = run.status === "completed" ? chalk2.green : run.status === "failed" ? chalk2.red : chalk2.yellow;
|
|
12742
|
+
console.log(`${chalk2.cyan(run.id)} [${statusColor(run.status)}] corpus=${run.corpus_size} accepted=${run.proposals_accepted}/${run.proposals_generated} ${run.started_at.slice(0, 10)}`);
|
|
11418
12743
|
}
|
|
11419
12744
|
});
|
|
11420
12745
|
synthesisCmd.command("rollback <runId>").description("Roll back a synthesis run").action(async (runId) => {
|
|
11421
12746
|
const { rollbackSynthesis: rollbackSynthesis2 } = await Promise.resolve().then(() => (init_synthesis2(), exports_synthesis2));
|
|
11422
|
-
console.log(
|
|
12747
|
+
console.log(chalk2.yellow(`Rolling back synthesis run ${runId}...`));
|
|
11423
12748
|
const result = await rollbackSynthesis2(runId);
|
|
11424
|
-
console.log(
|
|
12749
|
+
console.log(chalk2.green(`\u2713 Rolled back ${result.rolled_back} proposals`));
|
|
11425
12750
|
if (result.errors.length > 0) {
|
|
11426
|
-
console.log(
|
|
12751
|
+
console.log(chalk2.red(` Errors: ${result.errors.join(", ")}`));
|
|
11427
12752
|
}
|
|
11428
12753
|
});
|
|
11429
12754
|
var sessionCmd = program2.command("session").description("Session auto-memory \u2014 ingest session transcripts for memory extraction");
|
|
11430
12755
|
sessionCmd.command("ingest <transcriptFile>").description("Ingest a session transcript file for memory extraction").option("--session-id <id>", "Session ID (default: auto-generated)").option("--agent <id>", "Agent ID").option("--project <id>", "Project ID").option("--source <source>", "Source (claude-code, codex, manual, open-sessions)", "manual").action(async (transcriptFile, opts) => {
|
|
11431
|
-
const { readFileSync:
|
|
12756
|
+
const { readFileSync: readFileSync4 } = await import("fs");
|
|
11432
12757
|
const { createSessionJob: createSessionJob2 } = await Promise.resolve().then(() => (init_session_jobs(), exports_session_jobs));
|
|
11433
12758
|
const { enqueueSessionJob: enqueueSessionJob2 } = await Promise.resolve().then(() => (init_session_queue(), exports_session_queue));
|
|
11434
|
-
const transcript =
|
|
12759
|
+
const transcript = readFileSync4(transcriptFile, "utf-8");
|
|
11435
12760
|
const sessionId = opts.sessionId ?? `cli-${Date.now()}`;
|
|
11436
12761
|
const job = createSessionJob2({
|
|
11437
12762
|
session_id: sessionId,
|
|
@@ -11441,7 +12766,7 @@ sessionCmd.command("ingest <transcriptFile>").description("Ingest a session tran
|
|
|
11441
12766
|
project_id: opts.project
|
|
11442
12767
|
});
|
|
11443
12768
|
enqueueSessionJob2(job.id);
|
|
11444
|
-
console.log(
|
|
12769
|
+
console.log(chalk2.green(`\u2713 Session queued: ${chalk2.cyan(job.id)}`));
|
|
11445
12770
|
console.log(` Session: ${sessionId}`);
|
|
11446
12771
|
console.log(` Length: ${transcript.length} chars`);
|
|
11447
12772
|
});
|
|
@@ -11449,16 +12774,16 @@ sessionCmd.command("status <jobId>").description("Check status of a session extr
|
|
|
11449
12774
|
const { getSessionJob: getSessionJob2 } = await Promise.resolve().then(() => (init_session_jobs(), exports_session_jobs));
|
|
11450
12775
|
const job = getSessionJob2(jobId);
|
|
11451
12776
|
if (!job) {
|
|
11452
|
-
console.error(
|
|
12777
|
+
console.error(chalk2.red(`Job not found: ${jobId}`));
|
|
11453
12778
|
process.exit(1);
|
|
11454
12779
|
}
|
|
11455
|
-
const statusColor = job.status === "completed" ?
|
|
11456
|
-
console.log(`${
|
|
12780
|
+
const statusColor = job.status === "completed" ? chalk2.green : job.status === "failed" ? chalk2.red : chalk2.yellow;
|
|
12781
|
+
console.log(`${chalk2.cyan(job.id)} [${statusColor(job.status)}]`);
|
|
11457
12782
|
console.log(` Session: ${job.session_id}`);
|
|
11458
12783
|
console.log(` Chunks: ${job.chunk_count}`);
|
|
11459
12784
|
console.log(` Extracted: ${job.memories_extracted} memories`);
|
|
11460
12785
|
if (job.error)
|
|
11461
|
-
console.log(
|
|
12786
|
+
console.log(chalk2.red(` Error: ${job.error}`));
|
|
11462
12787
|
});
|
|
11463
12788
|
sessionCmd.command("list").description("List session extraction jobs").option("--agent <id>", "Filter by agent").option("--project <id>", "Filter by project").option("--status <status>", "Filter by status").option("--limit <n>", "Max results", "20").action(async (opts) => {
|
|
11464
12789
|
const { listSessionJobs: listSessionJobs2 } = await Promise.resolve().then(() => (init_session_jobs(), exports_session_jobs));
|
|
@@ -11469,12 +12794,12 @@ sessionCmd.command("list").description("List session extraction jobs").option("-
|
|
|
11469
12794
|
limit: parseInt(opts.limit)
|
|
11470
12795
|
});
|
|
11471
12796
|
if (jobs.length === 0) {
|
|
11472
|
-
console.log(
|
|
12797
|
+
console.log(chalk2.gray("No session jobs found."));
|
|
11473
12798
|
return;
|
|
11474
12799
|
}
|
|
11475
12800
|
for (const job of jobs) {
|
|
11476
|
-
const statusColor = job.status === "completed" ?
|
|
11477
|
-
console.log(`${
|
|
12801
|
+
const statusColor = job.status === "completed" ? chalk2.green : job.status === "failed" ? chalk2.red : chalk2.yellow;
|
|
12802
|
+
console.log(`${chalk2.cyan(job.id.slice(0, 8))} [${statusColor(job.status)}] ${job.memories_extracted} memories | ${job.created_at.slice(0, 10)}`);
|
|
11478
12803
|
}
|
|
11479
12804
|
});
|
|
11480
12805
|
sessionCmd.command("setup-hook").description("Install mementos session hook into Claude Code or Codex").option("--claude", "Install Claude Code stop hook").option("--codex", "Install Codex session hook").option("--show", "Print hook script instead of installing").action(async (opts) => {
|
|
@@ -11483,26 +12808,26 @@ sessionCmd.command("setup-hook").description("Install mementos session hook into
|
|
|
11483
12808
|
if (opts.claude) {
|
|
11484
12809
|
const script = `${hookPath}/claude-stop-hook.ts`;
|
|
11485
12810
|
if (opts.show) {
|
|
11486
|
-
const { readFileSync:
|
|
11487
|
-
console.log(
|
|
12811
|
+
const { readFileSync: readFileSync4 } = await import("fs");
|
|
12812
|
+
console.log(readFileSync4(script, "utf-8"));
|
|
11488
12813
|
return;
|
|
11489
12814
|
}
|
|
11490
|
-
console.log(
|
|
12815
|
+
console.log(chalk2.bold("Claude Code stop hook installation:"));
|
|
11491
12816
|
console.log("");
|
|
11492
12817
|
console.log("Add to your .claude/settings.json:");
|
|
11493
|
-
console.log(
|
|
12818
|
+
console.log(chalk2.cyan(JSON.stringify({
|
|
11494
12819
|
hooks: {
|
|
11495
12820
|
Stop: [{ matcher: "", hooks: [{ type: "command", command: `bun ${script}` }] }]
|
|
11496
12821
|
}
|
|
11497
12822
|
}, null, 2)));
|
|
11498
12823
|
console.log("");
|
|
11499
|
-
console.log(`Or run: ${
|
|
12824
|
+
console.log(`Or run: ${chalk2.cyan(`claude hooks add Stop "bun ${script}"`)}`);
|
|
11500
12825
|
} else if (opts.codex) {
|
|
11501
12826
|
const script = `${hookPath}/codex-stop-hook.ts`;
|
|
11502
|
-
console.log(
|
|
12827
|
+
console.log(chalk2.bold("Codex session hook installation:"));
|
|
11503
12828
|
console.log("");
|
|
11504
12829
|
console.log("Add to ~/.codex/config.toml:");
|
|
11505
|
-
console.log(
|
|
12830
|
+
console.log(chalk2.cyan(`[hooks]
|
|
11506
12831
|
session_end = "bun ${script}"`));
|
|
11507
12832
|
} else {
|
|
11508
12833
|
console.log("Usage: mementos session setup-hook --claude | --codex");
|
|
@@ -11526,7 +12851,7 @@ program2.command("heartbeat [agent-id]").description("Update last_seen_at to sig
|
|
|
11526
12851
|
if (globalOpts.json)
|
|
11527
12852
|
console.log(JSON.stringify({ agent_id: agent.id, name: agent.name, last_seen_at: new Date().toISOString() }));
|
|
11528
12853
|
else
|
|
11529
|
-
console.log(
|
|
12854
|
+
console.log(chalk2.green(`\u2665 ${agent.name} (${agent.id}) \u2014 heartbeat sent`));
|
|
11530
12855
|
});
|
|
11531
12856
|
program2.command("set-focus [project]").description("Focus this agent on a project (or clear focus if no project given)").option("--agent <id>", "Agent ID").action((project, opts) => {
|
|
11532
12857
|
const globalOpts = program2.opts();
|
|
@@ -11538,9 +12863,9 @@ program2.command("set-focus [project]").description("Focus this agent on a proje
|
|
|
11538
12863
|
}
|
|
11539
12864
|
setFocus(agentId, project ?? null);
|
|
11540
12865
|
if (project)
|
|
11541
|
-
console.log(
|
|
12866
|
+
console.log(chalk2.green(`Focused: ${agentId} \u2192 project ${project}`));
|
|
11542
12867
|
else
|
|
11543
|
-
console.log(
|
|
12868
|
+
console.log(chalk2.dim(`Focus cleared for ${agentId}`));
|
|
11544
12869
|
});
|
|
11545
12870
|
program2.command("get-focus").description("Show the current project focus for an agent").option("--agent <id>", "Agent ID").action((opts) => {
|
|
11546
12871
|
const globalOpts = program2.opts();
|
|
@@ -11552,9 +12877,9 @@ program2.command("get-focus").description("Show the current project focus for an
|
|
|
11552
12877
|
}
|
|
11553
12878
|
const focus = getFocus(agentId);
|
|
11554
12879
|
if (focus)
|
|
11555
|
-
console.log(
|
|
12880
|
+
console.log(chalk2.cyan(`Focus: ${focus}`));
|
|
11556
12881
|
else
|
|
11557
|
-
console.log(
|
|
12882
|
+
console.log(chalk2.dim("No focus set."));
|
|
11558
12883
|
});
|
|
11559
12884
|
program2.command("remove <nameOrId>").description("Remove/delete a memory by name or ID (alias for memory delete)").option("--agent <id>", "Agent ID").action((nameOrId, opts) => {
|
|
11560
12885
|
const globalOpts = program2.opts();
|
|
@@ -11570,15 +12895,271 @@ program2.command("remove <nameOrId>").description("Remove/delete a memory by nam
|
|
|
11570
12895
|
id = mem.id;
|
|
11571
12896
|
}
|
|
11572
12897
|
if (!id) {
|
|
11573
|
-
console.error(
|
|
12898
|
+
console.error(chalk2.red(`Memory not found: ${nameOrId}`));
|
|
11574
12899
|
process.exit(1);
|
|
11575
12900
|
}
|
|
11576
12901
|
const deleted = deleteMemory2(id);
|
|
11577
12902
|
if (deleted)
|
|
11578
|
-
console.log(
|
|
12903
|
+
console.log(chalk2.green(`\u2713 Memory ${id.slice(0, 8)} removed`));
|
|
11579
12904
|
else {
|
|
11580
|
-
console.error(
|
|
12905
|
+
console.error(chalk2.red(`Memory not found: ${nameOrId}`));
|
|
11581
12906
|
process.exit(1);
|
|
11582
12907
|
}
|
|
11583
12908
|
});
|
|
12909
|
+
program2.command("tool-events [tool_name]").description("List tool events, optionally filtered by tool name").option("--limit <n>", "Max results (default: 20)", parseInt).option("--project-id <id>", "Filter by project ID").action((toolName, opts) => {
|
|
12910
|
+
try {
|
|
12911
|
+
const globalOpts = program2.opts();
|
|
12912
|
+
const { getToolEvents: getToolEvents2 } = (init_tool_events(), __toCommonJS(exports_tool_events));
|
|
12913
|
+
const limit = opts.limit || 20;
|
|
12914
|
+
const events = getToolEvents2({
|
|
12915
|
+
tool_name: toolName,
|
|
12916
|
+
project_id: opts.projectId,
|
|
12917
|
+
limit
|
|
12918
|
+
});
|
|
12919
|
+
if (globalOpts.json) {
|
|
12920
|
+
outputJson(events);
|
|
12921
|
+
return;
|
|
12922
|
+
}
|
|
12923
|
+
if (events.length === 0) {
|
|
12924
|
+
console.log(chalk2.yellow("No tool events found."));
|
|
12925
|
+
return;
|
|
12926
|
+
}
|
|
12927
|
+
console.log(chalk2.bold(`${events.length} tool event${events.length === 1 ? "" : "s"}:`));
|
|
12928
|
+
console.log(` ${chalk2.dim("tool_name".padEnd(24))} ${chalk2.dim("action".padEnd(16))} ${chalk2.dim("success")} ${chalk2.dim("error_type".padEnd(20))} ${chalk2.dim("created_at")}`);
|
|
12929
|
+
for (const e of events) {
|
|
12930
|
+
const successStr = e.success ? chalk2.green("true ") : chalk2.red("false ");
|
|
12931
|
+
const errorType = (e.error_type || "").padEnd(20);
|
|
12932
|
+
const action = (e.action || "-").padEnd(16);
|
|
12933
|
+
console.log(` ${e.tool_name.padEnd(24)} ${action} ${successStr} ${errorType} ${chalk2.dim(e.created_at)}`);
|
|
12934
|
+
}
|
|
12935
|
+
} catch (e) {
|
|
12936
|
+
handleError(e);
|
|
12937
|
+
}
|
|
12938
|
+
});
|
|
12939
|
+
program2.command("tool-insights <tool_name>").description("Show tool guide/stats and lessons for a tool").option("--project-id <id>", "Filter by project ID").action((toolName, opts) => {
|
|
12940
|
+
try {
|
|
12941
|
+
const globalOpts = program2.opts();
|
|
12942
|
+
const { getToolStats: getToolStats2, getToolLessons: getToolLessons2 } = (init_tool_events(), __toCommonJS(exports_tool_events));
|
|
12943
|
+
const projectId = opts.projectId;
|
|
12944
|
+
const stats = getToolStats2(toolName, projectId);
|
|
12945
|
+
const lessons = getToolLessons2(toolName, projectId);
|
|
12946
|
+
if (globalOpts.json) {
|
|
12947
|
+
outputJson({ stats, lessons });
|
|
12948
|
+
return;
|
|
12949
|
+
}
|
|
12950
|
+
const successRate = (stats.success_rate * 100).toFixed(1);
|
|
12951
|
+
console.log(chalk2.bold(`Tool: ${toolName}`));
|
|
12952
|
+
console.log(` Calls: ${stats.total_calls} Success: ${chalk2.green(String(stats.success_count))} Failures: ${chalk2.red(String(stats.failure_count))} Rate: ${successRate}%` + (stats.avg_latency_ms !== null ? ` Avg latency: ${stats.avg_latency_ms.toFixed(0)}ms` : "") + (stats.last_used ? ` Last used: ${chalk2.dim(stats.last_used)}` : ""));
|
|
12953
|
+
if (stats.common_errors.length > 0) {
|
|
12954
|
+
console.log(chalk2.bold(`
|
|
12955
|
+
Common errors:`));
|
|
12956
|
+
for (const err of stats.common_errors) {
|
|
12957
|
+
console.log(` ${chalk2.red(err.error_type)}: ${err.count} times`);
|
|
12958
|
+
}
|
|
12959
|
+
}
|
|
12960
|
+
if (lessons.length === 0) {
|
|
12961
|
+
console.log(chalk2.dim(`
|
|
12962
|
+
No lessons recorded.`));
|
|
12963
|
+
return;
|
|
12964
|
+
}
|
|
12965
|
+
console.log(chalk2.bold(`
|
|
12966
|
+
Lessons (${lessons.length}):`));
|
|
12967
|
+
for (const l of lessons) {
|
|
12968
|
+
console.log(` ${chalk2.cyan("-")} ${l.lesson} ${chalk2.dim(`(${l.created_at.slice(0, 10)})`)}`);
|
|
12969
|
+
if (l.when_to_use) {
|
|
12970
|
+
console.log(` ${chalk2.dim("when_to_use:")} ${l.when_to_use}`);
|
|
12971
|
+
}
|
|
12972
|
+
}
|
|
12973
|
+
} catch (e) {
|
|
12974
|
+
handleError(e);
|
|
12975
|
+
}
|
|
12976
|
+
});
|
|
12977
|
+
program2.command("synthesized-profile").description("Show or refresh the synthesized agent/project profile").option("--project-id <id>", "Project ID").option("--refresh", "Force refresh the profile (re-synthesize from memories)").action(async (opts) => {
|
|
12978
|
+
try {
|
|
12979
|
+
const globalOpts = program2.opts();
|
|
12980
|
+
const { synthesizeProfile: synthesizeProfile2 } = await Promise.resolve().then(() => (init_profile_synthesizer(), exports_profile_synthesizer));
|
|
12981
|
+
let projectId = opts.projectId;
|
|
12982
|
+
if (!projectId && globalOpts.project) {
|
|
12983
|
+
const project = getProject(resolve3(globalOpts.project));
|
|
12984
|
+
if (project)
|
|
12985
|
+
projectId = project.id;
|
|
12986
|
+
}
|
|
12987
|
+
const result = await synthesizeProfile2({
|
|
12988
|
+
project_id: projectId,
|
|
12989
|
+
agent_id: globalOpts.agent,
|
|
12990
|
+
force_refresh: !!opts.refresh
|
|
12991
|
+
});
|
|
12992
|
+
if (!result) {
|
|
12993
|
+
if (globalOpts.json) {
|
|
12994
|
+
outputJson({ error: "No profile available (no preference/fact memories found)" });
|
|
12995
|
+
} else {
|
|
12996
|
+
console.log(chalk2.yellow("No profile available \u2014 save some preference or fact memories first."));
|
|
12997
|
+
}
|
|
12998
|
+
return;
|
|
12999
|
+
}
|
|
13000
|
+
if (globalOpts.json) {
|
|
13001
|
+
outputJson(result);
|
|
13002
|
+
return;
|
|
13003
|
+
}
|
|
13004
|
+
if (result.from_cache) {
|
|
13005
|
+
console.log(chalk2.dim(`(cached profile)
|
|
13006
|
+
`));
|
|
13007
|
+
} else {
|
|
13008
|
+
console.log(chalk2.dim(`(synthesized from ${result.memory_count} memories)
|
|
13009
|
+
`));
|
|
13010
|
+
}
|
|
13011
|
+
console.log(result.profile);
|
|
13012
|
+
} catch (e) {
|
|
13013
|
+
handleError(e);
|
|
13014
|
+
}
|
|
13015
|
+
});
|
|
13016
|
+
program2.command("chain <sequence_group>").description("Show a memory chain (memories linked by sequence_group, ordered by sequence_order)").action((sequenceGroup) => {
|
|
13017
|
+
try {
|
|
13018
|
+
const globalOpts = program2.opts();
|
|
13019
|
+
const db = getDatabase();
|
|
13020
|
+
const rows = db.query("SELECT * FROM memories WHERE sequence_group = ? AND status = 'active' ORDER BY sequence_order ASC").all(sequenceGroup);
|
|
13021
|
+
const memories = rows.map(parseMemoryRow);
|
|
13022
|
+
if (globalOpts.json) {
|
|
13023
|
+
outputJson(memories);
|
|
13024
|
+
return;
|
|
13025
|
+
}
|
|
13026
|
+
if (memories.length === 0) {
|
|
13027
|
+
console.log(chalk2.yellow(`No chain found for sequence group: ${sequenceGroup}`));
|
|
13028
|
+
return;
|
|
13029
|
+
}
|
|
13030
|
+
console.log(chalk2.bold(`Chain: ${sequenceGroup} (${memories.length} step${memories.length === 1 ? "" : "s"}):
|
|
13031
|
+
`));
|
|
13032
|
+
for (let i = 0;i < memories.length; i++) {
|
|
13033
|
+
const m = memories[i];
|
|
13034
|
+
const order = m.sequence_order !== null && m.sequence_order !== undefined ? m.sequence_order : i + 1;
|
|
13035
|
+
const value = m.value.length > 120 ? m.value.slice(0, 120) + "..." : m.value;
|
|
13036
|
+
console.log(` ${chalk2.cyan(String(order) + ".")} ${chalk2.bold(m.key)}: ${value}`);
|
|
13037
|
+
}
|
|
13038
|
+
} catch (e) {
|
|
13039
|
+
handleError(e);
|
|
13040
|
+
}
|
|
13041
|
+
});
|
|
13042
|
+
program2.command("when-to-use <memory_id>").description("Show the when_to_use guidance for a memory").action((memoryId) => {
|
|
13043
|
+
try {
|
|
13044
|
+
const globalOpts = program2.opts();
|
|
13045
|
+
const resolvedId = resolveMemoryId(memoryId);
|
|
13046
|
+
const memory = getMemory(resolvedId);
|
|
13047
|
+
if (!memory) {
|
|
13048
|
+
if (globalOpts.json) {
|
|
13049
|
+
outputJson({ error: `Memory not found: ${memoryId}` });
|
|
13050
|
+
} else {
|
|
13051
|
+
console.error(chalk2.red(`Memory not found: ${memoryId}`));
|
|
13052
|
+
}
|
|
13053
|
+
process.exit(1);
|
|
13054
|
+
}
|
|
13055
|
+
const whenToUse = memory.when_to_use ?? null;
|
|
13056
|
+
if (globalOpts.json) {
|
|
13057
|
+
outputJson({ id: memory.id, key: memory.key, when_to_use: whenToUse });
|
|
13058
|
+
return;
|
|
13059
|
+
}
|
|
13060
|
+
console.log(chalk2.bold(`${memory.key} (${memory.id.slice(0, 8)})`));
|
|
13061
|
+
if (whenToUse) {
|
|
13062
|
+
console.log(` ${chalk2.cyan("when_to_use:")} ${whenToUse}`);
|
|
13063
|
+
} else {
|
|
13064
|
+
console.log(chalk2.dim(" (not set)"));
|
|
13065
|
+
}
|
|
13066
|
+
} catch (e) {
|
|
13067
|
+
handleError(e);
|
|
13068
|
+
}
|
|
13069
|
+
});
|
|
13070
|
+
var sessionsCmd = program2.command("sessions").description("Session registry \u2014 list, clean, and inspect active Claude Code sessions");
|
|
13071
|
+
sessionsCmd.command("list").description("List all active sessions in the registry").option("--project <name>", "Filter by project name").option("--agent <name>", "Filter by agent name").option("--format <fmt>", "Output format: compact (default), json, yaml").action((opts) => {
|
|
13072
|
+
try {
|
|
13073
|
+
const globalOpts = program2.opts();
|
|
13074
|
+
const { listSessions: listSessions2 } = (init_session_registry(), __toCommonJS(exports_session_registry));
|
|
13075
|
+
const sessions = listSessions2({
|
|
13076
|
+
project_name: opts.project,
|
|
13077
|
+
agent_name: opts.agent || globalOpts.agent
|
|
13078
|
+
});
|
|
13079
|
+
const fmt = getOutputFormat(opts.format);
|
|
13080
|
+
if (fmt === "json") {
|
|
13081
|
+
outputJson(sessions);
|
|
13082
|
+
return;
|
|
13083
|
+
}
|
|
13084
|
+
if (fmt === "yaml") {
|
|
13085
|
+
outputYaml(sessions);
|
|
13086
|
+
return;
|
|
13087
|
+
}
|
|
13088
|
+
if (sessions.length === 0) {
|
|
13089
|
+
console.log(chalk2.yellow("No active sessions found."));
|
|
13090
|
+
return;
|
|
13091
|
+
}
|
|
13092
|
+
console.log(chalk2.bold(`${sessions.length} active session${sessions.length === 1 ? "" : "s"}:
|
|
13093
|
+
`));
|
|
13094
|
+
for (const s of sessions) {
|
|
13095
|
+
const pid = chalk2.dim(`pid:${s.pid}`);
|
|
13096
|
+
const agent = s.agent_name ? chalk2.cyan(s.agent_name) : chalk2.dim("(no agent)");
|
|
13097
|
+
const project = s.project_name ? chalk2.yellow(s.project_name) : chalk2.dim("(no project)");
|
|
13098
|
+
const mcp = chalk2.dim(s.mcp_server);
|
|
13099
|
+
const self = s.pid === process.pid ? chalk2.green(" (self)") : "";
|
|
13100
|
+
console.log(` ${chalk2.bold(s.id)} ${pid}${self} ${agent} ${project} ${mcp}`);
|
|
13101
|
+
console.log(` ${chalk2.dim(`cwd: ${s.cwd}`)} ${chalk2.dim(`last seen: ${s.last_seen_at}`)}`);
|
|
13102
|
+
}
|
|
13103
|
+
} catch (e) {
|
|
13104
|
+
handleError(e);
|
|
13105
|
+
}
|
|
13106
|
+
});
|
|
13107
|
+
sessionsCmd.command("send <message>").description("Send a message to sessions (use MCP tool memory_send_channel for full support)").option("--agent <name>", "Target agent name").option("--project <name>", "Target project name").option("--all", "Broadcast to all sessions").action((message, opts) => {
|
|
13108
|
+
try {
|
|
13109
|
+
const globalOpts = program2.opts();
|
|
13110
|
+
const { listSessions: listSessions2 } = (init_session_registry(), __toCommonJS(exports_session_registry));
|
|
13111
|
+
if (!opts.agent && !opts.project && !opts.all) {
|
|
13112
|
+
console.error(chalk2.red("Specify a target: --agent <name>, --project <name>, or --all"));
|
|
13113
|
+
process.exit(1);
|
|
13114
|
+
}
|
|
13115
|
+
const filter = {};
|
|
13116
|
+
if (opts.agent)
|
|
13117
|
+
filter.agent_name = opts.agent;
|
|
13118
|
+
if (opts.project)
|
|
13119
|
+
filter.project_name = opts.project;
|
|
13120
|
+
const sessions = listSessions2(opts.all ? undefined : filter);
|
|
13121
|
+
if (globalOpts.json) {
|
|
13122
|
+
outputJson({
|
|
13123
|
+
message: "Channel push requires MCP server context. Use the memory_send_channel MCP tool.",
|
|
13124
|
+
matching_sessions: sessions.length,
|
|
13125
|
+
sessions: sessions.map((s) => ({
|
|
13126
|
+
id: s.id,
|
|
13127
|
+
pid: s.pid,
|
|
13128
|
+
agent_name: s.agent_name,
|
|
13129
|
+
project_name: s.project_name
|
|
13130
|
+
}))
|
|
13131
|
+
});
|
|
13132
|
+
return;
|
|
13133
|
+
}
|
|
13134
|
+
console.log(chalk2.bold(`Found ${sessions.length} matching session${sessions.length === 1 ? "" : "s"}:`));
|
|
13135
|
+
for (const s of sessions) {
|
|
13136
|
+
const agent = s.agent_name ? chalk2.cyan(s.agent_name) : chalk2.dim("(no agent)");
|
|
13137
|
+
const project = s.project_name ? chalk2.yellow(s.project_name) : "";
|
|
13138
|
+
console.log(` ${s.id} pid:${s.pid} ${agent} ${project}`);
|
|
13139
|
+
}
|
|
13140
|
+
console.log();
|
|
13141
|
+
console.log(chalk2.dim("Channel push requires the MCP server context."));
|
|
13142
|
+
console.log(chalk2.dim("Use the MCP tool:"));
|
|
13143
|
+
console.log(chalk2.cyan(` memory_send_channel(content="${message.slice(0, 60)}${message.length > 60 ? "..." : ""}")`));
|
|
13144
|
+
} catch (e) {
|
|
13145
|
+
handleError(e);
|
|
13146
|
+
}
|
|
13147
|
+
});
|
|
13148
|
+
sessionsCmd.command("clean").description("Remove dead/stale sessions from the registry").action(() => {
|
|
13149
|
+
try {
|
|
13150
|
+
const globalOpts = program2.opts();
|
|
13151
|
+
const { cleanStaleSessions: cleanStaleSessions2 } = (init_session_registry(), __toCommonJS(exports_session_registry));
|
|
13152
|
+
const cleaned = cleanStaleSessions2();
|
|
13153
|
+
if (globalOpts.json) {
|
|
13154
|
+
outputJson({ cleaned });
|
|
13155
|
+
} else if (cleaned === 0) {
|
|
13156
|
+
console.log(chalk2.green("No stale sessions found \u2014 registry is clean."));
|
|
13157
|
+
} else {
|
|
13158
|
+
console.log(chalk2.green(`Cleaned ${cleaned} stale session${cleaned === 1 ? "" : "s"}.`));
|
|
13159
|
+
}
|
|
13160
|
+
} catch (e) {
|
|
13161
|
+
handleError(e);
|
|
13162
|
+
}
|
|
13163
|
+
});
|
|
13164
|
+
program2.addCommand(makeBrainsCommand());
|
|
11584
13165
|
program2.parse(process.argv);
|