@hasna/mementos 0.11.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/brains.d.ts +3 -0
- package/dist/cli/brains.d.ts.map +1 -0
- package/dist/cli/index.js +2064 -504
- 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 +172 -4
- 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 +3816 -383
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +873 -5
- package/dist/types/index.d.ts +57 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -1
package/dist/cli/index.js
CHANGED
|
@@ -2909,6 +2909,44 @@ CREATE INDEX IF NOT EXISTS idx_memory_embeddings_model ON memory_embeddings(mode
|
|
|
2909
2909
|
|
|
2910
2910
|
PRAGMA foreign_keys = ON;
|
|
2911
2911
|
INSERT OR IGNORE INTO _migrations (id) VALUES (29);
|
|
2912
|
+
`,
|
|
2913
|
+
`
|
|
2914
|
+
ALTER TABLE memories ADD COLUMN when_to_use TEXT DEFAULT NULL;
|
|
2915
|
+
CREATE INDEX IF NOT EXISTS idx_memories_when_to_use ON memories(when_to_use) WHERE when_to_use IS NOT NULL;
|
|
2916
|
+
ALTER TABLE memory_versions ADD COLUMN when_to_use TEXT;
|
|
2917
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (30);
|
|
2918
|
+
`,
|
|
2919
|
+
`
|
|
2920
|
+
CREATE TABLE IF NOT EXISTS tool_events (
|
|
2921
|
+
id TEXT PRIMARY KEY,
|
|
2922
|
+
tool_name TEXT NOT NULL,
|
|
2923
|
+
action TEXT,
|
|
2924
|
+
success INTEGER NOT NULL DEFAULT 1,
|
|
2925
|
+
error_type TEXT CHECK(error_type IS NULL OR error_type IN ('timeout', 'permission', 'not_found', 'syntax', 'rate_limit', 'other')),
|
|
2926
|
+
error_message TEXT,
|
|
2927
|
+
tokens_used INTEGER,
|
|
2928
|
+
latency_ms INTEGER,
|
|
2929
|
+
context TEXT,
|
|
2930
|
+
lesson TEXT,
|
|
2931
|
+
when_to_use TEXT,
|
|
2932
|
+
agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
|
|
2933
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2934
|
+
session_id TEXT,
|
|
2935
|
+
metadata TEXT DEFAULT '{}',
|
|
2936
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2937
|
+
);
|
|
2938
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_tool_name ON tool_events(tool_name);
|
|
2939
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_agent ON tool_events(agent_id);
|
|
2940
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_project ON tool_events(project_id);
|
|
2941
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_success ON tool_events(success);
|
|
2942
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_created ON tool_events(created_at);
|
|
2943
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (31);
|
|
2944
|
+
`,
|
|
2945
|
+
`
|
|
2946
|
+
ALTER TABLE memories ADD COLUMN sequence_group TEXT DEFAULT NULL;
|
|
2947
|
+
ALTER TABLE memories ADD COLUMN sequence_order INTEGER DEFAULT NULL;
|
|
2948
|
+
CREATE INDEX IF NOT EXISTS idx_memories_sequence_group ON memories(sequence_group) WHERE sequence_group IS NOT NULL;
|
|
2949
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (32);
|
|
2912
2950
|
`
|
|
2913
2951
|
];
|
|
2914
2952
|
});
|
|
@@ -3317,6 +3355,9 @@ function parseMemoryRow(row) {
|
|
|
3317
3355
|
session_id: row["session_id"] || null,
|
|
3318
3356
|
machine_id: row["machine_id"] || null,
|
|
3319
3357
|
flag: row["flag"] || null,
|
|
3358
|
+
when_to_use: row["when_to_use"] || null,
|
|
3359
|
+
sequence_group: row["sequence_group"] || null,
|
|
3360
|
+
sequence_order: row["sequence_order"] ?? null,
|
|
3320
3361
|
content_type: row["content_type"] || "text",
|
|
3321
3362
|
namespace: row["namespace"] || null,
|
|
3322
3363
|
created_by_agent: row["created_by_agent"] || null,
|
|
@@ -3375,6 +3416,7 @@ function createMemory(input, dedupeMode = "merge", db) {
|
|
|
3375
3416
|
d.run(`UPDATE memories SET
|
|
3376
3417
|
value = ?, category = ?, summary = ?, tags = ?,
|
|
3377
3418
|
importance = ?, metadata = ?, expires_at = ?,
|
|
3419
|
+
when_to_use = ?,
|
|
3378
3420
|
pinned = COALESCE(pinned, 0),
|
|
3379
3421
|
version = version + 1, updated_at = ?
|
|
3380
3422
|
WHERE id = ?`, [
|
|
@@ -3385,6 +3427,7 @@ function createMemory(input, dedupeMode = "merge", db) {
|
|
|
3385
3427
|
input.importance ?? 5,
|
|
3386
3428
|
metadataJson,
|
|
3387
3429
|
expiresAt,
|
|
3430
|
+
input.when_to_use || null,
|
|
3388
3431
|
timestamp,
|
|
3389
3432
|
existing.id
|
|
3390
3433
|
]);
|
|
@@ -3409,8 +3452,8 @@ function createMemory(input, dedupeMode = "merge", db) {
|
|
|
3409
3452
|
return merged;
|
|
3410
3453
|
}
|
|
3411
3454
|
}
|
|
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, ?, ?, ?, ?, ?, ?)`, [
|
|
3455
|
+
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)
|
|
3456
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 1, ?, ?, ?, ?, ?, ?)`, [
|
|
3414
3457
|
id,
|
|
3415
3458
|
input.key,
|
|
3416
3459
|
input.value,
|
|
@@ -3426,6 +3469,9 @@ function createMemory(input, dedupeMode = "merge", db) {
|
|
|
3426
3469
|
input.machine_id || null,
|
|
3427
3470
|
input.namespace || null,
|
|
3428
3471
|
input.agent_id || null,
|
|
3472
|
+
input.when_to_use || null,
|
|
3473
|
+
input.sequence_group || null,
|
|
3474
|
+
input.sequence_order ?? null,
|
|
3429
3475
|
metadataJson,
|
|
3430
3476
|
expiresAt,
|
|
3431
3477
|
input.metadata?.valid_from ?? timestamp,
|
|
@@ -3636,8 +3682,8 @@ function updateMemory(id, input, db) {
|
|
|
3636
3682
|
throw new VersionConflictError(id, input.version, existing.version);
|
|
3637
3683
|
}
|
|
3638
3684
|
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
3685
|
+
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)
|
|
3686
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
3641
3687
|
uuid(),
|
|
3642
3688
|
existing.id,
|
|
3643
3689
|
existing.version,
|
|
@@ -3649,6 +3695,7 @@ function updateMemory(id, input, db) {
|
|
|
3649
3695
|
existing.summary,
|
|
3650
3696
|
existing.pinned ? 1 : 0,
|
|
3651
3697
|
existing.status,
|
|
3698
|
+
existing.when_to_use || null,
|
|
3652
3699
|
existing.updated_at
|
|
3653
3700
|
]);
|
|
3654
3701
|
} catch {}
|
|
@@ -3694,6 +3741,10 @@ function updateMemory(id, input, db) {
|
|
|
3694
3741
|
sets.push("flag = ?");
|
|
3695
3742
|
params.push(input.flag ?? null);
|
|
3696
3743
|
}
|
|
3744
|
+
if (input.when_to_use !== undefined) {
|
|
3745
|
+
sets.push("when_to_use = ?");
|
|
3746
|
+
params.push(input.when_to_use ?? null);
|
|
3747
|
+
}
|
|
3697
3748
|
if (input.tags !== undefined) {
|
|
3698
3749
|
sets.push("tags = ?");
|
|
3699
3750
|
params.push(JSON.stringify(input.tags));
|
|
@@ -5965,6 +6016,311 @@ var init_synthesis = __esm(() => {
|
|
|
5965
6016
|
init_database();
|
|
5966
6017
|
});
|
|
5967
6018
|
|
|
6019
|
+
// src/lib/when-to-use-generator.ts
|
|
6020
|
+
var exports_when_to_use_generator = {};
|
|
6021
|
+
__export(exports_when_to_use_generator, {
|
|
6022
|
+
generateWhenToUse: () => generateWhenToUse,
|
|
6023
|
+
autoGenerateWhenToUse: () => autoGenerateWhenToUse
|
|
6024
|
+
});
|
|
6025
|
+
async function generateWhenToUse(key, value, category, tags) {
|
|
6026
|
+
if (process.env["MEMENTOS_AUTO_WHEN_TO_USE"] !== "true")
|
|
6027
|
+
return null;
|
|
6028
|
+
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
6029
|
+
if (!apiKey)
|
|
6030
|
+
return null;
|
|
6031
|
+
try {
|
|
6032
|
+
const userMessage = `Key: "${key}"
|
|
6033
|
+
Value: "${value}"
|
|
6034
|
+
Category: ${category}
|
|
6035
|
+
Tags: ${tags.join(", ") || "none"}
|
|
6036
|
+
|
|
6037
|
+
Generate the when_to_use activation context (1-2 sentences):`;
|
|
6038
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
6039
|
+
method: "POST",
|
|
6040
|
+
headers: {
|
|
6041
|
+
"x-api-key": apiKey,
|
|
6042
|
+
"anthropic-version": "2023-06-01",
|
|
6043
|
+
"content-type": "application/json"
|
|
6044
|
+
},
|
|
6045
|
+
body: JSON.stringify({
|
|
6046
|
+
model: "claude-haiku-4-5-20251001",
|
|
6047
|
+
max_tokens: 150,
|
|
6048
|
+
system: SYSTEM_PROMPT2,
|
|
6049
|
+
messages: [{ role: "user", content: userMessage }]
|
|
6050
|
+
})
|
|
6051
|
+
});
|
|
6052
|
+
if (!response.ok)
|
|
6053
|
+
return null;
|
|
6054
|
+
const data = await response.json();
|
|
6055
|
+
const text = data.content?.[0]?.text?.trim();
|
|
6056
|
+
return text || null;
|
|
6057
|
+
} catch {
|
|
6058
|
+
return null;
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
async function autoGenerateWhenToUse(ctx) {
|
|
6062
|
+
if (ctx.memory.when_to_use)
|
|
6063
|
+
return;
|
|
6064
|
+
if (process.env["MEMENTOS_AUTO_WHEN_TO_USE"] !== "true")
|
|
6065
|
+
return;
|
|
6066
|
+
try {
|
|
6067
|
+
const whenToUse = await generateWhenToUse(ctx.memory.key, ctx.memory.value, ctx.memory.category, ctx.memory.tags);
|
|
6068
|
+
if (whenToUse) {
|
|
6069
|
+
const db = getDatabase();
|
|
6070
|
+
db.run("UPDATE memories SET when_to_use = ? WHERE id = ? AND when_to_use IS NULL", [whenToUse, ctx.memory.id]);
|
|
6071
|
+
}
|
|
6072
|
+
} catch {}
|
|
6073
|
+
}
|
|
6074
|
+
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.
|
|
6075
|
+
|
|
6076
|
+
Rules:
|
|
6077
|
+
- Start with "When" or "If"
|
|
6078
|
+
- Describe the situation, not the content
|
|
6079
|
+
- Be specific enough to avoid false matches but general enough to catch relevant scenarios
|
|
6080
|
+
- Focus on the task/action the agent would be doing, not what the memory contains
|
|
6081
|
+
|
|
6082
|
+
Examples:
|
|
6083
|
+
- Key: "preferred-language", Value: "Always use TypeScript, never JavaScript" \u2192 "When choosing a programming language for a new file or project"
|
|
6084
|
+
- Key: "db-migration-order", Value: "Always run migrations before deploying" \u2192 "When deploying or updating database schema"
|
|
6085
|
+
- Key: "bash-chain-bug", Value: "Bash tool mangles && chains" \u2192 "When chaining commands with && in the Bash tool"`;
|
|
6086
|
+
var init_when_to_use_generator = __esm(() => {
|
|
6087
|
+
init_database();
|
|
6088
|
+
});
|
|
6089
|
+
|
|
6090
|
+
// src/lib/profile-synthesizer.ts
|
|
6091
|
+
var exports_profile_synthesizer = {};
|
|
6092
|
+
__export(exports_profile_synthesizer, {
|
|
6093
|
+
synthesizeProfile: () => synthesizeProfile,
|
|
6094
|
+
markProfileStale: () => markProfileStale,
|
|
6095
|
+
getProfileKey: () => getProfileKey
|
|
6096
|
+
});
|
|
6097
|
+
function getProfileKey(scope, id) {
|
|
6098
|
+
return `_profile_${scope}_${id}`;
|
|
6099
|
+
}
|
|
6100
|
+
async function synthesizeProfile(options) {
|
|
6101
|
+
const scope = options.scope || (options.project_id ? "project" : options.agent_id ? "agent" : "global");
|
|
6102
|
+
const id = options.project_id || options.agent_id || "global";
|
|
6103
|
+
const profileKey = getProfileKey(scope, id);
|
|
6104
|
+
if (!options.force_refresh) {
|
|
6105
|
+
const cached = getMemoryByKey(profileKey, "shared", undefined, options.project_id);
|
|
6106
|
+
if (cached) {
|
|
6107
|
+
const age = Date.now() - new Date(cached.updated_at).getTime();
|
|
6108
|
+
const maxAge = 24 * 60 * 60 * 1000;
|
|
6109
|
+
const isStale = cached.metadata?.stale === true;
|
|
6110
|
+
if (age < maxAge && !isStale) {
|
|
6111
|
+
return { profile: cached.value, memory_count: 0, from_cache: true };
|
|
6112
|
+
}
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
const prefMemories = listMemories({
|
|
6116
|
+
category: "preference",
|
|
6117
|
+
project_id: options.project_id,
|
|
6118
|
+
status: "active",
|
|
6119
|
+
limit: 30
|
|
6120
|
+
});
|
|
6121
|
+
const factMemories = listMemories({
|
|
6122
|
+
category: "fact",
|
|
6123
|
+
project_id: options.project_id,
|
|
6124
|
+
status: "active",
|
|
6125
|
+
limit: 30
|
|
6126
|
+
});
|
|
6127
|
+
const allMemories = [...prefMemories, ...factMemories];
|
|
6128
|
+
if (allMemories.length === 0)
|
|
6129
|
+
return null;
|
|
6130
|
+
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
6131
|
+
if (!apiKey) {
|
|
6132
|
+
const lines = allMemories.map((m) => `- ${m.key}: ${m.value}`).join(`
|
|
6133
|
+
`);
|
|
6134
|
+
const fallbackProfile = `## Profile
|
|
6135
|
+
${lines}`;
|
|
6136
|
+
saveProfile(profileKey, fallbackProfile, allMemories.length, options);
|
|
6137
|
+
return { profile: fallbackProfile, memory_count: allMemories.length, from_cache: false };
|
|
6138
|
+
}
|
|
6139
|
+
try {
|
|
6140
|
+
const memoryList = allMemories.sort((a, b) => b.importance - a.importance).map((m) => `[${m.category}] ${m.key}: ${m.value}`).join(`
|
|
6141
|
+
`);
|
|
6142
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
6143
|
+
method: "POST",
|
|
6144
|
+
headers: {
|
|
6145
|
+
"x-api-key": apiKey,
|
|
6146
|
+
"anthropic-version": "2023-06-01",
|
|
6147
|
+
"content-type": "application/json"
|
|
6148
|
+
},
|
|
6149
|
+
body: JSON.stringify({
|
|
6150
|
+
model: "claude-haiku-4-5-20251001",
|
|
6151
|
+
max_tokens: 500,
|
|
6152
|
+
system: PROFILE_PROMPT,
|
|
6153
|
+
messages: [{ role: "user", content: `Synthesize a profile from these ${allMemories.length} memories:
|
|
6154
|
+
|
|
6155
|
+
${memoryList}` }]
|
|
6156
|
+
})
|
|
6157
|
+
});
|
|
6158
|
+
if (!response.ok)
|
|
6159
|
+
return null;
|
|
6160
|
+
const data = await response.json();
|
|
6161
|
+
const profile = data.content?.[0]?.text?.trim();
|
|
6162
|
+
if (!profile)
|
|
6163
|
+
return null;
|
|
6164
|
+
saveProfile(profileKey, profile, allMemories.length, options);
|
|
6165
|
+
return { profile, memory_count: allMemories.length, from_cache: false };
|
|
6166
|
+
} catch {
|
|
6167
|
+
return null;
|
|
6168
|
+
}
|
|
6169
|
+
}
|
|
6170
|
+
function saveProfile(key, value, memoryCount, options) {
|
|
6171
|
+
try {
|
|
6172
|
+
createMemory({
|
|
6173
|
+
key,
|
|
6174
|
+
value,
|
|
6175
|
+
category: "fact",
|
|
6176
|
+
scope: "shared",
|
|
6177
|
+
importance: 10,
|
|
6178
|
+
source: "auto",
|
|
6179
|
+
tags: ["profile", "synthesized"],
|
|
6180
|
+
when_to_use: "When needing to understand this agent's or project's preferences, style, and conventions",
|
|
6181
|
+
metadata: { memory_count: memoryCount, synthesized_at: new Date().toISOString(), stale: false },
|
|
6182
|
+
agent_id: options.agent_id,
|
|
6183
|
+
project_id: options.project_id
|
|
6184
|
+
});
|
|
6185
|
+
} catch {}
|
|
6186
|
+
}
|
|
6187
|
+
function markProfileStale(projectId, _agentId) {
|
|
6188
|
+
try {
|
|
6189
|
+
const { getDatabase: getDatabase2 } = (init_database(), __toCommonJS(exports_database));
|
|
6190
|
+
const db = getDatabase2();
|
|
6191
|
+
db.run(`UPDATE memories SET metadata = json_set(COALESCE(metadata, '{}'), '$.stale', json('true'))
|
|
6192
|
+
WHERE key LIKE '_profile_%' AND COALESCE(project_id, '') = ?`, [projectId || ""]);
|
|
6193
|
+
} catch {}
|
|
6194
|
+
}
|
|
6195
|
+
var PROFILE_PROMPT = `You synthesize a coherent agent/project profile from individual preference and fact memories.
|
|
6196
|
+
|
|
6197
|
+
Output a concise profile (200-300 words max) organized by:
|
|
6198
|
+
- **Stack & Tools**: Languages, frameworks, package managers, etc.
|
|
6199
|
+
- **Code Style**: Formatting, patterns, naming conventions
|
|
6200
|
+
- **Workflow**: Testing, deployment, git practices
|
|
6201
|
+
- **Communication**: Response style, verbosity, formatting preferences
|
|
6202
|
+
- **Key Facts**: Architecture decisions, constraints, team conventions
|
|
6203
|
+
|
|
6204
|
+
Only include sections that have relevant data. Be specific and actionable.
|
|
6205
|
+
Output in markdown format.`;
|
|
6206
|
+
var init_profile_synthesizer = __esm(() => {
|
|
6207
|
+
init_memories();
|
|
6208
|
+
});
|
|
6209
|
+
|
|
6210
|
+
// src/lib/contradiction.ts
|
|
6211
|
+
var exports_contradiction = {};
|
|
6212
|
+
__export(exports_contradiction, {
|
|
6213
|
+
invalidateFact: () => invalidateFact,
|
|
6214
|
+
detectContradiction: () => detectContradiction
|
|
6215
|
+
});
|
|
6216
|
+
function invalidateFact(oldMemoryId, newMemoryId, db) {
|
|
6217
|
+
const d = db || getDatabase();
|
|
6218
|
+
const timestamp = now();
|
|
6219
|
+
d.run("UPDATE memories SET valid_until = ?, updated_at = ? WHERE id = ?", [timestamp, timestamp, oldMemoryId]);
|
|
6220
|
+
if (newMemoryId) {
|
|
6221
|
+
const row = d.query("SELECT metadata FROM memories WHERE id = ?").get(newMemoryId);
|
|
6222
|
+
if (row) {
|
|
6223
|
+
const metadata = JSON.parse(row.metadata || "{}");
|
|
6224
|
+
metadata.supersedes_id = oldMemoryId;
|
|
6225
|
+
d.run("UPDATE memories SET metadata = ?, updated_at = ? WHERE id = ?", [JSON.stringify(metadata), timestamp, newMemoryId]);
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
return {
|
|
6229
|
+
invalidated_memory_id: oldMemoryId,
|
|
6230
|
+
new_memory_id: newMemoryId || null,
|
|
6231
|
+
valid_until: timestamp,
|
|
6232
|
+
supersedes_id: oldMemoryId
|
|
6233
|
+
};
|
|
6234
|
+
}
|
|
6235
|
+
function heuristicContradictionScore(newValue, existingValue, newKey, existingKey) {
|
|
6236
|
+
if (newKey !== existingKey)
|
|
6237
|
+
return 0;
|
|
6238
|
+
const newLower = newValue.toLowerCase().trim();
|
|
6239
|
+
const existingLower = existingValue.toLowerCase().trim();
|
|
6240
|
+
if (newLower === existingLower)
|
|
6241
|
+
return 0;
|
|
6242
|
+
const newWords = new Set(newLower.split(/\s+/));
|
|
6243
|
+
const existingWords = new Set(existingLower.split(/\s+/));
|
|
6244
|
+
let overlap = 0;
|
|
6245
|
+
for (const w of newWords) {
|
|
6246
|
+
if (existingWords.has(w))
|
|
6247
|
+
overlap++;
|
|
6248
|
+
}
|
|
6249
|
+
const totalUnique = new Set([...newWords, ...existingWords]).size;
|
|
6250
|
+
const overlapRatio = totalUnique > 0 ? overlap / totalUnique : 0;
|
|
6251
|
+
if (overlapRatio < 0.3)
|
|
6252
|
+
return 0.7;
|
|
6253
|
+
if (overlapRatio < 0.5)
|
|
6254
|
+
return 0.4;
|
|
6255
|
+
return 0.1;
|
|
6256
|
+
}
|
|
6257
|
+
async function llmContradictionCheck(_newValue, _existingValue, _key) {
|
|
6258
|
+
const provider = providerRegistry.getAvailable();
|
|
6259
|
+
if (!provider) {
|
|
6260
|
+
return { contradicts: false, confidence: 0, reasoning: "No LLM provider available" };
|
|
6261
|
+
}
|
|
6262
|
+
try {
|
|
6263
|
+
return { contradicts: false, confidence: 0, reasoning: "LLM check skipped \u2014 using heuristic only" };
|
|
6264
|
+
} catch {
|
|
6265
|
+
return { contradicts: false, confidence: 0, reasoning: "LLM check failed" };
|
|
6266
|
+
}
|
|
6267
|
+
}
|
|
6268
|
+
async function detectContradiction(newKey, newValue, options = {}, db) {
|
|
6269
|
+
const d = db || getDatabase();
|
|
6270
|
+
const { scope, project_id, min_importance = 7, use_llm = false } = options;
|
|
6271
|
+
const conditions = ["key = ?", "status = 'active'", "importance >= ?"];
|
|
6272
|
+
const params = [newKey, min_importance];
|
|
6273
|
+
if (scope) {
|
|
6274
|
+
conditions.push("scope = ?");
|
|
6275
|
+
params.push(scope);
|
|
6276
|
+
}
|
|
6277
|
+
if (project_id) {
|
|
6278
|
+
conditions.push("project_id = ?");
|
|
6279
|
+
params.push(project_id);
|
|
6280
|
+
}
|
|
6281
|
+
conditions.push("(valid_until IS NULL OR valid_until > datetime('now'))");
|
|
6282
|
+
const sql = `SELECT * FROM memories WHERE ${conditions.join(" AND ")} ORDER BY importance DESC LIMIT 10`;
|
|
6283
|
+
const rows = d.query(sql).all(...params);
|
|
6284
|
+
if (rows.length === 0) {
|
|
6285
|
+
return { contradicts: false, conflicting_memory: null, confidence: 0, reasoning: "No existing memories with this key" };
|
|
6286
|
+
}
|
|
6287
|
+
let bestContradiction = {
|
|
6288
|
+
contradicts: false,
|
|
6289
|
+
conflicting_memory: null,
|
|
6290
|
+
confidence: 0,
|
|
6291
|
+
reasoning: "No contradiction detected"
|
|
6292
|
+
};
|
|
6293
|
+
for (const row of rows) {
|
|
6294
|
+
const existing = parseMemoryRow(row);
|
|
6295
|
+
const heuristicScore = heuristicContradictionScore(newValue, existing.value, newKey, existing.key);
|
|
6296
|
+
if (heuristicScore > bestContradiction.confidence) {
|
|
6297
|
+
bestContradiction = {
|
|
6298
|
+
contradicts: heuristicScore >= 0.5,
|
|
6299
|
+
conflicting_memory: existing,
|
|
6300
|
+
confidence: heuristicScore,
|
|
6301
|
+
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}"`
|
|
6302
|
+
};
|
|
6303
|
+
}
|
|
6304
|
+
}
|
|
6305
|
+
if (use_llm && bestContradiction.confidence >= 0.3 && bestContradiction.confidence < 0.7 && bestContradiction.conflicting_memory) {
|
|
6306
|
+
const llmResult = await llmContradictionCheck(newValue, bestContradiction.conflicting_memory.value, newKey);
|
|
6307
|
+
if (llmResult.confidence > bestContradiction.confidence) {
|
|
6308
|
+
bestContradiction = {
|
|
6309
|
+
...bestContradiction,
|
|
6310
|
+
contradicts: llmResult.contradicts,
|
|
6311
|
+
confidence: llmResult.confidence,
|
|
6312
|
+
reasoning: llmResult.reasoning
|
|
6313
|
+
};
|
|
6314
|
+
}
|
|
6315
|
+
}
|
|
6316
|
+
return bestContradiction;
|
|
6317
|
+
}
|
|
6318
|
+
var init_contradiction = __esm(() => {
|
|
6319
|
+
init_database();
|
|
6320
|
+
init_memories();
|
|
6321
|
+
init_registry();
|
|
6322
|
+
});
|
|
6323
|
+
|
|
5968
6324
|
// src/lib/built-in-hooks.ts
|
|
5969
6325
|
var exports_built_in_hooks = {};
|
|
5970
6326
|
__export(exports_built_in_hooks, {
|
|
@@ -6091,10 +6447,76 @@ var init_built_in_hooks = __esm(() => {
|
|
|
6091
6447
|
description: "Generate and store vector embedding for semantic memory search",
|
|
6092
6448
|
handler: async (ctx) => {
|
|
6093
6449
|
const { indexMemoryEmbedding: indexMemoryEmbedding2 } = await Promise.resolve().then(() => (init_memories(), exports_memories));
|
|
6094
|
-
const text = [ctx.memory.value, ctx.memory.summary].filter(Boolean).join(" ");
|
|
6450
|
+
const text = ctx.memory.when_to_use || [ctx.memory.value, ctx.memory.summary].filter(Boolean).join(" ");
|
|
6095
6451
|
indexMemoryEmbedding2(ctx.memory.id, text);
|
|
6096
6452
|
}
|
|
6097
6453
|
});
|
|
6454
|
+
hookRegistry.register({
|
|
6455
|
+
type: "PostMemorySave",
|
|
6456
|
+
blocking: false,
|
|
6457
|
+
builtin: true,
|
|
6458
|
+
priority: 60,
|
|
6459
|
+
description: "Auto-generate when_to_use activation context via LLM if missing",
|
|
6460
|
+
handler: async (ctx) => {
|
|
6461
|
+
const { autoGenerateWhenToUse: autoGenerateWhenToUse2 } = await Promise.resolve().then(() => (init_when_to_use_generator(), exports_when_to_use_generator));
|
|
6462
|
+
await autoGenerateWhenToUse2(ctx);
|
|
6463
|
+
}
|
|
6464
|
+
});
|
|
6465
|
+
hookRegistry.register({
|
|
6466
|
+
type: "PostMemorySave",
|
|
6467
|
+
blocking: false,
|
|
6468
|
+
builtin: true,
|
|
6469
|
+
priority: 65,
|
|
6470
|
+
description: "Mark synthesized profile as stale when a preference or fact memory is saved",
|
|
6471
|
+
handler: async (ctx) => {
|
|
6472
|
+
const category = ctx.memory?.category;
|
|
6473
|
+
if (category !== "preference" && category !== "fact")
|
|
6474
|
+
return;
|
|
6475
|
+
try {
|
|
6476
|
+
const { markProfileStale: markProfileStale2 } = await Promise.resolve().then(() => (init_profile_synthesizer(), exports_profile_synthesizer));
|
|
6477
|
+
markProfileStale2(ctx.projectId, ctx.agentId);
|
|
6478
|
+
} catch {}
|
|
6479
|
+
}
|
|
6480
|
+
});
|
|
6481
|
+
hookRegistry.register({
|
|
6482
|
+
type: "PostMemorySave",
|
|
6483
|
+
blocking: false,
|
|
6484
|
+
builtin: true,
|
|
6485
|
+
priority: 70,
|
|
6486
|
+
description: "Auto-decay importance of existing memories contradicted by the newly saved memory",
|
|
6487
|
+
handler: async (ctx) => {
|
|
6488
|
+
if (ctx.wasUpdated)
|
|
6489
|
+
return;
|
|
6490
|
+
const memory = ctx.memory;
|
|
6491
|
+
if (memory.category !== "fact" && memory.category !== "knowledge")
|
|
6492
|
+
return;
|
|
6493
|
+
try {
|
|
6494
|
+
const { detectContradiction: detectContradiction2 } = await Promise.resolve().then(() => (init_contradiction(), exports_contradiction));
|
|
6495
|
+
const { updateMemory: updateMemory2, getMemory: getMemory2 } = await Promise.resolve().then(() => (init_memories(), exports_memories));
|
|
6496
|
+
const result = await detectContradiction2(memory.key, memory.value, {
|
|
6497
|
+
scope: memory.scope,
|
|
6498
|
+
project_id: ctx.projectId,
|
|
6499
|
+
min_importance: 1
|
|
6500
|
+
});
|
|
6501
|
+
if (!result.contradicts || !result.conflicting_memory)
|
|
6502
|
+
return;
|
|
6503
|
+
const conflicting = result.conflicting_memory;
|
|
6504
|
+
if (conflicting.id === memory.id)
|
|
6505
|
+
return;
|
|
6506
|
+
const fresh = getMemory2(conflicting.id);
|
|
6507
|
+
if (!fresh || fresh.status !== "active")
|
|
6508
|
+
return;
|
|
6509
|
+
const halvedImportance = Math.max(1, Math.floor(fresh.importance / 2));
|
|
6510
|
+
const metadata = { ...fresh.metadata || {}, contradicted_by: memory.id };
|
|
6511
|
+
updateMemory2(fresh.id, {
|
|
6512
|
+
importance: halvedImportance,
|
|
6513
|
+
flag: "contradicted",
|
|
6514
|
+
metadata,
|
|
6515
|
+
version: fresh.version
|
|
6516
|
+
});
|
|
6517
|
+
} catch {}
|
|
6518
|
+
}
|
|
6519
|
+
});
|
|
6098
6520
|
hookRegistry.register({
|
|
6099
6521
|
type: "PostMemoryInject",
|
|
6100
6522
|
blocking: false,
|
|
@@ -7163,44 +7585,417 @@ var init_session_jobs = __esm(() => {
|
|
|
7163
7585
|
init_database();
|
|
7164
7586
|
});
|
|
7165
7587
|
|
|
7166
|
-
// src/
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7588
|
+
// src/db/tool-events.ts
|
|
7589
|
+
var exports_tool_events = {};
|
|
7590
|
+
__export(exports_tool_events, {
|
|
7591
|
+
saveToolEvent: () => saveToolEvent,
|
|
7592
|
+
getToolStats: () => getToolStats,
|
|
7593
|
+
getToolLessons: () => getToolLessons,
|
|
7594
|
+
getToolEvents: () => getToolEvents,
|
|
7595
|
+
getToolEvent: () => getToolEvent,
|
|
7596
|
+
deleteToolEvents: () => deleteToolEvents
|
|
7597
|
+
});
|
|
7598
|
+
function parseToolEventRow(row) {
|
|
7599
|
+
return {
|
|
7600
|
+
id: row["id"],
|
|
7601
|
+
tool_name: row["tool_name"],
|
|
7602
|
+
action: row["action"] || null,
|
|
7603
|
+
success: !!row["success"],
|
|
7604
|
+
error_type: row["error_type"] || null,
|
|
7605
|
+
error_message: row["error_message"] || null,
|
|
7606
|
+
tokens_used: row["tokens_used"] ?? null,
|
|
7607
|
+
latency_ms: row["latency_ms"] ?? null,
|
|
7608
|
+
context: row["context"] || null,
|
|
7609
|
+
lesson: row["lesson"] || null,
|
|
7610
|
+
when_to_use: row["when_to_use"] || null,
|
|
7611
|
+
agent_id: row["agent_id"] || null,
|
|
7612
|
+
project_id: row["project_id"] || null,
|
|
7613
|
+
session_id: row["session_id"] || null,
|
|
7614
|
+
metadata: JSON.parse(row["metadata"] || "{}"),
|
|
7615
|
+
created_at: row["created_at"]
|
|
7616
|
+
};
|
|
7617
|
+
}
|
|
7618
|
+
function saveToolEvent(input, db) {
|
|
7619
|
+
const d = db || getDatabase();
|
|
7620
|
+
const id = uuid();
|
|
7621
|
+
const timestamp = now();
|
|
7622
|
+
const metadataJson = JSON.stringify(input.metadata || {});
|
|
7623
|
+
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)
|
|
7624
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
7625
|
+
id,
|
|
7626
|
+
input.tool_name,
|
|
7627
|
+
input.action || null,
|
|
7628
|
+
input.success ? 1 : 0,
|
|
7629
|
+
input.error_type || null,
|
|
7630
|
+
input.error_message || null,
|
|
7631
|
+
input.tokens_used ?? null,
|
|
7632
|
+
input.latency_ms ?? null,
|
|
7633
|
+
input.context || null,
|
|
7634
|
+
input.lesson || null,
|
|
7635
|
+
input.when_to_use || null,
|
|
7636
|
+
input.agent_id || null,
|
|
7637
|
+
input.project_id || null,
|
|
7638
|
+
input.session_id || null,
|
|
7639
|
+
metadataJson,
|
|
7640
|
+
timestamp
|
|
7641
|
+
]);
|
|
7642
|
+
return getToolEvent(id, d);
|
|
7643
|
+
}
|
|
7644
|
+
function getToolEvent(id, db) {
|
|
7645
|
+
const d = db || getDatabase();
|
|
7646
|
+
const row = d.query("SELECT * FROM tool_events WHERE id = ?").get(id);
|
|
7647
|
+
if (!row)
|
|
7648
|
+
return null;
|
|
7649
|
+
return parseToolEventRow(row);
|
|
7650
|
+
}
|
|
7651
|
+
function getToolEvents(filters, db) {
|
|
7652
|
+
const d = db || getDatabase();
|
|
7653
|
+
const conditions = [];
|
|
7654
|
+
const params = [];
|
|
7655
|
+
if (filters.tool_name) {
|
|
7656
|
+
conditions.push("tool_name = ?");
|
|
7657
|
+
params.push(filters.tool_name);
|
|
7180
7658
|
}
|
|
7181
|
-
|
|
7659
|
+
if (filters.agent_id) {
|
|
7660
|
+
conditions.push("agent_id = ?");
|
|
7661
|
+
params.push(filters.agent_id);
|
|
7662
|
+
}
|
|
7663
|
+
if (filters.project_id) {
|
|
7664
|
+
conditions.push("project_id = ?");
|
|
7665
|
+
params.push(filters.project_id);
|
|
7666
|
+
}
|
|
7667
|
+
if (filters.success !== undefined) {
|
|
7668
|
+
conditions.push("success = ?");
|
|
7669
|
+
params.push(filters.success ? 1 : 0);
|
|
7670
|
+
}
|
|
7671
|
+
if (filters.from_date) {
|
|
7672
|
+
conditions.push("created_at >= ?");
|
|
7673
|
+
params.push(filters.from_date);
|
|
7674
|
+
}
|
|
7675
|
+
if (filters.to_date) {
|
|
7676
|
+
conditions.push("created_at <= ?");
|
|
7677
|
+
params.push(filters.to_date);
|
|
7678
|
+
}
|
|
7679
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
7680
|
+
const limit = filters.limit || 50;
|
|
7681
|
+
const offset = filters.offset || 0;
|
|
7682
|
+
const rows = d.query(`SELECT * FROM tool_events ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
7683
|
+
return rows.map(parseToolEventRow);
|
|
7182
7684
|
}
|
|
7183
|
-
|
|
7184
|
-
const
|
|
7185
|
-
|
|
7685
|
+
function getToolStats(tool_name, project_id, db) {
|
|
7686
|
+
const d = db || getDatabase();
|
|
7687
|
+
let where = "WHERE tool_name = ?";
|
|
7688
|
+
const params = [tool_name];
|
|
7689
|
+
if (project_id) {
|
|
7690
|
+
where += " AND project_id = ?";
|
|
7691
|
+
params.push(project_id);
|
|
7692
|
+
}
|
|
7693
|
+
const stats = d.query(`SELECT
|
|
7694
|
+
COUNT(*) as total_calls,
|
|
7695
|
+
SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as success_count,
|
|
7696
|
+
SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as failure_count,
|
|
7697
|
+
AVG(CASE WHEN tokens_used IS NOT NULL THEN tokens_used END) as avg_tokens,
|
|
7698
|
+
AVG(CASE WHEN latency_ms IS NOT NULL THEN latency_ms END) as avg_latency_ms,
|
|
7699
|
+
MAX(created_at) as last_used
|
|
7700
|
+
FROM tool_events ${where}`).get(...params);
|
|
7701
|
+
const total = stats["total_calls"] || 0;
|
|
7702
|
+
const successCount = stats["success_count"] || 0;
|
|
7703
|
+
const errorRows = d.query(`SELECT error_type, COUNT(*) as count
|
|
7704
|
+
FROM tool_events ${where} AND error_type IS NOT NULL
|
|
7705
|
+
GROUP BY error_type ORDER BY count DESC LIMIT 5`).all(...params);
|
|
7706
|
+
return {
|
|
7707
|
+
tool_name,
|
|
7708
|
+
total_calls: total,
|
|
7709
|
+
success_count: successCount,
|
|
7710
|
+
failure_count: stats["failure_count"] || 0,
|
|
7711
|
+
success_rate: total > 0 ? successCount / total : 0,
|
|
7712
|
+
avg_tokens: stats["avg_tokens"] ?? null,
|
|
7713
|
+
avg_latency_ms: stats["avg_latency_ms"] ?? null,
|
|
7714
|
+
common_errors: errorRows,
|
|
7715
|
+
last_used: stats["last_used"] || ""
|
|
7716
|
+
};
|
|
7717
|
+
}
|
|
7718
|
+
function getToolLessons(tool_name, project_id, limit, db) {
|
|
7719
|
+
const d = db || getDatabase();
|
|
7720
|
+
let where = "WHERE tool_name = ? AND lesson IS NOT NULL";
|
|
7721
|
+
const params = [tool_name];
|
|
7722
|
+
if (project_id) {
|
|
7723
|
+
where += " AND project_id = ?";
|
|
7724
|
+
params.push(project_id);
|
|
7725
|
+
}
|
|
7726
|
+
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);
|
|
7727
|
+
return rows;
|
|
7728
|
+
}
|
|
7729
|
+
function deleteToolEvents(filters, db) {
|
|
7730
|
+
const d = db || getDatabase();
|
|
7731
|
+
const conditions = [];
|
|
7732
|
+
const params = [];
|
|
7733
|
+
if (filters.tool_name) {
|
|
7734
|
+
conditions.push("tool_name = ?");
|
|
7735
|
+
params.push(filters.tool_name);
|
|
7736
|
+
}
|
|
7737
|
+
if (filters.agent_id) {
|
|
7738
|
+
conditions.push("agent_id = ?");
|
|
7739
|
+
params.push(filters.agent_id);
|
|
7740
|
+
}
|
|
7741
|
+
if (filters.project_id) {
|
|
7742
|
+
conditions.push("project_id = ?");
|
|
7743
|
+
params.push(filters.project_id);
|
|
7744
|
+
}
|
|
7745
|
+
if (filters.before_date) {
|
|
7746
|
+
conditions.push("created_at < ?");
|
|
7747
|
+
params.push(filters.before_date);
|
|
7748
|
+
}
|
|
7749
|
+
if (conditions.length === 0)
|
|
7186
7750
|
return 0;
|
|
7751
|
+
const result = d.run(`DELETE FROM tool_events WHERE ${conditions.join(" AND ")}`, params);
|
|
7752
|
+
return result.changes;
|
|
7753
|
+
}
|
|
7754
|
+
var init_tool_events = __esm(() => {
|
|
7755
|
+
init_database();
|
|
7756
|
+
});
|
|
7757
|
+
|
|
7758
|
+
// src/lib/tool-lesson-extractor.ts
|
|
7759
|
+
async function extractToolLessons(transcript, options) {
|
|
7760
|
+
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
7761
|
+
if (!apiKey)
|
|
7762
|
+
return [];
|
|
7187
7763
|
try {
|
|
7188
|
-
const
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7764
|
+
const truncated = transcript.length > 8000 ? transcript.slice(0, 8000) + `
|
|
7765
|
+
[...truncated]` : transcript;
|
|
7766
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
7767
|
+
method: "POST",
|
|
7768
|
+
headers: {
|
|
7769
|
+
"x-api-key": apiKey,
|
|
7770
|
+
"anthropic-version": "2023-06-01",
|
|
7771
|
+
"content-type": "application/json"
|
|
7772
|
+
},
|
|
7773
|
+
body: JSON.stringify({
|
|
7774
|
+
model: "claude-haiku-4-5-20251001",
|
|
7775
|
+
max_tokens: 1500,
|
|
7776
|
+
system: SYSTEM_PROMPT3,
|
|
7777
|
+
messages: [{ role: "user", content: `Extract tool lessons from this session transcript:
|
|
7778
|
+
|
|
7779
|
+
${truncated}` }]
|
|
7780
|
+
})
|
|
7192
7781
|
});
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7782
|
+
if (!response.ok)
|
|
7783
|
+
return [];
|
|
7784
|
+
const data = await response.json();
|
|
7785
|
+
const text = data.content?.[0]?.text?.trim();
|
|
7786
|
+
if (!text)
|
|
7787
|
+
return [];
|
|
7788
|
+
const lessons = JSON.parse(text);
|
|
7789
|
+
if (!Array.isArray(lessons))
|
|
7790
|
+
return [];
|
|
7791
|
+
for (const lesson of lessons) {
|
|
7792
|
+
if (!lesson.tool_name || !lesson.lesson)
|
|
7197
7793
|
continue;
|
|
7794
|
+
try {
|
|
7795
|
+
saveToolEvent({
|
|
7796
|
+
tool_name: lesson.tool_name,
|
|
7797
|
+
success: lesson.success,
|
|
7798
|
+
error_type: lesson.error_type || undefined,
|
|
7799
|
+
lesson: lesson.lesson,
|
|
7800
|
+
when_to_use: lesson.when_to_use,
|
|
7801
|
+
context: "extracted from session transcript",
|
|
7802
|
+
agent_id: options?.agent_id,
|
|
7803
|
+
project_id: options?.project_id,
|
|
7804
|
+
session_id: options?.session_id
|
|
7805
|
+
});
|
|
7806
|
+
} catch {}
|
|
7198
7807
|
try {
|
|
7199
7808
|
createMemory({
|
|
7200
|
-
key:
|
|
7201
|
-
value:
|
|
7202
|
-
category:
|
|
7203
|
-
scope:
|
|
7809
|
+
key: `tool-lesson-${lesson.tool_name}-${Date.now()}`,
|
|
7810
|
+
value: lesson.lesson,
|
|
7811
|
+
category: "knowledge",
|
|
7812
|
+
scope: "shared",
|
|
7813
|
+
importance: 7,
|
|
7814
|
+
source: "auto",
|
|
7815
|
+
tags: ["tool-memory", lesson.tool_name, "auto-extracted"],
|
|
7816
|
+
when_to_use: lesson.when_to_use,
|
|
7817
|
+
agent_id: options?.agent_id,
|
|
7818
|
+
project_id: options?.project_id,
|
|
7819
|
+
session_id: options?.session_id
|
|
7820
|
+
});
|
|
7821
|
+
} catch {}
|
|
7822
|
+
}
|
|
7823
|
+
return lessons;
|
|
7824
|
+
} catch {
|
|
7825
|
+
return [];
|
|
7826
|
+
}
|
|
7827
|
+
}
|
|
7828
|
+
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.
|
|
7829
|
+
|
|
7830
|
+
For each tool lesson, output a JSON array of objects with these fields:
|
|
7831
|
+
- tool_name: name of the tool
|
|
7832
|
+
- lesson: the insight (1-2 sentences)
|
|
7833
|
+
- when_to_use: activation context \u2014 when should an agent recall this lesson (start with "When" or "If")
|
|
7834
|
+
- success: boolean \u2014 was the tool call that taught this lesson successful?
|
|
7835
|
+
- error_type: if failed, one of: timeout, permission, not_found, syntax, rate_limit, other (or null if success)
|
|
7836
|
+
|
|
7837
|
+
Focus on:
|
|
7838
|
+
1. Successful patterns: what worked and why
|
|
7839
|
+
2. Failure lessons: what went wrong and how to avoid it
|
|
7840
|
+
3. Parameter insights: optimal settings discovered
|
|
7841
|
+
4. Alternative tools: when one tool is better than another
|
|
7842
|
+
5. Error recovery: what to do when a specific error occurs
|
|
7843
|
+
|
|
7844
|
+
Only extract genuinely useful, non-obvious lessons. Skip trivial observations.
|
|
7845
|
+
Output ONLY the JSON array, no markdown or explanation.`;
|
|
7846
|
+
var init_tool_lesson_extractor = __esm(() => {
|
|
7847
|
+
init_tool_events();
|
|
7848
|
+
init_memories();
|
|
7849
|
+
});
|
|
7850
|
+
|
|
7851
|
+
// src/lib/procedural-extractor.ts
|
|
7852
|
+
async function extractProcedures(transcript, options) {
|
|
7853
|
+
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
7854
|
+
if (!apiKey)
|
|
7855
|
+
return [];
|
|
7856
|
+
try {
|
|
7857
|
+
const truncated = transcript.length > 8000 ? transcript.slice(0, 8000) + `
|
|
7858
|
+
[...truncated]` : transcript;
|
|
7859
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
7860
|
+
method: "POST",
|
|
7861
|
+
headers: {
|
|
7862
|
+
"x-api-key": apiKey,
|
|
7863
|
+
"anthropic-version": "2023-06-01",
|
|
7864
|
+
"content-type": "application/json"
|
|
7865
|
+
},
|
|
7866
|
+
body: JSON.stringify({
|
|
7867
|
+
model: "claude-haiku-4-5-20251001",
|
|
7868
|
+
max_tokens: 2000,
|
|
7869
|
+
system: SYSTEM_PROMPT4,
|
|
7870
|
+
messages: [{ role: "user", content: `Extract procedures from this session:
|
|
7871
|
+
|
|
7872
|
+
${truncated}` }]
|
|
7873
|
+
})
|
|
7874
|
+
});
|
|
7875
|
+
if (!response.ok)
|
|
7876
|
+
return [];
|
|
7877
|
+
const data = await response.json();
|
|
7878
|
+
const text = data.content?.[0]?.text?.trim();
|
|
7879
|
+
if (!text)
|
|
7880
|
+
return [];
|
|
7881
|
+
const procedures = JSON.parse(text);
|
|
7882
|
+
if (!Array.isArray(procedures))
|
|
7883
|
+
return [];
|
|
7884
|
+
for (const proc of procedures) {
|
|
7885
|
+
if (!proc.title || !proc.steps?.length)
|
|
7886
|
+
continue;
|
|
7887
|
+
const sequenceGroup = `proc-${shortUuid()}`;
|
|
7888
|
+
for (let i = 0;i < proc.steps.length; i++) {
|
|
7889
|
+
const step = proc.steps[i];
|
|
7890
|
+
if (!step)
|
|
7891
|
+
continue;
|
|
7892
|
+
try {
|
|
7893
|
+
createMemory({
|
|
7894
|
+
key: `${sequenceGroup}-step-${i + 1}`,
|
|
7895
|
+
value: step.action,
|
|
7896
|
+
category: "procedural",
|
|
7897
|
+
scope: "shared",
|
|
7898
|
+
importance: 7,
|
|
7899
|
+
source: "auto",
|
|
7900
|
+
tags: ["procedure", "auto-extracted", proc.title.toLowerCase().replace(/\s+/g, "-")],
|
|
7901
|
+
when_to_use: step.when_to_use || proc.when_to_use,
|
|
7902
|
+
sequence_group: sequenceGroup,
|
|
7903
|
+
sequence_order: i + 1,
|
|
7904
|
+
agent_id: options?.agent_id,
|
|
7905
|
+
project_id: options?.project_id,
|
|
7906
|
+
session_id: options?.session_id
|
|
7907
|
+
});
|
|
7908
|
+
} catch {}
|
|
7909
|
+
}
|
|
7910
|
+
for (const pattern of proc.failure_patterns || []) {
|
|
7911
|
+
try {
|
|
7912
|
+
createMemory({
|
|
7913
|
+
key: `${sequenceGroup}-warning-${shortUuid()}`,
|
|
7914
|
+
value: `WARNING: ${pattern}`,
|
|
7915
|
+
category: "procedural",
|
|
7916
|
+
scope: "shared",
|
|
7917
|
+
importance: 8,
|
|
7918
|
+
source: "auto",
|
|
7919
|
+
tags: ["procedure", "failure-pattern", "auto-extracted"],
|
|
7920
|
+
when_to_use: proc.when_to_use,
|
|
7921
|
+
sequence_group: sequenceGroup,
|
|
7922
|
+
sequence_order: 999,
|
|
7923
|
+
agent_id: options?.agent_id,
|
|
7924
|
+
project_id: options?.project_id,
|
|
7925
|
+
session_id: options?.session_id
|
|
7926
|
+
});
|
|
7927
|
+
} catch {}
|
|
7928
|
+
}
|
|
7929
|
+
}
|
|
7930
|
+
return procedures;
|
|
7931
|
+
} catch {
|
|
7932
|
+
return [];
|
|
7933
|
+
}
|
|
7934
|
+
}
|
|
7935
|
+
var SYSTEM_PROMPT4 = `You extract procedural knowledge from session transcripts \u2014 workflows, step sequences, and problem-solution patterns.
|
|
7936
|
+
|
|
7937
|
+
For each procedure found, output a JSON array of objects:
|
|
7938
|
+
{
|
|
7939
|
+
"title": "short name for the workflow",
|
|
7940
|
+
"steps": [
|
|
7941
|
+
{"action": "what to do", "when_to_use": "activation context for this step"},
|
|
7942
|
+
{"action": "next step", "when_to_use": "activation context"}
|
|
7943
|
+
],
|
|
7944
|
+
"failure_patterns": ["what to avoid and why"],
|
|
7945
|
+
"when_to_use": "overall activation context for the whole procedure"
|
|
7946
|
+
}
|
|
7947
|
+
|
|
7948
|
+
Focus on:
|
|
7949
|
+
1. Multi-step workflows that were completed successfully
|
|
7950
|
+
2. Step sequences where order matters
|
|
7951
|
+
3. Failure \u2192 recovery patterns (what went wrong, how it was fixed)
|
|
7952
|
+
4. Problem-solution pairs (when X happens, do Y)
|
|
7953
|
+
|
|
7954
|
+
Only extract non-trivial procedures (3+ steps or genuinely useful patterns).
|
|
7955
|
+
Output ONLY the JSON array.`;
|
|
7956
|
+
var init_procedural_extractor = __esm(() => {
|
|
7957
|
+
init_memories();
|
|
7958
|
+
init_database();
|
|
7959
|
+
});
|
|
7960
|
+
|
|
7961
|
+
// src/lib/session-processor.ts
|
|
7962
|
+
function chunkTranscript(transcript, chunkSize = 2000, overlap = 200) {
|
|
7963
|
+
if (!transcript || transcript.length === 0)
|
|
7964
|
+
return [];
|
|
7965
|
+
if (transcript.length <= chunkSize)
|
|
7966
|
+
return [transcript];
|
|
7967
|
+
const chunks = [];
|
|
7968
|
+
let start = 0;
|
|
7969
|
+
while (start < transcript.length) {
|
|
7970
|
+
const end = Math.min(start + chunkSize, transcript.length);
|
|
7971
|
+
chunks.push(transcript.slice(start, end));
|
|
7972
|
+
if (end === transcript.length)
|
|
7973
|
+
break;
|
|
7974
|
+
start += chunkSize - overlap;
|
|
7975
|
+
}
|
|
7976
|
+
return chunks;
|
|
7977
|
+
}
|
|
7978
|
+
async function extractMemoriesFromChunk(chunk, context, db) {
|
|
7979
|
+
const provider = providerRegistry.getAvailable();
|
|
7980
|
+
if (!provider)
|
|
7981
|
+
return 0;
|
|
7982
|
+
try {
|
|
7983
|
+
const extracted = await provider.extractMemories(SESSION_EXTRACTION_USER_TEMPLATE(chunk, context.sessionId), {
|
|
7984
|
+
sessionId: context.sessionId,
|
|
7985
|
+
agentId: context.agentId,
|
|
7986
|
+
projectId: context.projectId
|
|
7987
|
+
});
|
|
7988
|
+
let savedCount = 0;
|
|
7989
|
+
const sourceTag = context.source ? `source:${context.source}` : "source:manual";
|
|
7990
|
+
for (const memory of extracted) {
|
|
7991
|
+
if (!memory.content || !memory.content.trim())
|
|
7992
|
+
continue;
|
|
7993
|
+
try {
|
|
7994
|
+
createMemory({
|
|
7995
|
+
key: memory.content.slice(0, 120).replace(/\s+/g, "-").toLowerCase(),
|
|
7996
|
+
value: memory.content,
|
|
7997
|
+
category: memory.category,
|
|
7998
|
+
scope: memory.suggestedScope ?? "shared",
|
|
7204
7999
|
importance: memory.importance,
|
|
7205
8000
|
tags: [
|
|
7206
8001
|
...memory.tags,
|
|
@@ -7320,6 +8115,20 @@ async function processSessionJob(jobId, db) {
|
|
|
7320
8115
|
}
|
|
7321
8116
|
}
|
|
7322
8117
|
result.memoriesExtracted = totalMemories;
|
|
8118
|
+
try {
|
|
8119
|
+
await extractToolLessons(job.transcript, {
|
|
8120
|
+
agent_id: job.agent_id ?? undefined,
|
|
8121
|
+
project_id: job.project_id ?? undefined,
|
|
8122
|
+
session_id: job.session_id
|
|
8123
|
+
});
|
|
8124
|
+
} catch {}
|
|
8125
|
+
try {
|
|
8126
|
+
await extractProcedures(job.transcript, {
|
|
8127
|
+
agent_id: job.agent_id ?? undefined,
|
|
8128
|
+
project_id: job.project_id ?? undefined,
|
|
8129
|
+
session_id: job.session_id
|
|
8130
|
+
});
|
|
8131
|
+
} catch {}
|
|
7323
8132
|
try {
|
|
7324
8133
|
if (result.errors.length > 0 && result.chunksProcessed === 0) {
|
|
7325
8134
|
updateSessionJob(jobId, {
|
|
@@ -7351,6 +8160,8 @@ var init_session_processor = __esm(() => {
|
|
|
7351
8160
|
init_memories();
|
|
7352
8161
|
init_registry();
|
|
7353
8162
|
init_session_jobs();
|
|
8163
|
+
init_tool_lesson_extractor();
|
|
8164
|
+
init_procedural_extractor();
|
|
7354
8165
|
});
|
|
7355
8166
|
|
|
7356
8167
|
// src/lib/session-queue.ts
|
|
@@ -7440,6 +8251,204 @@ var init_session_queue = __esm(() => {
|
|
|
7440
8251
|
_pendingQueue = new Set;
|
|
7441
8252
|
});
|
|
7442
8253
|
|
|
8254
|
+
// src/lib/session-registry.ts
|
|
8255
|
+
var exports_session_registry = {};
|
|
8256
|
+
__export(exports_session_registry, {
|
|
8257
|
+
updateSessionAgent: () => updateSessionAgent,
|
|
8258
|
+
unregisterSession: () => unregisterSession,
|
|
8259
|
+
registerSession: () => registerSession,
|
|
8260
|
+
listSessions: () => listSessions,
|
|
8261
|
+
heartbeatSession: () => heartbeatSession,
|
|
8262
|
+
getSessionsByProject: () => getSessionsByProject,
|
|
8263
|
+
getSessionByAgent: () => getSessionByAgent,
|
|
8264
|
+
getSession: () => getSession,
|
|
8265
|
+
closeRegistry: () => closeRegistry,
|
|
8266
|
+
cleanStaleSessions: () => cleanStaleSessions
|
|
8267
|
+
});
|
|
8268
|
+
import { Database as Database2 } from "bun:sqlite";
|
|
8269
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
8270
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
8271
|
+
function getDb() {
|
|
8272
|
+
if (_db2)
|
|
8273
|
+
return _db2;
|
|
8274
|
+
const dir = dirname3(DB_PATH);
|
|
8275
|
+
if (!existsSync5(dir))
|
|
8276
|
+
mkdirSync5(dir, { recursive: true });
|
|
8277
|
+
_db2 = new Database2(DB_PATH, { create: true });
|
|
8278
|
+
_db2.run("PRAGMA journal_mode = WAL");
|
|
8279
|
+
_db2.run("PRAGMA busy_timeout = 3000");
|
|
8280
|
+
_db2.exec(`
|
|
8281
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
8282
|
+
id TEXT PRIMARY KEY,
|
|
8283
|
+
pid INTEGER NOT NULL,
|
|
8284
|
+
cwd TEXT NOT NULL,
|
|
8285
|
+
git_root TEXT,
|
|
8286
|
+
agent_name TEXT,
|
|
8287
|
+
project_name TEXT,
|
|
8288
|
+
tty TEXT,
|
|
8289
|
+
mcp_server TEXT NOT NULL,
|
|
8290
|
+
metadata TEXT DEFAULT '{}',
|
|
8291
|
+
registered_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
8292
|
+
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
8293
|
+
);
|
|
8294
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_pid_mcp
|
|
8295
|
+
ON sessions(pid, mcp_server);
|
|
8296
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project
|
|
8297
|
+
ON sessions(project_name);
|
|
8298
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_agent
|
|
8299
|
+
ON sessions(agent_name);
|
|
8300
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_git_root
|
|
8301
|
+
ON sessions(git_root);
|
|
8302
|
+
`);
|
|
8303
|
+
return _db2;
|
|
8304
|
+
}
|
|
8305
|
+
function generateId() {
|
|
8306
|
+
return crypto.randomUUID().slice(0, 8);
|
|
8307
|
+
}
|
|
8308
|
+
function now2() {
|
|
8309
|
+
return new Date().toISOString();
|
|
8310
|
+
}
|
|
8311
|
+
function parseRow2(row) {
|
|
8312
|
+
return {
|
|
8313
|
+
id: row["id"],
|
|
8314
|
+
pid: row["pid"],
|
|
8315
|
+
cwd: row["cwd"],
|
|
8316
|
+
git_root: row["git_root"] || null,
|
|
8317
|
+
agent_name: row["agent_name"] || null,
|
|
8318
|
+
project_name: row["project_name"] || null,
|
|
8319
|
+
tty: row["tty"] || null,
|
|
8320
|
+
mcp_server: row["mcp_server"],
|
|
8321
|
+
metadata: JSON.parse(row["metadata"] || "{}"),
|
|
8322
|
+
registered_at: row["registered_at"],
|
|
8323
|
+
last_seen_at: row["last_seen_at"]
|
|
8324
|
+
};
|
|
8325
|
+
}
|
|
8326
|
+
function isProcessAlive(pid) {
|
|
8327
|
+
try {
|
|
8328
|
+
process.kill(pid, 0);
|
|
8329
|
+
return true;
|
|
8330
|
+
} catch {
|
|
8331
|
+
return false;
|
|
8332
|
+
}
|
|
8333
|
+
}
|
|
8334
|
+
function registerSession(opts) {
|
|
8335
|
+
const db = getDb();
|
|
8336
|
+
const pid = process.pid;
|
|
8337
|
+
const cwd = opts.cwd || process.cwd();
|
|
8338
|
+
const id = generateId();
|
|
8339
|
+
const timestamp = now2();
|
|
8340
|
+
const existing = db.query("SELECT id FROM sessions WHERE pid = ? AND mcp_server = ?").get(pid, opts.mcp_server);
|
|
8341
|
+
if (existing) {
|
|
8342
|
+
db.run(`UPDATE sessions SET agent_name = ?, project_name = ?, cwd = ?,
|
|
8343
|
+
git_root = ?, tty = ?, metadata = ?, last_seen_at = ? WHERE id = ?`, [
|
|
8344
|
+
opts.agent_name || null,
|
|
8345
|
+
opts.project_name || null,
|
|
8346
|
+
cwd,
|
|
8347
|
+
opts.git_root || null,
|
|
8348
|
+
opts.tty || null,
|
|
8349
|
+
JSON.stringify(opts.metadata || {}),
|
|
8350
|
+
timestamp,
|
|
8351
|
+
existing.id
|
|
8352
|
+
]);
|
|
8353
|
+
return getSession(existing.id);
|
|
8354
|
+
}
|
|
8355
|
+
db.run(`INSERT INTO sessions (id, pid, cwd, git_root, agent_name, project_name, tty, mcp_server, metadata, registered_at, last_seen_at)
|
|
8356
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
8357
|
+
id,
|
|
8358
|
+
pid,
|
|
8359
|
+
cwd,
|
|
8360
|
+
opts.git_root || null,
|
|
8361
|
+
opts.agent_name || null,
|
|
8362
|
+
opts.project_name || null,
|
|
8363
|
+
opts.tty || null,
|
|
8364
|
+
opts.mcp_server,
|
|
8365
|
+
JSON.stringify(opts.metadata || {}),
|
|
8366
|
+
timestamp,
|
|
8367
|
+
timestamp
|
|
8368
|
+
]);
|
|
8369
|
+
return getSession(id);
|
|
8370
|
+
}
|
|
8371
|
+
function heartbeatSession(id) {
|
|
8372
|
+
const db = getDb();
|
|
8373
|
+
db.run("UPDATE sessions SET last_seen_at = ? WHERE id = ?", [now2(), id]);
|
|
8374
|
+
}
|
|
8375
|
+
function unregisterSession(id) {
|
|
8376
|
+
const db = getDb();
|
|
8377
|
+
db.run("DELETE FROM sessions WHERE id = ?", [id]);
|
|
8378
|
+
}
|
|
8379
|
+
function getSession(id) {
|
|
8380
|
+
const db = getDb();
|
|
8381
|
+
const row = db.query("SELECT * FROM sessions WHERE id = ?").get(id);
|
|
8382
|
+
return row ? parseRow2(row) : null;
|
|
8383
|
+
}
|
|
8384
|
+
function listSessions(filter) {
|
|
8385
|
+
const db = getDb();
|
|
8386
|
+
const conditions = [];
|
|
8387
|
+
const params = [];
|
|
8388
|
+
if (filter?.project_name) {
|
|
8389
|
+
conditions.push("project_name = ?");
|
|
8390
|
+
params.push(filter.project_name);
|
|
8391
|
+
}
|
|
8392
|
+
if (filter?.git_root) {
|
|
8393
|
+
conditions.push("git_root = ?");
|
|
8394
|
+
params.push(filter.git_root);
|
|
8395
|
+
}
|
|
8396
|
+
if (filter?.mcp_server) {
|
|
8397
|
+
conditions.push("mcp_server = ?");
|
|
8398
|
+
params.push(filter.mcp_server);
|
|
8399
|
+
}
|
|
8400
|
+
if (filter?.agent_name) {
|
|
8401
|
+
conditions.push("agent_name = ?");
|
|
8402
|
+
params.push(filter.agent_name);
|
|
8403
|
+
}
|
|
8404
|
+
if (filter?.exclude_pid) {
|
|
8405
|
+
conditions.push("pid != ?");
|
|
8406
|
+
params.push(filter.exclude_pid);
|
|
8407
|
+
}
|
|
8408
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
8409
|
+
const rows = db.query(`SELECT * FROM sessions ${where} ORDER BY last_seen_at DESC`).all(...params);
|
|
8410
|
+
return rows.map(parseRow2).filter((s) => {
|
|
8411
|
+
if (isProcessAlive(s.pid))
|
|
8412
|
+
return true;
|
|
8413
|
+
db.run("DELETE FROM sessions WHERE id = ?", [s.id]);
|
|
8414
|
+
return false;
|
|
8415
|
+
});
|
|
8416
|
+
}
|
|
8417
|
+
function getSessionByAgent(agentName) {
|
|
8418
|
+
const sessions = listSessions({ agent_name: agentName });
|
|
8419
|
+
return sessions[0] || null;
|
|
8420
|
+
}
|
|
8421
|
+
function getSessionsByProject(projectName) {
|
|
8422
|
+
return listSessions({ project_name: projectName });
|
|
8423
|
+
}
|
|
8424
|
+
function cleanStaleSessions() {
|
|
8425
|
+
const db = getDb();
|
|
8426
|
+
const rows = db.query("SELECT id, pid FROM sessions").all();
|
|
8427
|
+
let cleaned = 0;
|
|
8428
|
+
for (const row of rows) {
|
|
8429
|
+
if (!isProcessAlive(row.pid)) {
|
|
8430
|
+
db.run("DELETE FROM sessions WHERE id = ?", [row.id]);
|
|
8431
|
+
cleaned++;
|
|
8432
|
+
}
|
|
8433
|
+
}
|
|
8434
|
+
return cleaned;
|
|
8435
|
+
}
|
|
8436
|
+
function updateSessionAgent(mcpServer, agentName) {
|
|
8437
|
+
const db = getDb();
|
|
8438
|
+
const pid = process.pid;
|
|
8439
|
+
db.run("UPDATE sessions SET agent_name = ?, last_seen_at = ? WHERE pid = ? AND mcp_server = ?", [agentName, now2(), pid, mcpServer]);
|
|
8440
|
+
}
|
|
8441
|
+
function closeRegistry() {
|
|
8442
|
+
if (_db2) {
|
|
8443
|
+
_db2.close();
|
|
8444
|
+
_db2 = null;
|
|
8445
|
+
}
|
|
8446
|
+
}
|
|
8447
|
+
var DB_PATH, _db2 = null;
|
|
8448
|
+
var init_session_registry = __esm(() => {
|
|
8449
|
+
DB_PATH = join5(process.env["HOME"] || process.env["USERPROFILE"] || "~", ".open-sessions-registry.db");
|
|
8450
|
+
});
|
|
8451
|
+
|
|
7443
8452
|
// node_modules/commander/esm.mjs
|
|
7444
8453
|
var import__ = __toESM(require_commander(), 1);
|
|
7445
8454
|
var {
|
|
@@ -7459,10 +8468,10 @@ var {
|
|
|
7459
8468
|
// src/cli/index.tsx
|
|
7460
8469
|
init_database();
|
|
7461
8470
|
init_memories();
|
|
7462
|
-
import
|
|
7463
|
-
import { readFileSync as
|
|
7464
|
-
import { dirname as
|
|
7465
|
-
import { homedir as
|
|
8471
|
+
import chalk2 from "chalk";
|
|
8472
|
+
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";
|
|
8473
|
+
import { dirname as dirname4, join as join6, resolve as resolve3 } from "path";
|
|
8474
|
+
import { homedir as homedir4 } from "os";
|
|
7466
8475
|
import { fileURLToPath } from "url";
|
|
7467
8476
|
|
|
7468
8477
|
// src/db/agents.ts
|
|
@@ -7945,37 +8954,332 @@ var FORMAT_UNITS = [
|
|
|
7945
8954
|
init_entities();
|
|
7946
8955
|
init_relations();
|
|
7947
8956
|
init_entity_memories();
|
|
8957
|
+
|
|
8958
|
+
// src/cli/brains.ts
|
|
8959
|
+
import {
|
|
8960
|
+
existsSync as existsSync4,
|
|
8961
|
+
mkdirSync as mkdirSync4,
|
|
8962
|
+
writeFileSync as writeFileSync3,
|
|
8963
|
+
readdirSync as readdirSync2
|
|
8964
|
+
} from "fs";
|
|
8965
|
+
import { homedir as homedir3 } from "os";
|
|
8966
|
+
import { join as join4 } from "path";
|
|
8967
|
+
import chalk from "chalk";
|
|
8968
|
+
|
|
8969
|
+
// src/lib/gatherer.ts
|
|
8970
|
+
init_memories();
|
|
8971
|
+
var SYSTEM_PROMPT = "You are an AI assistant with persistent memory that recalls and saves information across sessions.";
|
|
8972
|
+
function memoryToRecallExample(memory) {
|
|
8973
|
+
return {
|
|
8974
|
+
messages: [
|
|
8975
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
8976
|
+
{
|
|
8977
|
+
role: "user",
|
|
8978
|
+
content: `What do you remember about "${memory.key}"?`
|
|
8979
|
+
},
|
|
8980
|
+
{
|
|
8981
|
+
role: "assistant",
|
|
8982
|
+
content: memory.summary ? `${memory.value}
|
|
8983
|
+
|
|
8984
|
+
Summary: ${memory.summary}` : memory.value
|
|
8985
|
+
}
|
|
8986
|
+
]
|
|
8987
|
+
};
|
|
8988
|
+
}
|
|
8989
|
+
function memoryToSaveExample(memory) {
|
|
8990
|
+
const tags = memory.tags ?? [];
|
|
8991
|
+
return {
|
|
8992
|
+
messages: [
|
|
8993
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
8994
|
+
{
|
|
8995
|
+
role: "user",
|
|
8996
|
+
content: `Remember this for me: ${memory.key} = ${memory.value}${tags.length ? ` (tags: ${tags.join(", ")})` : ""}`
|
|
8997
|
+
},
|
|
8998
|
+
{
|
|
8999
|
+
role: "assistant",
|
|
9000
|
+
content: `Saved to memory: "${memory.key}" with ${memory.category} category, importance ${memory.importance}/10, scope: ${memory.scope}.`
|
|
9001
|
+
}
|
|
9002
|
+
]
|
|
9003
|
+
};
|
|
9004
|
+
}
|
|
9005
|
+
function memoryToSearchExample(memories, category) {
|
|
9006
|
+
const matched = memories.filter((m) => m.category === category && m.status === "active").slice(0, 5);
|
|
9007
|
+
return {
|
|
9008
|
+
messages: [
|
|
9009
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
9010
|
+
{ role: "user", content: `What ${category} memories do you have?` },
|
|
9011
|
+
{
|
|
9012
|
+
role: "assistant",
|
|
9013
|
+
content: matched.length > 0 ? `Here are my ${category} memories:
|
|
9014
|
+
${matched.map((m) => `- ${m.key}: ${m.value.slice(0, 120)}${m.value.length > 120 ? "..." : ""}`).join(`
|
|
9015
|
+
`)}` : `I don't have any ${category} memories stored yet.`
|
|
9016
|
+
}
|
|
9017
|
+
]
|
|
9018
|
+
};
|
|
9019
|
+
}
|
|
9020
|
+
var gatherTrainingData = async (options = {}) => {
|
|
9021
|
+
const allMemories = listMemories({ status: "active" });
|
|
9022
|
+
const filtered = options.since ? allMemories.filter((m) => new Date(m.created_at) >= options.since) : allMemories;
|
|
9023
|
+
const sorted = filtered.slice().sort((a, b) => b.importance - a.importance);
|
|
9024
|
+
const fetchSet = options.limit ? sorted.slice(0, options.limit * 3) : sorted;
|
|
9025
|
+
const examples = [];
|
|
9026
|
+
for (const memory of fetchSet) {
|
|
9027
|
+
examples.push(memoryToRecallExample(memory));
|
|
9028
|
+
examples.push(memoryToSaveExample(memory));
|
|
9029
|
+
}
|
|
9030
|
+
const categories = [...new Set(fetchSet.map((m) => m.category))];
|
|
9031
|
+
for (const category of categories) {
|
|
9032
|
+
examples.push(memoryToSearchExample(fetchSet, category));
|
|
9033
|
+
}
|
|
9034
|
+
const finalExamples = options.limit ? examples.slice(0, options.limit) : examples;
|
|
9035
|
+
return {
|
|
9036
|
+
source: "mementos",
|
|
9037
|
+
examples: finalExamples,
|
|
9038
|
+
count: finalExamples.length
|
|
9039
|
+
};
|
|
9040
|
+
};
|
|
9041
|
+
|
|
9042
|
+
// src/lib/model-config.ts
|
|
9043
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
9044
|
+
import { homedir as homedir2 } from "os";
|
|
9045
|
+
import { join as join3 } from "path";
|
|
9046
|
+
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
9047
|
+
var CONFIG_DIR = join3(homedir2(), ".mementos");
|
|
9048
|
+
var CONFIG_PATH = join3(CONFIG_DIR, "config.json");
|
|
9049
|
+
function readConfig() {
|
|
9050
|
+
if (!existsSync3(CONFIG_PATH))
|
|
9051
|
+
return {};
|
|
9052
|
+
try {
|
|
9053
|
+
const raw = readFileSync2(CONFIG_PATH, "utf-8");
|
|
9054
|
+
return JSON.parse(raw);
|
|
9055
|
+
} catch {
|
|
9056
|
+
return {};
|
|
9057
|
+
}
|
|
9058
|
+
}
|
|
9059
|
+
function writeConfig(config) {
|
|
9060
|
+
if (!existsSync3(CONFIG_DIR)) {
|
|
9061
|
+
mkdirSync3(CONFIG_DIR, { recursive: true });
|
|
9062
|
+
}
|
|
9063
|
+
writeFileSync2(CONFIG_PATH, JSON.stringify(config, null, 2) + `
|
|
9064
|
+
`, "utf-8");
|
|
9065
|
+
}
|
|
9066
|
+
function getActiveModel() {
|
|
9067
|
+
const config = readConfig();
|
|
9068
|
+
return config.activeModel ?? DEFAULT_MODEL;
|
|
9069
|
+
}
|
|
9070
|
+
function setActiveModel(modelId) {
|
|
9071
|
+
const config = readConfig();
|
|
9072
|
+
config.activeModel = modelId;
|
|
9073
|
+
writeConfig(config);
|
|
9074
|
+
}
|
|
9075
|
+
function clearActiveModel() {
|
|
9076
|
+
const config = readConfig();
|
|
9077
|
+
delete config.activeModel;
|
|
9078
|
+
writeConfig(config);
|
|
9079
|
+
}
|
|
9080
|
+
|
|
9081
|
+
// src/cli/brains.ts
|
|
9082
|
+
function printSuccess(msg) {
|
|
9083
|
+
console.log(chalk.green("\u2713 " + msg));
|
|
9084
|
+
}
|
|
9085
|
+
function printError(msg) {
|
|
9086
|
+
console.error(chalk.red("\u2717 " + msg));
|
|
9087
|
+
}
|
|
9088
|
+
function printInfo(msg) {
|
|
9089
|
+
console.log(chalk.cyan("\u2139 " + msg));
|
|
9090
|
+
}
|
|
9091
|
+
function makeBrainsCommand() {
|
|
9092
|
+
const brains = new Command("brains");
|
|
9093
|
+
brains.description("Fine-tuned model training and management (via @hasna/brains)");
|
|
9094
|
+
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) => {
|
|
9095
|
+
try {
|
|
9096
|
+
const since = opts.since ? new Date(opts.since) : undefined;
|
|
9097
|
+
if (since && isNaN(since.getTime())) {
|
|
9098
|
+
printError(`Invalid date: ${opts.since}`);
|
|
9099
|
+
process.exit(1);
|
|
9100
|
+
}
|
|
9101
|
+
if (!opts.json) {
|
|
9102
|
+
printInfo("Gathering training data from memories...");
|
|
9103
|
+
}
|
|
9104
|
+
const result = await gatherTrainingData({
|
|
9105
|
+
limit: opts.limit,
|
|
9106
|
+
since
|
|
9107
|
+
});
|
|
9108
|
+
const outputDir = opts.output ?? join4(homedir3(), ".mementos", "training");
|
|
9109
|
+
if (!existsSync4(outputDir)) {
|
|
9110
|
+
mkdirSync4(outputDir, { recursive: true });
|
|
9111
|
+
}
|
|
9112
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
9113
|
+
const outputPath = join4(outputDir, `mementos-training-${timestamp}.jsonl`);
|
|
9114
|
+
const jsonl = result.examples.map((ex) => JSON.stringify(ex)).join(`
|
|
9115
|
+
`);
|
|
9116
|
+
writeFileSync3(outputPath, jsonl + `
|
|
9117
|
+
`, "utf-8");
|
|
9118
|
+
if (opts.json) {
|
|
9119
|
+
console.log(JSON.stringify({
|
|
9120
|
+
source: result.source,
|
|
9121
|
+
count: result.count,
|
|
9122
|
+
path: outputPath
|
|
9123
|
+
}));
|
|
9124
|
+
} else {
|
|
9125
|
+
printSuccess(`Gathered ${result.count} training examples from memories`);
|
|
9126
|
+
console.log(chalk.dim(` Output: ${outputPath}`));
|
|
9127
|
+
}
|
|
9128
|
+
} catch (err) {
|
|
9129
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9130
|
+
process.exit(1);
|
|
9131
|
+
}
|
|
9132
|
+
});
|
|
9133
|
+
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) => {
|
|
9134
|
+
try {
|
|
9135
|
+
let datasetPath = opts.dataset;
|
|
9136
|
+
if (!datasetPath) {
|
|
9137
|
+
const trainingDir = join4(homedir3(), ".mementos", "training");
|
|
9138
|
+
if (!existsSync4(trainingDir)) {
|
|
9139
|
+
printError("No training data found. Run `mementos brains gather` first.");
|
|
9140
|
+
process.exit(1);
|
|
9141
|
+
}
|
|
9142
|
+
const files = readdirSync2(trainingDir).filter((f) => f.endsWith(".jsonl")).sort().reverse();
|
|
9143
|
+
const latestFile = files[0];
|
|
9144
|
+
if (!latestFile) {
|
|
9145
|
+
printError("No JSONL training files found. Run `mementos brains gather` first.");
|
|
9146
|
+
process.exit(1);
|
|
9147
|
+
}
|
|
9148
|
+
datasetPath = join4(trainingDir, latestFile);
|
|
9149
|
+
}
|
|
9150
|
+
if (!datasetPath || !existsSync4(datasetPath)) {
|
|
9151
|
+
printError(`Dataset file not found: ${datasetPath ?? "(unresolved)"}`);
|
|
9152
|
+
process.exit(1);
|
|
9153
|
+
}
|
|
9154
|
+
if (!opts.json) {
|
|
9155
|
+
printInfo(`Starting fine-tuning job with dataset: ${datasetPath}`);
|
|
9156
|
+
}
|
|
9157
|
+
let brainsSDK;
|
|
9158
|
+
try {
|
|
9159
|
+
brainsSDK = await import("@hasna/brains");
|
|
9160
|
+
} catch {
|
|
9161
|
+
printError("@hasna/brains is not installed. Run `bun add @hasna/brains` to enable training.");
|
|
9162
|
+
process.exit(1);
|
|
9163
|
+
}
|
|
9164
|
+
const startFinetune = brainsSDK["startFinetune"];
|
|
9165
|
+
if (typeof startFinetune !== "function") {
|
|
9166
|
+
printError("@hasna/brains does not export startFinetune. Please update @hasna/brains.");
|
|
9167
|
+
process.exit(1);
|
|
9168
|
+
}
|
|
9169
|
+
const modelName = opts.name ?? `mementos-${new Date().toISOString().slice(0, 10)}`;
|
|
9170
|
+
const jobResult = await startFinetune({
|
|
9171
|
+
provider: opts.provider,
|
|
9172
|
+
baseModel: opts.baseModel,
|
|
9173
|
+
datasetPath,
|
|
9174
|
+
name: modelName
|
|
9175
|
+
});
|
|
9176
|
+
if (opts.json) {
|
|
9177
|
+
console.log(JSON.stringify(jobResult));
|
|
9178
|
+
} else {
|
|
9179
|
+
printSuccess(`Fine-tuning job started: ${String(jobResult["jobId"] ?? "(unknown)")}`);
|
|
9180
|
+
console.log(chalk.dim(` Provider: ${opts.provider}`));
|
|
9181
|
+
console.log(chalk.dim(` Base model: ${opts.baseModel}`));
|
|
9182
|
+
console.log(chalk.dim(` Name: ${modelName}`));
|
|
9183
|
+
if (jobResult["jobId"]) {
|
|
9184
|
+
console.log();
|
|
9185
|
+
printInfo(`Use \`mementos brains model set <model-id>\` once training completes.`);
|
|
9186
|
+
}
|
|
9187
|
+
}
|
|
9188
|
+
} catch (err) {
|
|
9189
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9190
|
+
process.exit(1);
|
|
9191
|
+
}
|
|
9192
|
+
});
|
|
9193
|
+
const modelCmd = brains.command("model").description("Manage the active fine-tuned model");
|
|
9194
|
+
modelCmd.command("get").description("Show the currently active fine-tuned model").option("--json", "Output as JSON").action((opts) => {
|
|
9195
|
+
try {
|
|
9196
|
+
const active = getActiveModel();
|
|
9197
|
+
const isDefault = active === DEFAULT_MODEL;
|
|
9198
|
+
if (opts.json) {
|
|
9199
|
+
console.log(JSON.stringify({ activeModel: active, isDefault }));
|
|
9200
|
+
} else {
|
|
9201
|
+
if (isDefault) {
|
|
9202
|
+
console.log(`Active model: ${chalk.cyan(active)} ${chalk.dim("(default)")}`);
|
|
9203
|
+
} else {
|
|
9204
|
+
console.log(`Active model: ${chalk.green(active)}`);
|
|
9205
|
+
}
|
|
9206
|
+
}
|
|
9207
|
+
} catch (err) {
|
|
9208
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9209
|
+
process.exit(1);
|
|
9210
|
+
}
|
|
9211
|
+
});
|
|
9212
|
+
modelCmd.command("set <modelId>").description("Set the active fine-tuned model ID").action((modelId) => {
|
|
9213
|
+
try {
|
|
9214
|
+
setActiveModel(modelId);
|
|
9215
|
+
printSuccess(`Active model set to: ${modelId}`);
|
|
9216
|
+
} catch (err) {
|
|
9217
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9218
|
+
process.exit(1);
|
|
9219
|
+
}
|
|
9220
|
+
});
|
|
9221
|
+
modelCmd.command("clear").description(`Clear the active fine-tuned model (reverts to ${DEFAULT_MODEL})`).action(() => {
|
|
9222
|
+
try {
|
|
9223
|
+
clearActiveModel();
|
|
9224
|
+
printSuccess(`Active model cleared. Using default: ${DEFAULT_MODEL}`);
|
|
9225
|
+
} catch (err) {
|
|
9226
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9227
|
+
process.exit(1);
|
|
9228
|
+
}
|
|
9229
|
+
});
|
|
9230
|
+
modelCmd.action((opts) => {
|
|
9231
|
+
try {
|
|
9232
|
+
const active = getActiveModel();
|
|
9233
|
+
const isDefault = active === DEFAULT_MODEL;
|
|
9234
|
+
if (opts.json) {
|
|
9235
|
+
console.log(JSON.stringify({ activeModel: active, isDefault }));
|
|
9236
|
+
} else {
|
|
9237
|
+
if (isDefault) {
|
|
9238
|
+
console.log(`Active model: ${chalk.cyan(active)} ${chalk.dim("(default)")}`);
|
|
9239
|
+
} else {
|
|
9240
|
+
console.log(`Active model: ${chalk.green(active)}`);
|
|
9241
|
+
}
|
|
9242
|
+
}
|
|
9243
|
+
} catch (err) {
|
|
9244
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
9245
|
+
process.exit(1);
|
|
9246
|
+
}
|
|
9247
|
+
});
|
|
9248
|
+
return brains;
|
|
9249
|
+
}
|
|
9250
|
+
|
|
9251
|
+
// src/cli/index.tsx
|
|
7948
9252
|
function getPackageVersion() {
|
|
7949
9253
|
try {
|
|
7950
|
-
const pkgPath =
|
|
7951
|
-
const pkg = JSON.parse(
|
|
9254
|
+
const pkgPath = join6(dirname4(fileURLToPath(import.meta.url)), "..", "..", "package.json");
|
|
9255
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
7952
9256
|
return pkg.version || "0.0.0";
|
|
7953
9257
|
} catch {
|
|
7954
9258
|
return "0.0.0";
|
|
7955
9259
|
}
|
|
7956
9260
|
}
|
|
7957
9261
|
var scopeColor = {
|
|
7958
|
-
global:
|
|
7959
|
-
shared:
|
|
7960
|
-
private:
|
|
7961
|
-
working:
|
|
9262
|
+
global: chalk2.cyan,
|
|
9263
|
+
shared: chalk2.yellow,
|
|
9264
|
+
private: chalk2.magenta,
|
|
9265
|
+
working: chalk2.gray
|
|
7962
9266
|
};
|
|
7963
9267
|
var categoryColor = {
|
|
7964
|
-
preference:
|
|
7965
|
-
fact:
|
|
7966
|
-
knowledge:
|
|
7967
|
-
history:
|
|
7968
|
-
procedural:
|
|
7969
|
-
resource:
|
|
9268
|
+
preference: chalk2.blue,
|
|
9269
|
+
fact: chalk2.green,
|
|
9270
|
+
knowledge: chalk2.yellow,
|
|
9271
|
+
history: chalk2.gray,
|
|
9272
|
+
procedural: chalk2.cyan,
|
|
9273
|
+
resource: chalk2.magenta
|
|
7970
9274
|
};
|
|
7971
9275
|
function importanceColor(importance) {
|
|
7972
9276
|
if (importance >= 9)
|
|
7973
|
-
return
|
|
9277
|
+
return chalk2.red.bold;
|
|
7974
9278
|
if (importance >= 7)
|
|
7975
|
-
return
|
|
9279
|
+
return chalk2.yellow;
|
|
7976
9280
|
if (importance >= 5)
|
|
7977
|
-
return
|
|
7978
|
-
return
|
|
9281
|
+
return chalk2.green;
|
|
9282
|
+
return chalk2.dim;
|
|
7979
9283
|
}
|
|
7980
9284
|
function colorScope(scope) {
|
|
7981
9285
|
return scopeColor[scope](scope);
|
|
@@ -7987,17 +9291,17 @@ function colorImportance(importance) {
|
|
|
7987
9291
|
return importanceColor(importance)(String(importance));
|
|
7988
9292
|
}
|
|
7989
9293
|
var entityTypeColor = {
|
|
7990
|
-
person:
|
|
7991
|
-
project:
|
|
7992
|
-
tool:
|
|
7993
|
-
concept:
|
|
7994
|
-
file:
|
|
7995
|
-
api:
|
|
7996
|
-
pattern:
|
|
7997
|
-
organization:
|
|
9294
|
+
person: chalk2.cyan,
|
|
9295
|
+
project: chalk2.yellow,
|
|
9296
|
+
tool: chalk2.green,
|
|
9297
|
+
concept: chalk2.blue,
|
|
9298
|
+
file: chalk2.magenta,
|
|
9299
|
+
api: chalk2.red,
|
|
9300
|
+
pattern: chalk2.gray,
|
|
9301
|
+
organization: chalk2.white
|
|
7998
9302
|
};
|
|
7999
9303
|
function colorEntityType(type) {
|
|
8000
|
-
const colorFn = entityTypeColor[type] ||
|
|
9304
|
+
const colorFn = entityTypeColor[type] || chalk2.white;
|
|
8001
9305
|
return colorFn(type);
|
|
8002
9306
|
}
|
|
8003
9307
|
function resolveEntityArg(nameOrId, type) {
|
|
@@ -8008,7 +9312,7 @@ function resolveEntityArg(nameOrId, type) {
|
|
|
8008
9312
|
const id = resolvePartialId(db, "entities", nameOrId);
|
|
8009
9313
|
if (id)
|
|
8010
9314
|
return getEntity(id);
|
|
8011
|
-
console.error(
|
|
9315
|
+
console.error(chalk2.red(`Entity not found: ${nameOrId}`));
|
|
8012
9316
|
process.exit(1);
|
|
8013
9317
|
}
|
|
8014
9318
|
function outputJson(data) {
|
|
@@ -8092,44 +9396,44 @@ ${formatObj({ [k]: v }, " ").replace(/^\s*\S+:\n/, "")}`);
|
|
|
8092
9396
|
}
|
|
8093
9397
|
}
|
|
8094
9398
|
function formatMemoryLine(m) {
|
|
8095
|
-
const id =
|
|
9399
|
+
const id = chalk2.dim(m.id.slice(0, 8));
|
|
8096
9400
|
const scope = colorScope(m.scope);
|
|
8097
9401
|
const cat = colorCategory(m.category);
|
|
8098
9402
|
const imp = colorImportance(m.importance);
|
|
8099
|
-
const pin = m.pinned ?
|
|
9403
|
+
const pin = m.pinned ? chalk2.red(" *") : "";
|
|
8100
9404
|
const value = m.value.length > 80 ? m.value.slice(0, 80) + "..." : m.value;
|
|
8101
|
-
return `${id} [${scope}/${cat}] ${
|
|
9405
|
+
return `${id} [${scope}/${cat}] ${chalk2.bold(m.key)} = ${value} (${imp})${pin}`;
|
|
8102
9406
|
}
|
|
8103
9407
|
function formatMemoryDetail(m) {
|
|
8104
9408
|
const lines = [
|
|
8105
|
-
`${
|
|
8106
|
-
`${
|
|
8107
|
-
`${
|
|
8108
|
-
`${
|
|
8109
|
-
`${
|
|
8110
|
-
`${
|
|
8111
|
-
`${
|
|
8112
|
-
`${
|
|
8113
|
-
`${
|
|
9409
|
+
`${chalk2.bold("ID:")} ${m.id}`,
|
|
9410
|
+
`${chalk2.bold("Key:")} ${m.key}`,
|
|
9411
|
+
`${chalk2.bold("Value:")} ${m.value}`,
|
|
9412
|
+
`${chalk2.bold("Scope:")} ${colorScope(m.scope)}`,
|
|
9413
|
+
`${chalk2.bold("Category:")} ${colorCategory(m.category)}`,
|
|
9414
|
+
`${chalk2.bold("Importance:")} ${colorImportance(m.importance)}/10`,
|
|
9415
|
+
`${chalk2.bold("Source:")} ${m.source}`,
|
|
9416
|
+
`${chalk2.bold("Status:")} ${m.status}`,
|
|
9417
|
+
`${chalk2.bold("Pinned:")} ${m.pinned ? chalk2.red("yes") : "no"}`
|
|
8114
9418
|
];
|
|
8115
9419
|
if (m.summary)
|
|
8116
|
-
lines.push(`${
|
|
9420
|
+
lines.push(`${chalk2.bold("Summary:")} ${m.summary}`);
|
|
8117
9421
|
if (m.tags.length > 0)
|
|
8118
|
-
lines.push(`${
|
|
9422
|
+
lines.push(`${chalk2.bold("Tags:")} ${m.tags.join(", ")}`);
|
|
8119
9423
|
if (m.agent_id)
|
|
8120
|
-
lines.push(`${
|
|
9424
|
+
lines.push(`${chalk2.bold("Agent:")} ${m.agent_id}`);
|
|
8121
9425
|
if (m.project_id)
|
|
8122
|
-
lines.push(`${
|
|
9426
|
+
lines.push(`${chalk2.bold("Project:")} ${m.project_id}`);
|
|
8123
9427
|
if (m.session_id)
|
|
8124
|
-
lines.push(`${
|
|
9428
|
+
lines.push(`${chalk2.bold("Session:")} ${m.session_id}`);
|
|
8125
9429
|
if (m.expires_at)
|
|
8126
|
-
lines.push(`${
|
|
8127
|
-
lines.push(`${
|
|
8128
|
-
lines.push(`${
|
|
8129
|
-
lines.push(`${
|
|
8130
|
-
lines.push(`${
|
|
9430
|
+
lines.push(`${chalk2.bold("Expires:")} ${m.expires_at}`);
|
|
9431
|
+
lines.push(`${chalk2.bold("Access:")} ${m.access_count}`);
|
|
9432
|
+
lines.push(`${chalk2.bold("Version:")} ${m.version}`);
|
|
9433
|
+
lines.push(`${chalk2.bold("Created:")} ${m.created_at}`);
|
|
9434
|
+
lines.push(`${chalk2.bold("Updated:")} ${m.updated_at}`);
|
|
8131
9435
|
if (m.accessed_at)
|
|
8132
|
-
lines.push(`${
|
|
9436
|
+
lines.push(`${chalk2.bold("Accessed:")} ${m.accessed_at}`);
|
|
8133
9437
|
return lines.join(`
|
|
8134
9438
|
`);
|
|
8135
9439
|
}
|
|
@@ -8141,7 +9445,7 @@ function handleError(e) {
|
|
|
8141
9445
|
error: e instanceof Error ? e.message : String(e)
|
|
8142
9446
|
});
|
|
8143
9447
|
} else {
|
|
8144
|
-
console.error(
|
|
9448
|
+
console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
|
|
8145
9449
|
}
|
|
8146
9450
|
process.exit(1);
|
|
8147
9451
|
}
|
|
@@ -8149,7 +9453,7 @@ function resolveMemoryId(partialId) {
|
|
|
8149
9453
|
const db = getDatabase();
|
|
8150
9454
|
const id = resolvePartialId(db, "memories", partialId);
|
|
8151
9455
|
if (!id) {
|
|
8152
|
-
console.error(
|
|
9456
|
+
console.error(chalk2.red(`Could not resolve memory ID: ${partialId}`));
|
|
8153
9457
|
process.exit(1);
|
|
8154
9458
|
}
|
|
8155
9459
|
return id;
|
|
@@ -8175,10 +9479,10 @@ function resolveKeyOrId(keyOrId, opts, globalOpts) {
|
|
|
8175
9479
|
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
9480
|
program2.command("init").description("One-command setup: register MCP, install stop hook, configure auto-start").action(async () => {
|
|
8177
9481
|
const { platform } = process;
|
|
8178
|
-
const home =
|
|
9482
|
+
const home = homedir4();
|
|
8179
9483
|
const isMac = platform === "darwin";
|
|
8180
9484
|
console.log("");
|
|
8181
|
-
console.log(
|
|
9485
|
+
console.log(chalk2.bold(" mementos \u2014 setting up your memory layer"));
|
|
8182
9486
|
console.log("");
|
|
8183
9487
|
async function run(cmd) {
|
|
8184
9488
|
try {
|
|
@@ -8223,24 +9527,24 @@ program2.command("init").description("One-command setup: register MCP, install s
|
|
|
8223
9527
|
mcpError = e instanceof Error ? e.message : String(e);
|
|
8224
9528
|
}
|
|
8225
9529
|
if (mcpAlreadyInstalled) {
|
|
8226
|
-
console.log(
|
|
9530
|
+
console.log(chalk2.dim(" \xB7 MCP already registered"));
|
|
8227
9531
|
} else if (mcpError) {
|
|
8228
|
-
console.log(
|
|
8229
|
-
console.log(
|
|
9532
|
+
console.log(chalk2.red(` \u2717 Failed to register MCP: ${mcpError}`));
|
|
9533
|
+
console.log(chalk2.dim(" (Is Claude Code installed? Try: claude mcp add --transport stdio --scope user mementos -- mementos-mcp)"));
|
|
8230
9534
|
} else {
|
|
8231
|
-
console.log(
|
|
9535
|
+
console.log(chalk2.green(" \u2713 MCP server registered with Claude Code"));
|
|
8232
9536
|
}
|
|
8233
|
-
const hooksDir =
|
|
8234
|
-
const hookDest =
|
|
8235
|
-
const settingsPath =
|
|
9537
|
+
const hooksDir = join6(home, ".claude", "hooks");
|
|
9538
|
+
const hookDest = join6(hooksDir, "mementos-stop-hook.ts");
|
|
9539
|
+
const settingsPath = join6(home, ".claude", "settings.json");
|
|
8236
9540
|
const hookCommand = `bun ${hookDest}`;
|
|
8237
9541
|
let hookAlreadyInstalled = false;
|
|
8238
9542
|
let hookError = null;
|
|
8239
9543
|
try {
|
|
8240
9544
|
let settings = {};
|
|
8241
|
-
if (
|
|
9545
|
+
if (existsSync6(settingsPath)) {
|
|
8242
9546
|
try {
|
|
8243
|
-
settings = JSON.parse(
|
|
9547
|
+
settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
|
|
8244
9548
|
} catch {
|
|
8245
9549
|
settings = {};
|
|
8246
9550
|
}
|
|
@@ -8251,19 +9555,19 @@ program2.command("init").description("One-command setup: register MCP, install s
|
|
|
8251
9555
|
if (alreadyHasMementos) {
|
|
8252
9556
|
hookAlreadyInstalled = true;
|
|
8253
9557
|
} else {
|
|
8254
|
-
if (!
|
|
8255
|
-
|
|
9558
|
+
if (!existsSync6(hooksDir)) {
|
|
9559
|
+
mkdirSync6(hooksDir, { recursive: true });
|
|
8256
9560
|
}
|
|
8257
|
-
if (!
|
|
8258
|
-
const packageDir =
|
|
9561
|
+
if (!existsSync6(hookDest)) {
|
|
9562
|
+
const packageDir = dirname4(dirname4(fileURLToPath(import.meta.url)));
|
|
8259
9563
|
const candidatePaths = [
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
9564
|
+
join6(packageDir, "scripts", "hooks", "claude-stop-hook.ts"),
|
|
9565
|
+
join6(packageDir, "..", "scripts", "hooks", "claude-stop-hook.ts"),
|
|
9566
|
+
join6(home, ".bun", "install", "global", "node_modules", "@hasna", "mementos", "scripts", "hooks", "claude-stop-hook.ts")
|
|
8263
9567
|
];
|
|
8264
9568
|
let hookSourceFound = false;
|
|
8265
9569
|
for (const src of candidatePaths) {
|
|
8266
|
-
if (
|
|
9570
|
+
if (existsSync6(src)) {
|
|
8267
9571
|
copyFileSync(src, hookDest);
|
|
8268
9572
|
hookSourceFound = true;
|
|
8269
9573
|
break;
|
|
@@ -8303,7 +9607,7 @@ async function main() {
|
|
|
8303
9607
|
|
|
8304
9608
|
main().catch(() => {});
|
|
8305
9609
|
`;
|
|
8306
|
-
|
|
9610
|
+
writeFileSync4(hookDest, inlineHook, "utf-8");
|
|
8307
9611
|
}
|
|
8308
9612
|
}
|
|
8309
9613
|
const newStopEntry = {
|
|
@@ -8312,24 +9616,24 @@ main().catch(() => {});
|
|
|
8312
9616
|
};
|
|
8313
9617
|
hooksObj["Stop"] = [...stopHooks, newStopEntry];
|
|
8314
9618
|
settings["hooks"] = hooksObj;
|
|
8315
|
-
|
|
9619
|
+
writeFileSync4(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
8316
9620
|
}
|
|
8317
9621
|
} catch (e) {
|
|
8318
9622
|
hookError = e instanceof Error ? e.message : String(e);
|
|
8319
9623
|
}
|
|
8320
9624
|
if (hookAlreadyInstalled) {
|
|
8321
|
-
console.log(
|
|
9625
|
+
console.log(chalk2.dim(" \xB7 Stop hook already installed"));
|
|
8322
9626
|
} else if (hookError) {
|
|
8323
|
-
console.log(
|
|
9627
|
+
console.log(chalk2.red(` \u2717 Failed to install stop hook: ${hookError}`));
|
|
8324
9628
|
} else {
|
|
8325
|
-
console.log(
|
|
9629
|
+
console.log(chalk2.green(" \u2713 Stop hook installed (sessions \u2192 memories)"));
|
|
8326
9630
|
}
|
|
8327
9631
|
let autoStartAlreadyInstalled = false;
|
|
8328
9632
|
let autoStartError = null;
|
|
8329
9633
|
if (!isMac) {
|
|
8330
|
-
console.log(
|
|
9634
|
+
console.log(chalk2.dim(` \xB7 Auto-start skipped (not macOS \u2014 platform: ${platform})`));
|
|
8331
9635
|
} else {
|
|
8332
|
-
const plistPath =
|
|
9636
|
+
const plistPath = join6(home, "Library", "LaunchAgents", "com.hasna.mementos.plist");
|
|
8333
9637
|
const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
8334
9638
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
8335
9639
|
<plist version="1.0">
|
|
@@ -8354,30 +9658,30 @@ main().catch(() => {});
|
|
|
8354
9658
|
</plist>
|
|
8355
9659
|
`;
|
|
8356
9660
|
try {
|
|
8357
|
-
if (
|
|
9661
|
+
if (existsSync6(plistPath)) {
|
|
8358
9662
|
autoStartAlreadyInstalled = true;
|
|
8359
9663
|
} else {
|
|
8360
|
-
const launchAgentsDir =
|
|
8361
|
-
if (!
|
|
8362
|
-
|
|
9664
|
+
const launchAgentsDir = join6(home, "Library", "LaunchAgents");
|
|
9665
|
+
if (!existsSync6(launchAgentsDir)) {
|
|
9666
|
+
mkdirSync6(launchAgentsDir, { recursive: true });
|
|
8363
9667
|
}
|
|
8364
|
-
|
|
9668
|
+
writeFileSync4(plistPath, plistContent, "utf-8");
|
|
8365
9669
|
}
|
|
8366
9670
|
} catch (e) {
|
|
8367
9671
|
autoStartError = e instanceof Error ? e.message : String(e);
|
|
8368
9672
|
}
|
|
8369
9673
|
if (autoStartAlreadyInstalled) {
|
|
8370
|
-
console.log(
|
|
9674
|
+
console.log(chalk2.dim(" \xB7 Auto-start already configured"));
|
|
8371
9675
|
} else if (autoStartError) {
|
|
8372
|
-
console.log(
|
|
9676
|
+
console.log(chalk2.red(` \u2717 Failed to configure auto-start: ${autoStartError}`));
|
|
8373
9677
|
} else {
|
|
8374
|
-
console.log(
|
|
9678
|
+
console.log(chalk2.green(" \u2713 Auto-start configured (starts on login)"));
|
|
8375
9679
|
}
|
|
8376
9680
|
if (!autoStartAlreadyInstalled && !autoStartError) {
|
|
8377
|
-
const plistPath2 =
|
|
9681
|
+
const plistPath2 = join6(home, "Library", "LaunchAgents", "com.hasna.mementos.plist");
|
|
8378
9682
|
const loadResult = await run(["launchctl", "load", plistPath2]);
|
|
8379
9683
|
if (!loadResult.ok) {
|
|
8380
|
-
console.log(
|
|
9684
|
+
console.log(chalk2.dim(` \xB7 launchctl load: ${loadResult.output || "already loaded"}`));
|
|
8381
9685
|
}
|
|
8382
9686
|
}
|
|
8383
9687
|
}
|
|
@@ -8389,18 +9693,18 @@ main().catch(() => {});
|
|
|
8389
9693
|
serverRunning = res.ok;
|
|
8390
9694
|
} catch {}
|
|
8391
9695
|
if (serverRunning) {
|
|
8392
|
-
console.log(
|
|
9696
|
+
console.log(chalk2.green(" \u2713 Server running on http://127.0.0.1:19428"));
|
|
8393
9697
|
} else {
|
|
8394
|
-
console.log(
|
|
8395
|
-
console.log(
|
|
9698
|
+
console.log(chalk2.dim(" \xB7 Server not yet running \u2014 it will start automatically on next login"));
|
|
9699
|
+
console.log(chalk2.dim(" (Or start it now: mementos-serve)"));
|
|
8396
9700
|
}
|
|
8397
9701
|
console.log("");
|
|
8398
|
-
console.log(
|
|
9702
|
+
console.log(chalk2.bold(" You're all set. Restart Claude Code to activate."));
|
|
8399
9703
|
console.log("");
|
|
8400
9704
|
console.log(" Quick start:");
|
|
8401
|
-
console.log(` ${
|
|
8402
|
-
console.log(` ${
|
|
8403
|
-
console.log(` ${
|
|
9705
|
+
console.log(` ${chalk2.cyan('mementos save "my-preference" "I prefer bun over npm"')}`);
|
|
9706
|
+
console.log(` ${chalk2.cyan("mementos list")}`);
|
|
9707
|
+
console.log(` ${chalk2.cyan("mementos doctor")}`);
|
|
8404
9708
|
console.log("");
|
|
8405
9709
|
});
|
|
8406
9710
|
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 +9740,7 @@ program2.command("save <key> <value>").description("Save a memory (create or ups
|
|
|
8436
9740
|
if (opts.template) {
|
|
8437
9741
|
const tpl = templates[opts.template];
|
|
8438
9742
|
if (!tpl) {
|
|
8439
|
-
console.error(
|
|
9743
|
+
console.error(chalk2.red(`Unknown template: ${opts.template}. Valid templates: ${Object.keys(templates).join(", ")}`));
|
|
8440
9744
|
process.exit(1);
|
|
8441
9745
|
}
|
|
8442
9746
|
templateDefaults = tpl;
|
|
@@ -8465,7 +9769,7 @@ program2.command("save <key> <value>").description("Save a memory (create or ups
|
|
|
8465
9769
|
if (globalOpts.json) {
|
|
8466
9770
|
outputJson(memory);
|
|
8467
9771
|
} else {
|
|
8468
|
-
console.log(
|
|
9772
|
+
console.log(chalk2.green(`Saved: ${memory.key} (${memory.id.slice(0, 8)})`));
|
|
8469
9773
|
}
|
|
8470
9774
|
} catch (e) {
|
|
8471
9775
|
handleError(e);
|
|
@@ -8504,7 +9808,7 @@ program2.command("recall <key>").description("Recall a memory by key").option("-
|
|
|
8504
9808
|
if (globalOpts.json) {
|
|
8505
9809
|
outputJson({ fuzzy_match: true, score: best.score, match_type: best.match_type, memory: best.memory });
|
|
8506
9810
|
} else {
|
|
8507
|
-
console.log(
|
|
9811
|
+
console.log(chalk2.yellow(`No exact match, showing best result (score: ${best.score.toFixed(2)}, match: ${best.match_type}):`));
|
|
8508
9812
|
console.log(formatMemoryDetail(best.memory));
|
|
8509
9813
|
}
|
|
8510
9814
|
return;
|
|
@@ -8512,7 +9816,7 @@ program2.command("recall <key>").description("Recall a memory by key").option("-
|
|
|
8512
9816
|
if (globalOpts.json) {
|
|
8513
9817
|
outputJson({ error: `No memory found for key: ${key}` });
|
|
8514
9818
|
} else {
|
|
8515
|
-
console.error(
|
|
9819
|
+
console.error(chalk2.yellow(`No memory found for key: ${key}`));
|
|
8516
9820
|
}
|
|
8517
9821
|
process.exit(1);
|
|
8518
9822
|
} catch (e) {
|
|
@@ -8562,10 +9866,10 @@ program2.command("list").description("List memories with optional filters").opti
|
|
|
8562
9866
|
return;
|
|
8563
9867
|
}
|
|
8564
9868
|
if (memories.length === 0) {
|
|
8565
|
-
console.log(
|
|
9869
|
+
console.log(chalk2.yellow("No memories found."));
|
|
8566
9870
|
return;
|
|
8567
9871
|
}
|
|
8568
|
-
console.log(
|
|
9872
|
+
console.log(chalk2.bold(`${memories.length} memor${memories.length === 1 ? "y" : "ies"}:`));
|
|
8569
9873
|
for (const m of memories) {
|
|
8570
9874
|
console.log(formatMemoryLine(m));
|
|
8571
9875
|
}
|
|
@@ -8582,7 +9886,7 @@ program2.command("update <id>").description("Update a memory by ID").option("--v
|
|
|
8582
9886
|
if (globalOpts.json) {
|
|
8583
9887
|
outputJson({ error: `Memory not found: ${id}` });
|
|
8584
9888
|
} else {
|
|
8585
|
-
console.error(
|
|
9889
|
+
console.error(chalk2.red(`Memory not found: ${id}`));
|
|
8586
9890
|
}
|
|
8587
9891
|
process.exit(1);
|
|
8588
9892
|
}
|
|
@@ -8611,7 +9915,7 @@ program2.command("update <id>").description("Update a memory by ID").option("--v
|
|
|
8611
9915
|
if (globalOpts.json) {
|
|
8612
9916
|
outputJson(updated);
|
|
8613
9917
|
} else {
|
|
8614
|
-
console.log(
|
|
9918
|
+
console.log(chalk2.green(`Updated: ${updated.key} (${updated.id.slice(0, 8)})`));
|
|
8615
9919
|
}
|
|
8616
9920
|
} catch (e) {
|
|
8617
9921
|
handleError(e);
|
|
@@ -8627,7 +9931,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8627
9931
|
if (globalOpts.json) {
|
|
8628
9932
|
outputJson({ deleted: idMatch });
|
|
8629
9933
|
} else {
|
|
8630
|
-
console.log(
|
|
9934
|
+
console.log(chalk2.green(`Memory ${idMatch} deleted.`));
|
|
8631
9935
|
}
|
|
8632
9936
|
return;
|
|
8633
9937
|
}
|
|
@@ -8636,7 +9940,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8636
9940
|
if (globalOpts.json) {
|
|
8637
9941
|
outputJson({ error: `No memory found: ${keyOrId}` });
|
|
8638
9942
|
} else {
|
|
8639
|
-
console.error(
|
|
9943
|
+
console.error(chalk2.red(`No memory found: ${keyOrId}`));
|
|
8640
9944
|
}
|
|
8641
9945
|
process.exit(1);
|
|
8642
9946
|
}
|
|
@@ -8645,7 +9949,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8645
9949
|
if (globalOpts.json) {
|
|
8646
9950
|
outputJson({ deleted: matches[0].id, key: keyOrId });
|
|
8647
9951
|
} else {
|
|
8648
|
-
console.log(
|
|
9952
|
+
console.log(chalk2.green(`Memory "${keyOrId}" (${matches[0].id}) deleted.`));
|
|
8649
9953
|
}
|
|
8650
9954
|
return;
|
|
8651
9955
|
}
|
|
@@ -8656,7 +9960,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8656
9960
|
if (globalOpts.json) {
|
|
8657
9961
|
outputJson({ deleted: ids, key: keyOrId, count: ids.length });
|
|
8658
9962
|
} else {
|
|
8659
|
-
console.log(
|
|
9963
|
+
console.log(chalk2.green(`Deleted ${ids.length} memories with key "${keyOrId}".`));
|
|
8660
9964
|
}
|
|
8661
9965
|
return;
|
|
8662
9966
|
}
|
|
@@ -8672,7 +9976,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8672
9976
|
}))
|
|
8673
9977
|
});
|
|
8674
9978
|
} else {
|
|
8675
|
-
console.error(
|
|
9979
|
+
console.error(chalk2.yellow(`Ambiguous: ${matches.length} memories match key "${keyOrId}":
|
|
8676
9980
|
`));
|
|
8677
9981
|
for (const m of matches) {
|
|
8678
9982
|
const parts = [
|
|
@@ -8684,7 +9988,7 @@ program2.command("forget <keyOrId>").description("Delete a memory by key or ID")
|
|
|
8684
9988
|
].filter(Boolean).join(" ");
|
|
8685
9989
|
console.error(parts);
|
|
8686
9990
|
}
|
|
8687
|
-
console.error(
|
|
9991
|
+
console.error(chalk2.cyan(`
|
|
8688
9992
|
Use the full ID, or narrow with --scope, --agent, --project, or --all.`));
|
|
8689
9993
|
}
|
|
8690
9994
|
process.exit(1);
|
|
@@ -8702,12 +10006,12 @@ program2.command("search <query>").description("Full-text search across memories
|
|
|
8702
10006
|
return;
|
|
8703
10007
|
}
|
|
8704
10008
|
if (history.length === 0) {
|
|
8705
|
-
console.log(
|
|
10009
|
+
console.log(chalk2.yellow("No search history."));
|
|
8706
10010
|
return;
|
|
8707
10011
|
}
|
|
8708
|
-
console.log(
|
|
10012
|
+
console.log(chalk2.bold("Recent searches:"));
|
|
8709
10013
|
for (const h of history) {
|
|
8710
|
-
console.log(` ${
|
|
10014
|
+
console.log(` ${chalk2.cyan(h.query)} ${chalk2.dim(`(${h.result_count} results, ${h.created_at})`)}`);
|
|
8711
10015
|
}
|
|
8712
10016
|
return;
|
|
8713
10017
|
}
|
|
@@ -8719,12 +10023,12 @@ program2.command("search <query>").description("Full-text search across memories
|
|
|
8719
10023
|
return;
|
|
8720
10024
|
}
|
|
8721
10025
|
if (popular.length === 0) {
|
|
8722
|
-
console.log(
|
|
10026
|
+
console.log(chalk2.yellow("No search history."));
|
|
8723
10027
|
return;
|
|
8724
10028
|
}
|
|
8725
|
-
console.log(
|
|
10029
|
+
console.log(chalk2.bold("Popular searches:"));
|
|
8726
10030
|
for (const p of popular) {
|
|
8727
|
-
console.log(` ${
|
|
10031
|
+
console.log(` ${chalk2.cyan(p.query)} ${chalk2.dim(`(${p.count} times)`)}`);
|
|
8728
10032
|
}
|
|
8729
10033
|
return;
|
|
8730
10034
|
}
|
|
@@ -8771,16 +10075,16 @@ program2.command("search <query>").description("Full-text search across memories
|
|
|
8771
10075
|
return;
|
|
8772
10076
|
}
|
|
8773
10077
|
if (results.length === 0) {
|
|
8774
|
-
console.log(
|
|
10078
|
+
console.log(chalk2.yellow(`No memories found matching "${query}".`));
|
|
8775
10079
|
return;
|
|
8776
10080
|
}
|
|
8777
|
-
console.log(
|
|
10081
|
+
console.log(chalk2.bold(`${results.length} result${results.length === 1 ? "" : "s"} for "${query}":`));
|
|
8778
10082
|
for (const r of results) {
|
|
8779
|
-
const score =
|
|
10083
|
+
const score = chalk2.dim(`(score: ${r.score.toFixed(1)})`);
|
|
8780
10084
|
console.log(`${formatMemoryLine(r.memory)} ${score}`);
|
|
8781
10085
|
if (r.highlights && r.highlights.length > 0) {
|
|
8782
10086
|
for (const h of r.highlights) {
|
|
8783
|
-
console.log(
|
|
10087
|
+
console.log(chalk2.dim(` ${h.field}: ${h.snippet}`));
|
|
8784
10088
|
}
|
|
8785
10089
|
}
|
|
8786
10090
|
}
|
|
@@ -8849,16 +10153,16 @@ program2.command("stats").description("Show memory statistics").option("--format
|
|
|
8849
10153
|
console.log(`by_agent,${k},${v}`);
|
|
8850
10154
|
return;
|
|
8851
10155
|
}
|
|
8852
|
-
console.log(
|
|
8853
|
-
console.log(`${
|
|
8854
|
-
console.log(`${
|
|
8855
|
-
console.log(`${
|
|
8856
|
-
console.log(`${
|
|
8857
|
-
console.log(`${
|
|
8858
|
-
console.log(`${
|
|
10156
|
+
console.log(chalk2.bold("Memory Statistics"));
|
|
10157
|
+
console.log(`${chalk2.bold("Total active:")} ${chalk2.white(String(total))}`);
|
|
10158
|
+
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}`);
|
|
10159
|
+
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}`);
|
|
10160
|
+
console.log(`${chalk2.bold("By status:")} active=${stats.by_status.active} archived=${stats.by_status.archived} expired=${stats.by_status.expired}`);
|
|
10161
|
+
console.log(`${chalk2.bold("Pinned:")} ${stats.pinned_count}`);
|
|
10162
|
+
console.log(`${chalk2.bold("Expired:")} ${stats.expired_count}`);
|
|
8859
10163
|
if (Object.keys(stats.by_agent).length > 0) {
|
|
8860
10164
|
const agentParts = Object.entries(stats.by_agent).map(([k, v]) => `${k}=${v}`).join(" ");
|
|
8861
|
-
console.log(`${
|
|
10165
|
+
console.log(`${chalk2.bold("By agent:")} ${agentParts}`);
|
|
8862
10166
|
}
|
|
8863
10167
|
} catch (e) {
|
|
8864
10168
|
handleError(e);
|
|
@@ -8923,30 +10227,30 @@ program2.command("report").description("Rich summary of memory activity and top
|
|
|
8923
10227
|
const maxC = Math.max(...activityRows.map((x) => x.cnt), 1);
|
|
8924
10228
|
return bars[Math.round(r.cnt / maxC * 7)] || "\u2581";
|
|
8925
10229
|
}).join("");
|
|
8926
|
-
console.log(
|
|
10230
|
+
console.log(chalk2.bold(`
|
|
8927
10231
|
mementos report \u2014 last ${days} days
|
|
8928
10232
|
`));
|
|
8929
|
-
console.log(` ${
|
|
8930
|
-
console.log(` ${
|
|
8931
|
-
console.log(` ${
|
|
8932
|
-
console.log(` ${
|
|
8933
|
-
console.log(` ${
|
|
10233
|
+
console.log(` ${chalk2.cyan("Total:")} ${total} memories (${chalk2.yellow(String(pinned))} pinned)`);
|
|
10234
|
+
console.log(` ${chalk2.cyan("Recent:")} ${recentTotal} new \xB7 ${chalk2.dim(`~${avgPerDay}/day`)}`);
|
|
10235
|
+
console.log(` ${chalk2.cyan("Activity:")} ${sparkline || chalk2.dim("no activity")}`);
|
|
10236
|
+
console.log(` ${chalk2.cyan("Scopes:")} global=${byScope["global"] || 0} shared=${byScope["shared"] || 0} private=${byScope["private"] || 0}`);
|
|
10237
|
+
console.log(` ${chalk2.cyan("Categories:")} knowledge=${byCat["knowledge"] || 0} fact=${byCat["fact"] || 0} preference=${byCat["preference"] || 0} history=${byCat["history"] || 0}`);
|
|
8934
10238
|
if (topMems.length > 0) {
|
|
8935
10239
|
console.log(`
|
|
8936
|
-
${
|
|
10240
|
+
${chalk2.bold("Top memories by importance:")}`);
|
|
8937
10241
|
topMems.forEach((m) => {
|
|
8938
|
-
console.log(` ${
|
|
10242
|
+
console.log(` ${chalk2.green(`[${m.importance}]`)} ${chalk2.bold(m.key)} ${chalk2.dim(`(${m.scope}/${m.category})`)}`);
|
|
8939
10243
|
console.log(` ${m.value.slice(0, 90)}${m.value.length > 90 ? "..." : ""}`);
|
|
8940
10244
|
});
|
|
8941
10245
|
}
|
|
8942
10246
|
if (topAgents.length > 0) {
|
|
8943
10247
|
console.log(`
|
|
8944
|
-
${
|
|
10248
|
+
${chalk2.bold("Top agents:")}`);
|
|
8945
10249
|
topAgents.forEach((a) => console.log(` ${a.agent_id}: ${a.c} memories`));
|
|
8946
10250
|
}
|
|
8947
10251
|
console.log("");
|
|
8948
10252
|
} catch (e) {
|
|
8949
|
-
console.error(
|
|
10253
|
+
console.error(chalk2.red(`report failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
8950
10254
|
process.exit(1);
|
|
8951
10255
|
}
|
|
8952
10256
|
});
|
|
@@ -8991,21 +10295,21 @@ program2.command("stale").description("Find memories not accessed recently (for
|
|
|
8991
10295
|
return;
|
|
8992
10296
|
}
|
|
8993
10297
|
if (rows.length === 0) {
|
|
8994
|
-
console.log(
|
|
10298
|
+
console.log(chalk2.green(`No stale memories (not accessed in ${days}+ days).`));
|
|
8995
10299
|
return;
|
|
8996
10300
|
}
|
|
8997
|
-
console.log(
|
|
10301
|
+
console.log(chalk2.bold(`
|
|
8998
10302
|
Stale memories (not accessed in ${days}+ days):
|
|
8999
10303
|
`));
|
|
9000
10304
|
for (const m of rows) {
|
|
9001
|
-
const lastAccess = m.accessed_at ? m.accessed_at.slice(0, 10) :
|
|
9002
|
-
console.log(` ${
|
|
10305
|
+
const lastAccess = m.accessed_at ? m.accessed_at.slice(0, 10) : chalk2.dim("never");
|
|
10306
|
+
console.log(` ${chalk2.dim(`[${m.importance}]`)} ${chalk2.bold(m.key)} ${chalk2.dim(`(${m.scope}/${m.category})`)}`);
|
|
9003
10307
|
console.log(` Last accessed: ${lastAccess} \xB7 ${m.access_count} reads \xB7 ${m.value.slice(0, 80)}${m.value.length > 80 ? "..." : ""}`);
|
|
9004
10308
|
}
|
|
9005
10309
|
console.log(`
|
|
9006
|
-
${
|
|
10310
|
+
${chalk2.dim(`${rows.length} result(s). Run 'mementos archive <key>' or 'mementos forget <key>' to clean up.`)}`);
|
|
9007
10311
|
} catch (e) {
|
|
9008
|
-
console.error(
|
|
10312
|
+
console.error(chalk2.red(`stale failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
9009
10313
|
process.exit(1);
|
|
9010
10314
|
}
|
|
9011
10315
|
});
|
|
@@ -9040,9 +10344,9 @@ program2.command("import [file]").description("Import memories from a JSON file
|
|
|
9040
10344
|
if (file === "-" || !file && !process.stdin.isTTY) {
|
|
9041
10345
|
raw = await Bun.stdin.text();
|
|
9042
10346
|
} else if (file) {
|
|
9043
|
-
raw =
|
|
10347
|
+
raw = readFileSync3(resolve3(file), "utf-8");
|
|
9044
10348
|
} else {
|
|
9045
|
-
console.error(
|
|
10349
|
+
console.error(chalk2.red("No input: provide a file path, use '-' for stdin, or pipe data."));
|
|
9046
10350
|
process.exit(1);
|
|
9047
10351
|
}
|
|
9048
10352
|
const memories = JSON.parse(raw);
|
|
@@ -9058,7 +10362,7 @@ program2.command("import [file]").description("Import memories from a JSON file
|
|
|
9058
10362
|
if (globalOpts.json) {
|
|
9059
10363
|
outputJson({ imported });
|
|
9060
10364
|
} else {
|
|
9061
|
-
console.log(
|
|
10365
|
+
console.log(chalk2.green(`Imported ${imported} memor${imported === 1 ? "y" : "ies"}.`));
|
|
9062
10366
|
}
|
|
9063
10367
|
} catch (e) {
|
|
9064
10368
|
handleError(e);
|
|
@@ -9072,12 +10376,12 @@ program2.command("clean").description("Remove expired memories and enforce quota
|
|
|
9072
10376
|
if (globalOpts.json) {
|
|
9073
10377
|
outputJson(result);
|
|
9074
10378
|
} 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: ${
|
|
10379
|
+
console.log(chalk2.bold("Cleanup complete:"));
|
|
10380
|
+
console.log(` Expired removed: ${chalk2.red(String(result.expired))}`);
|
|
10381
|
+
console.log(` Evicted (quota): ${chalk2.yellow(String(result.evicted))}`);
|
|
10382
|
+
console.log(` Archived (stale): ${chalk2.gray(String(result.archived))}`);
|
|
10383
|
+
console.log(` Archived (unused): ${chalk2.gray(String(result.unused_archived))}`);
|
|
10384
|
+
console.log(` Deprioritized: ${chalk2.blue(String(result.deprioritized))}`);
|
|
9081
10385
|
}
|
|
9082
10386
|
} catch (e) {
|
|
9083
10387
|
handleError(e);
|
|
@@ -9090,11 +10394,11 @@ program2.command("register-agent <name>").alias("init-agent").description("Regis
|
|
|
9090
10394
|
if (globalOpts.json) {
|
|
9091
10395
|
outputJson(agent);
|
|
9092
10396
|
} else {
|
|
9093
|
-
console.log(
|
|
9094
|
-
console.log(` ${
|
|
9095
|
-
console.log(` ${
|
|
9096
|
-
console.log(` ${
|
|
9097
|
-
console.log(` ${
|
|
10397
|
+
console.log(chalk2.green("Agent registered:"));
|
|
10398
|
+
console.log(` ${chalk2.bold("ID:")} ${agent.id}`);
|
|
10399
|
+
console.log(` ${chalk2.bold("Name:")} ${agent.name}`);
|
|
10400
|
+
console.log(` ${chalk2.bold("Role:")} ${agent.role || "agent"}`);
|
|
10401
|
+
console.log(` ${chalk2.bold("Created:")} ${agent.created_at}`);
|
|
9098
10402
|
}
|
|
9099
10403
|
} catch (e) {
|
|
9100
10404
|
handleError(e);
|
|
@@ -9109,12 +10413,12 @@ program2.command("agents").description("List all registered agents").action(() =
|
|
|
9109
10413
|
return;
|
|
9110
10414
|
}
|
|
9111
10415
|
if (agents.length === 0) {
|
|
9112
|
-
console.log(
|
|
10416
|
+
console.log(chalk2.yellow("No agents registered."));
|
|
9113
10417
|
return;
|
|
9114
10418
|
}
|
|
9115
|
-
console.log(
|
|
10419
|
+
console.log(chalk2.bold(`${agents.length} agent${agents.length === 1 ? "" : "s"}:`));
|
|
9116
10420
|
for (const a of agents) {
|
|
9117
|
-
console.log(` ${
|
|
10421
|
+
console.log(` ${chalk2.dim(a.id)} ${chalk2.bold(a.name)} ${chalk2.gray(a.role || "agent")} ${chalk2.dim(`last seen: ${a.last_seen_at}`)}`);
|
|
9118
10422
|
}
|
|
9119
10423
|
} catch (e) {
|
|
9120
10424
|
handleError(e);
|
|
@@ -9134,7 +10438,7 @@ program2.command("agent-update <id>").description("Update an agent's name, descr
|
|
|
9134
10438
|
if (globalOpts.json) {
|
|
9135
10439
|
outputJson({ error: "No updates provided. Use --name, --description, or --role." });
|
|
9136
10440
|
} else {
|
|
9137
|
-
console.error(
|
|
10441
|
+
console.error(chalk2.red("No updates provided. Use --name, --description, or --role."));
|
|
9138
10442
|
}
|
|
9139
10443
|
process.exit(1);
|
|
9140
10444
|
}
|
|
@@ -9143,19 +10447,19 @@ program2.command("agent-update <id>").description("Update an agent's name, descr
|
|
|
9143
10447
|
if (globalOpts.json) {
|
|
9144
10448
|
outputJson({ error: `Agent not found: ${id}` });
|
|
9145
10449
|
} else {
|
|
9146
|
-
console.error(
|
|
10450
|
+
console.error(chalk2.red(`Agent not found: ${id}`));
|
|
9147
10451
|
}
|
|
9148
10452
|
process.exit(1);
|
|
9149
10453
|
}
|
|
9150
10454
|
if (globalOpts.json) {
|
|
9151
10455
|
outputJson(agent);
|
|
9152
10456
|
} else {
|
|
9153
|
-
console.log(
|
|
9154
|
-
console.log(` ${
|
|
9155
|
-
console.log(` ${
|
|
9156
|
-
console.log(` ${
|
|
9157
|
-
console.log(` ${
|
|
9158
|
-
console.log(` ${
|
|
10457
|
+
console.log(chalk2.green("Agent updated:"));
|
|
10458
|
+
console.log(` ${chalk2.bold("ID:")} ${agent.id}`);
|
|
10459
|
+
console.log(` ${chalk2.bold("Name:")} ${agent.name}`);
|
|
10460
|
+
console.log(` ${chalk2.bold("Description:")} ${agent.description || "-"}`);
|
|
10461
|
+
console.log(` ${chalk2.bold("Role:")} ${agent.role || "agent"}`);
|
|
10462
|
+
console.log(` ${chalk2.bold("Last seen:")} ${agent.last_seen_at}`);
|
|
9159
10463
|
}
|
|
9160
10464
|
} catch (e) {
|
|
9161
10465
|
handleError(e);
|
|
@@ -9168,17 +10472,17 @@ program2.command("projects").description("Manage projects").option("--add", "Add
|
|
|
9168
10472
|
const name = opts.name;
|
|
9169
10473
|
const path = opts.path;
|
|
9170
10474
|
if (!name || !path) {
|
|
9171
|
-
console.error(
|
|
10475
|
+
console.error(chalk2.red("--name and --path are required when adding a project"));
|
|
9172
10476
|
process.exit(1);
|
|
9173
10477
|
}
|
|
9174
10478
|
const project = registerProject(name, resolve3(path), opts.description);
|
|
9175
10479
|
if (globalOpts.json) {
|
|
9176
10480
|
outputJson(project);
|
|
9177
10481
|
} else {
|
|
9178
|
-
console.log(
|
|
9179
|
-
console.log(` ${
|
|
9180
|
-
console.log(` ${
|
|
9181
|
-
console.log(` ${
|
|
10482
|
+
console.log(chalk2.green("Project registered:"));
|
|
10483
|
+
console.log(` ${chalk2.bold("ID:")} ${project.id}`);
|
|
10484
|
+
console.log(` ${chalk2.bold("Name:")} ${project.name}`);
|
|
10485
|
+
console.log(` ${chalk2.bold("Path:")} ${project.path}`);
|
|
9182
10486
|
}
|
|
9183
10487
|
return;
|
|
9184
10488
|
}
|
|
@@ -9188,12 +10492,12 @@ program2.command("projects").description("Manage projects").option("--add", "Add
|
|
|
9188
10492
|
return;
|
|
9189
10493
|
}
|
|
9190
10494
|
if (projects.length === 0) {
|
|
9191
|
-
console.log(
|
|
10495
|
+
console.log(chalk2.yellow("No projects registered."));
|
|
9192
10496
|
return;
|
|
9193
10497
|
}
|
|
9194
|
-
console.log(
|
|
10498
|
+
console.log(chalk2.bold(`${projects.length} project${projects.length === 1 ? "" : "s"}:`));
|
|
9195
10499
|
for (const p of projects) {
|
|
9196
|
-
console.log(` ${
|
|
10500
|
+
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
10501
|
}
|
|
9198
10502
|
} catch (e) {
|
|
9199
10503
|
handleError(e);
|
|
@@ -9283,7 +10587,7 @@ program2.command("inject").description("Output injection context for agent syste
|
|
|
9283
10587
|
if (globalOpts.json) {
|
|
9284
10588
|
outputJson({ context: "", count: 0 });
|
|
9285
10589
|
} else {
|
|
9286
|
-
console.log(
|
|
10590
|
+
console.log(chalk2.yellow("No relevant memories found for injection."));
|
|
9287
10591
|
}
|
|
9288
10592
|
return;
|
|
9289
10593
|
}
|
|
@@ -9323,7 +10627,7 @@ program2.command("bulk <action> <ids...>").description("Batch operations: forget
|
|
|
9323
10627
|
"unpin"
|
|
9324
10628
|
];
|
|
9325
10629
|
if (!validActions.includes(action)) {
|
|
9326
|
-
console.error(
|
|
10630
|
+
console.error(chalk2.red(`Invalid action: ${action}. Valid: ${validActions.join(", ")}`));
|
|
9327
10631
|
process.exit(1);
|
|
9328
10632
|
}
|
|
9329
10633
|
const resolvedIds = ids.map((id) => resolveMemoryId(id));
|
|
@@ -9376,7 +10680,7 @@ program2.command("bulk <action> <ids...>").description("Batch operations: forget
|
|
|
9376
10680
|
if (globalOpts.json) {
|
|
9377
10681
|
outputJson({ action, affected, ids: resolvedIds });
|
|
9378
10682
|
} else {
|
|
9379
|
-
console.log(
|
|
10683
|
+
console.log(chalk2.green(`${action}: ${affected} memor${affected === 1 ? "y" : "ies"} affected.`));
|
|
9380
10684
|
}
|
|
9381
10685
|
} catch (e) {
|
|
9382
10686
|
handleError(e);
|
|
@@ -9389,7 +10693,7 @@ program2.command("doctor").description("Diagnose common issues with the mementos
|
|
|
9389
10693
|
checks.push({ name: "Version", status: "ok", detail: version });
|
|
9390
10694
|
const dbPath = getDbPath();
|
|
9391
10695
|
let db = null;
|
|
9392
|
-
if (dbPath !== ":memory:" &&
|
|
10696
|
+
if (dbPath !== ":memory:" && existsSync6(dbPath)) {
|
|
9393
10697
|
try {
|
|
9394
10698
|
accessSync(dbPath, fsConstants.R_OK | fsConstants.W_OK);
|
|
9395
10699
|
checks.push({ name: "Database file", status: "ok", detail: dbPath });
|
|
@@ -9408,7 +10712,7 @@ program2.command("doctor").description("Diagnose common issues with the mementos
|
|
|
9408
10712
|
checks.push({ name: "Database connection", status: "fail", detail: e instanceof Error ? e.message : String(e) });
|
|
9409
10713
|
}
|
|
9410
10714
|
try {
|
|
9411
|
-
if (dbPath !== ":memory:" &&
|
|
10715
|
+
if (dbPath !== ":memory:" && existsSync6(dbPath)) {
|
|
9412
10716
|
const stats = statSync(dbPath);
|
|
9413
10717
|
const sizeKb = (stats.size / 1024).toFixed(1);
|
|
9414
10718
|
const sizeMb = (stats.size / (1024 * 1024)).toFixed(2);
|
|
@@ -9446,14 +10750,14 @@ program2.command("doctor").description("Diagnose common issues with the mementos
|
|
|
9446
10750
|
const byScope = {};
|
|
9447
10751
|
let expiredCount = 0;
|
|
9448
10752
|
let staleCount = 0;
|
|
9449
|
-
const
|
|
10753
|
+
const now3 = Date.now();
|
|
9450
10754
|
const staleThreshold = 14 * 24 * 60 * 60 * 1000;
|
|
9451
10755
|
for (const m of all) {
|
|
9452
10756
|
byScope[m.scope] = (byScope[m.scope] || 0) + 1;
|
|
9453
10757
|
if (m.status === "expired")
|
|
9454
10758
|
expiredCount++;
|
|
9455
10759
|
const lastAccess = m.accessed_at ? new Date(m.accessed_at).getTime() : new Date(m.created_at).getTime();
|
|
9456
|
-
if (
|
|
10760
|
+
if (now3 - lastAccess > staleThreshold && m.status === "active") {
|
|
9457
10761
|
staleCount++;
|
|
9458
10762
|
}
|
|
9459
10763
|
}
|
|
@@ -9544,9 +10848,9 @@ program2.command("doctor").description("Diagnose common issues with the mementos
|
|
|
9544
10848
|
checks.push({ name: "MCP server", status: "warn", detail: "could not check (is claude CLI installed?)" });
|
|
9545
10849
|
}
|
|
9546
10850
|
try {
|
|
9547
|
-
const settingsFilePath =
|
|
9548
|
-
if (
|
|
9549
|
-
const settings = JSON.parse(
|
|
10851
|
+
const settingsFilePath = join6(homedir4(), ".claude", "settings.json");
|
|
10852
|
+
if (existsSync6(settingsFilePath)) {
|
|
10853
|
+
const settings = JSON.parse(readFileSync3(settingsFilePath, "utf-8"));
|
|
9550
10854
|
const hooksObj = settings["hooks"] || {};
|
|
9551
10855
|
const stopHooks = hooksObj["Stop"] || [];
|
|
9552
10856
|
const hasMementos = stopHooks.some((e) => e.hooks?.some((h) => h.command && h.command.includes("mementos")));
|
|
@@ -9562,11 +10866,11 @@ program2.command("doctor").description("Diagnose common issues with the mementos
|
|
|
9562
10866
|
checks.push({ name: "Stop hook", status: "warn", detail: "could not check stop hook" });
|
|
9563
10867
|
}
|
|
9564
10868
|
if (process.platform === "darwin") {
|
|
9565
|
-
const plistFilePath =
|
|
10869
|
+
const plistFilePath = join6(homedir4(), "Library", "LaunchAgents", "com.hasna.mementos.plist");
|
|
9566
10870
|
checks.push({
|
|
9567
10871
|
name: "Auto-start",
|
|
9568
|
-
status:
|
|
9569
|
-
detail:
|
|
10872
|
+
status: existsSync6(plistFilePath) ? "ok" : "warn",
|
|
10873
|
+
detail: existsSync6(plistFilePath) ? "configured (starts on login)" : "not configured \u2192 run: mementos init"
|
|
9570
10874
|
});
|
|
9571
10875
|
} else {
|
|
9572
10876
|
checks.push({ name: "Auto-start", status: "ok", detail: `n/a on ${process.platform}` });
|
|
@@ -9577,24 +10881,24 @@ function outputDoctorResults(globalOpts, checks) {
|
|
|
9577
10881
|
if (globalOpts.json) {
|
|
9578
10882
|
outputJson({ checks, healthy: checks.every((c) => c.status === "ok") });
|
|
9579
10883
|
} else {
|
|
9580
|
-
console.log(
|
|
10884
|
+
console.log(chalk2.bold(`
|
|
9581
10885
|
mementos doctor
|
|
9582
10886
|
`));
|
|
9583
10887
|
for (const check of checks) {
|
|
9584
|
-
const icon = check.status === "ok" ?
|
|
9585
|
-
console.log(` ${icon} ${
|
|
10888
|
+
const icon = check.status === "ok" ? chalk2.green("\u2713") : check.status === "warn" ? chalk2.yellow("\u26A0") : chalk2.red("\u2717");
|
|
10889
|
+
console.log(` ${icon} ${chalk2.bold(check.name)}: ${check.detail}`);
|
|
9586
10890
|
}
|
|
9587
10891
|
const healthy = checks.every((c) => c.status === "ok");
|
|
9588
10892
|
const warnings = checks.filter((c) => c.status === "warn").length;
|
|
9589
10893
|
const failures = checks.filter((c) => c.status === "fail").length;
|
|
9590
10894
|
console.log("");
|
|
9591
10895
|
if (healthy) {
|
|
9592
|
-
console.log(
|
|
10896
|
+
console.log(chalk2.green(" All checks passed."));
|
|
9593
10897
|
} else {
|
|
9594
10898
|
if (failures > 0)
|
|
9595
|
-
console.log(
|
|
10899
|
+
console.log(chalk2.red(` ${failures} check(s) failed.`));
|
|
9596
10900
|
if (warnings > 0)
|
|
9597
|
-
console.log(
|
|
10901
|
+
console.log(chalk2.yellow(` ${warnings} warning(s).`));
|
|
9598
10902
|
}
|
|
9599
10903
|
console.log("");
|
|
9600
10904
|
}
|
|
@@ -9608,7 +10912,7 @@ program2.command("show <id>").description("Show full detail of a memory by ID (s
|
|
|
9608
10912
|
if (globalOpts.json) {
|
|
9609
10913
|
outputJson({ error: `Memory not found: ${id}` });
|
|
9610
10914
|
} else {
|
|
9611
|
-
console.error(
|
|
10915
|
+
console.error(chalk2.red(`Memory not found: ${id}`));
|
|
9612
10916
|
}
|
|
9613
10917
|
process.exit(1);
|
|
9614
10918
|
}
|
|
@@ -9634,24 +10938,24 @@ program2.command("history").description("List memories sorted by most recently a
|
|
|
9634
10938
|
return;
|
|
9635
10939
|
}
|
|
9636
10940
|
if (memories.length === 0) {
|
|
9637
|
-
console.log(
|
|
10941
|
+
console.log(chalk2.yellow("No recently accessed memories."));
|
|
9638
10942
|
return;
|
|
9639
10943
|
}
|
|
9640
|
-
console.log(
|
|
10944
|
+
console.log(chalk2.bold(`${memories.length} recently accessed memor${memories.length === 1 ? "y" : "ies"}:`));
|
|
9641
10945
|
for (const m of memories) {
|
|
9642
|
-
const id =
|
|
10946
|
+
const id = chalk2.dim(m.id.slice(0, 8));
|
|
9643
10947
|
const scope = colorScope(m.scope);
|
|
9644
10948
|
const cat = colorCategory(m.category);
|
|
9645
10949
|
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}] ${
|
|
10950
|
+
const accessed = m.accessed_at ? chalk2.dim(m.accessed_at) : chalk2.dim("never");
|
|
10951
|
+
console.log(`${id} [${scope}/${cat}] ${chalk2.bold(m.key)} = ${value} ${accessed}`);
|
|
9648
10952
|
}
|
|
9649
10953
|
} catch (e) {
|
|
9650
10954
|
handleError(e);
|
|
9651
10955
|
}
|
|
9652
10956
|
});
|
|
9653
10957
|
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:
|
|
10958
|
+
const { readFileSync: readFileSync4, writeFileSync: writeFileSync5, existsSync: fileExists } = __require("fs");
|
|
9655
10959
|
const { join: pathJoin } = __require("path");
|
|
9656
10960
|
const { homedir: getHome } = __require("os");
|
|
9657
10961
|
const home = getHome();
|
|
@@ -9662,8 +10966,8 @@ program2.command("mcp").description("Install mementos MCP server into Claude Cod
|
|
|
9662
10966
|
opts.gemini ? "gemini" : null
|
|
9663
10967
|
].filter(Boolean);
|
|
9664
10968
|
if (targets.length === 0) {
|
|
9665
|
-
console.log(
|
|
9666
|
-
console.log(
|
|
10969
|
+
console.log(chalk2.yellow("Specify a target: --claude, --codex, --gemini, or --all"));
|
|
10970
|
+
console.log(chalk2.gray("Example: mementos mcp --all"));
|
|
9667
10971
|
return;
|
|
9668
10972
|
}
|
|
9669
10973
|
for (const target of targets) {
|
|
@@ -9673,25 +10977,25 @@ program2.command("mcp").description("Install mementos MCP server into Claude Cod
|
|
|
9673
10977
|
if (opts.uninstall) {
|
|
9674
10978
|
try {
|
|
9675
10979
|
execSync(`claude mcp remove mementos`, { stdio: "pipe" });
|
|
9676
|
-
console.log(
|
|
10980
|
+
console.log(chalk2.green("Removed mementos from Claude Code MCP"));
|
|
9677
10981
|
} catch {
|
|
9678
|
-
console.log(
|
|
10982
|
+
console.log(chalk2.yellow("mementos was not installed in Claude Code (or claude CLI not found)"));
|
|
9679
10983
|
}
|
|
9680
10984
|
} else {
|
|
9681
10985
|
try {
|
|
9682
10986
|
execSync(`claude mcp add --transport stdio --scope user mementos -- ${mementosCmd}`, { stdio: "pipe" });
|
|
9683
|
-
console.log(
|
|
9684
|
-
console.log(
|
|
10987
|
+
console.log(chalk2.green(`Installed mementos into Claude Code (user scope)`));
|
|
10988
|
+
console.log(chalk2.gray(" Restart Claude Code for the change to take effect."));
|
|
9685
10989
|
} catch (e) {
|
|
9686
|
-
console.log(
|
|
9687
|
-
console.log(
|
|
10990
|
+
console.log(chalk2.yellow("claude CLI not found. Run this manually:"));
|
|
10991
|
+
console.log(chalk2.white(` claude mcp add --transport stdio --scope user mementos -- ${mementosCmd}`));
|
|
9688
10992
|
}
|
|
9689
10993
|
}
|
|
9690
10994
|
}
|
|
9691
10995
|
if (target === "codex") {
|
|
9692
10996
|
const configPath = pathJoin(home, ".codex", "config.toml");
|
|
9693
10997
|
if (fileExists(configPath)) {
|
|
9694
|
-
let content =
|
|
10998
|
+
let content = readFileSync4(configPath, "utf-8");
|
|
9695
10999
|
if (opts.uninstall) {
|
|
9696
11000
|
content = content.replace(/\n\[mcp_servers\.mementos\]\ncommand = "[^"]*"\nargs = \[\]\n?/g, `
|
|
9697
11001
|
`);
|
|
@@ -9702,17 +11006,17 @@ command = "${mementosCmd}"
|
|
|
9702
11006
|
args = []
|
|
9703
11007
|
`;
|
|
9704
11008
|
}
|
|
9705
|
-
|
|
9706
|
-
console.log(
|
|
11009
|
+
writeFileSync5(configPath, content, "utf-8");
|
|
11010
|
+
console.log(chalk2.green(`${opts.uninstall ? "Removed from" : "Installed into"} Codex: ${configPath}`));
|
|
9707
11011
|
} else {
|
|
9708
|
-
console.log(
|
|
11012
|
+
console.log(chalk2.yellow(`Codex config not found: ${configPath}`));
|
|
9709
11013
|
}
|
|
9710
11014
|
}
|
|
9711
11015
|
if (target === "gemini") {
|
|
9712
11016
|
const configPath = pathJoin(home, ".gemini", "settings.json");
|
|
9713
11017
|
let config = {};
|
|
9714
11018
|
if (fileExists(configPath)) {
|
|
9715
|
-
config = JSON.parse(
|
|
11019
|
+
config = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
9716
11020
|
}
|
|
9717
11021
|
const servers = config["mcpServers"] || {};
|
|
9718
11022
|
if (opts.uninstall) {
|
|
@@ -9721,12 +11025,12 @@ args = []
|
|
|
9721
11025
|
servers["mementos"] = { command: mementosCmd, args: [] };
|
|
9722
11026
|
}
|
|
9723
11027
|
config["mcpServers"] = servers;
|
|
9724
|
-
|
|
11028
|
+
writeFileSync5(configPath, JSON.stringify(config, null, 2) + `
|
|
9725
11029
|
`, "utf-8");
|
|
9726
|
-
console.log(
|
|
11030
|
+
console.log(chalk2.green(`${opts.uninstall ? "Removed from" : "Installed into"} Gemini: ${configPath}`));
|
|
9727
11031
|
}
|
|
9728
11032
|
} catch (e) {
|
|
9729
|
-
console.error(
|
|
11033
|
+
console.error(chalk2.red(`Failed for ${target}: ${e instanceof Error ? e.message : String(e)}`));
|
|
9730
11034
|
}
|
|
9731
11035
|
}
|
|
9732
11036
|
});
|
|
@@ -9742,20 +11046,20 @@ program2.command("watch").description("Watch for new and changed memories in rea
|
|
|
9742
11046
|
projectId = project.id;
|
|
9743
11047
|
}
|
|
9744
11048
|
const intervalMs = opts.interval || 500;
|
|
9745
|
-
console.log(
|
|
11049
|
+
console.log(chalk2.bold.cyan("Watching memories...") + chalk2.dim(" (Ctrl+C to stop)"));
|
|
9746
11050
|
const filters = [];
|
|
9747
11051
|
if (opts.scope)
|
|
9748
11052
|
filters.push(`scope=${colorScope(opts.scope)}`);
|
|
9749
11053
|
if (opts.category)
|
|
9750
11054
|
filters.push(`category=${colorCategory(opts.category)}`);
|
|
9751
11055
|
if (agentId)
|
|
9752
|
-
filters.push(`agent=${
|
|
11056
|
+
filters.push(`agent=${chalk2.dim(agentId)}`);
|
|
9753
11057
|
if (projectId)
|
|
9754
|
-
filters.push(`project=${
|
|
11058
|
+
filters.push(`project=${chalk2.dim(projectId)}`);
|
|
9755
11059
|
if (filters.length > 0) {
|
|
9756
|
-
console.log(
|
|
11060
|
+
console.log(chalk2.dim("Filters: ") + filters.join(chalk2.dim(" | ")));
|
|
9757
11061
|
}
|
|
9758
|
-
console.log(
|
|
11062
|
+
console.log(chalk2.dim(`Poll interval: ${intervalMs}ms`));
|
|
9759
11063
|
console.log();
|
|
9760
11064
|
const filter = {
|
|
9761
11065
|
scope: opts.scope,
|
|
@@ -9766,14 +11070,14 @@ program2.command("watch").description("Watch for new and changed memories in rea
|
|
|
9766
11070
|
};
|
|
9767
11071
|
const recent = listMemories(filter);
|
|
9768
11072
|
if (recent.length > 0) {
|
|
9769
|
-
console.log(
|
|
11073
|
+
console.log(chalk2.bold.dim(`Recent (${recent.length}):`));
|
|
9770
11074
|
for (const m of recent.reverse()) {
|
|
9771
11075
|
console.log(formatWatchLine(m));
|
|
9772
11076
|
}
|
|
9773
11077
|
} else {
|
|
9774
|
-
console.log(
|
|
11078
|
+
console.log(chalk2.dim("No recent memories."));
|
|
9775
11079
|
}
|
|
9776
|
-
console.log(
|
|
11080
|
+
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
11081
|
console.log();
|
|
9778
11082
|
const { startPolling: startPolling2 } = (init_poll(), __toCommonJS(exports_poll));
|
|
9779
11083
|
const handle = startPolling2({
|
|
@@ -9789,13 +11093,13 @@ program2.command("watch").description("Watch for new and changed memories in rea
|
|
|
9789
11093
|
}
|
|
9790
11094
|
},
|
|
9791
11095
|
on_error: (err) => {
|
|
9792
|
-
console.error(
|
|
11096
|
+
console.error(chalk2.red(`Poll error: ${err.message}`));
|
|
9793
11097
|
}
|
|
9794
11098
|
});
|
|
9795
11099
|
const cleanup = () => {
|
|
9796
11100
|
handle.stop();
|
|
9797
11101
|
console.log();
|
|
9798
|
-
console.log(
|
|
11102
|
+
console.log(chalk2.dim("Stopped watching."));
|
|
9799
11103
|
process.exit(0);
|
|
9800
11104
|
};
|
|
9801
11105
|
process.on("SIGINT", cleanup);
|
|
@@ -9812,7 +11116,7 @@ program2.command("pin <keyOrId>").description("Pin a memory by key or partial ID
|
|
|
9812
11116
|
if (globalOpts.json) {
|
|
9813
11117
|
outputJson({ error: `No memory found: ${keyOrId}` });
|
|
9814
11118
|
} else {
|
|
9815
|
-
console.error(
|
|
11119
|
+
console.error(chalk2.red(`No memory found: ${keyOrId}`));
|
|
9816
11120
|
}
|
|
9817
11121
|
process.exit(1);
|
|
9818
11122
|
}
|
|
@@ -9823,7 +11127,7 @@ program2.command("pin <keyOrId>").description("Pin a memory by key or partial ID
|
|
|
9823
11127
|
if (globalOpts.json) {
|
|
9824
11128
|
outputJson(updated);
|
|
9825
11129
|
} else {
|
|
9826
|
-
console.log(
|
|
11130
|
+
console.log(chalk2.green(`Pinned: ${updated.key} (${updated.id.slice(0, 8)})`));
|
|
9827
11131
|
}
|
|
9828
11132
|
} catch (e) {
|
|
9829
11133
|
handleError(e);
|
|
@@ -9837,7 +11141,7 @@ program2.command("unpin <keyOrId>").description("Unpin a memory by key or partia
|
|
|
9837
11141
|
if (globalOpts.json) {
|
|
9838
11142
|
outputJson({ error: `No memory found: ${keyOrId}` });
|
|
9839
11143
|
} else {
|
|
9840
|
-
console.error(
|
|
11144
|
+
console.error(chalk2.red(`No memory found: ${keyOrId}`));
|
|
9841
11145
|
}
|
|
9842
11146
|
process.exit(1);
|
|
9843
11147
|
}
|
|
@@ -9848,7 +11152,7 @@ program2.command("unpin <keyOrId>").description("Unpin a memory by key or partia
|
|
|
9848
11152
|
if (globalOpts.json) {
|
|
9849
11153
|
outputJson(updated);
|
|
9850
11154
|
} else {
|
|
9851
|
-
console.log(
|
|
11155
|
+
console.log(chalk2.green(`Unpinned: ${updated.key} (${updated.id.slice(0, 8)})`));
|
|
9852
11156
|
}
|
|
9853
11157
|
} catch (e) {
|
|
9854
11158
|
handleError(e);
|
|
@@ -9859,17 +11163,17 @@ program2.command("archive <keyOrId>").description("Archive a memory by key or ID
|
|
|
9859
11163
|
const globalOpts = program2.opts();
|
|
9860
11164
|
let memory = getMemory(resolvePartialId(getDatabase(), "memories", keyOrId) || keyOrId) || getMemoryByKey(keyOrId, opts.scope, globalOpts.agent);
|
|
9861
11165
|
if (!memory) {
|
|
9862
|
-
console.error(
|
|
11166
|
+
console.error(chalk2.red(`No memory found: ${keyOrId}`));
|
|
9863
11167
|
process.exit(1);
|
|
9864
11168
|
}
|
|
9865
11169
|
updateMemory(memory.id, { status: "archived", version: memory.version });
|
|
9866
11170
|
if (globalOpts.json) {
|
|
9867
11171
|
outputJson({ archived: true, id: memory.id, key: memory.key });
|
|
9868
11172
|
} else {
|
|
9869
|
-
console.log(
|
|
11173
|
+
console.log(chalk2.green(`\u2713 Archived: ${chalk2.bold(memory.key)} (${memory.id.slice(0, 8)})`));
|
|
9870
11174
|
}
|
|
9871
11175
|
} catch (e) {
|
|
9872
|
-
console.error(
|
|
11176
|
+
console.error(chalk2.red(`archive failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
9873
11177
|
process.exit(1);
|
|
9874
11178
|
}
|
|
9875
11179
|
});
|
|
@@ -9878,7 +11182,7 @@ program2.command("versions <keyOrId>").description("Show version history for a m
|
|
|
9878
11182
|
const globalOpts = program2.opts();
|
|
9879
11183
|
let memory = getMemory(resolvePartialId(getDatabase(), "memories", keyOrId) || keyOrId) || getMemoryByKey(keyOrId, opts.scope, globalOpts.agent);
|
|
9880
11184
|
if (!memory) {
|
|
9881
|
-
console.error(
|
|
11185
|
+
console.error(chalk2.red(`No memory found: ${keyOrId}`));
|
|
9882
11186
|
process.exit(1);
|
|
9883
11187
|
}
|
|
9884
11188
|
const versions = getMemoryVersions(memory.id);
|
|
@@ -9886,20 +11190,20 @@ program2.command("versions <keyOrId>").description("Show version history for a m
|
|
|
9886
11190
|
outputJson({ memory: { id: memory.id, key: memory.key, current_version: memory.version }, versions });
|
|
9887
11191
|
return;
|
|
9888
11192
|
}
|
|
9889
|
-
console.log(
|
|
11193
|
+
console.log(chalk2.bold(`
|
|
9890
11194
|
Version history: ${memory.key} (current: v${memory.version})
|
|
9891
11195
|
`));
|
|
9892
11196
|
if (versions.length === 0) {
|
|
9893
|
-
console.log(
|
|
11197
|
+
console.log(chalk2.dim(" No previous versions."));
|
|
9894
11198
|
return;
|
|
9895
11199
|
}
|
|
9896
11200
|
for (const v of versions) {
|
|
9897
|
-
console.log(` ${
|
|
11201
|
+
console.log(` ${chalk2.cyan(`v${v.version}`)} ${chalk2.dim(v.created_at.slice(0, 16))} scope=${v.scope} imp=${v.importance}`);
|
|
9898
11202
|
console.log(` ${v.value.slice(0, 120)}${v.value.length > 120 ? "..." : ""}`);
|
|
9899
11203
|
}
|
|
9900
11204
|
console.log("");
|
|
9901
11205
|
} catch (e) {
|
|
9902
|
-
console.error(
|
|
11206
|
+
console.error(chalk2.red(`versions failed: ${e instanceof Error ? e.message : String(e)}`));
|
|
9903
11207
|
process.exit(1);
|
|
9904
11208
|
}
|
|
9905
11209
|
});
|
|
@@ -9919,20 +11223,20 @@ program2.command("tail").description("Watch for new/updated memories in real-tim
|
|
|
9919
11223
|
const notifyEnabled = !!opts.notify;
|
|
9920
11224
|
const startTime = new Date().toISOString();
|
|
9921
11225
|
if (!jsonMode) {
|
|
9922
|
-
console.log(
|
|
11226
|
+
console.log(chalk2.bold.cyan("Watching for memory changes...") + chalk2.dim(" (Ctrl+C to stop)"));
|
|
9923
11227
|
const filters = [];
|
|
9924
11228
|
if (opts.scope)
|
|
9925
11229
|
filters.push(`scope=${colorScope(opts.scope)}`);
|
|
9926
11230
|
if (opts.category)
|
|
9927
11231
|
filters.push(`category=${colorCategory(opts.category)}`);
|
|
9928
11232
|
if (agentId)
|
|
9929
|
-
filters.push(`agent=${
|
|
11233
|
+
filters.push(`agent=${chalk2.dim(agentId)}`);
|
|
9930
11234
|
if (projectId)
|
|
9931
|
-
filters.push(`project=${
|
|
11235
|
+
filters.push(`project=${chalk2.dim(projectId)}`);
|
|
9932
11236
|
if (filters.length > 0) {
|
|
9933
|
-
console.log(
|
|
11237
|
+
console.log(chalk2.dim("Filters: ") + filters.join(chalk2.dim(" | ")));
|
|
9934
11238
|
}
|
|
9935
|
-
console.log(
|
|
11239
|
+
console.log(chalk2.dim(`Poll interval: ${intervalMs}ms`));
|
|
9936
11240
|
console.log();
|
|
9937
11241
|
}
|
|
9938
11242
|
const { startPolling: startPolling2 } = (init_poll(), __toCommonJS(exports_poll));
|
|
@@ -9948,7 +11252,7 @@ program2.command("tail").description("Watch for new/updated memories in real-tim
|
|
|
9948
11252
|
if (jsonMode) {
|
|
9949
11253
|
console.log(JSON.stringify({ event: isNew ? "new" : "updated", memory: m }));
|
|
9950
11254
|
} else {
|
|
9951
|
-
const prefix = isNew ?
|
|
11255
|
+
const prefix = isNew ? chalk2.green.bold("+ ") : chalk2.yellow.bold("~ ");
|
|
9952
11256
|
console.log(prefix + formatWatchLine(m));
|
|
9953
11257
|
}
|
|
9954
11258
|
if (notifyEnabled)
|
|
@@ -9959,7 +11263,7 @@ program2.command("tail").description("Watch for new/updated memories in real-tim
|
|
|
9959
11263
|
if (jsonMode) {
|
|
9960
11264
|
console.error(JSON.stringify({ event: "error", message: err.message }));
|
|
9961
11265
|
} else {
|
|
9962
|
-
console.error(
|
|
11266
|
+
console.error(chalk2.red(`Poll error: ${err.message}`));
|
|
9963
11267
|
}
|
|
9964
11268
|
}
|
|
9965
11269
|
});
|
|
@@ -9967,7 +11271,7 @@ program2.command("tail").description("Watch for new/updated memories in real-tim
|
|
|
9967
11271
|
handle.stop();
|
|
9968
11272
|
if (!jsonMode) {
|
|
9969
11273
|
console.log();
|
|
9970
|
-
console.log(
|
|
11274
|
+
console.log(chalk2.dim("Stopped watching."));
|
|
9971
11275
|
}
|
|
9972
11276
|
process.exit(0);
|
|
9973
11277
|
};
|
|
@@ -10059,7 +11363,7 @@ ${lines.join(`
|
|
|
10059
11363
|
}
|
|
10060
11364
|
if (lines.length === 0) {
|
|
10061
11365
|
if (process.stdout.isTTY) {
|
|
10062
|
-
console.log(
|
|
11366
|
+
console.log(chalk2.yellow("No relevant memories found."));
|
|
10063
11367
|
}
|
|
10064
11368
|
return;
|
|
10065
11369
|
}
|
|
@@ -10075,18 +11379,18 @@ program2.command("backup [path]").description("Backup the SQLite database to a f
|
|
|
10075
11379
|
try {
|
|
10076
11380
|
const globalOpts = program2.opts();
|
|
10077
11381
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
10078
|
-
const backupsDir =
|
|
11382
|
+
const backupsDir = join6(home, ".mementos", "backups");
|
|
10079
11383
|
if (opts.list) {
|
|
10080
|
-
if (!
|
|
11384
|
+
if (!existsSync6(backupsDir)) {
|
|
10081
11385
|
if (globalOpts.json) {
|
|
10082
11386
|
outputJson({ backups: [] });
|
|
10083
11387
|
return;
|
|
10084
11388
|
}
|
|
10085
|
-
console.log(
|
|
11389
|
+
console.log(chalk2.yellow("No backups directory found."));
|
|
10086
11390
|
return;
|
|
10087
11391
|
}
|
|
10088
|
-
const files =
|
|
10089
|
-
const filePath =
|
|
11392
|
+
const files = readdirSync3(backupsDir).filter((f) => f.endsWith(".db")).map((f) => {
|
|
11393
|
+
const filePath = join6(backupsDir, f);
|
|
10090
11394
|
const st2 = statSync(filePath);
|
|
10091
11395
|
return { name: f, path: filePath, size: st2.size, mtime: st2.mtime };
|
|
10092
11396
|
}).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
@@ -10095,7 +11399,7 @@ program2.command("backup [path]").description("Backup the SQLite database to a f
|
|
|
10095
11399
|
outputJson({ backups: [] });
|
|
10096
11400
|
return;
|
|
10097
11401
|
}
|
|
10098
|
-
console.log(
|
|
11402
|
+
console.log(chalk2.yellow("No backups found."));
|
|
10099
11403
|
return;
|
|
10100
11404
|
}
|
|
10101
11405
|
if (globalOpts.json) {
|
|
@@ -10109,34 +11413,34 @@ program2.command("backup [path]").description("Backup the SQLite database to a f
|
|
|
10109
11413
|
});
|
|
10110
11414
|
return;
|
|
10111
11415
|
}
|
|
10112
|
-
console.log(
|
|
11416
|
+
console.log(chalk2.bold(`Backups in ${backupsDir}:`));
|
|
10113
11417
|
for (const f of files) {
|
|
10114
11418
|
const date = f.mtime.toISOString().replace("T", " ").slice(0, 19);
|
|
10115
11419
|
const sizeMB2 = (f.size / (1024 * 1024)).toFixed(1);
|
|
10116
11420
|
const sizeStr2 = f.size >= 1024 * 1024 ? `${sizeMB2} MB` : `${(f.size / 1024).toFixed(1)} KB`;
|
|
10117
|
-
console.log(` ${
|
|
11421
|
+
console.log(` ${chalk2.dim(date)} ${chalk2.cyan(sizeStr2.padStart(8))} ${f.name}`);
|
|
10118
11422
|
}
|
|
10119
11423
|
return;
|
|
10120
11424
|
}
|
|
10121
11425
|
const dbPath = getDbPath();
|
|
10122
|
-
if (!
|
|
10123
|
-
console.error(
|
|
11426
|
+
if (!existsSync6(dbPath)) {
|
|
11427
|
+
console.error(chalk2.red(`Database not found at ${dbPath}`));
|
|
10124
11428
|
process.exit(1);
|
|
10125
11429
|
}
|
|
10126
11430
|
let dest;
|
|
10127
11431
|
if (targetPath) {
|
|
10128
11432
|
dest = resolve3(targetPath);
|
|
10129
11433
|
} else {
|
|
10130
|
-
if (!
|
|
10131
|
-
|
|
11434
|
+
if (!existsSync6(backupsDir)) {
|
|
11435
|
+
mkdirSync6(backupsDir, { recursive: true });
|
|
10132
11436
|
}
|
|
10133
|
-
const
|
|
10134
|
-
const ts =
|
|
10135
|
-
dest =
|
|
11437
|
+
const now3 = new Date;
|
|
11438
|
+
const ts = now3.toISOString().replace(/[-:T]/g, "").replace(/\..+/, "").slice(0, 15);
|
|
11439
|
+
dest = join6(backupsDir, `mementos-${ts}.db`);
|
|
10136
11440
|
}
|
|
10137
|
-
const destDir =
|
|
10138
|
-
if (!
|
|
10139
|
-
|
|
11441
|
+
const destDir = dirname4(dest);
|
|
11442
|
+
if (!existsSync6(destDir)) {
|
|
11443
|
+
mkdirSync6(destDir, { recursive: true });
|
|
10140
11444
|
}
|
|
10141
11445
|
copyFileSync(dbPath, dest);
|
|
10142
11446
|
const st = statSync(dest);
|
|
@@ -10146,7 +11450,7 @@ program2.command("backup [path]").description("Backup the SQLite database to a f
|
|
|
10146
11450
|
outputJson({ backed_up_to: dest, size: st.size, source: dbPath });
|
|
10147
11451
|
return;
|
|
10148
11452
|
}
|
|
10149
|
-
console.log(`Backed up to: ${
|
|
11453
|
+
console.log(`Backed up to: ${chalk2.green(dest)} (size: ${sizeStr})`);
|
|
10150
11454
|
} catch (e) {
|
|
10151
11455
|
handleError(e);
|
|
10152
11456
|
}
|
|
@@ -10155,31 +11459,31 @@ program2.command("restore [file]").description("Restore the database from a back
|
|
|
10155
11459
|
try {
|
|
10156
11460
|
const globalOpts = program2.opts();
|
|
10157
11461
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
10158
|
-
const backupsDir =
|
|
11462
|
+
const backupsDir = join6(home, ".mementos", "backups");
|
|
10159
11463
|
let source;
|
|
10160
11464
|
if (opts.latest) {
|
|
10161
|
-
if (!
|
|
10162
|
-
console.error(
|
|
11465
|
+
if (!existsSync6(backupsDir)) {
|
|
11466
|
+
console.error(chalk2.red("No backups directory found."));
|
|
10163
11467
|
process.exit(1);
|
|
10164
11468
|
}
|
|
10165
|
-
const files =
|
|
10166
|
-
const fp =
|
|
11469
|
+
const files = readdirSync3(backupsDir).filter((f) => f.endsWith(".db")).map((f) => {
|
|
11470
|
+
const fp = join6(backupsDir, f);
|
|
10167
11471
|
const st = statSync(fp);
|
|
10168
11472
|
return { path: fp, mtime: st.mtime };
|
|
10169
11473
|
}).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
10170
11474
|
if (files.length === 0) {
|
|
10171
|
-
console.error(
|
|
11475
|
+
console.error(chalk2.red("No backups found in " + backupsDir));
|
|
10172
11476
|
process.exit(1);
|
|
10173
11477
|
}
|
|
10174
11478
|
source = files[0].path;
|
|
10175
11479
|
} else if (filePath) {
|
|
10176
11480
|
source = resolve3(filePath);
|
|
10177
11481
|
} else {
|
|
10178
|
-
console.error(
|
|
11482
|
+
console.error(chalk2.red("Provide a backup file path or use --latest"));
|
|
10179
11483
|
process.exit(1);
|
|
10180
11484
|
}
|
|
10181
|
-
if (!
|
|
10182
|
-
console.error(
|
|
11485
|
+
if (!existsSync6(source)) {
|
|
11486
|
+
console.error(chalk2.red(`Backup file not found: ${source}`));
|
|
10183
11487
|
process.exit(1);
|
|
10184
11488
|
}
|
|
10185
11489
|
const dbPath = getDbPath();
|
|
@@ -10187,7 +11491,7 @@ program2.command("restore [file]").description("Restore the database from a back
|
|
|
10187
11491
|
const backupSizeMB = (backupStat.size / (1024 * 1024)).toFixed(1);
|
|
10188
11492
|
const backupSizeStr = backupStat.size >= 1024 * 1024 ? `${backupSizeMB} MB` : `${(backupStat.size / 1024).toFixed(1)} KB`;
|
|
10189
11493
|
let currentCount = 0;
|
|
10190
|
-
if (
|
|
11494
|
+
if (existsSync6(dbPath)) {
|
|
10191
11495
|
try {
|
|
10192
11496
|
const db = getDatabase();
|
|
10193
11497
|
const row = db.query("SELECT COUNT(*) as count FROM memories").get();
|
|
@@ -10196,8 +11500,8 @@ program2.command("restore [file]").description("Restore the database from a back
|
|
|
10196
11500
|
}
|
|
10197
11501
|
let backupCount = 0;
|
|
10198
11502
|
try {
|
|
10199
|
-
const { Database:
|
|
10200
|
-
const backupDb = new
|
|
11503
|
+
const { Database: Database3 } = __require("bun:sqlite");
|
|
11504
|
+
const backupDb = new Database3(source, { readonly: true });
|
|
10201
11505
|
const row = backupDb.query("SELECT COUNT(*) as count FROM memories").get();
|
|
10202
11506
|
backupCount = row?.count ?? 0;
|
|
10203
11507
|
backupDb.close();
|
|
@@ -10216,13 +11520,13 @@ program2.command("restore [file]").description("Restore the database from a back
|
|
|
10216
11520
|
});
|
|
10217
11521
|
return;
|
|
10218
11522
|
}
|
|
10219
|
-
console.log(
|
|
10220
|
-
console.log(` Source: ${
|
|
10221
|
-
console.log(` Target: ${
|
|
10222
|
-
console.log(` Current memories: ${
|
|
10223
|
-
console.log(` Backup memories: ${
|
|
11523
|
+
console.log(chalk2.bold("Restore preview:"));
|
|
11524
|
+
console.log(` Source: ${chalk2.cyan(source)} (${backupSizeStr})`);
|
|
11525
|
+
console.log(` Target: ${chalk2.cyan(dbPath)}`);
|
|
11526
|
+
console.log(` Current memories: ${chalk2.yellow(String(currentCount))}`);
|
|
11527
|
+
console.log(` Backup memories: ${chalk2.green(String(backupCount))}`);
|
|
10224
11528
|
console.log();
|
|
10225
|
-
console.log(
|
|
11529
|
+
console.log(chalk2.yellow("Use --force to confirm restore"));
|
|
10226
11530
|
return;
|
|
10227
11531
|
}
|
|
10228
11532
|
copyFileSync(source, dbPath);
|
|
@@ -10245,9 +11549,9 @@ program2.command("restore [file]").description("Restore the database from a back
|
|
|
10245
11549
|
});
|
|
10246
11550
|
return;
|
|
10247
11551
|
}
|
|
10248
|
-
console.log(`Restored from: ${
|
|
10249
|
-
console.log(` Previous memories: ${
|
|
10250
|
-
console.log(` Restored memories: ${
|
|
11552
|
+
console.log(`Restored from: ${chalk2.green(source)}`);
|
|
11553
|
+
console.log(` Previous memories: ${chalk2.yellow(String(currentCount))}`);
|
|
11554
|
+
console.log(` Restored memories: ${chalk2.green(String(newCount))}`);
|
|
10251
11555
|
} catch (e) {
|
|
10252
11556
|
handleError(e);
|
|
10253
11557
|
}
|
|
@@ -10260,7 +11564,7 @@ program2.command("diff <id>").description("Show diff between memory versions").o
|
|
|
10260
11564
|
if (!memoryId) {
|
|
10261
11565
|
const mem = resolveKeyOrId(idArg, {}, globalOpts);
|
|
10262
11566
|
if (!mem) {
|
|
10263
|
-
console.error(
|
|
11567
|
+
console.error(chalk2.red(`Memory not found: ${idArg}`));
|
|
10264
11568
|
process.exit(1);
|
|
10265
11569
|
}
|
|
10266
11570
|
memoryId = mem.id;
|
|
@@ -10273,7 +11577,7 @@ program2.command("diff <id>").description("Show diff between memory versions").o
|
|
|
10273
11577
|
function diffMemory(memoryId, opts, globalOpts) {
|
|
10274
11578
|
const current = getMemory(memoryId);
|
|
10275
11579
|
if (!current) {
|
|
10276
|
-
console.error(
|
|
11580
|
+
console.error(chalk2.red(`Memory not found: ${memoryId}`));
|
|
10277
11581
|
process.exit(1);
|
|
10278
11582
|
}
|
|
10279
11583
|
const versions = getMemoryVersions(memoryId);
|
|
@@ -10281,8 +11585,8 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10281
11585
|
if (globalOpts.json) {
|
|
10282
11586
|
outputJson({ error: "No version history available", memory_id: memoryId });
|
|
10283
11587
|
} else {
|
|
10284
|
-
console.log(
|
|
10285
|
-
console.log(
|
|
11588
|
+
console.log(chalk2.yellow("No version history available."));
|
|
11589
|
+
console.log(chalk2.dim(`Memory "${current.key}" is at version ${current.version} but has no prior snapshots.`));
|
|
10286
11590
|
}
|
|
10287
11591
|
return;
|
|
10288
11592
|
}
|
|
@@ -10293,13 +11597,13 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10293
11597
|
if (opts.version) {
|
|
10294
11598
|
const targetVersion = parseInt(opts.version, 10);
|
|
10295
11599
|
if (isNaN(targetVersion) || targetVersion < 1) {
|
|
10296
|
-
console.error(
|
|
11600
|
+
console.error(chalk2.red("Version must be a positive integer."));
|
|
10297
11601
|
process.exit(1);
|
|
10298
11602
|
}
|
|
10299
11603
|
if (targetVersion === current.version) {
|
|
10300
11604
|
const prev = versions.find((v) => v.version === targetVersion - 1);
|
|
10301
11605
|
if (!prev) {
|
|
10302
|
-
console.error(
|
|
11606
|
+
console.error(chalk2.red(`No version ${targetVersion - 1} found to compare against.`));
|
|
10303
11607
|
process.exit(1);
|
|
10304
11608
|
}
|
|
10305
11609
|
older = prev;
|
|
@@ -10315,10 +11619,10 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10315
11619
|
olderVersion = prevSnap.version;
|
|
10316
11620
|
newerVersion = targetSnap.version;
|
|
10317
11621
|
} else if (targetSnap && !prevSnap) {
|
|
10318
|
-
console.error(
|
|
11622
|
+
console.error(chalk2.red(`No version ${targetVersion - 1} found to compare against.`));
|
|
10319
11623
|
process.exit(1);
|
|
10320
11624
|
} else {
|
|
10321
|
-
console.error(
|
|
11625
|
+
console.error(chalk2.red(`Version ${targetVersion} not found.`));
|
|
10322
11626
|
process.exit(1);
|
|
10323
11627
|
}
|
|
10324
11628
|
}
|
|
@@ -10357,8 +11661,8 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10357
11661
|
});
|
|
10358
11662
|
return;
|
|
10359
11663
|
}
|
|
10360
|
-
console.log(
|
|
10361
|
-
console.log(
|
|
11664
|
+
console.log(chalk2.bold(`Diff for "${current.key}" (${memoryId.slice(0, 8)})`));
|
|
11665
|
+
console.log(chalk2.dim(`Version ${olderVersion} \u2192 ${newerVersion}`));
|
|
10362
11666
|
console.log();
|
|
10363
11667
|
let hasChanges = false;
|
|
10364
11668
|
const n = newer;
|
|
@@ -10366,7 +11670,7 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10366
11670
|
const newValue = n.value;
|
|
10367
11671
|
if (oldValue !== newValue) {
|
|
10368
11672
|
hasChanges = true;
|
|
10369
|
-
console.log(
|
|
11673
|
+
console.log(chalk2.bold(" value:"));
|
|
10370
11674
|
diffLines(oldValue, newValue);
|
|
10371
11675
|
}
|
|
10372
11676
|
const scalarFields = [
|
|
@@ -10380,7 +11684,7 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10380
11684
|
for (const field of scalarFields) {
|
|
10381
11685
|
if (String(field.oldVal) !== String(field.newVal)) {
|
|
10382
11686
|
hasChanges = true;
|
|
10383
|
-
console.log(` ${
|
|
11687
|
+
console.log(` ${chalk2.bold(field.name + ":")} ${chalk2.red(String(field.oldVal))} \u2192 ${chalk2.green(String(field.newVal))}`);
|
|
10384
11688
|
}
|
|
10385
11689
|
}
|
|
10386
11690
|
const oldTags = older.tags;
|
|
@@ -10389,14 +11693,14 @@ function diffMemory(memoryId, opts, globalOpts) {
|
|
|
10389
11693
|
hasChanges = true;
|
|
10390
11694
|
const removed = oldTags.filter((t) => !newTags.includes(t));
|
|
10391
11695
|
const added = newTags.filter((t) => !oldTags.includes(t));
|
|
10392
|
-
console.log(` ${
|
|
11696
|
+
console.log(` ${chalk2.bold("tags:")}`);
|
|
10393
11697
|
for (const t of removed)
|
|
10394
|
-
console.log(
|
|
11698
|
+
console.log(chalk2.red(` - ${t}`));
|
|
10395
11699
|
for (const t of added)
|
|
10396
|
-
console.log(
|
|
11700
|
+
console.log(chalk2.green(` + ${t}`));
|
|
10397
11701
|
}
|
|
10398
11702
|
if (!hasChanges) {
|
|
10399
|
-
console.log(
|
|
11703
|
+
console.log(chalk2.dim(" No changes between these versions."));
|
|
10400
11704
|
}
|
|
10401
11705
|
}
|
|
10402
11706
|
function diffLines(oldText, newText) {
|
|
@@ -10405,22 +11709,22 @@ function diffLines(oldText, newText) {
|
|
|
10405
11709
|
const newLines = newText.split(`
|
|
10406
11710
|
`);
|
|
10407
11711
|
if (oldLines.length === 1 && newLines.length === 1) {
|
|
10408
|
-
console.log(
|
|
10409
|
-
console.log(
|
|
11712
|
+
console.log(chalk2.red(` - ${oldLines[0]}`));
|
|
11713
|
+
console.log(chalk2.green(` + ${newLines[0]}`));
|
|
10410
11714
|
return;
|
|
10411
11715
|
}
|
|
10412
11716
|
const oldSet = new Set(oldLines);
|
|
10413
11717
|
const newSet = new Set(newLines);
|
|
10414
11718
|
for (const line of oldLines) {
|
|
10415
11719
|
if (!newSet.has(line)) {
|
|
10416
|
-
console.log(
|
|
11720
|
+
console.log(chalk2.red(` - ${line}`));
|
|
10417
11721
|
} else {
|
|
10418
|
-
console.log(
|
|
11722
|
+
console.log(chalk2.dim(` ${line}`));
|
|
10419
11723
|
}
|
|
10420
11724
|
}
|
|
10421
11725
|
for (const line of newLines) {
|
|
10422
11726
|
if (!oldSet.has(line)) {
|
|
10423
|
-
console.log(
|
|
11727
|
+
console.log(chalk2.green(` + ${line}`));
|
|
10424
11728
|
}
|
|
10425
11729
|
}
|
|
10426
11730
|
}
|
|
@@ -10576,24 +11880,24 @@ function validateConfigKeyValue(key, value) {
|
|
|
10576
11880
|
return null;
|
|
10577
11881
|
}
|
|
10578
11882
|
function getConfigPath() {
|
|
10579
|
-
return
|
|
11883
|
+
return join6(homedir4(), ".mementos", "config.json");
|
|
10580
11884
|
}
|
|
10581
11885
|
function readFileConfig() {
|
|
10582
11886
|
const configPath = getConfigPath();
|
|
10583
|
-
if (!
|
|
11887
|
+
if (!existsSync6(configPath))
|
|
10584
11888
|
return {};
|
|
10585
11889
|
try {
|
|
10586
|
-
return JSON.parse(
|
|
11890
|
+
return JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
10587
11891
|
} catch {
|
|
10588
11892
|
return {};
|
|
10589
11893
|
}
|
|
10590
11894
|
}
|
|
10591
11895
|
function writeFileConfig(data) {
|
|
10592
11896
|
const configPath = getConfigPath();
|
|
10593
|
-
const dir =
|
|
10594
|
-
if (!
|
|
10595
|
-
|
|
10596
|
-
|
|
11897
|
+
const dir = dirname4(configPath);
|
|
11898
|
+
if (!existsSync6(dir))
|
|
11899
|
+
mkdirSync6(dir, { recursive: true });
|
|
11900
|
+
writeFileSync4(configPath, JSON.stringify(data, null, 2) + `
|
|
10597
11901
|
`, "utf-8");
|
|
10598
11902
|
}
|
|
10599
11903
|
program2.command("config [subcommand] [args...]").description("View or modify configuration. Subcommands: get <key>, set <key> <value>, reset [key], path").action((subcommand, args) => {
|
|
@@ -10621,13 +11925,13 @@ program2.command("config [subcommand] [args...]").description("View or modify co
|
|
|
10621
11925
|
if (subcommand === "get") {
|
|
10622
11926
|
const key = args[0];
|
|
10623
11927
|
if (!key) {
|
|
10624
|
-
console.error(
|
|
11928
|
+
console.error(chalk2.red("Usage: mementos config get <key>"));
|
|
10625
11929
|
process.exit(1);
|
|
10626
11930
|
}
|
|
10627
11931
|
const config = loadConfig();
|
|
10628
11932
|
const value = getNestedValue(config, key);
|
|
10629
11933
|
if (value === undefined) {
|
|
10630
|
-
console.error(
|
|
11934
|
+
console.error(chalk2.red(`Unknown config key: ${key}`));
|
|
10631
11935
|
process.exit(1);
|
|
10632
11936
|
}
|
|
10633
11937
|
if (useJson) {
|
|
@@ -10643,13 +11947,13 @@ program2.command("config [subcommand] [args...]").description("View or modify co
|
|
|
10643
11947
|
const key = args[0];
|
|
10644
11948
|
const rawValue = args[1];
|
|
10645
11949
|
if (!key || rawValue === undefined) {
|
|
10646
|
-
console.error(
|
|
11950
|
+
console.error(chalk2.red("Usage: mementos config set <key> <value>"));
|
|
10647
11951
|
process.exit(1);
|
|
10648
11952
|
}
|
|
10649
11953
|
const value = parseConfigValue(rawValue);
|
|
10650
11954
|
const err = validateConfigKeyValue(key, value);
|
|
10651
11955
|
if (err) {
|
|
10652
|
-
console.error(
|
|
11956
|
+
console.error(chalk2.red(err));
|
|
10653
11957
|
process.exit(1);
|
|
10654
11958
|
}
|
|
10655
11959
|
const fileConfig = readFileConfig();
|
|
@@ -10658,7 +11962,7 @@ program2.command("config [subcommand] [args...]").description("View or modify co
|
|
|
10658
11962
|
if (useJson) {
|
|
10659
11963
|
outputJson({ key, value, saved: true });
|
|
10660
11964
|
} else {
|
|
10661
|
-
console.log(
|
|
11965
|
+
console.log(chalk2.green(`Set ${key} = ${JSON.stringify(value)}`));
|
|
10662
11966
|
}
|
|
10663
11967
|
return;
|
|
10664
11968
|
}
|
|
@@ -10667,7 +11971,7 @@ program2.command("config [subcommand] [args...]").description("View or modify co
|
|
|
10667
11971
|
if (key) {
|
|
10668
11972
|
const defaultVal = getNestedValue(DEFAULT_CONFIG, key);
|
|
10669
11973
|
if (defaultVal === undefined) {
|
|
10670
|
-
console.error(
|
|
11974
|
+
console.error(chalk2.red(`Unknown config key: ${key}`));
|
|
10671
11975
|
process.exit(1);
|
|
10672
11976
|
}
|
|
10673
11977
|
const fileConfig = readFileConfig();
|
|
@@ -10676,22 +11980,22 @@ program2.command("config [subcommand] [args...]").description("View or modify co
|
|
|
10676
11980
|
if (useJson) {
|
|
10677
11981
|
outputJson({ key, reset: true, default_value: defaultVal });
|
|
10678
11982
|
} else {
|
|
10679
|
-
console.log(
|
|
11983
|
+
console.log(chalk2.green(`Reset ${key} to default (${JSON.stringify(defaultVal)})`));
|
|
10680
11984
|
}
|
|
10681
11985
|
} else {
|
|
10682
11986
|
const configPath = getConfigPath();
|
|
10683
|
-
if (
|
|
11987
|
+
if (existsSync6(configPath)) {
|
|
10684
11988
|
unlinkSync2(configPath);
|
|
10685
11989
|
}
|
|
10686
11990
|
if (useJson) {
|
|
10687
11991
|
outputJson({ reset: true, all: true });
|
|
10688
11992
|
} else {
|
|
10689
|
-
console.log(
|
|
11993
|
+
console.log(chalk2.green("Config reset to defaults (file removed)"));
|
|
10690
11994
|
}
|
|
10691
11995
|
}
|
|
10692
11996
|
return;
|
|
10693
11997
|
}
|
|
10694
|
-
console.error(
|
|
11998
|
+
console.error(chalk2.red(`Unknown config subcommand: ${subcommand}`));
|
|
10695
11999
|
console.error("Usage: mementos config [get|set|reset|path]");
|
|
10696
12000
|
process.exit(1);
|
|
10697
12001
|
} catch (e) {
|
|
@@ -10718,7 +12022,7 @@ entityCmd.command("create <name>").description("Create a knowledge graph entity"
|
|
|
10718
12022
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10719
12023
|
outputJson(entity);
|
|
10720
12024
|
} else {
|
|
10721
|
-
console.log(
|
|
12025
|
+
console.log(chalk2.green(`Entity: ${entity.name} (${entity.id.slice(0, 8)})`));
|
|
10722
12026
|
}
|
|
10723
12027
|
} catch (e) {
|
|
10724
12028
|
handleError(e);
|
|
@@ -10734,28 +12038,28 @@ entityCmd.command("show <nameOrId>").description("Show entity details with relat
|
|
|
10734
12038
|
outputJson({ ...entity, related, memories });
|
|
10735
12039
|
return;
|
|
10736
12040
|
}
|
|
10737
|
-
console.log(`${
|
|
10738
|
-
console.log(`${
|
|
10739
|
-
console.log(`${
|
|
12041
|
+
console.log(`${chalk2.bold("ID:")} ${entity.id}`);
|
|
12042
|
+
console.log(`${chalk2.bold("Name:")} ${entity.name}`);
|
|
12043
|
+
console.log(`${chalk2.bold("Type:")} ${colorEntityType(entity.type)}`);
|
|
10740
12044
|
if (entity.description)
|
|
10741
|
-
console.log(`${
|
|
12045
|
+
console.log(`${chalk2.bold("Description:")} ${entity.description}`);
|
|
10742
12046
|
if (entity.project_id)
|
|
10743
|
-
console.log(`${
|
|
10744
|
-
console.log(`${
|
|
10745
|
-
console.log(`${
|
|
12047
|
+
console.log(`${chalk2.bold("Project:")} ${entity.project_id}`);
|
|
12048
|
+
console.log(`${chalk2.bold("Created:")} ${entity.created_at}`);
|
|
12049
|
+
console.log(`${chalk2.bold("Updated:")} ${entity.updated_at}`);
|
|
10746
12050
|
if (related.length > 0) {
|
|
10747
12051
|
console.log(`
|
|
10748
|
-
${
|
|
12052
|
+
${chalk2.bold("Related entities:")}`);
|
|
10749
12053
|
for (const r of related) {
|
|
10750
|
-
console.log(` ${
|
|
12054
|
+
console.log(` ${chalk2.dim(r.id.slice(0, 8))} [${colorEntityType(r.type)}] ${r.name}${r.description ? chalk2.dim(` \u2014 ${r.description}`) : ""}`);
|
|
10751
12055
|
}
|
|
10752
12056
|
}
|
|
10753
12057
|
if (memories.length > 0) {
|
|
10754
12058
|
console.log(`
|
|
10755
|
-
${
|
|
12059
|
+
${chalk2.bold("Linked memories:")}`);
|
|
10756
12060
|
for (const m of memories) {
|
|
10757
12061
|
const value = m.value.length > 60 ? m.value.slice(0, 60) + "..." : m.value;
|
|
10758
|
-
console.log(` ${
|
|
12062
|
+
console.log(` ${chalk2.dim(m.id.slice(0, 8))} ${chalk2.bold(m.key)} = ${value}`);
|
|
10759
12063
|
}
|
|
10760
12064
|
}
|
|
10761
12065
|
} catch (e) {
|
|
@@ -10796,13 +12100,13 @@ entityCmd.command("list").description("List entities with optional filters").opt
|
|
|
10796
12100
|
return;
|
|
10797
12101
|
}
|
|
10798
12102
|
if (entities.length === 0) {
|
|
10799
|
-
console.log(
|
|
12103
|
+
console.log(chalk2.yellow("No entities found."));
|
|
10800
12104
|
return;
|
|
10801
12105
|
}
|
|
10802
|
-
console.log(
|
|
12106
|
+
console.log(chalk2.bold(`${entities.length} entit${entities.length === 1 ? "y" : "ies"}:`));
|
|
10803
12107
|
for (const e of entities) {
|
|
10804
|
-
const id =
|
|
10805
|
-
const desc = e.description ?
|
|
12108
|
+
const id = chalk2.dim(e.id.slice(0, 8));
|
|
12109
|
+
const desc = e.description ? chalk2.dim(` (${e.description})`) : "";
|
|
10806
12110
|
console.log(`${id} ${colorEntityType(e.type)} ${e.name}${desc}`);
|
|
10807
12111
|
}
|
|
10808
12112
|
} catch (e) {
|
|
@@ -10817,7 +12121,7 @@ entityCmd.command("delete <nameOrId>").description("Delete an entity and cascade
|
|
|
10817
12121
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10818
12122
|
outputJson({ deleted: entity.id, name: entity.name });
|
|
10819
12123
|
} else {
|
|
10820
|
-
console.log(
|
|
12124
|
+
console.log(chalk2.green(`Deleted entity: ${entity.name} (${entity.id.slice(0, 8)}) \u2014 relations and memory links cascaded`));
|
|
10821
12125
|
}
|
|
10822
12126
|
} catch (e) {
|
|
10823
12127
|
handleError(e);
|
|
@@ -10832,7 +12136,7 @@ entityCmd.command("merge <source> <target>").description("Merge source entity in
|
|
|
10832
12136
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10833
12137
|
outputJson(merged);
|
|
10834
12138
|
} else {
|
|
10835
|
-
console.log(
|
|
12139
|
+
console.log(chalk2.green(`Merged: ${srcEntity.name} \u2192 ${merged.name} (${merged.id.slice(0, 8)})`));
|
|
10836
12140
|
}
|
|
10837
12141
|
} catch (e) {
|
|
10838
12142
|
handleError(e);
|
|
@@ -10844,14 +12148,14 @@ entityCmd.command("link <entity> <memoryKeyOrId>").description("Link an entity t
|
|
|
10844
12148
|
const entity = resolveEntityArg(entityArg, opts.type);
|
|
10845
12149
|
const memory = resolveKeyOrId(memoryKeyOrId, {}, globalOpts);
|
|
10846
12150
|
if (!memory) {
|
|
10847
|
-
console.error(
|
|
12151
|
+
console.error(chalk2.red(`Memory not found: ${memoryKeyOrId}`));
|
|
10848
12152
|
process.exit(1);
|
|
10849
12153
|
}
|
|
10850
12154
|
const link = linkEntityToMemory(entity.id, memory.id, opts.role);
|
|
10851
12155
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10852
12156
|
outputJson(link);
|
|
10853
12157
|
} else {
|
|
10854
|
-
console.log(
|
|
12158
|
+
console.log(chalk2.green(`Linked: ${entity.name} \u2194 ${memory.key} (role: ${link.role})`));
|
|
10855
12159
|
}
|
|
10856
12160
|
} catch (e) {
|
|
10857
12161
|
handleError(e);
|
|
@@ -10872,7 +12176,7 @@ relationCmd.command("create <source> <target>").description("Create a relation b
|
|
|
10872
12176
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10873
12177
|
outputJson(relation);
|
|
10874
12178
|
} else {
|
|
10875
|
-
console.log(
|
|
12179
|
+
console.log(chalk2.green(`Relation: ${srcEntity.name} \u2014[${relation.relation_type}]\u2192 ${tgtEntity.name} (${relation.id.slice(0, 8)})`));
|
|
10876
12180
|
}
|
|
10877
12181
|
} catch (e) {
|
|
10878
12182
|
handleError(e);
|
|
@@ -10903,7 +12207,7 @@ relationCmd.command("list <entityNameOrId>").description("List relations for an
|
|
|
10903
12207
|
return;
|
|
10904
12208
|
}
|
|
10905
12209
|
if (relations.length === 0) {
|
|
10906
|
-
console.log(
|
|
12210
|
+
console.log(chalk2.yellow(`No relations found for: ${entity.name}`));
|
|
10907
12211
|
return;
|
|
10908
12212
|
}
|
|
10909
12213
|
const entityCache = new Map;
|
|
@@ -10919,13 +12223,13 @@ relationCmd.command("list <entityNameOrId>").description("List relations for an
|
|
|
10919
12223
|
return id.slice(0, 8);
|
|
10920
12224
|
}
|
|
10921
12225
|
};
|
|
10922
|
-
console.log(
|
|
12226
|
+
console.log(chalk2.bold(`${relations.length} relation${relations.length === 1 ? "" : "s"} for ${entity.name}:`));
|
|
10923
12227
|
for (const r of relations) {
|
|
10924
12228
|
const src = resolveName(r.source_entity_id);
|
|
10925
12229
|
const tgt = resolveName(r.target_entity_id);
|
|
10926
|
-
const id =
|
|
10927
|
-
const weight = r.weight !== 1 ?
|
|
10928
|
-
console.log(`${id} ${src} \u2014[${
|
|
12230
|
+
const id = chalk2.dim(r.id.slice(0, 8));
|
|
12231
|
+
const weight = r.weight !== 1 ? chalk2.dim(` w:${r.weight}`) : "";
|
|
12232
|
+
console.log(`${id} ${src} \u2014[${chalk2.cyan(r.relation_type)}]\u2192 ${tgt}${weight}`);
|
|
10929
12233
|
}
|
|
10930
12234
|
} catch (e) {
|
|
10931
12235
|
handleError(e);
|
|
@@ -10937,14 +12241,14 @@ relationCmd.command("delete <id>").description("Delete a relation by ID").action
|
|
|
10937
12241
|
const db = getDatabase();
|
|
10938
12242
|
const resolvedId = resolvePartialId(db, "relations", id);
|
|
10939
12243
|
if (!resolvedId) {
|
|
10940
|
-
console.error(
|
|
12244
|
+
console.error(chalk2.red(`Relation not found: ${id}`));
|
|
10941
12245
|
process.exit(1);
|
|
10942
12246
|
}
|
|
10943
12247
|
deleteRelation(resolvedId);
|
|
10944
12248
|
if (globalOpts.json || globalOpts.format === "json") {
|
|
10945
12249
|
outputJson({ deleted: resolvedId });
|
|
10946
12250
|
} else {
|
|
10947
|
-
console.log(
|
|
12251
|
+
console.log(chalk2.green(`Deleted relation: ${resolvedId.slice(0, 8)}`));
|
|
10948
12252
|
}
|
|
10949
12253
|
} catch (e) {
|
|
10950
12254
|
handleError(e);
|
|
@@ -10962,7 +12266,7 @@ graphCmd.command("show <entityNameOrId>").description("Show connected entities a
|
|
|
10962
12266
|
return;
|
|
10963
12267
|
}
|
|
10964
12268
|
if (graph.entities.length === 0) {
|
|
10965
|
-
console.log(
|
|
12269
|
+
console.log(chalk2.yellow(`No graph data for: ${entity.name}`));
|
|
10966
12270
|
return;
|
|
10967
12271
|
}
|
|
10968
12272
|
const adj = new Map;
|
|
@@ -10994,7 +12298,7 @@ graphCmd.command("show <entityNameOrId>").description("Show connected entities a
|
|
|
10994
12298
|
for (let i = 0;i < children.length; i++) {
|
|
10995
12299
|
const child = children[i];
|
|
10996
12300
|
const childIndent = indent + (indent === "" ? "" : isLast ? " " : "\u2502 ");
|
|
10997
|
-
const relLabel =
|
|
12301
|
+
const relLabel = chalk2.dim(` (${child.relation})`);
|
|
10998
12302
|
const childPrefix = i === children.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
10999
12303
|
visited.add(child.entity.id);
|
|
11000
12304
|
console.log(`${childIndent}${childPrefix}[${colorEntityType(child.entity.type)}] ${child.entity.name}${relLabel}`);
|
|
@@ -11003,7 +12307,7 @@ graphCmd.command("show <entityNameOrId>").description("Show connected entities a
|
|
|
11003
12307
|
const gc = grandChildren[j];
|
|
11004
12308
|
const gcIndent = childIndent + (i === children.length - 1 ? " " : "\u2502 ");
|
|
11005
12309
|
const gcPrefix = j === grandChildren.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
11006
|
-
const gcRelLabel =
|
|
12310
|
+
const gcRelLabel = chalk2.dim(` (${gc.relation})`);
|
|
11007
12311
|
visited.add(gc.entity.id);
|
|
11008
12312
|
console.log(`${gcIndent}${gcPrefix}[${colorEntityType(gc.entity.type)}] ${gc.entity.name}${gcRelLabel}`);
|
|
11009
12313
|
}
|
|
@@ -11025,10 +12329,10 @@ graphCmd.command("path <from> <to>").description("Show shortest path between two
|
|
|
11025
12329
|
return;
|
|
11026
12330
|
}
|
|
11027
12331
|
if (!path) {
|
|
11028
|
-
console.log(
|
|
12332
|
+
console.log(chalk2.yellow(`No path found between ${fromEntity.name} and ${toEntity.name}`));
|
|
11029
12333
|
return;
|
|
11030
12334
|
}
|
|
11031
|
-
console.log(
|
|
12335
|
+
console.log(chalk2.bold(`Path (${path.length} hop${path.length === 1 ? "" : "s"}):`));
|
|
11032
12336
|
for (let i = 0;i < path.length; i++) {
|
|
11033
12337
|
const e = path[i];
|
|
11034
12338
|
const arrow = i < path.length - 1 ? " \u2192" : "";
|
|
@@ -11055,19 +12359,19 @@ graphCmd.command("stats").description("Show knowledge graph statistics").action(
|
|
|
11055
12359
|
});
|
|
11056
12360
|
return;
|
|
11057
12361
|
}
|
|
11058
|
-
console.log(
|
|
12362
|
+
console.log(chalk2.bold("Knowledge Graph Stats"));
|
|
11059
12363
|
console.log();
|
|
11060
|
-
console.log(
|
|
12364
|
+
console.log(chalk2.bold(`Entities: ${totalEntities}`));
|
|
11061
12365
|
for (const r of entityRows) {
|
|
11062
12366
|
console.log(` ${colorEntityType(r.type)}: ${r.count}`);
|
|
11063
12367
|
}
|
|
11064
12368
|
console.log();
|
|
11065
|
-
console.log(
|
|
12369
|
+
console.log(chalk2.bold(`Relations: ${totalRelations}`));
|
|
11066
12370
|
for (const r of relationRows) {
|
|
11067
|
-
console.log(` ${
|
|
12371
|
+
console.log(` ${chalk2.cyan(r.relation_type)}: ${r.count}`);
|
|
11068
12372
|
}
|
|
11069
12373
|
console.log();
|
|
11070
|
-
console.log(
|
|
12374
|
+
console.log(chalk2.bold(`Memory links: ${linkCount.count}`));
|
|
11071
12375
|
} catch (e) {
|
|
11072
12376
|
handleError(e);
|
|
11073
12377
|
}
|
|
@@ -11077,14 +12381,14 @@ function formatWatchLine(m) {
|
|
|
11077
12381
|
const cat = colorCategory(m.category);
|
|
11078
12382
|
const imp = colorImportance(m.importance);
|
|
11079
12383
|
const value = m.value.length > 100 ? m.value.slice(0, 100) + "..." : m.value;
|
|
11080
|
-
const main = ` [${scope}/${cat}] ${
|
|
12384
|
+
const main = ` [${scope}/${cat}] ${chalk2.bold(m.key)} = ${value} ${chalk2.dim(`(importance: ${imp})`)}`;
|
|
11081
12385
|
const parts = [];
|
|
11082
12386
|
if (m.tags.length > 0)
|
|
11083
12387
|
parts.push(`Tags: ${m.tags.join(", ")}`);
|
|
11084
12388
|
if (m.agent_id)
|
|
11085
12389
|
parts.push(`Agent: ${m.agent_id}`);
|
|
11086
12390
|
parts.push(`Updated: ${m.updated_at}`);
|
|
11087
|
-
const detail =
|
|
12391
|
+
const detail = chalk2.dim(` ${parts.join(" | ")}`);
|
|
11088
12392
|
return `${main}
|
|
11089
12393
|
${detail}`;
|
|
11090
12394
|
}
|
|
@@ -11104,73 +12408,73 @@ profileCmd.command("list").description("List all available profiles").action(()
|
|
|
11104
12408
|
const profiles = listProfiles();
|
|
11105
12409
|
const active = getActiveProfile();
|
|
11106
12410
|
if (profiles.length === 0) {
|
|
11107
|
-
console.log(
|
|
12411
|
+
console.log(chalk2.dim("No profiles yet. Create one with: mementos profile set <name>"));
|
|
11108
12412
|
return;
|
|
11109
12413
|
}
|
|
11110
|
-
console.log(
|
|
12414
|
+
console.log(chalk2.bold("Profiles:"));
|
|
11111
12415
|
for (const p of profiles) {
|
|
11112
|
-
const marker = p === active ?
|
|
12416
|
+
const marker = p === active ? chalk2.green(" \u2713 (active)") : "";
|
|
11113
12417
|
console.log(` ${p}${marker}`);
|
|
11114
12418
|
}
|
|
11115
12419
|
if (!active) {
|
|
11116
|
-
console.log(
|
|
12420
|
+
console.log(chalk2.dim(`
|
|
11117
12421
|
(no active profile \u2014 using default DB)`));
|
|
11118
12422
|
}
|
|
11119
12423
|
});
|
|
11120
12424
|
profileCmd.command("get").description("Show the currently active profile").action(() => {
|
|
11121
12425
|
const active = getActiveProfile();
|
|
11122
12426
|
if (active) {
|
|
11123
|
-
console.log(
|
|
12427
|
+
console.log(chalk2.green(`Active profile: ${active}`));
|
|
11124
12428
|
if (!process.env["MEMENTOS_PROFILE"]) {
|
|
11125
|
-
console.log(
|
|
12429
|
+
console.log(chalk2.dim("(persisted in ~/.mementos/config.json)"));
|
|
11126
12430
|
} else {
|
|
11127
|
-
console.log(
|
|
12431
|
+
console.log(chalk2.dim("(from MEMENTOS_PROFILE env var)"));
|
|
11128
12432
|
}
|
|
11129
12433
|
} else {
|
|
11130
|
-
console.log(
|
|
12434
|
+
console.log(chalk2.dim("No active profile \u2014 using default DB (~/.mementos/mementos.db)"));
|
|
11131
12435
|
}
|
|
11132
12436
|
});
|
|
11133
12437
|
profileCmd.command("set <name>").description("Switch to a named profile (creates the DB on first use)").action((name) => {
|
|
11134
12438
|
const clean = name.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-");
|
|
11135
12439
|
if (!clean) {
|
|
11136
|
-
console.error(
|
|
12440
|
+
console.error(chalk2.red("Invalid profile name. Use letters, numbers, hyphens, underscores."));
|
|
11137
12441
|
process.exit(1);
|
|
11138
12442
|
}
|
|
11139
12443
|
setActiveProfile(clean);
|
|
11140
|
-
console.log(
|
|
11141
|
-
console.log(
|
|
12444
|
+
console.log(chalk2.green(`\u2713 Switched to profile: ${clean}`));
|
|
12445
|
+
console.log(chalk2.dim(` DB: ~/.mementos/profiles/${clean}.db (created on first use)`));
|
|
11142
12446
|
});
|
|
11143
12447
|
profileCmd.command("unset").description("Clear the active profile (revert to default DB)").action(() => {
|
|
11144
12448
|
const was = getActiveProfile();
|
|
11145
12449
|
setActiveProfile(null);
|
|
11146
12450
|
if (was) {
|
|
11147
|
-
console.log(
|
|
12451
|
+
console.log(chalk2.green(`\u2713 Cleared profile (was: ${was})`));
|
|
11148
12452
|
} else {
|
|
11149
|
-
console.log(
|
|
12453
|
+
console.log(chalk2.dim("No active profile was set."));
|
|
11150
12454
|
}
|
|
11151
|
-
console.log(
|
|
12455
|
+
console.log(chalk2.dim(" Now using default DB: ~/.mementos/mementos.db"));
|
|
11152
12456
|
});
|
|
11153
12457
|
profileCmd.command("delete <name>").description("Delete a profile and its DB file (irreversible)").option("-y, --yes", "Skip confirmation prompt").action(async (name, opts) => {
|
|
11154
12458
|
if (!opts.yes) {
|
|
11155
12459
|
const profiles = listProfiles();
|
|
11156
12460
|
if (!profiles.includes(name)) {
|
|
11157
|
-
console.error(
|
|
12461
|
+
console.error(chalk2.red(`Profile not found: ${name}`));
|
|
11158
12462
|
process.exit(1);
|
|
11159
12463
|
}
|
|
11160
|
-
process.stdout.write(
|
|
12464
|
+
process.stdout.write(chalk2.yellow(`Delete profile "${name}" and its DB? This cannot be undone. [y/N] `));
|
|
11161
12465
|
const answer = await new Promise((resolve4) => {
|
|
11162
12466
|
process.stdin.once("data", (d) => resolve4(d.toString().trim().toLowerCase()));
|
|
11163
12467
|
});
|
|
11164
12468
|
if (answer !== "y" && answer !== "yes") {
|
|
11165
|
-
console.log(
|
|
12469
|
+
console.log(chalk2.dim("Cancelled."));
|
|
11166
12470
|
return;
|
|
11167
12471
|
}
|
|
11168
12472
|
}
|
|
11169
12473
|
const deleted = deleteProfile(name);
|
|
11170
12474
|
if (deleted) {
|
|
11171
|
-
console.log(
|
|
12475
|
+
console.log(chalk2.green(`\u2713 Profile "${name}" deleted.`));
|
|
11172
12476
|
} else {
|
|
11173
|
-
console.error(
|
|
12477
|
+
console.error(chalk2.red(`Profile not found: ${name}`));
|
|
11174
12478
|
process.exit(1);
|
|
11175
12479
|
}
|
|
11176
12480
|
});
|
|
@@ -11181,23 +12485,23 @@ autoMemory.command("process <turn>").description("Enqueue text for async LLM mem
|
|
|
11181
12485
|
if (opts.sync) {
|
|
11182
12486
|
const provider = providerRegistry2.getAvailable();
|
|
11183
12487
|
if (!provider) {
|
|
11184
|
-
console.error(
|
|
12488
|
+
console.error(chalk2.red("No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, CEREBRAS_API_KEY, or XAI_API_KEY."));
|
|
11185
12489
|
process.exit(1);
|
|
11186
12490
|
}
|
|
11187
|
-
console.log(
|
|
12491
|
+
console.log(chalk2.dim(`Using ${provider.name} / ${provider.config.model}...`));
|
|
11188
12492
|
const memories = await provider.extractMemories(turn, {
|
|
11189
12493
|
agentId: opts.agent,
|
|
11190
12494
|
projectId: opts.project
|
|
11191
12495
|
});
|
|
11192
12496
|
if (memories.length === 0) {
|
|
11193
|
-
console.log(
|
|
12497
|
+
console.log(chalk2.dim("Nothing worth remembering extracted."));
|
|
11194
12498
|
} else {
|
|
11195
12499
|
memories.forEach((m, i) => {
|
|
11196
|
-
console.log(
|
|
12500
|
+
console.log(chalk2.bold(`
|
|
11197
12501
|
[${i + 1}] ${m.category} \xB7 importance ${m.importance}/10 \xB7 ${m.suggestedScope}`));
|
|
11198
12502
|
console.log(` ${m.content}`);
|
|
11199
12503
|
if (m.tags.length)
|
|
11200
|
-
console.log(
|
|
12504
|
+
console.log(chalk2.dim(` tags: ${m.tags.join(", ")}`));
|
|
11201
12505
|
});
|
|
11202
12506
|
}
|
|
11203
12507
|
} else {
|
|
@@ -11207,8 +12511,8 @@ autoMemory.command("process <turn>").description("Enqueue text for async LLM mem
|
|
|
11207
12511
|
sessionId: opts.session
|
|
11208
12512
|
});
|
|
11209
12513
|
const stats = getAutoMemoryStats2();
|
|
11210
|
-
console.log(
|
|
11211
|
-
console.log(
|
|
12514
|
+
console.log(chalk2.green("\u2713 Queued for extraction"));
|
|
12515
|
+
console.log(chalk2.dim(`Queue: ${stats.pending} pending \xB7 ${stats.processed} processed \xB7 ${stats.failed} failed`));
|
|
11212
12516
|
}
|
|
11213
12517
|
});
|
|
11214
12518
|
autoMemory.command("status").description("Show auto-memory queue stats and provider health").action(async () => {
|
|
@@ -11217,14 +12521,14 @@ autoMemory.command("status").description("Show auto-memory queue stats and provi
|
|
|
11217
12521
|
const stats = getAutoMemoryStats2();
|
|
11218
12522
|
const config = providerRegistry2.getConfig();
|
|
11219
12523
|
const health = providerRegistry2.health();
|
|
11220
|
-
console.log(
|
|
11221
|
-
console.log(` Provider: ${config.enabled ?
|
|
12524
|
+
console.log(chalk2.bold("Auto-Memory Status"));
|
|
12525
|
+
console.log(` Provider: ${config.enabled ? chalk2.green(config.provider) : chalk2.red("disabled")} / ${config.model ?? "default"}`);
|
|
11222
12526
|
console.log(` Queue: ${stats.pending} pending \xB7 ${stats.processing} processing \xB7 ${stats.processed} processed`);
|
|
11223
12527
|
console.log(` Errors: ${stats.failed} failed \xB7 ${stats.dropped} dropped`);
|
|
11224
|
-
console.log(
|
|
12528
|
+
console.log(chalk2.bold(`
|
|
11225
12529
|
Provider Health`));
|
|
11226
12530
|
for (const [name, info] of Object.entries(health)) {
|
|
11227
|
-
const icon = info.available ?
|
|
12531
|
+
const icon = info.available ? chalk2.green("\u2713") : chalk2.red("\u2717");
|
|
11228
12532
|
console.log(` ${icon} ${name.padEnd(12)} ${info.model}`);
|
|
11229
12533
|
}
|
|
11230
12534
|
});
|
|
@@ -11237,35 +12541,35 @@ autoMemory.command("config").description("Show or update auto-memory provider co
|
|
|
11237
12541
|
...opts.model && { model: opts.model },
|
|
11238
12542
|
...opts.minImportance && { minImportance: Number(opts.minImportance) }
|
|
11239
12543
|
});
|
|
11240
|
-
console.log(
|
|
12544
|
+
console.log(chalk2.green("\u2713 Config updated"));
|
|
11241
12545
|
}
|
|
11242
12546
|
const config = providerRegistry2.getConfig();
|
|
11243
|
-
console.log(
|
|
12547
|
+
console.log(chalk2.bold("Auto-Memory Config"));
|
|
11244
12548
|
console.log(JSON.stringify(config, null, 2));
|
|
11245
12549
|
});
|
|
11246
12550
|
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
12551
|
const { providerRegistry: providerRegistry2 } = await Promise.resolve().then(() => (init_registry(), exports_registry));
|
|
11248
12552
|
const provider = opts.provider ? providerRegistry2.getProvider(opts.provider) : providerRegistry2.getAvailable();
|
|
11249
12553
|
if (!provider) {
|
|
11250
|
-
console.error(
|
|
12554
|
+
console.error(chalk2.red("No LLM provider configured."));
|
|
11251
12555
|
process.exit(1);
|
|
11252
12556
|
}
|
|
11253
|
-
console.log(
|
|
12557
|
+
console.log(chalk2.dim(`DRY RUN \u2014 ${provider.name} / ${provider.config.model} \u2014 nothing will be saved
|
|
11254
12558
|
`));
|
|
11255
12559
|
const memories = await provider.extractMemories(turn, {
|
|
11256
12560
|
agentId: opts.agent,
|
|
11257
12561
|
projectId: opts.project
|
|
11258
12562
|
});
|
|
11259
12563
|
if (memories.length === 0) {
|
|
11260
|
-
console.log(
|
|
12564
|
+
console.log(chalk2.dim("Nothing extracted."));
|
|
11261
12565
|
} else {
|
|
11262
12566
|
memories.forEach((m, i) => {
|
|
11263
|
-
console.log(
|
|
11264
|
-
console.log(` ${
|
|
12567
|
+
console.log(chalk2.bold(`[${i + 1}] ${m.category.toUpperCase()} \xB7 importance ${m.importance}/10 \xB7 ${m.suggestedScope}`));
|
|
12568
|
+
console.log(` ${chalk2.white(m.content)}`);
|
|
11265
12569
|
if (m.tags.length)
|
|
11266
|
-
console.log(
|
|
12570
|
+
console.log(chalk2.dim(` tags: ${m.tags.join(", ")}`));
|
|
11267
12571
|
if (m.reasoning)
|
|
11268
|
-
console.log(
|
|
12572
|
+
console.log(chalk2.dim(` why: ${m.reasoning}`));
|
|
11269
12573
|
console.log();
|
|
11270
12574
|
});
|
|
11271
12575
|
}
|
|
@@ -11273,38 +12577,38 @@ autoMemory.command("test <turn>").description("Test extraction without saving (d
|
|
|
11273
12577
|
autoMemory.command("enable").description("Enable auto-memory extraction").action(async () => {
|
|
11274
12578
|
const { configureAutoMemory: configureAutoMemory2 } = await Promise.resolve().then(() => (init_auto_memory(), exports_auto_memory));
|
|
11275
12579
|
configureAutoMemory2({ enabled: true });
|
|
11276
|
-
console.log(
|
|
12580
|
+
console.log(chalk2.green("\u2713 Auto-memory enabled"));
|
|
11277
12581
|
});
|
|
11278
12582
|
autoMemory.command("disable").description("Disable auto-memory extraction").action(async () => {
|
|
11279
12583
|
const { configureAutoMemory: configureAutoMemory2 } = await Promise.resolve().then(() => (init_auto_memory(), exports_auto_memory));
|
|
11280
12584
|
configureAutoMemory2({ enabled: false });
|
|
11281
|
-
console.log(
|
|
12585
|
+
console.log(chalk2.yellow("\u26A0 Auto-memory disabled"));
|
|
11282
12586
|
});
|
|
11283
12587
|
var hooksCmd = program2.command("hooks").description("Hook registry and webhook management");
|
|
11284
12588
|
hooksCmd.command("list").description("List registered hooks in the in-memory registry").option("--type <type>", "Filter by hook type").action(async (opts) => {
|
|
11285
12589
|
const { hookRegistry: hookRegistry2 } = await Promise.resolve().then(() => (init_hooks(), exports_hooks));
|
|
11286
12590
|
const hooks = hookRegistry2.list(opts.type);
|
|
11287
12591
|
if (hooks.length === 0) {
|
|
11288
|
-
console.log(
|
|
12592
|
+
console.log(chalk2.gray("No hooks registered."));
|
|
11289
12593
|
return;
|
|
11290
12594
|
}
|
|
11291
12595
|
for (const h of hooks) {
|
|
11292
|
-
const builtinTag = h.builtin ?
|
|
11293
|
-
const blockingTag = h.blocking ?
|
|
11294
|
-
console.log(`${
|
|
12596
|
+
const builtinTag = h.builtin ? chalk2.blue(" [builtin]") : "";
|
|
12597
|
+
const blockingTag = h.blocking ? chalk2.red(" [blocking]") : chalk2.gray(" [non-blocking]");
|
|
12598
|
+
console.log(`${chalk2.cyan(h.id)} ${chalk2.bold(h.type)}${builtinTag}${blockingTag} priority=${h.priority}`);
|
|
11295
12599
|
if (h.description)
|
|
11296
|
-
console.log(` ${
|
|
12600
|
+
console.log(` ${chalk2.gray(h.description)}`);
|
|
11297
12601
|
}
|
|
11298
12602
|
});
|
|
11299
12603
|
hooksCmd.command("stats").description("Show hook registry statistics").action(async () => {
|
|
11300
12604
|
const { hookRegistry: hookRegistry2 } = await Promise.resolve().then(() => (init_hooks(), exports_hooks));
|
|
11301
12605
|
const stats = hookRegistry2.stats();
|
|
11302
|
-
console.log(
|
|
11303
|
-
console.log(` Total: ${
|
|
11304
|
-
console.log(` Blocking: ${
|
|
11305
|
-
console.log(` Non-blocking:${
|
|
12606
|
+
console.log(chalk2.bold("Hook Registry Stats"));
|
|
12607
|
+
console.log(` Total: ${chalk2.cyan(stats.total)}`);
|
|
12608
|
+
console.log(` Blocking: ${chalk2.red(stats.blocking)}`);
|
|
12609
|
+
console.log(` Non-blocking:${chalk2.green(stats.nonBlocking)}`);
|
|
11306
12610
|
if (Object.keys(stats.byType).length > 0) {
|
|
11307
|
-
console.log(
|
|
12611
|
+
console.log(chalk2.bold(`
|
|
11308
12612
|
By type:`));
|
|
11309
12613
|
for (const [type, count] of Object.entries(stats.byType)) {
|
|
11310
12614
|
console.log(` ${type}: ${count}`);
|
|
@@ -11319,16 +12623,16 @@ webhooksCmd.command("list").description("List all persisted webhook hooks").opti
|
|
|
11319
12623
|
enabled: opts.disabled ? false : undefined
|
|
11320
12624
|
});
|
|
11321
12625
|
if (webhooks.length === 0) {
|
|
11322
|
-
console.log(
|
|
12626
|
+
console.log(chalk2.gray("No webhooks registered."));
|
|
11323
12627
|
return;
|
|
11324
12628
|
}
|
|
11325
12629
|
for (const wh of webhooks) {
|
|
11326
|
-
const enabledTag = wh.enabled ?
|
|
11327
|
-
const blockingTag = wh.blocking ?
|
|
11328
|
-
console.log(`${
|
|
12630
|
+
const enabledTag = wh.enabled ? chalk2.green("enabled") : chalk2.red("disabled");
|
|
12631
|
+
const blockingTag = wh.blocking ? chalk2.red("blocking") : chalk2.gray("non-blocking");
|
|
12632
|
+
console.log(`${chalk2.cyan(wh.id)} [${enabledTag}] ${chalk2.bold(wh.type)} \u2192 ${wh.handlerUrl}`);
|
|
11329
12633
|
console.log(` ${blockingTag} | priority=${wh.priority} | invocations=${wh.invocationCount} failures=${wh.failureCount}`);
|
|
11330
12634
|
if (wh.description)
|
|
11331
|
-
console.log(` ${
|
|
12635
|
+
console.log(` ${chalk2.gray(wh.description)}`);
|
|
11332
12636
|
}
|
|
11333
12637
|
});
|
|
11334
12638
|
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 +12648,8 @@ webhooksCmd.command("create <type> <url>").description("Create a persistent webh
|
|
|
11344
12648
|
description: opts.description
|
|
11345
12649
|
});
|
|
11346
12650
|
reloadWebhooks2();
|
|
11347
|
-
console.log(
|
|
11348
|
-
console.log(` ID: ${
|
|
12651
|
+
console.log(chalk2.green("\u2713 Webhook created"));
|
|
12652
|
+
console.log(` ID: ${chalk2.cyan(wh.id)}`);
|
|
11349
12653
|
console.log(` Type: ${wh.type}`);
|
|
11350
12654
|
console.log(` URL: ${wh.handlerUrl}`);
|
|
11351
12655
|
});
|
|
@@ -11353,9 +12657,9 @@ webhooksCmd.command("delete <id>").description("Delete a webhook by ID").action(
|
|
|
11353
12657
|
const { deleteWebhookHook: deleteWebhookHook2 } = await Promise.resolve().then(() => (init_webhook_hooks(), exports_webhook_hooks));
|
|
11354
12658
|
const deleted = deleteWebhookHook2(id);
|
|
11355
12659
|
if (deleted) {
|
|
11356
|
-
console.log(
|
|
12660
|
+
console.log(chalk2.green(`\u2713 Webhook ${id} deleted`));
|
|
11357
12661
|
} else {
|
|
11358
|
-
console.error(
|
|
12662
|
+
console.error(chalk2.red(`Webhook not found: ${id}`));
|
|
11359
12663
|
process.exit(1);
|
|
11360
12664
|
}
|
|
11361
12665
|
});
|
|
@@ -11365,9 +12669,9 @@ webhooksCmd.command("enable <id>").description("Enable a webhook").action(async
|
|
|
11365
12669
|
const updated = updateWebhookHook2(id, { enabled: true });
|
|
11366
12670
|
if (updated) {
|
|
11367
12671
|
reloadWebhooks2();
|
|
11368
|
-
console.log(
|
|
12672
|
+
console.log(chalk2.green(`\u2713 Webhook ${id} enabled`));
|
|
11369
12673
|
} else {
|
|
11370
|
-
console.error(
|
|
12674
|
+
console.error(chalk2.red(`Webhook not found: ${id}`));
|
|
11371
12675
|
process.exit(1);
|
|
11372
12676
|
}
|
|
11373
12677
|
});
|
|
@@ -11377,16 +12681,16 @@ webhooksCmd.command("disable <id>").description("Disable a webhook (without dele
|
|
|
11377
12681
|
const updated = updateWebhookHook2(id, { enabled: false });
|
|
11378
12682
|
if (updated) {
|
|
11379
12683
|
reloadWebhooks2();
|
|
11380
|
-
console.log(
|
|
12684
|
+
console.log(chalk2.yellow(`\u2298 Webhook ${id} disabled`));
|
|
11381
12685
|
} else {
|
|
11382
|
-
console.error(
|
|
12686
|
+
console.error(chalk2.red(`Webhook not found: ${id}`));
|
|
11383
12687
|
process.exit(1);
|
|
11384
12688
|
}
|
|
11385
12689
|
});
|
|
11386
12690
|
var synthesisCmd = program2.command("synthesis").alias("synth").description("ALMA memory synthesis \u2014 analyze and consolidate memories");
|
|
11387
12691
|
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
12692
|
const { runSynthesis: runSynthesis2 } = await Promise.resolve().then(() => (init_synthesis2(), exports_synthesis2));
|
|
11389
|
-
console.log(
|
|
12693
|
+
console.log(chalk2.blue("Running memory synthesis..."));
|
|
11390
12694
|
const result = await runSynthesis2({
|
|
11391
12695
|
projectId: opts.project,
|
|
11392
12696
|
dryRun: opts.dryRun ?? false,
|
|
@@ -11394,44 +12698,44 @@ synthesisCmd.command("run").description("Run memory synthesis on the corpus").op
|
|
|
11394
12698
|
provider: opts.provider
|
|
11395
12699
|
});
|
|
11396
12700
|
if (result.dryRun) {
|
|
11397
|
-
console.log(
|
|
12701
|
+
console.log(chalk2.yellow(`DRY RUN \u2014 ${result.proposals.length} proposals generated (not applied)`));
|
|
11398
12702
|
} else {
|
|
11399
|
-
console.log(
|
|
12703
|
+
console.log(chalk2.green(`\u2713 Synthesis complete`));
|
|
11400
12704
|
console.log(` Corpus: ${result.run.corpus_size} memories`);
|
|
11401
12705
|
console.log(` Proposals: ${result.run.proposals_generated} generated, ${result.run.proposals_accepted} applied`);
|
|
11402
12706
|
}
|
|
11403
12707
|
if (result.metrics) {
|
|
11404
12708
|
console.log(` Reduction: ${(result.metrics.corpusReduction * 100).toFixed(1)}%`);
|
|
11405
12709
|
}
|
|
11406
|
-
console.log(` Run ID: ${
|
|
12710
|
+
console.log(` Run ID: ${chalk2.cyan(result.run.id)}`);
|
|
11407
12711
|
});
|
|
11408
12712
|
synthesisCmd.command("status").description("Show recent synthesis runs").option("--project <id>", "Filter by project").action(async (opts) => {
|
|
11409
12713
|
const { listSynthesisRuns: listSynthesisRuns2 } = await Promise.resolve().then(() => (init_synthesis(), exports_synthesis));
|
|
11410
12714
|
const runs = listSynthesisRuns2({ project_id: opts.project, limit: 10 });
|
|
11411
12715
|
if (runs.length === 0) {
|
|
11412
|
-
console.log(
|
|
12716
|
+
console.log(chalk2.gray("No synthesis runs found."));
|
|
11413
12717
|
return;
|
|
11414
12718
|
}
|
|
11415
12719
|
for (const run of runs) {
|
|
11416
|
-
const statusColor = run.status === "completed" ?
|
|
11417
|
-
console.log(`${
|
|
12720
|
+
const statusColor = run.status === "completed" ? chalk2.green : run.status === "failed" ? chalk2.red : chalk2.yellow;
|
|
12721
|
+
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
12722
|
}
|
|
11419
12723
|
});
|
|
11420
12724
|
synthesisCmd.command("rollback <runId>").description("Roll back a synthesis run").action(async (runId) => {
|
|
11421
12725
|
const { rollbackSynthesis: rollbackSynthesis2 } = await Promise.resolve().then(() => (init_synthesis2(), exports_synthesis2));
|
|
11422
|
-
console.log(
|
|
12726
|
+
console.log(chalk2.yellow(`Rolling back synthesis run ${runId}...`));
|
|
11423
12727
|
const result = await rollbackSynthesis2(runId);
|
|
11424
|
-
console.log(
|
|
12728
|
+
console.log(chalk2.green(`\u2713 Rolled back ${result.rolled_back} proposals`));
|
|
11425
12729
|
if (result.errors.length > 0) {
|
|
11426
|
-
console.log(
|
|
12730
|
+
console.log(chalk2.red(` Errors: ${result.errors.join(", ")}`));
|
|
11427
12731
|
}
|
|
11428
12732
|
});
|
|
11429
12733
|
var sessionCmd = program2.command("session").description("Session auto-memory \u2014 ingest session transcripts for memory extraction");
|
|
11430
12734
|
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:
|
|
12735
|
+
const { readFileSync: readFileSync4 } = await import("fs");
|
|
11432
12736
|
const { createSessionJob: createSessionJob2 } = await Promise.resolve().then(() => (init_session_jobs(), exports_session_jobs));
|
|
11433
12737
|
const { enqueueSessionJob: enqueueSessionJob2 } = await Promise.resolve().then(() => (init_session_queue(), exports_session_queue));
|
|
11434
|
-
const transcript =
|
|
12738
|
+
const transcript = readFileSync4(transcriptFile, "utf-8");
|
|
11435
12739
|
const sessionId = opts.sessionId ?? `cli-${Date.now()}`;
|
|
11436
12740
|
const job = createSessionJob2({
|
|
11437
12741
|
session_id: sessionId,
|
|
@@ -11441,7 +12745,7 @@ sessionCmd.command("ingest <transcriptFile>").description("Ingest a session tran
|
|
|
11441
12745
|
project_id: opts.project
|
|
11442
12746
|
});
|
|
11443
12747
|
enqueueSessionJob2(job.id);
|
|
11444
|
-
console.log(
|
|
12748
|
+
console.log(chalk2.green(`\u2713 Session queued: ${chalk2.cyan(job.id)}`));
|
|
11445
12749
|
console.log(` Session: ${sessionId}`);
|
|
11446
12750
|
console.log(` Length: ${transcript.length} chars`);
|
|
11447
12751
|
});
|
|
@@ -11449,16 +12753,16 @@ sessionCmd.command("status <jobId>").description("Check status of a session extr
|
|
|
11449
12753
|
const { getSessionJob: getSessionJob2 } = await Promise.resolve().then(() => (init_session_jobs(), exports_session_jobs));
|
|
11450
12754
|
const job = getSessionJob2(jobId);
|
|
11451
12755
|
if (!job) {
|
|
11452
|
-
console.error(
|
|
12756
|
+
console.error(chalk2.red(`Job not found: ${jobId}`));
|
|
11453
12757
|
process.exit(1);
|
|
11454
12758
|
}
|
|
11455
|
-
const statusColor = job.status === "completed" ?
|
|
11456
|
-
console.log(`${
|
|
12759
|
+
const statusColor = job.status === "completed" ? chalk2.green : job.status === "failed" ? chalk2.red : chalk2.yellow;
|
|
12760
|
+
console.log(`${chalk2.cyan(job.id)} [${statusColor(job.status)}]`);
|
|
11457
12761
|
console.log(` Session: ${job.session_id}`);
|
|
11458
12762
|
console.log(` Chunks: ${job.chunk_count}`);
|
|
11459
12763
|
console.log(` Extracted: ${job.memories_extracted} memories`);
|
|
11460
12764
|
if (job.error)
|
|
11461
|
-
console.log(
|
|
12765
|
+
console.log(chalk2.red(` Error: ${job.error}`));
|
|
11462
12766
|
});
|
|
11463
12767
|
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
12768
|
const { listSessionJobs: listSessionJobs2 } = await Promise.resolve().then(() => (init_session_jobs(), exports_session_jobs));
|
|
@@ -11469,12 +12773,12 @@ sessionCmd.command("list").description("List session extraction jobs").option("-
|
|
|
11469
12773
|
limit: parseInt(opts.limit)
|
|
11470
12774
|
});
|
|
11471
12775
|
if (jobs.length === 0) {
|
|
11472
|
-
console.log(
|
|
12776
|
+
console.log(chalk2.gray("No session jobs found."));
|
|
11473
12777
|
return;
|
|
11474
12778
|
}
|
|
11475
12779
|
for (const job of jobs) {
|
|
11476
|
-
const statusColor = job.status === "completed" ?
|
|
11477
|
-
console.log(`${
|
|
12780
|
+
const statusColor = job.status === "completed" ? chalk2.green : job.status === "failed" ? chalk2.red : chalk2.yellow;
|
|
12781
|
+
console.log(`${chalk2.cyan(job.id.slice(0, 8))} [${statusColor(job.status)}] ${job.memories_extracted} memories | ${job.created_at.slice(0, 10)}`);
|
|
11478
12782
|
}
|
|
11479
12783
|
});
|
|
11480
12784
|
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 +12787,26 @@ sessionCmd.command("setup-hook").description("Install mementos session hook into
|
|
|
11483
12787
|
if (opts.claude) {
|
|
11484
12788
|
const script = `${hookPath}/claude-stop-hook.ts`;
|
|
11485
12789
|
if (opts.show) {
|
|
11486
|
-
const { readFileSync:
|
|
11487
|
-
console.log(
|
|
12790
|
+
const { readFileSync: readFileSync4 } = await import("fs");
|
|
12791
|
+
console.log(readFileSync4(script, "utf-8"));
|
|
11488
12792
|
return;
|
|
11489
12793
|
}
|
|
11490
|
-
console.log(
|
|
12794
|
+
console.log(chalk2.bold("Claude Code stop hook installation:"));
|
|
11491
12795
|
console.log("");
|
|
11492
12796
|
console.log("Add to your .claude/settings.json:");
|
|
11493
|
-
console.log(
|
|
12797
|
+
console.log(chalk2.cyan(JSON.stringify({
|
|
11494
12798
|
hooks: {
|
|
11495
12799
|
Stop: [{ matcher: "", hooks: [{ type: "command", command: `bun ${script}` }] }]
|
|
11496
12800
|
}
|
|
11497
12801
|
}, null, 2)));
|
|
11498
12802
|
console.log("");
|
|
11499
|
-
console.log(`Or run: ${
|
|
12803
|
+
console.log(`Or run: ${chalk2.cyan(`claude hooks add Stop "bun ${script}"`)}`);
|
|
11500
12804
|
} else if (opts.codex) {
|
|
11501
12805
|
const script = `${hookPath}/codex-stop-hook.ts`;
|
|
11502
|
-
console.log(
|
|
12806
|
+
console.log(chalk2.bold("Codex session hook installation:"));
|
|
11503
12807
|
console.log("");
|
|
11504
12808
|
console.log("Add to ~/.codex/config.toml:");
|
|
11505
|
-
console.log(
|
|
12809
|
+
console.log(chalk2.cyan(`[hooks]
|
|
11506
12810
|
session_end = "bun ${script}"`));
|
|
11507
12811
|
} else {
|
|
11508
12812
|
console.log("Usage: mementos session setup-hook --claude | --codex");
|
|
@@ -11526,7 +12830,7 @@ program2.command("heartbeat [agent-id]").description("Update last_seen_at to sig
|
|
|
11526
12830
|
if (globalOpts.json)
|
|
11527
12831
|
console.log(JSON.stringify({ agent_id: agent.id, name: agent.name, last_seen_at: new Date().toISOString() }));
|
|
11528
12832
|
else
|
|
11529
|
-
console.log(
|
|
12833
|
+
console.log(chalk2.green(`\u2665 ${agent.name} (${agent.id}) \u2014 heartbeat sent`));
|
|
11530
12834
|
});
|
|
11531
12835
|
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
12836
|
const globalOpts = program2.opts();
|
|
@@ -11538,9 +12842,9 @@ program2.command("set-focus [project]").description("Focus this agent on a proje
|
|
|
11538
12842
|
}
|
|
11539
12843
|
setFocus(agentId, project ?? null);
|
|
11540
12844
|
if (project)
|
|
11541
|
-
console.log(
|
|
12845
|
+
console.log(chalk2.green(`Focused: ${agentId} \u2192 project ${project}`));
|
|
11542
12846
|
else
|
|
11543
|
-
console.log(
|
|
12847
|
+
console.log(chalk2.dim(`Focus cleared for ${agentId}`));
|
|
11544
12848
|
});
|
|
11545
12849
|
program2.command("get-focus").description("Show the current project focus for an agent").option("--agent <id>", "Agent ID").action((opts) => {
|
|
11546
12850
|
const globalOpts = program2.opts();
|
|
@@ -11552,9 +12856,9 @@ program2.command("get-focus").description("Show the current project focus for an
|
|
|
11552
12856
|
}
|
|
11553
12857
|
const focus = getFocus(agentId);
|
|
11554
12858
|
if (focus)
|
|
11555
|
-
console.log(
|
|
12859
|
+
console.log(chalk2.cyan(`Focus: ${focus}`));
|
|
11556
12860
|
else
|
|
11557
|
-
console.log(
|
|
12861
|
+
console.log(chalk2.dim("No focus set."));
|
|
11558
12862
|
});
|
|
11559
12863
|
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
12864
|
const globalOpts = program2.opts();
|
|
@@ -11570,15 +12874,271 @@ program2.command("remove <nameOrId>").description("Remove/delete a memory by nam
|
|
|
11570
12874
|
id = mem.id;
|
|
11571
12875
|
}
|
|
11572
12876
|
if (!id) {
|
|
11573
|
-
console.error(
|
|
12877
|
+
console.error(chalk2.red(`Memory not found: ${nameOrId}`));
|
|
11574
12878
|
process.exit(1);
|
|
11575
12879
|
}
|
|
11576
12880
|
const deleted = deleteMemory2(id);
|
|
11577
12881
|
if (deleted)
|
|
11578
|
-
console.log(
|
|
12882
|
+
console.log(chalk2.green(`\u2713 Memory ${id.slice(0, 8)} removed`));
|
|
11579
12883
|
else {
|
|
11580
|
-
console.error(
|
|
12884
|
+
console.error(chalk2.red(`Memory not found: ${nameOrId}`));
|
|
11581
12885
|
process.exit(1);
|
|
11582
12886
|
}
|
|
11583
12887
|
});
|
|
12888
|
+
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) => {
|
|
12889
|
+
try {
|
|
12890
|
+
const globalOpts = program2.opts();
|
|
12891
|
+
const { getToolEvents: getToolEvents2 } = (init_tool_events(), __toCommonJS(exports_tool_events));
|
|
12892
|
+
const limit = opts.limit || 20;
|
|
12893
|
+
const events = getToolEvents2({
|
|
12894
|
+
tool_name: toolName,
|
|
12895
|
+
project_id: opts.projectId,
|
|
12896
|
+
limit
|
|
12897
|
+
});
|
|
12898
|
+
if (globalOpts.json) {
|
|
12899
|
+
outputJson(events);
|
|
12900
|
+
return;
|
|
12901
|
+
}
|
|
12902
|
+
if (events.length === 0) {
|
|
12903
|
+
console.log(chalk2.yellow("No tool events found."));
|
|
12904
|
+
return;
|
|
12905
|
+
}
|
|
12906
|
+
console.log(chalk2.bold(`${events.length} tool event${events.length === 1 ? "" : "s"}:`));
|
|
12907
|
+
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")}`);
|
|
12908
|
+
for (const e of events) {
|
|
12909
|
+
const successStr = e.success ? chalk2.green("true ") : chalk2.red("false ");
|
|
12910
|
+
const errorType = (e.error_type || "").padEnd(20);
|
|
12911
|
+
const action = (e.action || "-").padEnd(16);
|
|
12912
|
+
console.log(` ${e.tool_name.padEnd(24)} ${action} ${successStr} ${errorType} ${chalk2.dim(e.created_at)}`);
|
|
12913
|
+
}
|
|
12914
|
+
} catch (e) {
|
|
12915
|
+
handleError(e);
|
|
12916
|
+
}
|
|
12917
|
+
});
|
|
12918
|
+
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) => {
|
|
12919
|
+
try {
|
|
12920
|
+
const globalOpts = program2.opts();
|
|
12921
|
+
const { getToolStats: getToolStats2, getToolLessons: getToolLessons2 } = (init_tool_events(), __toCommonJS(exports_tool_events));
|
|
12922
|
+
const projectId = opts.projectId;
|
|
12923
|
+
const stats = getToolStats2(toolName, projectId);
|
|
12924
|
+
const lessons = getToolLessons2(toolName, projectId);
|
|
12925
|
+
if (globalOpts.json) {
|
|
12926
|
+
outputJson({ stats, lessons });
|
|
12927
|
+
return;
|
|
12928
|
+
}
|
|
12929
|
+
const successRate = (stats.success_rate * 100).toFixed(1);
|
|
12930
|
+
console.log(chalk2.bold(`Tool: ${toolName}`));
|
|
12931
|
+
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)}` : ""));
|
|
12932
|
+
if (stats.common_errors.length > 0) {
|
|
12933
|
+
console.log(chalk2.bold(`
|
|
12934
|
+
Common errors:`));
|
|
12935
|
+
for (const err of stats.common_errors) {
|
|
12936
|
+
console.log(` ${chalk2.red(err.error_type)}: ${err.count} times`);
|
|
12937
|
+
}
|
|
12938
|
+
}
|
|
12939
|
+
if (lessons.length === 0) {
|
|
12940
|
+
console.log(chalk2.dim(`
|
|
12941
|
+
No lessons recorded.`));
|
|
12942
|
+
return;
|
|
12943
|
+
}
|
|
12944
|
+
console.log(chalk2.bold(`
|
|
12945
|
+
Lessons (${lessons.length}):`));
|
|
12946
|
+
for (const l of lessons) {
|
|
12947
|
+
console.log(` ${chalk2.cyan("-")} ${l.lesson} ${chalk2.dim(`(${l.created_at.slice(0, 10)})`)}`);
|
|
12948
|
+
if (l.when_to_use) {
|
|
12949
|
+
console.log(` ${chalk2.dim("when_to_use:")} ${l.when_to_use}`);
|
|
12950
|
+
}
|
|
12951
|
+
}
|
|
12952
|
+
} catch (e) {
|
|
12953
|
+
handleError(e);
|
|
12954
|
+
}
|
|
12955
|
+
});
|
|
12956
|
+
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) => {
|
|
12957
|
+
try {
|
|
12958
|
+
const globalOpts = program2.opts();
|
|
12959
|
+
const { synthesizeProfile: synthesizeProfile2 } = await Promise.resolve().then(() => (init_profile_synthesizer(), exports_profile_synthesizer));
|
|
12960
|
+
let projectId = opts.projectId;
|
|
12961
|
+
if (!projectId && globalOpts.project) {
|
|
12962
|
+
const project = getProject(resolve3(globalOpts.project));
|
|
12963
|
+
if (project)
|
|
12964
|
+
projectId = project.id;
|
|
12965
|
+
}
|
|
12966
|
+
const result = await synthesizeProfile2({
|
|
12967
|
+
project_id: projectId,
|
|
12968
|
+
agent_id: globalOpts.agent,
|
|
12969
|
+
force_refresh: !!opts.refresh
|
|
12970
|
+
});
|
|
12971
|
+
if (!result) {
|
|
12972
|
+
if (globalOpts.json) {
|
|
12973
|
+
outputJson({ error: "No profile available (no preference/fact memories found)" });
|
|
12974
|
+
} else {
|
|
12975
|
+
console.log(chalk2.yellow("No profile available \u2014 save some preference or fact memories first."));
|
|
12976
|
+
}
|
|
12977
|
+
return;
|
|
12978
|
+
}
|
|
12979
|
+
if (globalOpts.json) {
|
|
12980
|
+
outputJson(result);
|
|
12981
|
+
return;
|
|
12982
|
+
}
|
|
12983
|
+
if (result.from_cache) {
|
|
12984
|
+
console.log(chalk2.dim(`(cached profile)
|
|
12985
|
+
`));
|
|
12986
|
+
} else {
|
|
12987
|
+
console.log(chalk2.dim(`(synthesized from ${result.memory_count} memories)
|
|
12988
|
+
`));
|
|
12989
|
+
}
|
|
12990
|
+
console.log(result.profile);
|
|
12991
|
+
} catch (e) {
|
|
12992
|
+
handleError(e);
|
|
12993
|
+
}
|
|
12994
|
+
});
|
|
12995
|
+
program2.command("chain <sequence_group>").description("Show a memory chain (memories linked by sequence_group, ordered by sequence_order)").action((sequenceGroup) => {
|
|
12996
|
+
try {
|
|
12997
|
+
const globalOpts = program2.opts();
|
|
12998
|
+
const db = getDatabase();
|
|
12999
|
+
const rows = db.query("SELECT * FROM memories WHERE sequence_group = ? AND status = 'active' ORDER BY sequence_order ASC").all(sequenceGroup);
|
|
13000
|
+
const memories = rows.map(parseMemoryRow);
|
|
13001
|
+
if (globalOpts.json) {
|
|
13002
|
+
outputJson(memories);
|
|
13003
|
+
return;
|
|
13004
|
+
}
|
|
13005
|
+
if (memories.length === 0) {
|
|
13006
|
+
console.log(chalk2.yellow(`No chain found for sequence group: ${sequenceGroup}`));
|
|
13007
|
+
return;
|
|
13008
|
+
}
|
|
13009
|
+
console.log(chalk2.bold(`Chain: ${sequenceGroup} (${memories.length} step${memories.length === 1 ? "" : "s"}):
|
|
13010
|
+
`));
|
|
13011
|
+
for (let i = 0;i < memories.length; i++) {
|
|
13012
|
+
const m = memories[i];
|
|
13013
|
+
const order = m.sequence_order !== null && m.sequence_order !== undefined ? m.sequence_order : i + 1;
|
|
13014
|
+
const value = m.value.length > 120 ? m.value.slice(0, 120) + "..." : m.value;
|
|
13015
|
+
console.log(` ${chalk2.cyan(String(order) + ".")} ${chalk2.bold(m.key)}: ${value}`);
|
|
13016
|
+
}
|
|
13017
|
+
} catch (e) {
|
|
13018
|
+
handleError(e);
|
|
13019
|
+
}
|
|
13020
|
+
});
|
|
13021
|
+
program2.command("when-to-use <memory_id>").description("Show the when_to_use guidance for a memory").action((memoryId) => {
|
|
13022
|
+
try {
|
|
13023
|
+
const globalOpts = program2.opts();
|
|
13024
|
+
const resolvedId = resolveMemoryId(memoryId);
|
|
13025
|
+
const memory = getMemory(resolvedId);
|
|
13026
|
+
if (!memory) {
|
|
13027
|
+
if (globalOpts.json) {
|
|
13028
|
+
outputJson({ error: `Memory not found: ${memoryId}` });
|
|
13029
|
+
} else {
|
|
13030
|
+
console.error(chalk2.red(`Memory not found: ${memoryId}`));
|
|
13031
|
+
}
|
|
13032
|
+
process.exit(1);
|
|
13033
|
+
}
|
|
13034
|
+
const whenToUse = memory.when_to_use ?? null;
|
|
13035
|
+
if (globalOpts.json) {
|
|
13036
|
+
outputJson({ id: memory.id, key: memory.key, when_to_use: whenToUse });
|
|
13037
|
+
return;
|
|
13038
|
+
}
|
|
13039
|
+
console.log(chalk2.bold(`${memory.key} (${memory.id.slice(0, 8)})`));
|
|
13040
|
+
if (whenToUse) {
|
|
13041
|
+
console.log(` ${chalk2.cyan("when_to_use:")} ${whenToUse}`);
|
|
13042
|
+
} else {
|
|
13043
|
+
console.log(chalk2.dim(" (not set)"));
|
|
13044
|
+
}
|
|
13045
|
+
} catch (e) {
|
|
13046
|
+
handleError(e);
|
|
13047
|
+
}
|
|
13048
|
+
});
|
|
13049
|
+
var sessionsCmd = program2.command("sessions").description("Session registry \u2014 list, clean, and inspect active Claude Code sessions");
|
|
13050
|
+
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) => {
|
|
13051
|
+
try {
|
|
13052
|
+
const globalOpts = program2.opts();
|
|
13053
|
+
const { listSessions: listSessions2 } = (init_session_registry(), __toCommonJS(exports_session_registry));
|
|
13054
|
+
const sessions = listSessions2({
|
|
13055
|
+
project_name: opts.project,
|
|
13056
|
+
agent_name: opts.agent || globalOpts.agent
|
|
13057
|
+
});
|
|
13058
|
+
const fmt = getOutputFormat(opts.format);
|
|
13059
|
+
if (fmt === "json") {
|
|
13060
|
+
outputJson(sessions);
|
|
13061
|
+
return;
|
|
13062
|
+
}
|
|
13063
|
+
if (fmt === "yaml") {
|
|
13064
|
+
outputYaml(sessions);
|
|
13065
|
+
return;
|
|
13066
|
+
}
|
|
13067
|
+
if (sessions.length === 0) {
|
|
13068
|
+
console.log(chalk2.yellow("No active sessions found."));
|
|
13069
|
+
return;
|
|
13070
|
+
}
|
|
13071
|
+
console.log(chalk2.bold(`${sessions.length} active session${sessions.length === 1 ? "" : "s"}:
|
|
13072
|
+
`));
|
|
13073
|
+
for (const s of sessions) {
|
|
13074
|
+
const pid = chalk2.dim(`pid:${s.pid}`);
|
|
13075
|
+
const agent = s.agent_name ? chalk2.cyan(s.agent_name) : chalk2.dim("(no agent)");
|
|
13076
|
+
const project = s.project_name ? chalk2.yellow(s.project_name) : chalk2.dim("(no project)");
|
|
13077
|
+
const mcp = chalk2.dim(s.mcp_server);
|
|
13078
|
+
const self = s.pid === process.pid ? chalk2.green(" (self)") : "";
|
|
13079
|
+
console.log(` ${chalk2.bold(s.id)} ${pid}${self} ${agent} ${project} ${mcp}`);
|
|
13080
|
+
console.log(` ${chalk2.dim(`cwd: ${s.cwd}`)} ${chalk2.dim(`last seen: ${s.last_seen_at}`)}`);
|
|
13081
|
+
}
|
|
13082
|
+
} catch (e) {
|
|
13083
|
+
handleError(e);
|
|
13084
|
+
}
|
|
13085
|
+
});
|
|
13086
|
+
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) => {
|
|
13087
|
+
try {
|
|
13088
|
+
const globalOpts = program2.opts();
|
|
13089
|
+
const { listSessions: listSessions2 } = (init_session_registry(), __toCommonJS(exports_session_registry));
|
|
13090
|
+
if (!opts.agent && !opts.project && !opts.all) {
|
|
13091
|
+
console.error(chalk2.red("Specify a target: --agent <name>, --project <name>, or --all"));
|
|
13092
|
+
process.exit(1);
|
|
13093
|
+
}
|
|
13094
|
+
const filter = {};
|
|
13095
|
+
if (opts.agent)
|
|
13096
|
+
filter.agent_name = opts.agent;
|
|
13097
|
+
if (opts.project)
|
|
13098
|
+
filter.project_name = opts.project;
|
|
13099
|
+
const sessions = listSessions2(opts.all ? undefined : filter);
|
|
13100
|
+
if (globalOpts.json) {
|
|
13101
|
+
outputJson({
|
|
13102
|
+
message: "Channel push requires MCP server context. Use the memory_send_channel MCP tool.",
|
|
13103
|
+
matching_sessions: sessions.length,
|
|
13104
|
+
sessions: sessions.map((s) => ({
|
|
13105
|
+
id: s.id,
|
|
13106
|
+
pid: s.pid,
|
|
13107
|
+
agent_name: s.agent_name,
|
|
13108
|
+
project_name: s.project_name
|
|
13109
|
+
}))
|
|
13110
|
+
});
|
|
13111
|
+
return;
|
|
13112
|
+
}
|
|
13113
|
+
console.log(chalk2.bold(`Found ${sessions.length} matching session${sessions.length === 1 ? "" : "s"}:`));
|
|
13114
|
+
for (const s of sessions) {
|
|
13115
|
+
const agent = s.agent_name ? chalk2.cyan(s.agent_name) : chalk2.dim("(no agent)");
|
|
13116
|
+
const project = s.project_name ? chalk2.yellow(s.project_name) : "";
|
|
13117
|
+
console.log(` ${s.id} pid:${s.pid} ${agent} ${project}`);
|
|
13118
|
+
}
|
|
13119
|
+
console.log();
|
|
13120
|
+
console.log(chalk2.dim("Channel push requires the MCP server context."));
|
|
13121
|
+
console.log(chalk2.dim("Use the MCP tool:"));
|
|
13122
|
+
console.log(chalk2.cyan(` memory_send_channel(content="${message.slice(0, 60)}${message.length > 60 ? "..." : ""}")`));
|
|
13123
|
+
} catch (e) {
|
|
13124
|
+
handleError(e);
|
|
13125
|
+
}
|
|
13126
|
+
});
|
|
13127
|
+
sessionsCmd.command("clean").description("Remove dead/stale sessions from the registry").action(() => {
|
|
13128
|
+
try {
|
|
13129
|
+
const globalOpts = program2.opts();
|
|
13130
|
+
const { cleanStaleSessions: cleanStaleSessions2 } = (init_session_registry(), __toCommonJS(exports_session_registry));
|
|
13131
|
+
const cleaned = cleanStaleSessions2();
|
|
13132
|
+
if (globalOpts.json) {
|
|
13133
|
+
outputJson({ cleaned });
|
|
13134
|
+
} else if (cleaned === 0) {
|
|
13135
|
+
console.log(chalk2.green("No stale sessions found \u2014 registry is clean."));
|
|
13136
|
+
} else {
|
|
13137
|
+
console.log(chalk2.green(`Cleaned ${cleaned} stale session${cleaned === 1 ? "" : "s"}.`));
|
|
13138
|
+
}
|
|
13139
|
+
} catch (e) {
|
|
13140
|
+
handleError(e);
|
|
13141
|
+
}
|
|
13142
|
+
});
|
|
13143
|
+
program2.addCommand(makeBrainsCommand());
|
|
11584
13144
|
program2.parse(process.argv);
|