myagentmemory 0.4.12 → 0.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -49
- package/dist/cli-spec.d.ts +25 -0
- package/dist/cli-spec.js +211 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +435 -71
- package/dist/completions.d.ts +13 -0
- package/dist/completions.js +429 -0
- package/dist/core.d.ts +22 -1
- package/dist/core.js +299 -62
- package/dist/hooks.d.ts +42 -0
- package/dist/hooks.js +444 -0
- package/dist/plugin-bootstrap.d.ts +190 -0
- package/dist/plugin-bootstrap.js +628 -0
- package/dist/plugin-host.d.ts +136 -0
- package/dist/plugin-host.js +98 -0
- package/dist/plugin-runtime.d.ts +21 -0
- package/dist/plugin-runtime.js +208 -0
- package/dist/plugin-service.d.ts +45 -0
- package/dist/plugin-service.js +395 -0
- package/docs/official-plugin-bootstrap.md +335 -0
- package/package.json +62 -11
- package/scripts/install-skills.sh +4 -1
- package/scripts/postinstall.cjs +23 -4
- package/src/cli-spec.ts +236 -0
- package/src/cli.ts +455 -82
- package/src/completions.ts +501 -0
- package/src/core.ts +314 -62
- package/src/hooks.ts +485 -0
- package/src/plugin-bootstrap.ts +931 -0
- package/src/plugin-host.ts +255 -0
- package/src/plugin-runtime.ts +296 -0
- package/src/plugin-service.ts +451 -0
- package/dist/agent-memory +0 -0
package/src/cli.ts
CHANGED
|
@@ -12,14 +12,21 @@
|
|
|
12
12
|
* search — Search via qmd
|
|
13
13
|
* init — Create dirs, detect qmd, setup collection
|
|
14
14
|
* status — Show config, qmd status, file counts
|
|
15
|
+
* completion — Install or print shell completion
|
|
16
|
+
* install-hooks — Install managed session-start hooks
|
|
17
|
+
* uninstall-hooks — Remove managed session-start hooks
|
|
18
|
+
* plugin — Discover and bootstrap optional official plugins
|
|
15
19
|
*
|
|
16
20
|
* Global flags:
|
|
17
21
|
* --dir <path> Override memory directory
|
|
18
22
|
* --json Machine-readable JSON output
|
|
19
23
|
*/
|
|
20
24
|
|
|
25
|
+
import { spawn } from "node:child_process";
|
|
21
26
|
import * as fs from "node:fs";
|
|
22
27
|
|
|
28
|
+
import { type CompletionShell, detectCompletionShell, generateCompletion, installCompletion } from "./completions.js";
|
|
29
|
+
|
|
23
30
|
import {
|
|
24
31
|
_setBaseDir,
|
|
25
32
|
buildMemoryContext,
|
|
@@ -41,9 +48,12 @@ import {
|
|
|
41
48
|
getScratchpadFile,
|
|
42
49
|
getTopicsDir,
|
|
43
50
|
installSkills,
|
|
51
|
+
memoryWrite,
|
|
44
52
|
nowTimestamp,
|
|
45
53
|
parseScratchpad,
|
|
54
|
+
probeEmbeddings,
|
|
46
55
|
readFileSafe,
|
|
56
|
+
redactSecrets,
|
|
47
57
|
runQmdEmbedDetached,
|
|
48
58
|
runQmdSearch,
|
|
49
59
|
runQmdSync,
|
|
@@ -57,9 +67,28 @@ import {
|
|
|
57
67
|
topicPath,
|
|
58
68
|
uninstallSkills,
|
|
59
69
|
} from "./core.js";
|
|
70
|
+
import { detectHookAgents, type HookAgentKey, installHooks, uninstallHooks } from "./hooks.js";
|
|
71
|
+
import {
|
|
72
|
+
createDefaultPluginBootstrap,
|
|
73
|
+
PluginBootstrapFailure,
|
|
74
|
+
type PluginBootstrapResultV1,
|
|
75
|
+
} from "./plugin-bootstrap.js";
|
|
76
|
+
import { InstalledPluginRuntimeV1 } from "./plugin-runtime.js";
|
|
60
77
|
|
|
61
78
|
declare const __VERSION__: string;
|
|
62
|
-
|
|
79
|
+
|
|
80
|
+
function readPackageVersion(): string {
|
|
81
|
+
try {
|
|
82
|
+
const packageJson = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8")) as {
|
|
83
|
+
version?: unknown;
|
|
84
|
+
};
|
|
85
|
+
return typeof packageJson.version === "string" ? packageJson.version : "dev";
|
|
86
|
+
} catch {
|
|
87
|
+
return "dev";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : readPackageVersion();
|
|
63
92
|
|
|
64
93
|
// ---------------------------------------------------------------------------
|
|
65
94
|
// Arg parsing (no external deps)
|
|
@@ -133,6 +162,110 @@ function exitError(message: string, json: boolean): never {
|
|
|
133
162
|
process.exit(1);
|
|
134
163
|
}
|
|
135
164
|
|
|
165
|
+
function openExternalUrl(url: string): boolean {
|
|
166
|
+
let parsed: URL;
|
|
167
|
+
try {
|
|
168
|
+
parsed = new URL(url);
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
if (parsed.protocol !== "https:") return false;
|
|
173
|
+
try {
|
|
174
|
+
const child =
|
|
175
|
+
process.platform === "darwin"
|
|
176
|
+
? spawn("open", [parsed.toString()], { detached: true, stdio: "ignore" })
|
|
177
|
+
: process.platform === "win32"
|
|
178
|
+
? spawn("explorer.exe", [parsed.toString()], { detached: true, stdio: "ignore" })
|
|
179
|
+
: spawn("xdg-open", [parsed.toString()], { detached: true, stdio: "ignore" });
|
|
180
|
+
child.unref();
|
|
181
|
+
return true;
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function printProOverview(installed: boolean): void {
|
|
188
|
+
console.log("");
|
|
189
|
+
console.log("AgentMemory Pro includes:");
|
|
190
|
+
console.log(" Session Intelligence Recall decisions and context across Pi, Codex, and Claude Code sessions.");
|
|
191
|
+
console.log(" Guided Learning Turn repeated corrections into reviewable, reversible memory.");
|
|
192
|
+
console.log(" Local Web Console Inspect memories, activity, health, and settings in your browser.");
|
|
193
|
+
console.log("");
|
|
194
|
+
console.log("Your session content stays on this device.");
|
|
195
|
+
console.log("");
|
|
196
|
+
if (installed) {
|
|
197
|
+
console.log("Try it:");
|
|
198
|
+
console.log(' agent-memory recall "what did we decide about authentication?"');
|
|
199
|
+
console.log(" agent-memory learn");
|
|
200
|
+
console.log(" agent-memory web");
|
|
201
|
+
} else {
|
|
202
|
+
console.log("Start your Pro beta:");
|
|
203
|
+
console.log(" agent-memory plugin install");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function printPluginResult(result: PluginBootstrapResultV1, json: boolean, allowBrowser: boolean): void {
|
|
208
|
+
if (json) {
|
|
209
|
+
output(result, true);
|
|
210
|
+
} else if (result.command === "plugin.list" && result.plugins) {
|
|
211
|
+
for (const plugin of result.plugins) {
|
|
212
|
+
const state = plugin.available ? "available" : plugin.installed ? plugin.entitlement : "not installed";
|
|
213
|
+
console.log(`${plugin.name}: ${state}`);
|
|
214
|
+
}
|
|
215
|
+
printProOverview(Boolean(result.bundle));
|
|
216
|
+
} else {
|
|
217
|
+
const version = result.bundle?.version ? ` ${result.bundle.version}` : "";
|
|
218
|
+
let showOverview = false;
|
|
219
|
+
switch (result.result) {
|
|
220
|
+
case "installed":
|
|
221
|
+
console.log(`AgentMemory Pro${version} installed.`);
|
|
222
|
+
showOverview = true;
|
|
223
|
+
break;
|
|
224
|
+
case "upgraded":
|
|
225
|
+
console.log(`AgentMemory Pro upgraded to${version}.`);
|
|
226
|
+
showOverview = true;
|
|
227
|
+
break;
|
|
228
|
+
case "current":
|
|
229
|
+
console.log(
|
|
230
|
+
result.bundle
|
|
231
|
+
? `AgentMemory Pro${version} is installed and ready.`
|
|
232
|
+
: "AgentMemory Pro is not installed.",
|
|
233
|
+
);
|
|
234
|
+
showOverview = Boolean(result.bundle);
|
|
235
|
+
break;
|
|
236
|
+
case "update_available":
|
|
237
|
+
console.log(`AgentMemory Pro${version} has an update available.`);
|
|
238
|
+
break;
|
|
239
|
+
case "uninstalled":
|
|
240
|
+
console.log("AgentMemory Pro executable components were removed. Memory and billing state were preserved.");
|
|
241
|
+
break;
|
|
242
|
+
case "not_installed":
|
|
243
|
+
console.log("AgentMemory Pro is not installed.");
|
|
244
|
+
console.log("Run: agent-memory plugin install");
|
|
245
|
+
break;
|
|
246
|
+
case "auth_required":
|
|
247
|
+
console.log("Run this command in an interactive terminal to enter an email and activate temporary access.");
|
|
248
|
+
break;
|
|
249
|
+
case "renewal_required":
|
|
250
|
+
console.log("Renew AgentMemory Pro to continue using paid capabilities.");
|
|
251
|
+
break;
|
|
252
|
+
default:
|
|
253
|
+
console.log(result.error?.message ?? "AgentMemory Pro is currently unavailable.");
|
|
254
|
+
}
|
|
255
|
+
if (showOverview) printProOverview(true);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (result.nextAction) {
|
|
259
|
+
if (allowBrowser && openExternalUrl(result.nextAction.url)) {
|
|
260
|
+
if (!json) console.log("Opened the AgentMemory account website.");
|
|
261
|
+
} else if (!json) {
|
|
262
|
+
console.log(`Open: ${result.nextAction.url}`);
|
|
263
|
+
}
|
|
264
|
+
if (!json && result.nextAction.userCode) console.log(`Code: ${result.nextAction.userCode}`);
|
|
265
|
+
}
|
|
266
|
+
if (!result.ok) process.exitCode = 1;
|
|
267
|
+
}
|
|
268
|
+
|
|
136
269
|
// ---------------------------------------------------------------------------
|
|
137
270
|
// Commands
|
|
138
271
|
// ---------------------------------------------------------------------------
|
|
@@ -140,9 +273,11 @@ function exitError(message: string, json: boolean): never {
|
|
|
140
273
|
async function cmdContext(flags: Record<string, string | boolean>) {
|
|
141
274
|
const json = hasFlag(flags, "json");
|
|
142
275
|
const noSearch = hasFlag(flags, "no-search");
|
|
276
|
+
const query = getFlag(flags, "query") ?? "";
|
|
143
277
|
|
|
144
278
|
ensureDirs();
|
|
145
|
-
|
|
279
|
+
if (!noSearch && query) await ensureQmdAvailableForSync();
|
|
280
|
+
const searchResults = noSearch ? "" : await searchRelevantMemories(query);
|
|
146
281
|
const context = buildMemoryContext(searchResults);
|
|
147
282
|
|
|
148
283
|
if (json) {
|
|
@@ -161,82 +296,29 @@ async function cmdWrite(flags: Record<string, string | boolean>) {
|
|
|
161
296
|
const mode = getFlag(flags, "mode") ?? "append";
|
|
162
297
|
const topic = getFlag(flags, "topic");
|
|
163
298
|
const date = getFlag(flags, "date");
|
|
299
|
+
const sourceUri = getFlag(flags, "source-uri");
|
|
164
300
|
|
|
165
301
|
if (!["long_term", "daily", "topic"].includes(target)) {
|
|
166
302
|
exitError("--target must be 'long_term', 'daily', or 'topic' (default: daily)", json);
|
|
167
303
|
}
|
|
304
|
+
if (!["append", "overwrite"].includes(mode)) {
|
|
305
|
+
exitError("--mode must be 'append' or 'overwrite'", json);
|
|
306
|
+
}
|
|
168
307
|
if (!content) {
|
|
169
308
|
exitError("--content is required", json);
|
|
170
309
|
}
|
|
171
310
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
scheduleQmdUpdate();
|
|
184
|
-
output(
|
|
185
|
-
json
|
|
186
|
-
? { ok: true, path: filePath, target, mode: "append", timestamp: ts }
|
|
187
|
-
: `Appended to daily log: ${filePath}`,
|
|
188
|
-
json,
|
|
189
|
-
);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (target === "topic") {
|
|
194
|
-
if (!topic) {
|
|
195
|
-
exitError("--topic is required when --target is 'topic'", json);
|
|
196
|
-
}
|
|
197
|
-
const slug = slugifyTopic(topic);
|
|
198
|
-
if (!slug) {
|
|
199
|
-
exitError("--topic must include at least one letter or number", json);
|
|
200
|
-
}
|
|
201
|
-
const filePath = topicPath(slug);
|
|
202
|
-
const existing = readFileSafe(filePath) ?? "";
|
|
203
|
-
const linkDate = date?.trim() || todayStr();
|
|
204
|
-
const header = `# Topic: ${topic}\n\n<!-- created: ${ts} [${sid}] -->\n`;
|
|
205
|
-
const separator = existing.trim() ? "\n\n" : "";
|
|
206
|
-
const base = existing.trim() ? existing : header.trimEnd();
|
|
207
|
-
const stamped = `<!-- ${ts} [${sid}] -->\n${content.trim()}\nDaily: [[${linkDate}]]`;
|
|
208
|
-
fs.writeFileSync(filePath, `${base}${separator}${stamped}`, "utf-8");
|
|
209
|
-
await ensureQmdAvailableForUpdate();
|
|
210
|
-
scheduleQmdUpdate();
|
|
211
|
-
output(
|
|
212
|
-
json
|
|
213
|
-
? { ok: true, path: filePath, target, mode: "append", timestamp: ts, topic, slug, date: linkDate }
|
|
214
|
-
: `Appended to topic: ${filePath}`,
|
|
215
|
-
json,
|
|
216
|
-
);
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// long_term
|
|
221
|
-
const memFile = getMemoryFile();
|
|
222
|
-
const existing = readFileSafe(memFile) ?? "";
|
|
223
|
-
|
|
224
|
-
if (mode === "overwrite") {
|
|
225
|
-
const stamped = `<!-- last updated: ${ts} [${sid}] -->\n${content}`;
|
|
226
|
-
fs.writeFileSync(memFile, stamped, "utf-8");
|
|
227
|
-
} else {
|
|
228
|
-
const separator = existing.trim() ? "\n\n" : "";
|
|
229
|
-
const stamped = `<!-- ${ts} [${sid}] -->\n${content}`;
|
|
230
|
-
fs.writeFileSync(memFile, existing + separator + stamped, "utf-8");
|
|
231
|
-
}
|
|
232
|
-
await ensureQmdAvailableForUpdate();
|
|
233
|
-
scheduleQmdUpdate();
|
|
234
|
-
output(
|
|
235
|
-
json
|
|
236
|
-
? { ok: true, path: memFile, target, mode, timestamp: ts }
|
|
237
|
-
: `${mode === "overwrite" ? "Overwrote" : "Appended to"} MEMORY.md`,
|
|
238
|
-
json,
|
|
239
|
-
);
|
|
311
|
+
const result = await memoryWrite({
|
|
312
|
+
target: target as "long_term" | "daily" | "topic",
|
|
313
|
+
content,
|
|
314
|
+
mode: mode as "append" | "overwrite",
|
|
315
|
+
sessionId: "cli",
|
|
316
|
+
topic,
|
|
317
|
+
date,
|
|
318
|
+
sourceUri,
|
|
319
|
+
});
|
|
320
|
+
if (result.isError) exitError(result.text.replace(/^Error:\s*/, ""), json);
|
|
321
|
+
output(json ? { ok: true, ...result.details } : result.text.split("\n\n", 1)[0], json);
|
|
240
322
|
}
|
|
241
323
|
|
|
242
324
|
async function cmdRead(flags: Record<string, string | boolean>) {
|
|
@@ -349,7 +431,11 @@ async function cmdScratchpad(flags: Record<string, string | boolean>, positional
|
|
|
349
431
|
ensureDirs();
|
|
350
432
|
const spFile = getScratchpadFile();
|
|
351
433
|
const existing = readFileSafe(spFile) ?? "";
|
|
352
|
-
let items = parseScratchpad(existing)
|
|
434
|
+
let items = parseScratchpad(existing).map((item) => ({
|
|
435
|
+
...item,
|
|
436
|
+
text: redactSecrets(item.text).content,
|
|
437
|
+
meta: redactSecrets(item.meta).content,
|
|
438
|
+
}));
|
|
353
439
|
|
|
354
440
|
if (action === "list") {
|
|
355
441
|
if (items.length === 0) {
|
|
@@ -374,11 +460,12 @@ async function cmdScratchpad(flags: Record<string, string | boolean>, positional
|
|
|
374
460
|
if (action === "add") {
|
|
375
461
|
if (!text) exitError("--text is required for add", json);
|
|
376
462
|
const ts = nowTimestamp();
|
|
377
|
-
|
|
463
|
+
const safeText = redactSecrets(text!).content;
|
|
464
|
+
items.push({ done: false, text: safeText, meta: `<!-- ${ts} [cli] -->` });
|
|
378
465
|
fs.writeFileSync(spFile, serializeScratchpad(items), "utf-8");
|
|
379
466
|
await ensureQmdAvailableForUpdate();
|
|
380
467
|
scheduleQmdUpdate();
|
|
381
|
-
output(json ? { ok: true, action, text } : `Added: - [ ] ${
|
|
468
|
+
output(json ? { ok: true, action, text: safeText } : `Added: - [ ] ${safeText}`, json);
|
|
382
469
|
return;
|
|
383
470
|
}
|
|
384
471
|
|
|
@@ -539,6 +626,90 @@ function cmdInstallSkills(flags: Record<string, string | boolean>) {
|
|
|
539
626
|
}
|
|
540
627
|
}
|
|
541
628
|
|
|
629
|
+
async function promptYesNo(question: string, defaultYes: boolean): Promise<boolean> {
|
|
630
|
+
const readline = await import("node:readline/promises");
|
|
631
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
632
|
+
try {
|
|
633
|
+
const answer = (await rl.question(`${question} ${defaultYes ? "[Y/n]" : "[y/N]"} `)).trim().toLowerCase();
|
|
634
|
+
if (!answer) return defaultYes;
|
|
635
|
+
return answer === "y" || answer === "yes";
|
|
636
|
+
} finally {
|
|
637
|
+
rl.close();
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
async function cmdInstallHooks(flags: Record<string, string | boolean>): Promise<void> {
|
|
642
|
+
const json = hasFlag(flags, "json");
|
|
643
|
+
const requested = getFlag(flags, "only");
|
|
644
|
+
const requestedKeys = requested ? new Set(requested.split(",").map((value) => value.trim())) : null;
|
|
645
|
+
const { homeDir, targets } = detectHookAgents();
|
|
646
|
+
if (!homeDir) exitError("Home directory not found.", json);
|
|
647
|
+
const eligible = targets.filter(
|
|
648
|
+
(target) => target.supported && target.detected && (!requestedKeys || requestedKeys.has(target.key)),
|
|
649
|
+
);
|
|
650
|
+
const selected = new Set<HookAgentKey>();
|
|
651
|
+
const applyAll = hasFlag(flags, "yes") || hasFlag(flags, "all") || !process.stdin.isTTY;
|
|
652
|
+
for (const target of eligible) {
|
|
653
|
+
if (applyAll || (await promptYesNo(`Install SessionStart hook for ${target.label}?`, true)))
|
|
654
|
+
selected.add(target.key);
|
|
655
|
+
}
|
|
656
|
+
const report = installHooks(selected);
|
|
657
|
+
if (!report.ok) exitError(report.error ?? "install failed", json);
|
|
658
|
+
if (json) return output(report, true);
|
|
659
|
+
if (!report.results.length) return output("No eligible agents. Nothing to install.", false);
|
|
660
|
+
for (const result of report.results) {
|
|
661
|
+
console.log(
|
|
662
|
+
result.installed
|
|
663
|
+
? `Installed ${result.label} hook: ${result.path}`
|
|
664
|
+
: `Skipped ${result.label} (${result.reason ?? "unknown"})`,
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function cmdUninstallHooks(flags: Record<string, string | boolean>): void {
|
|
670
|
+
const json = hasFlag(flags, "json");
|
|
671
|
+
const only = getFlag(flags, "only");
|
|
672
|
+
const agents = only ? new Set(only.split(",").map((value) => value.trim()) as HookAgentKey[]) : undefined;
|
|
673
|
+
const report = uninstallHooks(agents);
|
|
674
|
+
if (!report.ok) exitError(report.error ?? "uninstall failed", json);
|
|
675
|
+
if (json) {
|
|
676
|
+
output(report, true);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
for (const result of report.results) {
|
|
680
|
+
console.log(
|
|
681
|
+
result.installed
|
|
682
|
+
? `Uninstalled ${result.label}: ${result.path}`
|
|
683
|
+
: `Skipped ${result.label} (${result.reason ?? "unknown"})`,
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function cmdCompletion(flags: Record<string, string | boolean>, positional: string[]): void {
|
|
689
|
+
const requestedShell = positional[0];
|
|
690
|
+
const shells: CompletionShell[] = ["bash", "zsh", "fish", "powershell"];
|
|
691
|
+
if (requestedShell && !shells.includes(requestedShell as CompletionShell))
|
|
692
|
+
exitError(
|
|
693
|
+
`Unsupported shell '${requestedShell}'. Choose bash, zsh, fish, or powershell.`,
|
|
694
|
+
hasFlag(flags, "json"),
|
|
695
|
+
);
|
|
696
|
+
const shell = (requestedShell as CompletionShell | undefined) ?? detectCompletionShell();
|
|
697
|
+
if (!shell)
|
|
698
|
+
exitError("Could not detect your shell. Specify bash, zsh, fish, or powershell.", hasFlag(flags, "json"));
|
|
699
|
+
if (hasFlag(flags, "stdout")) {
|
|
700
|
+
process.stdout.write(generateCompletion(shell));
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
const result = installCompletion(shell);
|
|
704
|
+
if (hasFlag(flags, "json")) {
|
|
705
|
+
output(result, true);
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
console.log(`Installed ${shell} completion: ${result.completionPath}`);
|
|
709
|
+
if (result.profilePath)
|
|
710
|
+
console.log(`${result.profileUpdated ? "Configured" : "Already configured"}: ${result.profilePath}`);
|
|
711
|
+
}
|
|
712
|
+
|
|
542
713
|
async function cmdSync(flags: Record<string, string | boolean>) {
|
|
543
714
|
const json = hasFlag(flags, "json");
|
|
544
715
|
|
|
@@ -633,6 +804,18 @@ async function cmdInit(flags: Record<string, string | boolean>) {
|
|
|
633
804
|
console.log(` qmd not found — search features unavailable.`);
|
|
634
805
|
console.log(` Install: bun install -g https://github.com/tobi/qmd`);
|
|
635
806
|
}
|
|
807
|
+
if (process.stdout.isTTY) {
|
|
808
|
+
try {
|
|
809
|
+
const plugin = await createDefaultPluginBootstrap(VERSION).list();
|
|
810
|
+
if (plugin.result === "not_installed") {
|
|
811
|
+
console.log("");
|
|
812
|
+
console.log("Optional: AgentMemory Pro adds session recall and a local Web Console.");
|
|
813
|
+
console.log("Run: agent-memory plugin install");
|
|
814
|
+
}
|
|
815
|
+
} catch {
|
|
816
|
+
// Commercial discovery must never make core initialization fail.
|
|
817
|
+
}
|
|
818
|
+
}
|
|
636
819
|
}
|
|
637
820
|
}
|
|
638
821
|
|
|
@@ -665,15 +848,37 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
|
|
|
665
848
|
const qmdFound = await detectQmd();
|
|
666
849
|
let hasCollection = false;
|
|
667
850
|
let health = null;
|
|
851
|
+
let embeddings: "ready" | "missing" | "unknown" | "n/a" = "n/a";
|
|
668
852
|
if (qmdFound) {
|
|
669
853
|
hasCollection = await checkCollection();
|
|
670
854
|
if (hasCollection) {
|
|
671
855
|
await ensureQmdAvailableForSync();
|
|
672
856
|
health = await getQmdHealth();
|
|
857
|
+
// A live semantic probe confirms embeddings are actually usable, but
|
|
858
|
+
// it costs a real qmd query (and a possible model load), so it's
|
|
859
|
+
// opt-in — the cheap pending-embed count below covers the common case.
|
|
860
|
+
if (hasFlag(flags, "probe")) {
|
|
861
|
+
embeddings = await probeEmbeddings();
|
|
862
|
+
}
|
|
673
863
|
}
|
|
674
864
|
}
|
|
675
865
|
|
|
676
866
|
const embedMode = getQmdEmbedMode();
|
|
867
|
+
let officialPlugin: { installed: boolean; result: string; entitlement: string } = {
|
|
868
|
+
installed: false,
|
|
869
|
+
result: "unavailable",
|
|
870
|
+
entitlement: "missing",
|
|
871
|
+
};
|
|
872
|
+
try {
|
|
873
|
+
const plugin = await createDefaultPluginBootstrap(VERSION).status();
|
|
874
|
+
officialPlugin = {
|
|
875
|
+
installed: Boolean(plugin.bundle),
|
|
876
|
+
result: plugin.result,
|
|
877
|
+
entitlement: plugin.entitlement.state,
|
|
878
|
+
};
|
|
879
|
+
} catch {
|
|
880
|
+
// Commercial status must never make core status fail.
|
|
881
|
+
}
|
|
677
882
|
|
|
678
883
|
if (json) {
|
|
679
884
|
output(
|
|
@@ -695,8 +900,10 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
|
|
|
695
900
|
available: qmdFound,
|
|
696
901
|
collection: hasCollection ? getCollectionName() : null,
|
|
697
902
|
health,
|
|
903
|
+
embeddings,
|
|
698
904
|
},
|
|
699
905
|
embedMode,
|
|
906
|
+
officialPlugin,
|
|
700
907
|
},
|
|
701
908
|
true,
|
|
702
909
|
);
|
|
@@ -725,6 +932,15 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
|
|
|
725
932
|
`Collection '${getCollectionName()}': ${hasCollection ? "configured" : "not configured — run: agent-memory init"}`,
|
|
726
933
|
);
|
|
727
934
|
console.log(`Embed mode: ${embedMode}`);
|
|
935
|
+
if (hasCollection && embeddings !== "n/a") {
|
|
936
|
+
const embLabel =
|
|
937
|
+
embeddings === "ready"
|
|
938
|
+
? "ready"
|
|
939
|
+
: embeddings === "missing"
|
|
940
|
+
? "missing — run: agent-memory sync"
|
|
941
|
+
: "unknown (could not verify within probe timeout)";
|
|
942
|
+
console.log(`Embeddings (semantic/deep search): ${embLabel}`);
|
|
943
|
+
}
|
|
728
944
|
if (health) {
|
|
729
945
|
if (health.totalFiles !== null) console.log(`Files indexed: ${health.totalFiles}`);
|
|
730
946
|
if (health.vectorsEmbedded !== null) console.log(`Vectors embedded: ${health.vectorsEmbedded}`);
|
|
@@ -737,6 +953,11 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
|
|
|
737
953
|
} else {
|
|
738
954
|
console.log("qmd: not installed");
|
|
739
955
|
}
|
|
956
|
+
if (!officialPlugin.installed) {
|
|
957
|
+
console.log("");
|
|
958
|
+
console.log("Optional official plugins: not installed");
|
|
959
|
+
console.log(" run: agent-memory plugin install");
|
|
960
|
+
}
|
|
740
961
|
}
|
|
741
962
|
}
|
|
742
963
|
|
|
@@ -767,6 +988,116 @@ async function cmdDistil(flags: Record<string, string | boolean>) {
|
|
|
767
988
|
}
|
|
768
989
|
}
|
|
769
990
|
|
|
991
|
+
function printPluginUsage(): void {
|
|
992
|
+
console.log(`agent-memory plugin — optional official plugins
|
|
993
|
+
|
|
994
|
+
Usage:
|
|
995
|
+
agent-memory plugin [list]
|
|
996
|
+
agent-memory plugin status
|
|
997
|
+
agent-memory plugin install [--channel stable] [--no-browser]
|
|
998
|
+
agent-memory plugin update [--channel stable]
|
|
999
|
+
agent-memory plugin uninstall --yes
|
|
1000
|
+
agent-memory plugin manage [--no-browser]
|
|
1001
|
+
|
|
1002
|
+
The public core remains fully usable without AgentMemory Pro. Interactive install
|
|
1003
|
+
opens a loopback website for temporary email activation and unlimited local use.
|
|
1004
|
+
Authentication and payment will be added later.`);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function pluginCommandFailure(command: string, error: unknown): PluginBootstrapResultV1 {
|
|
1008
|
+
return {
|
|
1009
|
+
schemaVersion: 1,
|
|
1010
|
+
command: `plugin.${command}`,
|
|
1011
|
+
ok: false,
|
|
1012
|
+
result: "unavailable",
|
|
1013
|
+
bundle: null,
|
|
1014
|
+
entitlement: {
|
|
1015
|
+
plan: null,
|
|
1016
|
+
state: "missing",
|
|
1017
|
+
features: [],
|
|
1018
|
+
capabilities: {},
|
|
1019
|
+
},
|
|
1020
|
+
nextAction: null,
|
|
1021
|
+
error: {
|
|
1022
|
+
code: error instanceof PluginBootstrapFailure ? error.code : "plugin_command_failed",
|
|
1023
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1024
|
+
...(error instanceof PluginBootstrapFailure && error.retryable ? { retryable: true } : {}),
|
|
1025
|
+
},
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
async function cmdPlugin(flags: Record<string, string | boolean>, positional: string[]): Promise<void> {
|
|
1030
|
+
const json = hasFlag(flags, "json");
|
|
1031
|
+
const subcommand = positional[0] ?? "list";
|
|
1032
|
+
if (subcommand === "help" || hasFlag(flags, "help")) {
|
|
1033
|
+
printPluginUsage();
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
const channel = getFlag(flags, "channel") ?? "stable";
|
|
1037
|
+
if (channel !== "stable") {
|
|
1038
|
+
printPluginResult(
|
|
1039
|
+
pluginCommandFailure(
|
|
1040
|
+
subcommand,
|
|
1041
|
+
new PluginBootstrapFailure("channel_invalid", "--channel supports only 'stable'"),
|
|
1042
|
+
),
|
|
1043
|
+
json,
|
|
1044
|
+
false,
|
|
1045
|
+
);
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
const allowBrowser = !json && !hasFlag(flags, "no-browser") && Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
1049
|
+
const manager = createDefaultPluginBootstrap(VERSION);
|
|
1050
|
+
|
|
1051
|
+
let result: PluginBootstrapResultV1;
|
|
1052
|
+
try {
|
|
1053
|
+
switch (subcommand) {
|
|
1054
|
+
case "list":
|
|
1055
|
+
result = await manager.list();
|
|
1056
|
+
break;
|
|
1057
|
+
case "status":
|
|
1058
|
+
result = await manager.status(channel);
|
|
1059
|
+
break;
|
|
1060
|
+
case "install":
|
|
1061
|
+
result = await manager.install({ channel, allowAuthentication: allowBrowser });
|
|
1062
|
+
break;
|
|
1063
|
+
case "update":
|
|
1064
|
+
result = await manager.update({ channel, allowAuthentication: false });
|
|
1065
|
+
break;
|
|
1066
|
+
case "uninstall":
|
|
1067
|
+
if (!hasFlag(flags, "yes")) {
|
|
1068
|
+
const status = await manager.status(channel);
|
|
1069
|
+
result = {
|
|
1070
|
+
...status,
|
|
1071
|
+
command: "plugin.uninstall",
|
|
1072
|
+
ok: false,
|
|
1073
|
+
result: "unavailable",
|
|
1074
|
+
error: {
|
|
1075
|
+
code: "confirmation_required",
|
|
1076
|
+
message: "Re-run with --yes to remove AgentMemory Pro executable components",
|
|
1077
|
+
},
|
|
1078
|
+
};
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
result = await manager.uninstall();
|
|
1082
|
+
break;
|
|
1083
|
+
case "manage":
|
|
1084
|
+
result = await manager.manage();
|
|
1085
|
+
break;
|
|
1086
|
+
default:
|
|
1087
|
+
result = pluginCommandFailure(
|
|
1088
|
+
subcommand,
|
|
1089
|
+
new PluginBootstrapFailure(
|
|
1090
|
+
"unknown_plugin_command",
|
|
1091
|
+
`Unknown plugin command: ${subcommand}. Available bootstrap commands: list, status, install, update, uninstall, manage.`,
|
|
1092
|
+
),
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
result = pluginCommandFailure(subcommand, error);
|
|
1097
|
+
}
|
|
1098
|
+
printPluginResult(result, json, allowBrowser && (subcommand === "install" || subcommand === "manage"));
|
|
1099
|
+
}
|
|
1100
|
+
|
|
770
1101
|
// ---------------------------------------------------------------------------
|
|
771
1102
|
// Usage
|
|
772
1103
|
// ---------------------------------------------------------------------------
|
|
@@ -781,15 +1112,19 @@ Commands:
|
|
|
781
1112
|
version Show binary version
|
|
782
1113
|
install-skills Install (or --uninstall) bundled skills
|
|
783
1114
|
uninstall-skills Uninstall bundled skills
|
|
784
|
-
context Build
|
|
785
|
-
write Write to memory files (default: daily)
|
|
1115
|
+
context Build context; optionally retrieve memories with --query
|
|
1116
|
+
write Write to memory files (default: daily; optional --source-uri)
|
|
786
1117
|
read Read memory files
|
|
787
1118
|
scratchpad Manage checklist items
|
|
788
1119
|
search Search across memory files (requires qmd)
|
|
789
1120
|
distil Generate compact MEMORY.md index from daily logs + topics
|
|
790
1121
|
sync Re-index and embed all files (requires qmd)
|
|
791
1122
|
init Initialize memory directory and qmd collection
|
|
792
|
-
status Show configuration and status
|
|
1123
|
+
status Show configuration and status (--probe for a live embeddings check)
|
|
1124
|
+
completion Install or print shell completion
|
|
1125
|
+
install-hooks Install managed SessionStart hooks
|
|
1126
|
+
uninstall-hooks Remove only managed SessionStart hooks
|
|
1127
|
+
plugin Discover, install, update, or remove optional official plugins
|
|
793
1128
|
|
|
794
1129
|
Global flags:
|
|
795
1130
|
--dir <path> Override memory directory
|
|
@@ -798,7 +1133,7 @@ Global flags:
|
|
|
798
1133
|
Examples:
|
|
799
1134
|
agent-memory init
|
|
800
1135
|
agent-memory write --content "Fixed auth bug in login flow"
|
|
801
|
-
agent-memory write --target long_term --content "User prefers dark mode"
|
|
1136
|
+
agent-memory write --target long_term --content "User prefers dark mode" --source-uri "session://agent/turn/12"
|
|
802
1137
|
agent-memory write --target topic --topic "auth" --content "Rolled JWT refresh to edge"
|
|
803
1138
|
agent-memory read --target long_term
|
|
804
1139
|
agent-memory read --target daily --date 2026-02-15
|
|
@@ -810,9 +1145,13 @@ Examples:
|
|
|
810
1145
|
agent-memory scratchpad done --text "PR #42"
|
|
811
1146
|
agent-memory search --query "database choice" --mode keyword
|
|
812
1147
|
agent-memory distil --dry-run
|
|
813
|
-
agent-memory context --
|
|
1148
|
+
agent-memory context --query "database choice"
|
|
814
1149
|
agent-memory sync
|
|
815
|
-
agent-memory status --json
|
|
1150
|
+
agent-memory status --json
|
|
1151
|
+
agent-memory completion zsh
|
|
1152
|
+
agent-memory install-hooks --yes
|
|
1153
|
+
agent-memory plugin status
|
|
1154
|
+
agent-memory plugin install`);
|
|
816
1155
|
}
|
|
817
1156
|
|
|
818
1157
|
// ---------------------------------------------------------------------------
|
|
@@ -834,7 +1173,7 @@ async function main() {
|
|
|
834
1173
|
return;
|
|
835
1174
|
}
|
|
836
1175
|
|
|
837
|
-
if (!command || command === "help" || hasFlag(flags, "help")) {
|
|
1176
|
+
if (!command || command === "help" || (hasFlag(flags, "help") && command !== "plugin")) {
|
|
838
1177
|
printUsage();
|
|
839
1178
|
return;
|
|
840
1179
|
}
|
|
@@ -874,8 +1213,42 @@ async function main() {
|
|
|
874
1213
|
case "status":
|
|
875
1214
|
await cmdStatus(flags);
|
|
876
1215
|
break;
|
|
877
|
-
|
|
878
|
-
|
|
1216
|
+
case "completion":
|
|
1217
|
+
cmdCompletion(flags, positional);
|
|
1218
|
+
break;
|
|
1219
|
+
case "install-hooks":
|
|
1220
|
+
await cmdInstallHooks(flags);
|
|
1221
|
+
break;
|
|
1222
|
+
case "uninstall-hooks":
|
|
1223
|
+
cmdUninstallHooks(flags);
|
|
1224
|
+
break;
|
|
1225
|
+
case "hook": {
|
|
1226
|
+
if (positional[0] !== "session-start") exitError("hook requires 'session-start'", json);
|
|
1227
|
+
const agent = getFlag(flags, "agent");
|
|
1228
|
+
if (!agent) exitError("hook session-start requires --agent", json);
|
|
1229
|
+
await cmdContext({ "no-search": true });
|
|
1230
|
+
break;
|
|
1231
|
+
}
|
|
1232
|
+
case "plugin":
|
|
1233
|
+
await cmdPlugin(flags, positional);
|
|
1234
|
+
break;
|
|
1235
|
+
default: {
|
|
1236
|
+
const controller = new AbortController();
|
|
1237
|
+
const abort = () => controller.abort();
|
|
1238
|
+
process.once("SIGINT", abort);
|
|
1239
|
+
try {
|
|
1240
|
+
const result = await new InstalledPluginRuntimeV1({ coreVersion: VERSION }).run(command, {
|
|
1241
|
+
args: positional,
|
|
1242
|
+
flags,
|
|
1243
|
+
signal: controller.signal,
|
|
1244
|
+
});
|
|
1245
|
+
if (!result) exitError(`Unknown command: ${command}. Run 'agent-memory help' for usage.`, json);
|
|
1246
|
+
if (!result.ok) exitError(result.error?.message ?? `Plugin command ${command} failed`, json);
|
|
1247
|
+
output(result.data ?? { ok: true }, json);
|
|
1248
|
+
} finally {
|
|
1249
|
+
process.removeListener("SIGINT", abort);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
879
1252
|
}
|
|
880
1253
|
}
|
|
881
1254
|
|