@hasna/mementos 0.1.1 → 0.2.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/dashboard/dist/assets/{index-C5oRjtKB.js → index-DqyMbv89.js} +20 -20
- package/dashboard/dist/assets/index-UBCddFo_.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/cli/index.js +727 -105
- package/dist/db/agents.d.ts +6 -0
- package/dist/db/agents.d.ts.map +1 -1
- package/dist/db/memories.d.ts +1 -0
- package/dist/db/memories.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -0
- package/dist/lib/poll.d.ts +17 -0
- package/dist/lib/poll.d.ts.map +1 -0
- package/dist/lib/project-detect.d.ts +18 -0
- package/dist/lib/project-detect.d.ts.map +1 -0
- package/dist/mcp/index.js +291 -17
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +36 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/index-C8vbNL_5.css +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var __create = Object.create;
|
|
|
4
4
|
var __getProtoOf = Object.getPrototypeOf;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
9
|
var __toESM = (mod, isNodeMode, target) => {
|
|
9
10
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
@@ -16,7 +17,31 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
16
17
|
});
|
|
17
18
|
return to;
|
|
18
19
|
};
|
|
20
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
21
|
+
var __toCommonJS = (from) => {
|
|
22
|
+
var entry = __moduleCache.get(from), desc;
|
|
23
|
+
if (entry)
|
|
24
|
+
return entry;
|
|
25
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
26
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
27
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
28
|
+
get: () => from[key],
|
|
29
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
30
|
+
}));
|
|
31
|
+
__moduleCache.set(from, entry);
|
|
32
|
+
return entry;
|
|
33
|
+
};
|
|
19
34
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
35
|
+
var __export = (target, all) => {
|
|
36
|
+
for (var name in all)
|
|
37
|
+
__defProp(target, name, {
|
|
38
|
+
get: all[name],
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
set: (newValue) => all[name] = () => newValue
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
20
45
|
var __require = import.meta.require;
|
|
21
46
|
|
|
22
47
|
// node_modules/commander/lib/error.js
|
|
@@ -2053,28 +2078,6 @@ var require_commander = __commonJS((exports) => {
|
|
|
2053
2078
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
2054
2079
|
});
|
|
2055
2080
|
|
|
2056
|
-
// node_modules/commander/esm.mjs
|
|
2057
|
-
var import__ = __toESM(require_commander(), 1);
|
|
2058
|
-
var {
|
|
2059
|
-
program,
|
|
2060
|
-
createCommand,
|
|
2061
|
-
createArgument,
|
|
2062
|
-
createOption,
|
|
2063
|
-
CommanderError,
|
|
2064
|
-
InvalidArgumentError,
|
|
2065
|
-
InvalidOptionArgumentError,
|
|
2066
|
-
Command,
|
|
2067
|
-
Argument,
|
|
2068
|
-
Option,
|
|
2069
|
-
Help
|
|
2070
|
-
} = import__.default;
|
|
2071
|
-
|
|
2072
|
-
// src/cli/index.tsx
|
|
2073
|
-
import chalk from "chalk";
|
|
2074
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
2075
|
-
import { dirname as dirname3, join as join3, resolve as resolve3 } from "path";
|
|
2076
|
-
import { fileURLToPath } from "url";
|
|
2077
|
-
|
|
2078
2081
|
// src/db/database.ts
|
|
2079
2082
|
import { Database } from "bun:sqlite";
|
|
2080
2083
|
import { existsSync, mkdirSync } from "fs";
|
|
@@ -2132,8 +2135,59 @@ function ensureDir(filePath) {
|
|
|
2132
2135
|
mkdirSync(dir, { recursive: true });
|
|
2133
2136
|
}
|
|
2134
2137
|
}
|
|
2135
|
-
|
|
2136
|
-
|
|
2138
|
+
function getDatabase(dbPath) {
|
|
2139
|
+
if (_db)
|
|
2140
|
+
return _db;
|
|
2141
|
+
const path = dbPath || getDbPath();
|
|
2142
|
+
ensureDir(path);
|
|
2143
|
+
_db = new Database(path, { create: true });
|
|
2144
|
+
_db.run("PRAGMA journal_mode = WAL");
|
|
2145
|
+
_db.run("PRAGMA busy_timeout = 5000");
|
|
2146
|
+
_db.run("PRAGMA foreign_keys = ON");
|
|
2147
|
+
runMigrations(_db);
|
|
2148
|
+
return _db;
|
|
2149
|
+
}
|
|
2150
|
+
function runMigrations(db) {
|
|
2151
|
+
try {
|
|
2152
|
+
const result = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
|
|
2153
|
+
const currentLevel = result?.max_id ?? 0;
|
|
2154
|
+
for (let i = currentLevel;i < MIGRATIONS.length; i++) {
|
|
2155
|
+
try {
|
|
2156
|
+
db.exec(MIGRATIONS[i]);
|
|
2157
|
+
} catch {}
|
|
2158
|
+
}
|
|
2159
|
+
} catch {
|
|
2160
|
+
for (const migration of MIGRATIONS) {
|
|
2161
|
+
try {
|
|
2162
|
+
db.exec(migration);
|
|
2163
|
+
} catch {}
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
function now() {
|
|
2168
|
+
return new Date().toISOString();
|
|
2169
|
+
}
|
|
2170
|
+
function uuid() {
|
|
2171
|
+
return crypto.randomUUID();
|
|
2172
|
+
}
|
|
2173
|
+
function shortUuid() {
|
|
2174
|
+
return crypto.randomUUID().slice(0, 8);
|
|
2175
|
+
}
|
|
2176
|
+
function resolvePartialId(db, table, partialId) {
|
|
2177
|
+
if (partialId.length >= 36) {
|
|
2178
|
+
const row = db.query(`SELECT id FROM ${table} WHERE id = ?`).get(partialId);
|
|
2179
|
+
return row?.id ?? null;
|
|
2180
|
+
}
|
|
2181
|
+
const rows = db.query(`SELECT id FROM ${table} WHERE id LIKE ?`).all(`${partialId}%`);
|
|
2182
|
+
if (rows.length === 1) {
|
|
2183
|
+
return rows[0].id;
|
|
2184
|
+
}
|
|
2185
|
+
return null;
|
|
2186
|
+
}
|
|
2187
|
+
var MIGRATIONS, _db = null;
|
|
2188
|
+
var init_database = __esm(() => {
|
|
2189
|
+
MIGRATIONS = [
|
|
2190
|
+
`
|
|
2137
2191
|
CREATE TABLE IF NOT EXISTS projects (
|
|
2138
2192
|
id TEXT PRIMARY KEY,
|
|
2139
2193
|
name TEXT NOT NULL,
|
|
@@ -2220,75 +2274,29 @@ var MIGRATIONS = [
|
|
|
2220
2274
|
|
|
2221
2275
|
INSERT OR IGNORE INTO _migrations (id) VALUES (1);
|
|
2222
2276
|
`
|
|
2223
|
-
];
|
|
2224
|
-
|
|
2225
|
-
function getDatabase(dbPath) {
|
|
2226
|
-
if (_db)
|
|
2227
|
-
return _db;
|
|
2228
|
-
const path = dbPath || getDbPath();
|
|
2229
|
-
ensureDir(path);
|
|
2230
|
-
_db = new Database(path, { create: true });
|
|
2231
|
-
_db.run("PRAGMA journal_mode = WAL");
|
|
2232
|
-
_db.run("PRAGMA busy_timeout = 5000");
|
|
2233
|
-
_db.run("PRAGMA foreign_keys = ON");
|
|
2234
|
-
runMigrations(_db);
|
|
2235
|
-
return _db;
|
|
2236
|
-
}
|
|
2237
|
-
function runMigrations(db) {
|
|
2238
|
-
try {
|
|
2239
|
-
const result = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
|
|
2240
|
-
const currentLevel = result?.max_id ?? 0;
|
|
2241
|
-
for (let i = currentLevel;i < MIGRATIONS.length; i++) {
|
|
2242
|
-
try {
|
|
2243
|
-
db.exec(MIGRATIONS[i]);
|
|
2244
|
-
} catch {}
|
|
2245
|
-
}
|
|
2246
|
-
} catch {
|
|
2247
|
-
for (const migration of MIGRATIONS) {
|
|
2248
|
-
try {
|
|
2249
|
-
db.exec(migration);
|
|
2250
|
-
} catch {}
|
|
2251
|
-
}
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
function now() {
|
|
2255
|
-
return new Date().toISOString();
|
|
2256
|
-
}
|
|
2257
|
-
function uuid() {
|
|
2258
|
-
return crypto.randomUUID();
|
|
2259
|
-
}
|
|
2260
|
-
function shortUuid() {
|
|
2261
|
-
return crypto.randomUUID().slice(0, 8);
|
|
2262
|
-
}
|
|
2263
|
-
function resolvePartialId(db, table, partialId) {
|
|
2264
|
-
if (partialId.length >= 36) {
|
|
2265
|
-
const row = db.query(`SELECT id FROM ${table} WHERE id = ?`).get(partialId);
|
|
2266
|
-
return row?.id ?? null;
|
|
2267
|
-
}
|
|
2268
|
-
const rows = db.query(`SELECT id FROM ${table} WHERE id LIKE ?`).all(`${partialId}%`);
|
|
2269
|
-
if (rows.length === 1) {
|
|
2270
|
-
return rows[0].id;
|
|
2271
|
-
}
|
|
2272
|
-
return null;
|
|
2273
|
-
}
|
|
2277
|
+
];
|
|
2278
|
+
});
|
|
2274
2279
|
|
|
2275
2280
|
// src/types/index.ts
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2281
|
+
var MemoryNotFoundError, VersionConflictError;
|
|
2282
|
+
var init_types = __esm(() => {
|
|
2283
|
+
MemoryNotFoundError = class MemoryNotFoundError extends Error {
|
|
2284
|
+
constructor(id) {
|
|
2285
|
+
super(`Memory not found: ${id}`);
|
|
2286
|
+
this.name = "MemoryNotFoundError";
|
|
2287
|
+
}
|
|
2288
|
+
};
|
|
2289
|
+
VersionConflictError = class VersionConflictError extends Error {
|
|
2290
|
+
expected;
|
|
2291
|
+
actual;
|
|
2292
|
+
constructor(id, expected, actual) {
|
|
2293
|
+
super(`Version conflict for memory ${id}: expected ${expected}, got ${actual}`);
|
|
2294
|
+
this.name = "VersionConflictError";
|
|
2295
|
+
this.expected = expected;
|
|
2296
|
+
this.actual = actual;
|
|
2297
|
+
}
|
|
2298
|
+
};
|
|
2299
|
+
});
|
|
2292
2300
|
|
|
2293
2301
|
// src/db/memories.ts
|
|
2294
2302
|
function parseMemoryRow(row) {
|
|
@@ -2591,8 +2599,150 @@ function cleanExpiredMemories(db) {
|
|
|
2591
2599
|
const result = d.run("DELETE FROM memories WHERE expires_at IS NOT NULL AND expires_at < ?", [timestamp]);
|
|
2592
2600
|
return result.changes;
|
|
2593
2601
|
}
|
|
2602
|
+
var init_memories = __esm(() => {
|
|
2603
|
+
init_types();
|
|
2604
|
+
init_database();
|
|
2605
|
+
});
|
|
2606
|
+
|
|
2607
|
+
// src/lib/poll.ts
|
|
2608
|
+
var exports_poll = {};
|
|
2609
|
+
__export(exports_poll, {
|
|
2610
|
+
startPolling: () => startPolling
|
|
2611
|
+
});
|
|
2612
|
+
function startPolling(opts) {
|
|
2613
|
+
const interval = opts.interval_ms ?? 500;
|
|
2614
|
+
const db = opts.db ?? getDatabase();
|
|
2615
|
+
let stopped = false;
|
|
2616
|
+
let inFlight = false;
|
|
2617
|
+
let lastSeen = null;
|
|
2618
|
+
const seedLastSeen = () => {
|
|
2619
|
+
try {
|
|
2620
|
+
const latest = listMemories({
|
|
2621
|
+
scope: opts.scope,
|
|
2622
|
+
category: opts.category,
|
|
2623
|
+
agent_id: opts.agent_id,
|
|
2624
|
+
project_id: opts.project_id,
|
|
2625
|
+
limit: 1
|
|
2626
|
+
}, db);
|
|
2627
|
+
if (latest.length > 0 && latest[0]) {
|
|
2628
|
+
lastSeen = latest[0].updated_at;
|
|
2629
|
+
}
|
|
2630
|
+
} catch (err) {
|
|
2631
|
+
opts.on_error?.(err instanceof Error ? err : new Error(String(err)));
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2634
|
+
const poll = () => {
|
|
2635
|
+
if (stopped || inFlight)
|
|
2636
|
+
return;
|
|
2637
|
+
inFlight = true;
|
|
2638
|
+
try {
|
|
2639
|
+
const conditions = ["status = 'active'"];
|
|
2640
|
+
const params = [];
|
|
2641
|
+
if (lastSeen) {
|
|
2642
|
+
conditions.push("(updated_at > ? OR created_at > ?)");
|
|
2643
|
+
params.push(lastSeen, lastSeen);
|
|
2644
|
+
}
|
|
2645
|
+
if (opts.scope) {
|
|
2646
|
+
conditions.push("scope = ?");
|
|
2647
|
+
params.push(opts.scope);
|
|
2648
|
+
}
|
|
2649
|
+
if (opts.category) {
|
|
2650
|
+
conditions.push("category = ?");
|
|
2651
|
+
params.push(opts.category);
|
|
2652
|
+
}
|
|
2653
|
+
if (opts.agent_id) {
|
|
2654
|
+
conditions.push("agent_id = ?");
|
|
2655
|
+
params.push(opts.agent_id);
|
|
2656
|
+
}
|
|
2657
|
+
if (opts.project_id) {
|
|
2658
|
+
conditions.push("project_id = ?");
|
|
2659
|
+
params.push(opts.project_id);
|
|
2660
|
+
}
|
|
2661
|
+
const sql = `SELECT * FROM memories WHERE ${conditions.join(" AND ")} ORDER BY updated_at ASC`;
|
|
2662
|
+
const rows = db.query(sql).all(...params);
|
|
2663
|
+
if (rows.length > 0) {
|
|
2664
|
+
const memories = rows.map(parseMemoryRow3);
|
|
2665
|
+
const lastRow = memories[memories.length - 1];
|
|
2666
|
+
if (lastRow) {
|
|
2667
|
+
lastSeen = lastRow.updated_at;
|
|
2668
|
+
}
|
|
2669
|
+
try {
|
|
2670
|
+
opts.on_memories(memories);
|
|
2671
|
+
} catch (err) {
|
|
2672
|
+
opts.on_error?.(err instanceof Error ? err : new Error(String(err)));
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
} catch (err) {
|
|
2676
|
+
opts.on_error?.(err instanceof Error ? err : new Error(String(err)));
|
|
2677
|
+
} finally {
|
|
2678
|
+
inFlight = false;
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2681
|
+
seedLastSeen();
|
|
2682
|
+
const timer = setInterval(poll, interval);
|
|
2683
|
+
return {
|
|
2684
|
+
stop: () => {
|
|
2685
|
+
stopped = true;
|
|
2686
|
+
clearInterval(timer);
|
|
2687
|
+
}
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2690
|
+
function parseMemoryRow3(row) {
|
|
2691
|
+
return {
|
|
2692
|
+
id: row["id"],
|
|
2693
|
+
key: row["key"],
|
|
2694
|
+
value: row["value"],
|
|
2695
|
+
category: row["category"],
|
|
2696
|
+
scope: row["scope"],
|
|
2697
|
+
summary: row["summary"] || null,
|
|
2698
|
+
tags: JSON.parse(row["tags"] || "[]"),
|
|
2699
|
+
importance: row["importance"],
|
|
2700
|
+
source: row["source"],
|
|
2701
|
+
status: row["status"],
|
|
2702
|
+
pinned: !!row["pinned"],
|
|
2703
|
+
agent_id: row["agent_id"] || null,
|
|
2704
|
+
project_id: row["project_id"] || null,
|
|
2705
|
+
session_id: row["session_id"] || null,
|
|
2706
|
+
metadata: JSON.parse(row["metadata"] || "{}"),
|
|
2707
|
+
access_count: row["access_count"],
|
|
2708
|
+
version: row["version"],
|
|
2709
|
+
expires_at: row["expires_at"] || null,
|
|
2710
|
+
created_at: row["created_at"],
|
|
2711
|
+
updated_at: row["updated_at"],
|
|
2712
|
+
accessed_at: row["accessed_at"] || null
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
var init_poll = __esm(() => {
|
|
2716
|
+
init_memories();
|
|
2717
|
+
init_database();
|
|
2718
|
+
});
|
|
2719
|
+
|
|
2720
|
+
// node_modules/commander/esm.mjs
|
|
2721
|
+
var import__ = __toESM(require_commander(), 1);
|
|
2722
|
+
var {
|
|
2723
|
+
program,
|
|
2724
|
+
createCommand,
|
|
2725
|
+
createArgument,
|
|
2726
|
+
createOption,
|
|
2727
|
+
CommanderError,
|
|
2728
|
+
InvalidArgumentError,
|
|
2729
|
+
InvalidOptionArgumentError,
|
|
2730
|
+
Command,
|
|
2731
|
+
Argument,
|
|
2732
|
+
Option,
|
|
2733
|
+
Help
|
|
2734
|
+
} = import__.default;
|
|
2735
|
+
|
|
2736
|
+
// src/cli/index.tsx
|
|
2737
|
+
init_database();
|
|
2738
|
+
init_memories();
|
|
2739
|
+
import chalk from "chalk";
|
|
2740
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3, accessSync, constants as fsConstants } from "fs";
|
|
2741
|
+
import { dirname as dirname3, join as join3, resolve as resolve3 } from "path";
|
|
2742
|
+
import { fileURLToPath } from "url";
|
|
2594
2743
|
|
|
2595
2744
|
// src/db/agents.ts
|
|
2745
|
+
init_database();
|
|
2596
2746
|
function parseAgentRow(row) {
|
|
2597
2747
|
return {
|
|
2598
2748
|
id: row["id"],
|
|
@@ -2650,8 +2800,34 @@ function listAgents(db) {
|
|
|
2650
2800
|
const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
|
|
2651
2801
|
return rows.map(parseAgentRow);
|
|
2652
2802
|
}
|
|
2803
|
+
function updateAgent(id, updates, db) {
|
|
2804
|
+
const d = db || getDatabase();
|
|
2805
|
+
const agent = getAgent(id, d);
|
|
2806
|
+
if (!agent)
|
|
2807
|
+
return null;
|
|
2808
|
+
const timestamp = now();
|
|
2809
|
+
if (updates.name && updates.name !== agent.name) {
|
|
2810
|
+
const existing = d.query("SELECT id FROM agents WHERE name = ? AND id != ?").get(updates.name, agent.id);
|
|
2811
|
+
if (existing) {
|
|
2812
|
+
throw new Error(`Agent name already taken: ${updates.name}`);
|
|
2813
|
+
}
|
|
2814
|
+
d.run("UPDATE agents SET name = ? WHERE id = ?", [updates.name, agent.id]);
|
|
2815
|
+
}
|
|
2816
|
+
if (updates.description !== undefined) {
|
|
2817
|
+
d.run("UPDATE agents SET description = ? WHERE id = ?", [updates.description, agent.id]);
|
|
2818
|
+
}
|
|
2819
|
+
if (updates.role !== undefined) {
|
|
2820
|
+
d.run("UPDATE agents SET role = ? WHERE id = ?", [updates.role, agent.id]);
|
|
2821
|
+
}
|
|
2822
|
+
if (updates.metadata !== undefined) {
|
|
2823
|
+
d.run("UPDATE agents SET metadata = ? WHERE id = ?", [JSON.stringify(updates.metadata), agent.id]);
|
|
2824
|
+
}
|
|
2825
|
+
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [timestamp, agent.id]);
|
|
2826
|
+
return getAgent(agent.id, d);
|
|
2827
|
+
}
|
|
2653
2828
|
|
|
2654
2829
|
// src/db/projects.ts
|
|
2830
|
+
init_database();
|
|
2655
2831
|
function parseProjectRow(row) {
|
|
2656
2832
|
return {
|
|
2657
2833
|
id: row["id"],
|
|
@@ -2696,6 +2872,7 @@ function listProjects(db) {
|
|
|
2696
2872
|
}
|
|
2697
2873
|
|
|
2698
2874
|
// src/lib/search.ts
|
|
2875
|
+
init_database();
|
|
2699
2876
|
function parseMemoryRow2(row) {
|
|
2700
2877
|
return {
|
|
2701
2878
|
id: row["id"],
|
|
@@ -2929,6 +3106,8 @@ function loadConfig() {
|
|
|
2929
3106
|
}
|
|
2930
3107
|
|
|
2931
3108
|
// src/lib/retention.ts
|
|
3109
|
+
init_database();
|
|
3110
|
+
init_memories();
|
|
2932
3111
|
function enforceQuotas(config, db) {
|
|
2933
3112
|
const d = db || getDatabase();
|
|
2934
3113
|
let totalEvicted = 0;
|
|
@@ -2971,6 +3150,94 @@ function runCleanup(config, db) {
|
|
|
2971
3150
|
return { expired, evicted, archived };
|
|
2972
3151
|
}
|
|
2973
3152
|
|
|
3153
|
+
// src/lib/injector.ts
|
|
3154
|
+
init_memories();
|
|
3155
|
+
class MemoryInjector {
|
|
3156
|
+
config;
|
|
3157
|
+
injectedIds = new Set;
|
|
3158
|
+
constructor(config) {
|
|
3159
|
+
this.config = config || loadConfig();
|
|
3160
|
+
}
|
|
3161
|
+
getInjectionContext(options = {}) {
|
|
3162
|
+
const maxTokens = options.max_tokens || this.config.injection.max_tokens;
|
|
3163
|
+
const minImportance = options.min_importance || this.config.injection.min_importance;
|
|
3164
|
+
const categories = options.categories || this.config.injection.categories;
|
|
3165
|
+
const db = options.db;
|
|
3166
|
+
const allMemories = [];
|
|
3167
|
+
const globalMems = listMemories({
|
|
3168
|
+
scope: "global",
|
|
3169
|
+
category: categories,
|
|
3170
|
+
min_importance: minImportance,
|
|
3171
|
+
status: "active",
|
|
3172
|
+
limit: 100
|
|
3173
|
+
}, db);
|
|
3174
|
+
allMemories.push(...globalMems);
|
|
3175
|
+
if (options.project_id) {
|
|
3176
|
+
const sharedMems = listMemories({
|
|
3177
|
+
scope: "shared",
|
|
3178
|
+
category: categories,
|
|
3179
|
+
min_importance: minImportance,
|
|
3180
|
+
status: "active",
|
|
3181
|
+
project_id: options.project_id,
|
|
3182
|
+
limit: 100
|
|
3183
|
+
}, db);
|
|
3184
|
+
allMemories.push(...sharedMems);
|
|
3185
|
+
}
|
|
3186
|
+
if (options.agent_id) {
|
|
3187
|
+
const privateMems = listMemories({
|
|
3188
|
+
scope: "private",
|
|
3189
|
+
category: categories,
|
|
3190
|
+
min_importance: minImportance,
|
|
3191
|
+
status: "active",
|
|
3192
|
+
agent_id: options.agent_id,
|
|
3193
|
+
limit: 100
|
|
3194
|
+
}, db);
|
|
3195
|
+
allMemories.push(...privateMems);
|
|
3196
|
+
}
|
|
3197
|
+
const seen = new Set;
|
|
3198
|
+
const unique = allMemories.filter((m) => {
|
|
3199
|
+
if (seen.has(m.id))
|
|
3200
|
+
return false;
|
|
3201
|
+
seen.add(m.id);
|
|
3202
|
+
return true;
|
|
3203
|
+
});
|
|
3204
|
+
unique.sort((a, b) => {
|
|
3205
|
+
if (a.pinned !== b.pinned)
|
|
3206
|
+
return a.pinned ? -1 : 1;
|
|
3207
|
+
if (b.importance !== a.importance)
|
|
3208
|
+
return b.importance - a.importance;
|
|
3209
|
+
return new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime();
|
|
3210
|
+
});
|
|
3211
|
+
const charBudget = maxTokens * 4;
|
|
3212
|
+
const lines = [];
|
|
3213
|
+
let totalChars = 0;
|
|
3214
|
+
for (const m of unique) {
|
|
3215
|
+
if (this.injectedIds.has(m.id))
|
|
3216
|
+
continue;
|
|
3217
|
+
const line = `- [${m.scope}/${m.category}] ${m.key}: ${m.value}`;
|
|
3218
|
+
if (totalChars + line.length > charBudget)
|
|
3219
|
+
break;
|
|
3220
|
+
lines.push(line);
|
|
3221
|
+
totalChars += line.length;
|
|
3222
|
+
this.injectedIds.add(m.id);
|
|
3223
|
+
touchMemory(m.id, db);
|
|
3224
|
+
}
|
|
3225
|
+
if (lines.length === 0) {
|
|
3226
|
+
return "";
|
|
3227
|
+
}
|
|
3228
|
+
return `<agent-memories>
|
|
3229
|
+
${lines.join(`
|
|
3230
|
+
`)}
|
|
3231
|
+
</agent-memories>`;
|
|
3232
|
+
}
|
|
3233
|
+
resetDedup() {
|
|
3234
|
+
this.injectedIds.clear();
|
|
3235
|
+
}
|
|
3236
|
+
getInjectedCount() {
|
|
3237
|
+
return this.injectedIds.size;
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
|
|
2974
3241
|
// src/cli/index.tsx
|
|
2975
3242
|
function getPackageVersion() {
|
|
2976
3243
|
try {
|
|
@@ -3075,16 +3342,53 @@ function resolveMemoryId(partialId) {
|
|
|
3075
3342
|
return id;
|
|
3076
3343
|
}
|
|
3077
3344
|
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("--agent <name>", "Agent name or ID").option("--session <id>", "Session ID");
|
|
3078
|
-
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 <ms>", "Time-to-live in milliseconds", parseInt).option("--source <src>", "Source: user, agent, system, auto, imported").action((key, value, opts) => {
|
|
3345
|
+
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 <ms>", "Time-to-live in milliseconds", parseInt).option("--source <src>", "Source: user, agent, system, auto, imported").option("--template <name>", "Apply a template: correction, preference, decision, learning").action((key, value, opts) => {
|
|
3079
3346
|
try {
|
|
3080
3347
|
const globalOpts = program2.opts();
|
|
3348
|
+
const templates = {
|
|
3349
|
+
correction: {
|
|
3350
|
+
scope: "shared",
|
|
3351
|
+
category: "knowledge",
|
|
3352
|
+
importance: 9,
|
|
3353
|
+
tags: ["correction"]
|
|
3354
|
+
},
|
|
3355
|
+
preference: {
|
|
3356
|
+
scope: "global",
|
|
3357
|
+
category: "preference",
|
|
3358
|
+
importance: 8,
|
|
3359
|
+
tags: []
|
|
3360
|
+
},
|
|
3361
|
+
decision: {
|
|
3362
|
+
scope: "shared",
|
|
3363
|
+
category: "fact",
|
|
3364
|
+
importance: 8,
|
|
3365
|
+
tags: ["decision"]
|
|
3366
|
+
},
|
|
3367
|
+
learning: {
|
|
3368
|
+
scope: "shared",
|
|
3369
|
+
category: "knowledge",
|
|
3370
|
+
importance: 7,
|
|
3371
|
+
tags: ["learning"]
|
|
3372
|
+
}
|
|
3373
|
+
};
|
|
3374
|
+
let templateDefaults;
|
|
3375
|
+
if (opts.template) {
|
|
3376
|
+
const tpl = templates[opts.template];
|
|
3377
|
+
if (!tpl) {
|
|
3378
|
+
console.error(chalk.red(`Unknown template: ${opts.template}. Valid templates: ${Object.keys(templates).join(", ")}`));
|
|
3379
|
+
process.exit(1);
|
|
3380
|
+
}
|
|
3381
|
+
templateDefaults = tpl;
|
|
3382
|
+
}
|
|
3383
|
+
const explicitTags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined;
|
|
3384
|
+
const mergedTags = explicitTags ? explicitTags : templateDefaults?.tags && templateDefaults.tags.length > 0 ? templateDefaults.tags : undefined;
|
|
3081
3385
|
const input = {
|
|
3082
3386
|
key,
|
|
3083
3387
|
value,
|
|
3084
|
-
category: opts.category,
|
|
3085
|
-
scope: opts.scope,
|
|
3086
|
-
importance: opts.importance,
|
|
3087
|
-
tags:
|
|
3388
|
+
category: opts.category ?? templateDefaults?.category,
|
|
3389
|
+
scope: opts.scope ?? templateDefaults?.scope,
|
|
3390
|
+
importance: opts.importance ?? templateDefaults?.importance,
|
|
3391
|
+
tags: mergedTags,
|
|
3088
3392
|
summary: opts.summary,
|
|
3089
3393
|
ttl_ms: opts.ttl,
|
|
3090
3394
|
source: opts.source,
|
|
@@ -3119,20 +3423,38 @@ program2.command("recall <key>").description("Recall a memory by key").option("-
|
|
|
3119
3423
|
projectId = project.id;
|
|
3120
3424
|
}
|
|
3121
3425
|
const memory = getMemoryByKey(key, opts.scope, agentId, projectId);
|
|
3122
|
-
if (
|
|
3426
|
+
if (memory) {
|
|
3427
|
+
touchMemory(memory.id);
|
|
3123
3428
|
if (globalOpts.json) {
|
|
3124
|
-
outputJson(
|
|
3429
|
+
outputJson(memory);
|
|
3125
3430
|
} else {
|
|
3126
|
-
console.
|
|
3431
|
+
console.log(formatMemoryDetail(memory));
|
|
3127
3432
|
}
|
|
3128
|
-
|
|
3433
|
+
return;
|
|
3434
|
+
}
|
|
3435
|
+
const results = searchMemories(key, {
|
|
3436
|
+
scope: opts.scope,
|
|
3437
|
+
agent_id: agentId,
|
|
3438
|
+
project_id: projectId,
|
|
3439
|
+
limit: 1
|
|
3440
|
+
});
|
|
3441
|
+
if (results.length > 0) {
|
|
3442
|
+
const best = results[0];
|
|
3443
|
+
touchMemory(best.memory.id);
|
|
3444
|
+
if (globalOpts.json) {
|
|
3445
|
+
outputJson({ fuzzy_match: true, score: best.score, match_type: best.match_type, memory: best.memory });
|
|
3446
|
+
} else {
|
|
3447
|
+
console.log(chalk.yellow(`No exact match, showing best result (score: ${best.score.toFixed(2)}, match: ${best.match_type}):`));
|
|
3448
|
+
console.log(formatMemoryDetail(best.memory));
|
|
3449
|
+
}
|
|
3450
|
+
return;
|
|
3129
3451
|
}
|
|
3130
|
-
touchMemory(memory.id);
|
|
3131
3452
|
if (globalOpts.json) {
|
|
3132
|
-
outputJson(memory);
|
|
3453
|
+
outputJson({ error: `No memory found for key: ${key}` });
|
|
3133
3454
|
} else {
|
|
3134
|
-
console.
|
|
3455
|
+
console.error(chalk.yellow(`No memory found for key: ${key}`));
|
|
3135
3456
|
}
|
|
3457
|
+
process.exit(1);
|
|
3136
3458
|
} catch (e) {
|
|
3137
3459
|
handleError(e);
|
|
3138
3460
|
}
|
|
@@ -3363,10 +3685,18 @@ program2.command("export").description("Export memories as JSON").option("-s, --
|
|
|
3363
3685
|
handleError(e);
|
|
3364
3686
|
}
|
|
3365
3687
|
});
|
|
3366
|
-
program2.command("import
|
|
3688
|
+
program2.command("import [file]").description("Import memories from a JSON file or stdin (use '-' or pipe data)").option("--overwrite", "Overwrite existing memories (default: merge)").action(async (file, opts) => {
|
|
3367
3689
|
try {
|
|
3368
3690
|
const globalOpts = program2.opts();
|
|
3369
|
-
|
|
3691
|
+
let raw;
|
|
3692
|
+
if (file === "-" || !file && !process.stdin.isTTY) {
|
|
3693
|
+
raw = await Bun.stdin.text();
|
|
3694
|
+
} else if (file) {
|
|
3695
|
+
raw = readFileSync2(resolve3(file), "utf-8");
|
|
3696
|
+
} else {
|
|
3697
|
+
console.error(chalk.red("No input: provide a file path, use '-' for stdin, or pipe data."));
|
|
3698
|
+
process.exit(1);
|
|
3699
|
+
}
|
|
3370
3700
|
const memories = JSON.parse(raw);
|
|
3371
3701
|
if (!Array.isArray(memories)) {
|
|
3372
3702
|
throw new Error("JSON file must contain an array of memories");
|
|
@@ -3440,6 +3770,47 @@ program2.command("agents").description("List all registered agents").action(() =
|
|
|
3440
3770
|
handleError(e);
|
|
3441
3771
|
}
|
|
3442
3772
|
});
|
|
3773
|
+
program2.command("agent-update <id>").description("Update an agent's name, description, or role").option("--name <name>", "New agent name").option("-d, --description <text>", "New description").option("-r, --role <role>", "New role").action((id, opts) => {
|
|
3774
|
+
try {
|
|
3775
|
+
const globalOpts = program2.opts();
|
|
3776
|
+
const updates = {};
|
|
3777
|
+
if (opts.name !== undefined)
|
|
3778
|
+
updates.name = opts.name;
|
|
3779
|
+
if (opts.description !== undefined)
|
|
3780
|
+
updates.description = opts.description;
|
|
3781
|
+
if (opts.role !== undefined)
|
|
3782
|
+
updates.role = opts.role;
|
|
3783
|
+
if (Object.keys(updates).length === 0) {
|
|
3784
|
+
if (globalOpts.json) {
|
|
3785
|
+
outputJson({ error: "No updates provided. Use --name, --description, or --role." });
|
|
3786
|
+
} else {
|
|
3787
|
+
console.error(chalk.red("No updates provided. Use --name, --description, or --role."));
|
|
3788
|
+
}
|
|
3789
|
+
process.exit(1);
|
|
3790
|
+
}
|
|
3791
|
+
const agent = updateAgent(id, updates);
|
|
3792
|
+
if (!agent) {
|
|
3793
|
+
if (globalOpts.json) {
|
|
3794
|
+
outputJson({ error: `Agent not found: ${id}` });
|
|
3795
|
+
} else {
|
|
3796
|
+
console.error(chalk.red(`Agent not found: ${id}`));
|
|
3797
|
+
}
|
|
3798
|
+
process.exit(1);
|
|
3799
|
+
}
|
|
3800
|
+
if (globalOpts.json) {
|
|
3801
|
+
outputJson(agent);
|
|
3802
|
+
} else {
|
|
3803
|
+
console.log(chalk.green("Agent updated:"));
|
|
3804
|
+
console.log(` ${chalk.bold("ID:")} ${agent.id}`);
|
|
3805
|
+
console.log(` ${chalk.bold("Name:")} ${agent.name}`);
|
|
3806
|
+
console.log(` ${chalk.bold("Description:")} ${agent.description || "-"}`);
|
|
3807
|
+
console.log(` ${chalk.bold("Role:")} ${agent.role || "agent"}`);
|
|
3808
|
+
console.log(` ${chalk.bold("Last seen:")} ${agent.last_seen_at}`);
|
|
3809
|
+
}
|
|
3810
|
+
} catch (e) {
|
|
3811
|
+
handleError(e);
|
|
3812
|
+
}
|
|
3813
|
+
});
|
|
3443
3814
|
program2.command("projects").description("Manage projects").option("--add", "Add a new project").option("--name <name>", "Project name").option("--path <path>", "Project path").option("--description <text>", "Project description").action((opts) => {
|
|
3444
3815
|
try {
|
|
3445
3816
|
const globalOpts = program2.opts();
|
|
@@ -3640,6 +4011,156 @@ program2.command("bulk <action> <ids...>").description("Batch operations: forget
|
|
|
3640
4011
|
handleError(e);
|
|
3641
4012
|
}
|
|
3642
4013
|
});
|
|
4014
|
+
program2.command("doctor").description("Run health checks on the mementos database").action(() => {
|
|
4015
|
+
const globalOpts = program2.opts();
|
|
4016
|
+
const checks = [];
|
|
4017
|
+
const dbPath = getDbPath();
|
|
4018
|
+
if (existsSync3(dbPath)) {
|
|
4019
|
+
try {
|
|
4020
|
+
accessSync(dbPath, fsConstants.R_OK | fsConstants.W_OK);
|
|
4021
|
+
checks.push({ name: "Database file", status: "ok", detail: dbPath });
|
|
4022
|
+
} catch {
|
|
4023
|
+
checks.push({ name: "Database file", status: "fail", detail: `Not readable/writable: ${dbPath}` });
|
|
4024
|
+
}
|
|
4025
|
+
} else {
|
|
4026
|
+
checks.push({ name: "Database file", status: "fail", detail: `Not found: ${dbPath}` });
|
|
4027
|
+
}
|
|
4028
|
+
try {
|
|
4029
|
+
const db = getDatabase();
|
|
4030
|
+
const migRow = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
|
|
4031
|
+
const schemaVersion = migRow?.max_id ?? 0;
|
|
4032
|
+
checks.push({ name: "Schema version", status: schemaVersion > 0 ? "ok" : "warn", detail: `v${schemaVersion}` });
|
|
4033
|
+
const memCount = db.query("SELECT COUNT(*) as c FROM memories").get().c;
|
|
4034
|
+
const agentCount = db.query("SELECT COUNT(*) as c FROM agents").get().c;
|
|
4035
|
+
const projectCount = db.query("SELECT COUNT(*) as c FROM projects").get().c;
|
|
4036
|
+
checks.push({ name: "Memories", status: "ok", detail: String(memCount) });
|
|
4037
|
+
checks.push({ name: "Agents", status: "ok", detail: String(agentCount) });
|
|
4038
|
+
checks.push({ name: "Projects", status: "ok", detail: String(projectCount) });
|
|
4039
|
+
const orphanedTags = db.query("SELECT COUNT(*) as c FROM memory_tags WHERE memory_id NOT IN (SELECT id FROM memories)").get().c;
|
|
4040
|
+
checks.push({
|
|
4041
|
+
name: "Orphaned tags",
|
|
4042
|
+
status: orphanedTags > 0 ? "warn" : "ok",
|
|
4043
|
+
detail: orphanedTags > 0 ? `${orphanedTags} orphaned tag(s)` : "None"
|
|
4044
|
+
});
|
|
4045
|
+
const expiredCount = db.query("SELECT COUNT(*) as c FROM memories WHERE status != 'expired' AND expires_at IS NOT NULL AND expires_at < datetime('now')").get().c;
|
|
4046
|
+
checks.push({
|
|
4047
|
+
name: "Expired memories",
|
|
4048
|
+
status: expiredCount > 0 ? "warn" : "ok",
|
|
4049
|
+
detail: expiredCount > 0 ? `${expiredCount} expired but not cleaned up (run 'mementos clean')` : "None pending"
|
|
4050
|
+
});
|
|
4051
|
+
} catch (e) {
|
|
4052
|
+
checks.push({ name: "Database connection", status: "fail", detail: e instanceof Error ? e.message : String(e) });
|
|
4053
|
+
}
|
|
4054
|
+
if (globalOpts.json) {
|
|
4055
|
+
outputJson({ checks, healthy: checks.every((c) => c.status === "ok") });
|
|
4056
|
+
} else {
|
|
4057
|
+
console.log(chalk.bold(`
|
|
4058
|
+
Mementos Health Report
|
|
4059
|
+
`));
|
|
4060
|
+
for (const check of checks) {
|
|
4061
|
+
const icon = check.status === "ok" ? chalk.green("\u2713") : check.status === "warn" ? chalk.yellow("!") : chalk.red("\u2717");
|
|
4062
|
+
console.log(` ${icon} ${chalk.bold(check.name)}: ${check.detail}`);
|
|
4063
|
+
}
|
|
4064
|
+
const healthy = checks.every((c) => c.status === "ok");
|
|
4065
|
+
const warnings = checks.filter((c) => c.status === "warn").length;
|
|
4066
|
+
const failures = checks.filter((c) => c.status === "fail").length;
|
|
4067
|
+
console.log("");
|
|
4068
|
+
if (healthy) {
|
|
4069
|
+
console.log(chalk.green(" All checks passed."));
|
|
4070
|
+
} else {
|
|
4071
|
+
if (failures > 0)
|
|
4072
|
+
console.log(chalk.red(` ${failures} check(s) failed.`));
|
|
4073
|
+
if (warnings > 0)
|
|
4074
|
+
console.log(chalk.yellow(` ${warnings} warning(s).`));
|
|
4075
|
+
}
|
|
4076
|
+
console.log("");
|
|
4077
|
+
}
|
|
4078
|
+
});
|
|
4079
|
+
program2.command("show <id>").description("Show full detail of a memory by ID (supports partial IDs)").action((id) => {
|
|
4080
|
+
try {
|
|
4081
|
+
const globalOpts = program2.opts();
|
|
4082
|
+
const resolvedId = resolveMemoryId(id);
|
|
4083
|
+
const memory = getMemory(resolvedId);
|
|
4084
|
+
if (!memory) {
|
|
4085
|
+
if (globalOpts.json) {
|
|
4086
|
+
outputJson({ error: `Memory not found: ${id}` });
|
|
4087
|
+
} else {
|
|
4088
|
+
console.error(chalk.red(`Memory not found: ${id}`));
|
|
4089
|
+
}
|
|
4090
|
+
process.exit(1);
|
|
4091
|
+
}
|
|
4092
|
+
touchMemory(memory.id);
|
|
4093
|
+
if (globalOpts.json) {
|
|
4094
|
+
outputJson(memory);
|
|
4095
|
+
} else {
|
|
4096
|
+
console.log(formatMemoryDetail(memory));
|
|
4097
|
+
}
|
|
4098
|
+
} catch (e) {
|
|
4099
|
+
handleError(e);
|
|
4100
|
+
}
|
|
4101
|
+
});
|
|
4102
|
+
program2.command("history").description("List memories sorted by most recently accessed").option("--limit <n>", "Max results (default: 20)", parseInt).action((opts) => {
|
|
4103
|
+
try {
|
|
4104
|
+
const globalOpts = program2.opts();
|
|
4105
|
+
const limit = opts.limit || 20;
|
|
4106
|
+
const db = getDatabase();
|
|
4107
|
+
const rows = db.query("SELECT * FROM memories WHERE status = 'active' AND accessed_at IS NOT NULL ORDER BY accessed_at DESC LIMIT ?").all(limit);
|
|
4108
|
+
const memories = rows.map(parseMemoryRow);
|
|
4109
|
+
if (globalOpts.json) {
|
|
4110
|
+
outputJson(memories);
|
|
4111
|
+
return;
|
|
4112
|
+
}
|
|
4113
|
+
if (memories.length === 0) {
|
|
4114
|
+
console.log(chalk.yellow("No recently accessed memories."));
|
|
4115
|
+
return;
|
|
4116
|
+
}
|
|
4117
|
+
console.log(chalk.bold(`${memories.length} recently accessed memor${memories.length === 1 ? "y" : "ies"}:`));
|
|
4118
|
+
for (const m of memories) {
|
|
4119
|
+
const id = chalk.dim(m.id.slice(0, 8));
|
|
4120
|
+
const scope = colorScope(m.scope);
|
|
4121
|
+
const cat = colorCategory(m.category);
|
|
4122
|
+
const value = m.value.length > 60 ? m.value.slice(0, 60) + "..." : m.value;
|
|
4123
|
+
const accessed = m.accessed_at ? chalk.dim(m.accessed_at) : chalk.dim("never");
|
|
4124
|
+
console.log(`${id} [${scope}/${cat}] ${chalk.bold(m.key)} = ${value} ${accessed}`);
|
|
4125
|
+
}
|
|
4126
|
+
} catch (e) {
|
|
4127
|
+
handleError(e);
|
|
4128
|
+
}
|
|
4129
|
+
});
|
|
4130
|
+
program2.command("context").description("Output formatted injection context (for piping into agent prompts)").option("--agent <name>", "Agent ID for private memory scope").option("--project <path>", "Project path for shared memory scope").option("--max-tokens <n>", "Token budget for context", parseInt).option("--categories <cats>", "Comma-separated categories: preference, fact, knowledge, history").action((opts) => {
|
|
4131
|
+
try {
|
|
4132
|
+
const globalOpts = program2.opts();
|
|
4133
|
+
const agentId = opts.agent || globalOpts.agent;
|
|
4134
|
+
const projectPath = opts.project || globalOpts.project;
|
|
4135
|
+
let projectId;
|
|
4136
|
+
if (projectPath) {
|
|
4137
|
+
const project = getProject(resolve3(projectPath));
|
|
4138
|
+
if (project)
|
|
4139
|
+
projectId = project.id;
|
|
4140
|
+
}
|
|
4141
|
+
const categories = opts.categories ? opts.categories.split(",").map((c) => c.trim()) : undefined;
|
|
4142
|
+
const injector = new MemoryInjector;
|
|
4143
|
+
const context = injector.getInjectionContext({
|
|
4144
|
+
agent_id: agentId,
|
|
4145
|
+
project_id: projectId,
|
|
4146
|
+
max_tokens: opts.maxTokens,
|
|
4147
|
+
categories
|
|
4148
|
+
});
|
|
4149
|
+
if (globalOpts.json) {
|
|
4150
|
+
outputJson({
|
|
4151
|
+
context,
|
|
4152
|
+
injected_count: injector.getInjectedCount()
|
|
4153
|
+
});
|
|
4154
|
+
} else if (context) {
|
|
4155
|
+
console.log(context);
|
|
4156
|
+
} else {
|
|
4157
|
+
console.error(chalk.yellow("No memories matched the injection criteria."));
|
|
4158
|
+
process.exit(1);
|
|
4159
|
+
}
|
|
4160
|
+
} catch (e) {
|
|
4161
|
+
handleError(e);
|
|
4162
|
+
}
|
|
4163
|
+
});
|
|
3643
4164
|
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) => {
|
|
3644
4165
|
const { readFileSync: readFileSync3, writeFileSync, existsSync: fileExists } = __require("fs");
|
|
3645
4166
|
const { join: pathJoin } = __require("path");
|
|
@@ -3717,4 +4238,105 @@ args = []
|
|
|
3717
4238
|
}
|
|
3718
4239
|
}
|
|
3719
4240
|
});
|
|
4241
|
+
program2.command("watch").description("Watch for new and changed memories in real-time").option("-s, --scope <scope>", "Scope filter: global, shared, private").option("-c, --category <cat>", "Category filter: preference, fact, knowledge, history").option("--agent <name>", "Agent filter").option("--project <path>", "Project filter").option("--interval <ms>", "Poll interval in milliseconds", parseInt).action((opts) => {
|
|
4242
|
+
try {
|
|
4243
|
+
const globalOpts = program2.opts();
|
|
4244
|
+
const agentId = opts.agent || globalOpts.agent;
|
|
4245
|
+
const projectPath = opts.project || globalOpts.project;
|
|
4246
|
+
let projectId;
|
|
4247
|
+
if (projectPath) {
|
|
4248
|
+
const project = getProject(resolve3(projectPath));
|
|
4249
|
+
if (project)
|
|
4250
|
+
projectId = project.id;
|
|
4251
|
+
}
|
|
4252
|
+
const intervalMs = opts.interval || 500;
|
|
4253
|
+
console.log(chalk.bold.cyan("Watching memories...") + chalk.dim(" (Ctrl+C to stop)"));
|
|
4254
|
+
const filters = [];
|
|
4255
|
+
if (opts.scope)
|
|
4256
|
+
filters.push(`scope=${colorScope(opts.scope)}`);
|
|
4257
|
+
if (opts.category)
|
|
4258
|
+
filters.push(`category=${colorCategory(opts.category)}`);
|
|
4259
|
+
if (agentId)
|
|
4260
|
+
filters.push(`agent=${chalk.dim(agentId)}`);
|
|
4261
|
+
if (projectId)
|
|
4262
|
+
filters.push(`project=${chalk.dim(projectId)}`);
|
|
4263
|
+
if (filters.length > 0) {
|
|
4264
|
+
console.log(chalk.dim("Filters: ") + filters.join(chalk.dim(" | ")));
|
|
4265
|
+
}
|
|
4266
|
+
console.log(chalk.dim(`Poll interval: ${intervalMs}ms`));
|
|
4267
|
+
console.log();
|
|
4268
|
+
const filter = {
|
|
4269
|
+
scope: opts.scope,
|
|
4270
|
+
category: opts.category,
|
|
4271
|
+
agent_id: agentId,
|
|
4272
|
+
project_id: projectId,
|
|
4273
|
+
limit: 20
|
|
4274
|
+
};
|
|
4275
|
+
const recent = listMemories(filter);
|
|
4276
|
+
if (recent.length > 0) {
|
|
4277
|
+
console.log(chalk.bold.dim(`Recent (${recent.length}):`));
|
|
4278
|
+
for (const m of recent.reverse()) {
|
|
4279
|
+
console.log(formatWatchLine(m));
|
|
4280
|
+
}
|
|
4281
|
+
} else {
|
|
4282
|
+
console.log(chalk.dim("No recent memories."));
|
|
4283
|
+
}
|
|
4284
|
+
console.log(chalk.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"));
|
|
4285
|
+
console.log();
|
|
4286
|
+
const { startPolling: startPolling2 } = (init_poll(), __toCommonJS(exports_poll));
|
|
4287
|
+
const handle = startPolling2({
|
|
4288
|
+
interval_ms: intervalMs,
|
|
4289
|
+
scope: opts.scope,
|
|
4290
|
+
category: opts.category,
|
|
4291
|
+
agent_id: agentId,
|
|
4292
|
+
project_id: projectId,
|
|
4293
|
+
on_memories: (memories) => {
|
|
4294
|
+
for (const m of memories) {
|
|
4295
|
+
console.log(formatWatchLine(m));
|
|
4296
|
+
sendNotification(m);
|
|
4297
|
+
}
|
|
4298
|
+
},
|
|
4299
|
+
on_error: (err) => {
|
|
4300
|
+
console.error(chalk.red(`Poll error: ${err.message}`));
|
|
4301
|
+
}
|
|
4302
|
+
});
|
|
4303
|
+
const cleanup = () => {
|
|
4304
|
+
handle.stop();
|
|
4305
|
+
console.log();
|
|
4306
|
+
console.log(chalk.dim("Stopped watching."));
|
|
4307
|
+
process.exit(0);
|
|
4308
|
+
};
|
|
4309
|
+
process.on("SIGINT", cleanup);
|
|
4310
|
+
process.on("SIGTERM", cleanup);
|
|
4311
|
+
} catch (e) {
|
|
4312
|
+
handleError(e);
|
|
4313
|
+
}
|
|
4314
|
+
});
|
|
4315
|
+
function formatWatchLine(m) {
|
|
4316
|
+
const scope = colorScope(m.scope);
|
|
4317
|
+
const cat = colorCategory(m.category);
|
|
4318
|
+
const imp = colorImportance(m.importance);
|
|
4319
|
+
const value = m.value.length > 100 ? m.value.slice(0, 100) + "..." : m.value;
|
|
4320
|
+
const main = ` [${scope}/${cat}] ${chalk.bold(m.key)} = ${value} ${chalk.dim(`(importance: ${imp})`)}`;
|
|
4321
|
+
const parts = [];
|
|
4322
|
+
if (m.tags.length > 0)
|
|
4323
|
+
parts.push(`Tags: ${m.tags.join(", ")}`);
|
|
4324
|
+
if (m.agent_id)
|
|
4325
|
+
parts.push(`Agent: ${m.agent_id}`);
|
|
4326
|
+
parts.push(`Updated: ${m.updated_at}`);
|
|
4327
|
+
const detail = chalk.dim(` ${parts.join(" | ")}`);
|
|
4328
|
+
return `${main}
|
|
4329
|
+
${detail}`;
|
|
4330
|
+
}
|
|
4331
|
+
function sendNotification(m) {
|
|
4332
|
+
if (process.platform !== "darwin")
|
|
4333
|
+
return;
|
|
4334
|
+
try {
|
|
4335
|
+
const title = "Mementos";
|
|
4336
|
+
const msg = `[${m.scope}/${m.category}] ${m.key} = ${m.value.slice(0, 60)}`;
|
|
4337
|
+
const escaped = msg.replace(/"/g, "\\\"");
|
|
4338
|
+
const { execSync } = __require("child_process");
|
|
4339
|
+
execSync(`osascript -e 'display notification "${escaped}" with title "${title}"'`, { stdio: "ignore", timeout: 2000 });
|
|
4340
|
+
} catch {}
|
|
4341
|
+
}
|
|
3720
4342
|
program2.parse(process.argv);
|