myagentmemory 0.4.3
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/LICENSE +21 -0
- package/README.md +231 -0
- package/dist/agent-memory +0 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +510 -0
- package/dist/core.d.ts +131 -0
- package/dist/core.js +883 -0
- package/package.json +70 -0
- package/scripts/install-skills.ps1 +48 -0
- package/scripts/install-skills.sh +55 -0
- package/scripts/postinstall.cjs +44 -0
- package/skills/claude-code/SKILL.md +101 -0
- package/skills/codex/SKILL.md +104 -0
- package/src/cli.ts +596 -0
- package/src/core.ts +1082 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* agent-memory CLI
|
|
4
|
+
*
|
|
5
|
+
* Subcommands:
|
|
6
|
+
* context — Build & print context injection string to stdout
|
|
7
|
+
* write — Write to memory files
|
|
8
|
+
* read — Read memory files
|
|
9
|
+
* scratchpad — Manage checklist
|
|
10
|
+
* search — Search via qmd
|
|
11
|
+
* init — Create dirs, detect qmd, setup collection
|
|
12
|
+
* status — Show config, qmd status, file counts
|
|
13
|
+
*
|
|
14
|
+
* Global flags:
|
|
15
|
+
* --dir <path> Override memory directory
|
|
16
|
+
* --json Machine-readable JSON output
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from "node:fs";
|
|
19
|
+
import { _setBaseDir, buildMemoryContext, checkCollection, dailyPath, detectQmd, ensureDirs, ensureQmdAvailableForUpdate, getCollectionName, getDailyDir, getMemoryDir, getMemoryFile, getQmdResultPath, getQmdResultText, getScratchpadFile, nowTimestamp, parseScratchpad, readFileSafe, runQmdSearch, scheduleQmdUpdate, searchRelevantMemories, serializeScratchpad, setupQmdCollection, todayStr, } from "./core.js";
|
|
20
|
+
function parseArgs(argv) {
|
|
21
|
+
const flags = {};
|
|
22
|
+
const positional = [];
|
|
23
|
+
let command = "";
|
|
24
|
+
for (let i = 0; i < argv.length; i++) {
|
|
25
|
+
const arg = argv[i];
|
|
26
|
+
if (!command && !arg.startsWith("-")) {
|
|
27
|
+
command = arg;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (arg.startsWith("--")) {
|
|
31
|
+
const key = arg.slice(2);
|
|
32
|
+
const next = argv[i + 1];
|
|
33
|
+
if (next && !next.startsWith("--")) {
|
|
34
|
+
flags[key] = next;
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
flags[key] = true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (!arg.startsWith("-")) {
|
|
42
|
+
positional.push(arg);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { command, flags, positional };
|
|
46
|
+
}
|
|
47
|
+
function getFlag(flags, key) {
|
|
48
|
+
const val = flags[key];
|
|
49
|
+
return typeof val === "string" ? val : undefined;
|
|
50
|
+
}
|
|
51
|
+
function hasFlag(flags, key) {
|
|
52
|
+
return key in flags;
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Output helpers
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
function output(data, json) {
|
|
58
|
+
if (json) {
|
|
59
|
+
console.log(JSON.stringify(data, null, 2));
|
|
60
|
+
}
|
|
61
|
+
else if (typeof data === "string") {
|
|
62
|
+
console.log(data);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log(JSON.stringify(data, null, 2));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function exitError(message, json) {
|
|
69
|
+
if (json) {
|
|
70
|
+
console.error(JSON.stringify({ error: message }));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.error(`Error: ${message}`);
|
|
74
|
+
}
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Commands
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
async function cmdContext(flags) {
|
|
81
|
+
const json = hasFlag(flags, "json");
|
|
82
|
+
const noSearch = hasFlag(flags, "no-search");
|
|
83
|
+
ensureDirs();
|
|
84
|
+
const searchResults = noSearch ? "" : await searchRelevantMemories("");
|
|
85
|
+
const context = buildMemoryContext(searchResults);
|
|
86
|
+
if (json) {
|
|
87
|
+
output({ context, directory: getMemoryDir() }, true);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
if (context) {
|
|
91
|
+
process.stdout.write(context);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function cmdWrite(flags) {
|
|
96
|
+
const json = hasFlag(flags, "json");
|
|
97
|
+
const target = getFlag(flags, "target");
|
|
98
|
+
const content = getFlag(flags, "content");
|
|
99
|
+
const mode = getFlag(flags, "mode") ?? "append";
|
|
100
|
+
if (!target || !["long_term", "daily"].includes(target)) {
|
|
101
|
+
exitError("--target must be 'long_term' or 'daily'", json);
|
|
102
|
+
}
|
|
103
|
+
if (!content) {
|
|
104
|
+
exitError("--content is required", json);
|
|
105
|
+
}
|
|
106
|
+
ensureDirs();
|
|
107
|
+
const ts = nowTimestamp();
|
|
108
|
+
const sid = "cli";
|
|
109
|
+
if (target === "daily") {
|
|
110
|
+
const filePath = dailyPath(todayStr());
|
|
111
|
+
const existing = readFileSafe(filePath) ?? "";
|
|
112
|
+
const separator = existing.trim() ? "\n\n" : "";
|
|
113
|
+
const stamped = `<!-- ${ts} [${sid}] -->\n${content}`;
|
|
114
|
+
fs.writeFileSync(filePath, existing + separator + stamped, "utf-8");
|
|
115
|
+
await ensureQmdAvailableForUpdate();
|
|
116
|
+
scheduleQmdUpdate();
|
|
117
|
+
output(json
|
|
118
|
+
? { ok: true, path: filePath, target, mode: "append", timestamp: ts }
|
|
119
|
+
: `Appended to daily log: ${filePath}`, json);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// long_term
|
|
123
|
+
const memFile = getMemoryFile();
|
|
124
|
+
const existing = readFileSafe(memFile) ?? "";
|
|
125
|
+
if (mode === "overwrite") {
|
|
126
|
+
const stamped = `<!-- last updated: ${ts} [${sid}] -->\n${content}`;
|
|
127
|
+
fs.writeFileSync(memFile, stamped, "utf-8");
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const separator = existing.trim() ? "\n\n" : "";
|
|
131
|
+
const stamped = `<!-- ${ts} [${sid}] -->\n${content}`;
|
|
132
|
+
fs.writeFileSync(memFile, existing + separator + stamped, "utf-8");
|
|
133
|
+
}
|
|
134
|
+
await ensureQmdAvailableForUpdate();
|
|
135
|
+
scheduleQmdUpdate();
|
|
136
|
+
output(json
|
|
137
|
+
? { ok: true, path: memFile, target, mode, timestamp: ts }
|
|
138
|
+
: `${mode === "overwrite" ? "Overwrote" : "Appended to"} MEMORY.md`, json);
|
|
139
|
+
}
|
|
140
|
+
async function cmdRead(flags) {
|
|
141
|
+
const json = hasFlag(flags, "json");
|
|
142
|
+
const target = getFlag(flags, "target");
|
|
143
|
+
const date = getFlag(flags, "date");
|
|
144
|
+
if (!target || !["long_term", "scratchpad", "daily", "list"].includes(target)) {
|
|
145
|
+
exitError("--target must be 'long_term', 'scratchpad', 'daily', or 'list'", json);
|
|
146
|
+
}
|
|
147
|
+
ensureDirs();
|
|
148
|
+
if (target === "list") {
|
|
149
|
+
try {
|
|
150
|
+
const files = fs
|
|
151
|
+
.readdirSync(getDailyDir())
|
|
152
|
+
.filter((f) => f.endsWith(".md"))
|
|
153
|
+
.sort()
|
|
154
|
+
.reverse();
|
|
155
|
+
if (json) {
|
|
156
|
+
output({ files }, true);
|
|
157
|
+
}
|
|
158
|
+
else if (files.length === 0) {
|
|
159
|
+
console.log("No daily logs found.");
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.log(`Daily logs:\n${files.map((f) => `- ${f}`).join("\n")}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
output(json ? { files: [] } : "No daily logs directory.", json);
|
|
167
|
+
}
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (target === "daily") {
|
|
171
|
+
const d = date ?? todayStr();
|
|
172
|
+
const filePath = dailyPath(d);
|
|
173
|
+
const content = readFileSafe(filePath);
|
|
174
|
+
if (!content) {
|
|
175
|
+
output(json ? { content: null, date: d } : `No daily log for ${d}.`, json);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
output(json ? { content, date: d, path: filePath } : content, json);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (target === "scratchpad") {
|
|
182
|
+
const content = readFileSafe(getScratchpadFile());
|
|
183
|
+
if (!content?.trim()) {
|
|
184
|
+
output(json ? { content: null } : "SCRATCHPAD.md is empty or does not exist.", json);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
output(json ? { content, path: getScratchpadFile() } : content, json);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// long_term
|
|
191
|
+
const content = readFileSafe(getMemoryFile());
|
|
192
|
+
if (!content) {
|
|
193
|
+
output(json ? { content: null } : "MEMORY.md is empty or does not exist.", json);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
output(json ? { content, path: getMemoryFile() } : content, json);
|
|
197
|
+
}
|
|
198
|
+
async function cmdScratchpad(flags, positional) {
|
|
199
|
+
const json = hasFlag(flags, "json");
|
|
200
|
+
const action = positional[0];
|
|
201
|
+
const text = getFlag(flags, "text");
|
|
202
|
+
if (!action || !["add", "done", "undo", "clear_done", "list"].includes(action)) {
|
|
203
|
+
exitError("Usage: agent-memory scratchpad <add|done|undo|clear_done|list> [--text <text>]", json);
|
|
204
|
+
}
|
|
205
|
+
ensureDirs();
|
|
206
|
+
const spFile = getScratchpadFile();
|
|
207
|
+
const existing = readFileSafe(spFile) ?? "";
|
|
208
|
+
let items = parseScratchpad(existing);
|
|
209
|
+
if (action === "list") {
|
|
210
|
+
if (items.length === 0) {
|
|
211
|
+
output(json ? { items: [], count: 0, open: 0 } : "Scratchpad is empty.", json);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (json) {
|
|
215
|
+
output({
|
|
216
|
+
items: items.map((i) => ({ done: i.done, text: i.text })),
|
|
217
|
+
count: items.length,
|
|
218
|
+
open: items.filter((i) => !i.done).length,
|
|
219
|
+
}, true);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
console.log(serializeScratchpad(items));
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (action === "add") {
|
|
227
|
+
if (!text)
|
|
228
|
+
exitError("--text is required for add", json);
|
|
229
|
+
const ts = nowTimestamp();
|
|
230
|
+
items.push({ done: false, text: text, meta: `<!-- ${ts} [cli] -->` });
|
|
231
|
+
fs.writeFileSync(spFile, serializeScratchpad(items), "utf-8");
|
|
232
|
+
await ensureQmdAvailableForUpdate();
|
|
233
|
+
scheduleQmdUpdate();
|
|
234
|
+
output(json ? { ok: true, action, text } : `Added: - [ ] ${text}`, json);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (action === "done" || action === "undo") {
|
|
238
|
+
if (!text)
|
|
239
|
+
exitError(`--text is required for ${action}`, json);
|
|
240
|
+
const needle = text.toLowerCase();
|
|
241
|
+
const targetDone = action === "done";
|
|
242
|
+
let matched = false;
|
|
243
|
+
for (const item of items) {
|
|
244
|
+
if (item.done !== targetDone && item.text.toLowerCase().includes(needle)) {
|
|
245
|
+
item.done = targetDone;
|
|
246
|
+
matched = true;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (!matched) {
|
|
251
|
+
exitError(`No matching ${targetDone ? "open" : "done"} item found for: "${text}"`, json);
|
|
252
|
+
}
|
|
253
|
+
fs.writeFileSync(spFile, serializeScratchpad(items), "utf-8");
|
|
254
|
+
await ensureQmdAvailableForUpdate();
|
|
255
|
+
scheduleQmdUpdate();
|
|
256
|
+
output(json ? { ok: true, action, text } : "Updated.", json);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (action === "clear_done") {
|
|
260
|
+
const before = items.length;
|
|
261
|
+
items = items.filter((i) => !i.done);
|
|
262
|
+
const removed = before - items.length;
|
|
263
|
+
fs.writeFileSync(spFile, serializeScratchpad(items), "utf-8");
|
|
264
|
+
await ensureQmdAvailableForUpdate();
|
|
265
|
+
scheduleQmdUpdate();
|
|
266
|
+
output(json ? { ok: true, action, removed } : `Cleared ${removed} done item(s).`, json);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function cmdSearch(flags) {
|
|
270
|
+
const json = hasFlag(flags, "json");
|
|
271
|
+
const query = getFlag(flags, "query");
|
|
272
|
+
const mode = (getFlag(flags, "mode") ?? "keyword");
|
|
273
|
+
const limit = Number.parseInt(getFlag(flags, "limit") ?? "5", 10);
|
|
274
|
+
if (!query)
|
|
275
|
+
exitError("--query is required", json);
|
|
276
|
+
if (!["keyword", "semantic", "deep"].includes(mode)) {
|
|
277
|
+
exitError("--mode must be 'keyword', 'semantic', or 'deep'", json);
|
|
278
|
+
}
|
|
279
|
+
const qmdFound = await detectQmd();
|
|
280
|
+
if (!qmdFound) {
|
|
281
|
+
exitError("qmd is not installed. Install: bun install -g https://github.com/tobi/qmd", json);
|
|
282
|
+
}
|
|
283
|
+
const collName = getCollectionName();
|
|
284
|
+
const hasCollection = await checkCollection(collName);
|
|
285
|
+
if (!hasCollection) {
|
|
286
|
+
exitError(`qmd collection '${collName}' not found. Run: agent-memory init`, json);
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
const { results, stderr } = await runQmdSearch(mode, query, limit);
|
|
290
|
+
if (json) {
|
|
291
|
+
output({ mode, query, count: results.length, results }, true);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (results.length === 0) {
|
|
295
|
+
const needsEmbed = /need embeddings/i.test(stderr ?? "");
|
|
296
|
+
if (needsEmbed && (mode === "semantic" || mode === "deep")) {
|
|
297
|
+
console.log(`No results found. qmd reports missing embeddings — run: qmd embed`);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
console.log(`No results found for "${query}" (mode: ${mode}).`);
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
for (let i = 0; i < results.length; i++) {
|
|
305
|
+
const r = results[i];
|
|
306
|
+
const filePath = getQmdResultPath(r);
|
|
307
|
+
const text = getQmdResultText(r);
|
|
308
|
+
console.log(`--- Result ${i + 1} ---`);
|
|
309
|
+
if (filePath)
|
|
310
|
+
console.log(`File: ${filePath}`);
|
|
311
|
+
if (r.score != null)
|
|
312
|
+
console.log(`Score: ${r.score}`);
|
|
313
|
+
if (text)
|
|
314
|
+
console.log(text);
|
|
315
|
+
console.log("");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
exitError(`Search failed: ${err instanceof Error ? err.message : String(err)}`, json);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async function cmdInit(flags) {
|
|
323
|
+
const json = hasFlag(flags, "json");
|
|
324
|
+
ensureDirs();
|
|
325
|
+
const dir = getMemoryDir();
|
|
326
|
+
const qmdFound = await detectQmd();
|
|
327
|
+
let collectionCreated = false;
|
|
328
|
+
if (qmdFound) {
|
|
329
|
+
const collName = getCollectionName();
|
|
330
|
+
const hasCollection = await checkCollection(collName);
|
|
331
|
+
if (!hasCollection) {
|
|
332
|
+
collectionCreated = await setupQmdCollection();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (json) {
|
|
336
|
+
output({
|
|
337
|
+
ok: true,
|
|
338
|
+
directory: dir,
|
|
339
|
+
qmd: qmdFound,
|
|
340
|
+
collectionCreated,
|
|
341
|
+
}, true);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
console.log(`Memory directory: ${dir}`);
|
|
345
|
+
console.log(` MEMORY.md, SCRATCHPAD.md, daily/ created.`);
|
|
346
|
+
if (qmdFound) {
|
|
347
|
+
if (collectionCreated) {
|
|
348
|
+
console.log(` qmd collection '${getCollectionName()}' created.`);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
console.log(` qmd collection '${getCollectionName()}' already exists.`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
console.log(` qmd not found — search features unavailable.`);
|
|
356
|
+
console.log(` Install: bun install -g https://github.com/tobi/qmd`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
async function cmdStatus(flags) {
|
|
361
|
+
const json = hasFlag(flags, "json");
|
|
362
|
+
ensureDirs();
|
|
363
|
+
const dir = getMemoryDir();
|
|
364
|
+
const memFile = getMemoryFile();
|
|
365
|
+
const spFile = getScratchpadFile();
|
|
366
|
+
const dailyDir = getDailyDir();
|
|
367
|
+
const memContent = readFileSafe(memFile);
|
|
368
|
+
const spContent = readFileSafe(spFile);
|
|
369
|
+
let dailyCount = 0;
|
|
370
|
+
try {
|
|
371
|
+
dailyCount = fs.readdirSync(dailyDir).filter((f) => f.endsWith(".md")).length;
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
// directory may not exist
|
|
375
|
+
}
|
|
376
|
+
const qmdFound = await detectQmd();
|
|
377
|
+
let hasCollection = false;
|
|
378
|
+
if (qmdFound) {
|
|
379
|
+
hasCollection = await checkCollection();
|
|
380
|
+
}
|
|
381
|
+
if (json) {
|
|
382
|
+
output({
|
|
383
|
+
directory: dir,
|
|
384
|
+
memoryFile: {
|
|
385
|
+
exists: memContent !== null,
|
|
386
|
+
chars: memContent?.length ?? 0,
|
|
387
|
+
lines: memContent ? memContent.split("\n").length : 0,
|
|
388
|
+
},
|
|
389
|
+
scratchpadFile: {
|
|
390
|
+
exists: spContent !== null,
|
|
391
|
+
items: spContent ? parseScratchpad(spContent).length : 0,
|
|
392
|
+
openItems: spContent ? parseScratchpad(spContent).filter((i) => !i.done).length : 0,
|
|
393
|
+
},
|
|
394
|
+
dailyLogs: dailyCount,
|
|
395
|
+
qmd: {
|
|
396
|
+
available: qmdFound,
|
|
397
|
+
collection: hasCollection ? getCollectionName() : null,
|
|
398
|
+
},
|
|
399
|
+
}, true);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
console.log(`Memory directory: ${dir}`);
|
|
403
|
+
console.log("");
|
|
404
|
+
if (memContent !== null) {
|
|
405
|
+
const lines = memContent.split("\n").length;
|
|
406
|
+
console.log(`MEMORY.md: ${memContent.length} chars, ${lines} lines`);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
console.log("MEMORY.md: not created yet");
|
|
410
|
+
}
|
|
411
|
+
if (spContent !== null) {
|
|
412
|
+
const items = parseScratchpad(spContent);
|
|
413
|
+
const open = items.filter((i) => !i.done).length;
|
|
414
|
+
console.log(`SCRATCHPAD.md: ${items.length} items (${open} open)`);
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
console.log("SCRATCHPAD.md: not created yet");
|
|
418
|
+
}
|
|
419
|
+
console.log(`Daily logs: ${dailyCount} file(s)`);
|
|
420
|
+
console.log("");
|
|
421
|
+
if (qmdFound) {
|
|
422
|
+
console.log(`qmd: available`);
|
|
423
|
+
console.log(`Collection '${getCollectionName()}': ${hasCollection ? "configured" : "not configured — run: agent-memory init"}`);
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
console.log("qmd: not installed");
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
// ---------------------------------------------------------------------------
|
|
431
|
+
// Usage
|
|
432
|
+
// ---------------------------------------------------------------------------
|
|
433
|
+
function printUsage() {
|
|
434
|
+
console.log(`agent-memory — persistent memory for coding agents
|
|
435
|
+
|
|
436
|
+
Usage:
|
|
437
|
+
agent-memory <command> [options]
|
|
438
|
+
|
|
439
|
+
Commands:
|
|
440
|
+
context Build & print context injection string
|
|
441
|
+
write Write to memory files
|
|
442
|
+
read Read memory files
|
|
443
|
+
scratchpad Manage checklist items
|
|
444
|
+
search Search across memory files (requires qmd)
|
|
445
|
+
init Initialize memory directory and qmd collection
|
|
446
|
+
status Show configuration and status
|
|
447
|
+
|
|
448
|
+
Global flags:
|
|
449
|
+
--dir <path> Override memory directory
|
|
450
|
+
--json Machine-readable JSON output
|
|
451
|
+
|
|
452
|
+
Examples:
|
|
453
|
+
agent-memory init
|
|
454
|
+
agent-memory write --target long_term --content "User prefers dark mode"
|
|
455
|
+
agent-memory write --target daily --content "Fixed auth bug in login flow"
|
|
456
|
+
agent-memory read --target long_term
|
|
457
|
+
agent-memory read --target daily --date 2026-02-15
|
|
458
|
+
agent-memory read --target list
|
|
459
|
+
agent-memory scratchpad add --text "Review PR #42"
|
|
460
|
+
agent-memory scratchpad list
|
|
461
|
+
agent-memory scratchpad done --text "PR #42"
|
|
462
|
+
agent-memory search --query "database choice" --mode keyword
|
|
463
|
+
agent-memory context --no-search
|
|
464
|
+
agent-memory status --json`);
|
|
465
|
+
}
|
|
466
|
+
// ---------------------------------------------------------------------------
|
|
467
|
+
// Main
|
|
468
|
+
// ---------------------------------------------------------------------------
|
|
469
|
+
async function main() {
|
|
470
|
+
const { command, flags, positional } = parseArgs(process.argv.slice(2));
|
|
471
|
+
const json = hasFlag(flags, "json");
|
|
472
|
+
// Apply --dir override
|
|
473
|
+
const dir = getFlag(flags, "dir");
|
|
474
|
+
if (dir) {
|
|
475
|
+
_setBaseDir(dir);
|
|
476
|
+
}
|
|
477
|
+
if (!command || command === "help" || hasFlag(flags, "help")) {
|
|
478
|
+
printUsage();
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
switch (command) {
|
|
482
|
+
case "context":
|
|
483
|
+
await cmdContext(flags);
|
|
484
|
+
break;
|
|
485
|
+
case "write":
|
|
486
|
+
await cmdWrite(flags);
|
|
487
|
+
break;
|
|
488
|
+
case "read":
|
|
489
|
+
await cmdRead(flags);
|
|
490
|
+
break;
|
|
491
|
+
case "scratchpad":
|
|
492
|
+
await cmdScratchpad(flags, positional);
|
|
493
|
+
break;
|
|
494
|
+
case "search":
|
|
495
|
+
await cmdSearch(flags);
|
|
496
|
+
break;
|
|
497
|
+
case "init":
|
|
498
|
+
await cmdInit(flags);
|
|
499
|
+
break;
|
|
500
|
+
case "status":
|
|
501
|
+
await cmdStatus(flags);
|
|
502
|
+
break;
|
|
503
|
+
default:
|
|
504
|
+
exitError(`Unknown command: ${command}. Run 'agent-memory help' for usage.`, json);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
main().catch((err) => {
|
|
508
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
509
|
+
process.exit(1);
|
|
510
|
+
});
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared core logic for agent-memory.
|
|
3
|
+
*
|
|
4
|
+
* Core logic for agent-memory CLI and skills.
|
|
5
|
+
* Zero pi peer dependencies — only node:fs, node:path, node:child_process.
|
|
6
|
+
*/
|
|
7
|
+
import { execFile } from "node:child_process";
|
|
8
|
+
/** Override base directory (for testing or platform-specific defaults). */
|
|
9
|
+
export declare function _setBaseDir(baseDir: string): void;
|
|
10
|
+
/** Reset to default paths. */
|
|
11
|
+
export declare function _resetBaseDir(): void;
|
|
12
|
+
/** Get the current memory directory path. */
|
|
13
|
+
export declare function getMemoryDir(): string;
|
|
14
|
+
/** Get the current MEMORY.md path. */
|
|
15
|
+
export declare function getMemoryFile(): string;
|
|
16
|
+
/** Get the current SCRATCHPAD.md path. */
|
|
17
|
+
export declare function getScratchpadFile(): string;
|
|
18
|
+
/** Get the current daily log directory path. */
|
|
19
|
+
export declare function getDailyDir(): string;
|
|
20
|
+
export declare function ensureDirs(): void;
|
|
21
|
+
export declare function todayStr(): string;
|
|
22
|
+
export declare function yesterdayStr(): string;
|
|
23
|
+
export declare function nowTimestamp(): string;
|
|
24
|
+
export declare function shortSessionId(sessionId: string): string;
|
|
25
|
+
export declare function readFileSafe(filePath: string): string | null;
|
|
26
|
+
export declare function dailyPath(date: string): string;
|
|
27
|
+
export declare const RESPONSE_PREVIEW_MAX_CHARS = 4000;
|
|
28
|
+
export declare const RESPONSE_PREVIEW_MAX_LINES = 120;
|
|
29
|
+
export type TruncateMode = "start" | "end" | "middle";
|
|
30
|
+
export interface PreviewResult {
|
|
31
|
+
preview: string;
|
|
32
|
+
truncated: boolean;
|
|
33
|
+
totalLines: number;
|
|
34
|
+
totalChars: number;
|
|
35
|
+
previewLines: number;
|
|
36
|
+
previewChars: number;
|
|
37
|
+
}
|
|
38
|
+
export declare function truncateLines(lines: string[], maxLines: number, mode: TruncateMode): {
|
|
39
|
+
lines: string[];
|
|
40
|
+
truncated: boolean;
|
|
41
|
+
};
|
|
42
|
+
export declare function truncateText(text: string, maxChars: number, mode: TruncateMode): {
|
|
43
|
+
text: string;
|
|
44
|
+
truncated: boolean;
|
|
45
|
+
};
|
|
46
|
+
export declare function buildPreview(content: string, options: {
|
|
47
|
+
maxLines: number;
|
|
48
|
+
maxChars: number;
|
|
49
|
+
mode: TruncateMode;
|
|
50
|
+
}): PreviewResult;
|
|
51
|
+
export declare function formatPreviewBlock(label: string, content: string, mode: TruncateMode): string;
|
|
52
|
+
export declare function formatContextSection(label: string, content: string, mode: TruncateMode, maxLines: number, maxChars: number): string;
|
|
53
|
+
export interface ScratchpadItem {
|
|
54
|
+
done: boolean;
|
|
55
|
+
text: string;
|
|
56
|
+
meta: string;
|
|
57
|
+
}
|
|
58
|
+
export declare function parseScratchpad(content: string): ScratchpadItem[];
|
|
59
|
+
export declare function serializeScratchpad(items: ScratchpadItem[]): string;
|
|
60
|
+
export declare function buildMemoryContext(searchResults?: string): string;
|
|
61
|
+
type ExecFileFn = typeof execFile;
|
|
62
|
+
/** Override execFile implementation (for testing). */
|
|
63
|
+
export declare function _setExecFileForTest(fn: ExecFileFn): void;
|
|
64
|
+
/** Reset execFile implementation (for testing). */
|
|
65
|
+
export declare function _resetExecFileForTest(): void;
|
|
66
|
+
/** Set qmd availability flag (for testing). */
|
|
67
|
+
export declare function _setQmdAvailable(value: boolean): void;
|
|
68
|
+
/** Get current qmd availability flag. */
|
|
69
|
+
export declare function _getQmdAvailable(): boolean;
|
|
70
|
+
/** Get current update timer (for testing). */
|
|
71
|
+
export declare function _getUpdateTimer(): ReturnType<typeof setTimeout> | null;
|
|
72
|
+
/** Clear the update timer (for testing). */
|
|
73
|
+
export declare function _clearUpdateTimer(): void;
|
|
74
|
+
/** Get the current QMD collection name. */
|
|
75
|
+
export declare function getCollectionName(): string;
|
|
76
|
+
/** Set the QMD collection name (for platform-specific overrides). */
|
|
77
|
+
export declare function setCollectionName(name: string): void;
|
|
78
|
+
export declare function qmdInstallInstructions(): string;
|
|
79
|
+
export declare function qmdCollectionInstructions(): string;
|
|
80
|
+
/** Auto-create the qmd collection and path contexts. */
|
|
81
|
+
export declare function setupQmdCollection(): Promise<boolean>;
|
|
82
|
+
export declare function detectQmd(): Promise<boolean>;
|
|
83
|
+
export declare function checkCollection(name?: string): Promise<boolean>;
|
|
84
|
+
export declare function getQmdUpdateMode(): "background" | "manual" | "off";
|
|
85
|
+
export declare function ensureQmdAvailableForUpdate(): Promise<boolean>;
|
|
86
|
+
export declare function scheduleQmdUpdate(): void;
|
|
87
|
+
export declare function runQmdUpdateNow(): Promise<void>;
|
|
88
|
+
/** Search for memories relevant to the user's prompt. Returns formatted markdown or empty string on error. */
|
|
89
|
+
export declare function searchRelevantMemories(prompt: string): Promise<string>;
|
|
90
|
+
export interface QmdSearchResult {
|
|
91
|
+
path?: string;
|
|
92
|
+
file?: string;
|
|
93
|
+
score?: number;
|
|
94
|
+
content?: string;
|
|
95
|
+
chunk?: string;
|
|
96
|
+
snippet?: string;
|
|
97
|
+
title?: string;
|
|
98
|
+
[key: string]: unknown;
|
|
99
|
+
}
|
|
100
|
+
export declare function getQmdResultPath(r: QmdSearchResult): string | undefined;
|
|
101
|
+
export declare function getQmdResultText(r: QmdSearchResult): string;
|
|
102
|
+
export declare function runQmdSearch(mode: "keyword" | "semantic" | "deep", query: string, limit: number): Promise<{
|
|
103
|
+
results: QmdSearchResult[];
|
|
104
|
+
stderr: string;
|
|
105
|
+
}>;
|
|
106
|
+
export interface ToolResult {
|
|
107
|
+
text: string;
|
|
108
|
+
details: Record<string, unknown>;
|
|
109
|
+
isError?: boolean;
|
|
110
|
+
}
|
|
111
|
+
export declare function memoryWrite(params: {
|
|
112
|
+
target: "long_term" | "daily";
|
|
113
|
+
content: string;
|
|
114
|
+
mode?: "append" | "overwrite";
|
|
115
|
+
sessionId?: string;
|
|
116
|
+
}): Promise<ToolResult>;
|
|
117
|
+
export declare function scratchpadAction(params: {
|
|
118
|
+
action: "add" | "done" | "undo" | "clear_done" | "list";
|
|
119
|
+
text?: string;
|
|
120
|
+
sessionId?: string;
|
|
121
|
+
}): Promise<ToolResult>;
|
|
122
|
+
export declare function memoryRead(params: {
|
|
123
|
+
target: "long_term" | "scratchpad" | "daily" | "list";
|
|
124
|
+
date?: string;
|
|
125
|
+
}): Promise<ToolResult>;
|
|
126
|
+
export declare function memorySearch(params: {
|
|
127
|
+
query: string;
|
|
128
|
+
mode?: "keyword" | "semantic" | "deep";
|
|
129
|
+
limit?: number;
|
|
130
|
+
}): Promise<ToolResult>;
|
|
131
|
+
export {};
|