myagentmemory 0.4.15 → 0.4.17

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/src/cli.ts CHANGED
@@ -25,6 +25,7 @@
25
25
  import { spawn } from "node:child_process";
26
26
  import * as fs from "node:fs";
27
27
 
28
+ import { COMMAND_DESCRIPTIONS, COMMANDS } from "./cli-spec.js";
28
29
  import { type CompletionShell, detectCompletionShell, generateCompletion, installCompletion } from "./completions.js";
29
30
 
30
31
  import {
@@ -73,6 +74,7 @@ import {
73
74
  PluginBootstrapFailure,
74
75
  type PluginBootstrapResultV1,
75
76
  } from "./plugin-bootstrap.js";
77
+ import type { PluginContextSectionV1 } from "./plugin-host.js";
76
78
  import { InstalledPluginRuntimeV1 } from "./plugin-runtime.js";
77
79
 
78
80
  declare const __VERSION__: string;
@@ -186,21 +188,23 @@ function openExternalUrl(url: string): boolean {
186
188
 
187
189
  function printProOverview(installed: boolean): void {
188
190
  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.");
191
+ console.log("Core remembers what you save. Pro learns from what you do.");
193
192
  console.log("");
194
- console.log("Your session content stays on this device.");
193
+ console.log("AgentMemory Pro:");
194
+ console.log(" Recall coding history Find decisions and context across Pi, Codex, and Claude Code sessions.");
195
+ console.log(" Learn from corrections Turn repeated fixes into reviewable, reversible memory.");
196
+ console.log(" See and control learning Inspect what AgentMemory remembers and why in the Memory Dashboard.");
197
+ console.log("");
198
+ console.log("No account is required for the free preview. Your coding history stays on this device.");
195
199
  console.log("");
196
200
  if (installed) {
197
201
  console.log("Try it:");
198
202
  console.log(' agent-memory recall "what did we decide about authentication?"');
199
203
  console.log(" agent-memory learn");
200
- console.log(" agent-memory web");
204
+ console.log(" agent-memory dashboard");
201
205
  } else {
202
- console.log("Start your Pro beta:");
203
- console.log(" agent-memory plugin install");
206
+ console.log("Start your free Pro preview:");
207
+ console.log(" agent-memory pro install");
204
208
  }
205
209
  }
206
210
 
@@ -241,12 +245,10 @@ function printPluginResult(result: PluginBootstrapResultV1, json: boolean, allow
241
245
  break;
242
246
  case "not_installed":
243
247
  console.log("AgentMemory Pro is not installed.");
244
- console.log("Run: agent-memory plugin install");
248
+ console.log("Run: agent-memory pro install");
245
249
  break;
246
250
  case "auth_required":
247
- console.log(
248
- "Run this command in an interactive terminal to enter an email and activate free daily access.",
249
- );
251
+ console.log("Run agent-memory pro install to activate the free preview.");
250
252
  break;
251
253
  case "renewal_required":
252
254
  console.log("Renew AgentMemory Pro to continue using paid capabilities.");
@@ -280,10 +282,23 @@ async function cmdContext(flags: Record<string, string | boolean>) {
280
282
  ensureDirs();
281
283
  if (!noSearch && query) await ensureQmdAvailableForSync();
282
284
  const searchResults = noSearch ? "" : await searchRelevantMemories(query);
283
- const context = buildMemoryContext(searchResults);
285
+ const coreContext = buildMemoryContext(searchResults);
286
+ let pluginSections: PluginContextSectionV1[] = [];
287
+ try {
288
+ pluginSections = await new InstalledPluginRuntimeV1({ coreVersion: VERSION }).provideContext({
289
+ host: "agent-memory-cli",
290
+ cwd: process.cwd(),
291
+ query: query || undefined,
292
+ signal: new AbortController().signal,
293
+ });
294
+ } catch {
295
+ // Optional Pro context must never make public-core context unavailable.
296
+ }
297
+ const pluginContext = pluginSections.map((section) => `${section.label}\n\n${section.content}`).join("\n\n");
298
+ const context = [coreContext, pluginContext].filter(Boolean).join("\n\n");
284
299
 
285
300
  if (json) {
286
- output({ context, directory: getMemoryDir() }, true);
301
+ output({ context, directory: getMemoryDir(), ...(pluginSections.length ? { pluginSections } : {}) }, true);
287
302
  } else {
288
303
  if (context) {
289
304
  process.stdout.write(context);
@@ -811,8 +826,8 @@ async function cmdInit(flags: Record<string, string | boolean>) {
811
826
  const plugin = await createDefaultPluginBootstrap(VERSION).list();
812
827
  if (plugin.result === "not_installed") {
813
828
  console.log("");
814
- console.log("Optional: AgentMemory Pro adds session recall and a local Web Console.");
815
- console.log("Run: agent-memory plugin install");
829
+ console.log("Optional: Pro recalls coding history and learns from repeated corrections.");
830
+ console.log("Try it without an account: agent-memory pro install");
816
831
  }
817
832
  } catch {
818
833
  // Commercial discovery must never make core initialization fail.
@@ -957,8 +972,8 @@ async function cmdStatus(flags: Record<string, string | boolean>) {
957
972
  }
958
973
  if (!officialPlugin.installed) {
959
974
  console.log("");
960
- console.log("Optional official plugins: not installed");
961
- console.log(" run: agent-memory plugin install");
975
+ console.log("AgentMemory Pro: not installed");
976
+ console.log(" try without an account: agent-memory pro install");
962
977
  }
963
978
  }
964
979
  }
@@ -1001,9 +1016,10 @@ Usage:
1001
1016
  agent-memory plugin uninstall --yes
1002
1017
  agent-memory plugin manage [--no-browser]
1003
1018
 
1004
- The public core remains fully usable without AgentMemory Pro. Interactive install
1005
- opens a loopback website for email activation and a configurable free daily
1006
- agent-session allowance. Memory and session content stay on this device.`);
1019
+ The public core remains fully usable without AgentMemory Pro. Install uses a random
1020
+ installation identifier and requires no account or email. The free preview includes
1021
+ 10 recalls and one learning scan per local day; indexing and the Memory Dashboard
1022
+ remain available. Memory and session content stay on this device.`);
1007
1023
  }
1008
1024
 
1009
1025
  function pluginCommandFailure(command: string, error: unknown): PluginBootstrapResultV1 {
@@ -1028,24 +1044,24 @@ function pluginCommandFailure(command: string, error: unknown): PluginBootstrapR
1028
1044
  };
1029
1045
  }
1030
1046
 
1031
- async function cmdPlugin(flags: Record<string, string | boolean>, positional: string[]): Promise<void> {
1047
+ async function cmdPlugin(
1048
+ flags: Record<string, string | boolean>,
1049
+ positional: string[],
1050
+ ): Promise<PluginBootstrapResultV1 | null> {
1032
1051
  const json = hasFlag(flags, "json");
1033
1052
  const subcommand = positional[0] ?? "list";
1034
1053
  if (subcommand === "help" || hasFlag(flags, "help")) {
1035
1054
  printPluginUsage();
1036
- return;
1055
+ return null;
1037
1056
  }
1038
1057
  const channel = getFlag(flags, "channel") ?? "stable";
1039
1058
  if (channel !== "stable") {
1040
- printPluginResult(
1041
- pluginCommandFailure(
1042
- subcommand,
1043
- new PluginBootstrapFailure("channel_invalid", "--channel supports only 'stable'"),
1044
- ),
1045
- json,
1046
- false,
1059
+ const failure = pluginCommandFailure(
1060
+ subcommand,
1061
+ new PluginBootstrapFailure("channel_invalid", "--channel supports only 'stable'"),
1047
1062
  );
1048
- return;
1063
+ printPluginResult(failure, json, false);
1064
+ return failure;
1049
1065
  }
1050
1066
  const allowBrowser = !json && !hasFlag(flags, "no-browser") && Boolean(process.stdin.isTTY && process.stdout.isTTY);
1051
1067
  const manager = createDefaultPluginBootstrap(VERSION);
@@ -1098,6 +1114,55 @@ async function cmdPlugin(flags: Record<string, string | boolean>, positional: st
1098
1114
  result = pluginCommandFailure(subcommand, error);
1099
1115
  }
1100
1116
  printPluginResult(result, json, allowBrowser && (subcommand === "install" || subcommand === "manage"));
1117
+ return result;
1118
+ }
1119
+
1120
+ async function printFirstRunProof(): Promise<void> {
1121
+ try {
1122
+ const result = await new InstalledPluginRuntimeV1({ coreVersion: VERSION }).run("index", {
1123
+ args: [],
1124
+ flags: {},
1125
+ signal: new AbortController().signal,
1126
+ });
1127
+ if (!result?.ok || !result.data || typeof result.data !== "object") return;
1128
+ const stats = (result.data as { stats?: { discovered?: Record<string, number>; selected?: number } }).stats;
1129
+ if (!stats?.discovered) return;
1130
+ const hosts = [
1131
+ ["Claude Code", stats.discovered.claude ?? 0],
1132
+ ["Codex", stats.discovered.codex ?? 0],
1133
+ ["Pi", stats.discovered.pi ?? 0],
1134
+ ] as const;
1135
+ console.log("");
1136
+ console.log("Found local coding history:");
1137
+ for (const [label, count] of hosts) console.log(` ${label.padEnd(13)} ${count} sessions`);
1138
+ console.log("");
1139
+ console.log(`${stats.selected ?? 0} sessions available for local recall. Nothing was uploaded.`);
1140
+ console.log("");
1141
+ console.log('Try: agent-memory recall "what did we decide about authentication?"');
1142
+ console.log("Open: agent-memory dashboard");
1143
+ } catch {
1144
+ // Personalized proof is helpful but must never turn a successful install into a failure.
1145
+ }
1146
+ }
1147
+
1148
+ async function cmdPro(flags: Record<string, string | boolean>, positional: string[]): Promise<void> {
1149
+ const subcommand = positional[0];
1150
+ if (!subcommand) {
1151
+ await cmdPlugin(flags, ["list"]);
1152
+ return;
1153
+ }
1154
+ const mapped = subcommand === "upgrade" ? "update" : subcommand;
1155
+ if (!["install", "status", "update", "manage"].includes(mapped)) {
1156
+ const json = hasFlag(flags, "json");
1157
+ const message = `Unknown Pro command: ${subcommand}. Available commands: install, status, upgrade, manage.`;
1158
+ if (json) console.log(JSON.stringify({ error: message }));
1159
+ else console.error(`Error: ${message}`);
1160
+ process.exitCode = 1;
1161
+ return;
1162
+ }
1163
+ const result = await cmdPlugin(flags, [mapped]);
1164
+ if (!hasFlag(flags, "json") && mapped === "install" && ["installed", "upgraded"].includes(result?.result ?? ""))
1165
+ await printFirstRunProof();
1101
1166
  }
1102
1167
 
1103
1168
  // ---------------------------------------------------------------------------
@@ -1105,28 +1170,18 @@ async function cmdPlugin(flags: Record<string, string | boolean>, positional: st
1105
1170
  // ---------------------------------------------------------------------------
1106
1171
 
1107
1172
  function printUsage() {
1173
+ const commandWidth = Math.max(...COMMANDS.map((command) => command.length));
1174
+ const commandList = COMMANDS.map(
1175
+ (command) => ` ${command.padEnd(commandWidth)} ${COMMAND_DESCRIPTIONS[command]}`,
1176
+ ).join("\n");
1177
+
1108
1178
  console.log(`agent-memory — persistent memory for coding agents
1109
1179
 
1110
1180
  Usage:
1111
1181
  agent-memory <command> [options]
1112
1182
 
1113
1183
  Commands:
1114
- version Show binary version
1115
- install-skills Install (or --uninstall) bundled skills
1116
- uninstall-skills Uninstall bundled skills
1117
- context Build context; optionally retrieve memories with --query
1118
- write Write to memory files (default: daily; optional --source-uri)
1119
- read Read memory files
1120
- scratchpad Manage checklist items
1121
- search Search across memory files (requires qmd)
1122
- distil Generate compact MEMORY.md index from daily logs + topics
1123
- sync Re-index and embed all files (requires qmd)
1124
- init Initialize memory directory and qmd collection
1125
- status Show configuration and status (--probe for a live embeddings check)
1126
- completion Install or print shell completion
1127
- install-hooks Install managed SessionStart hooks
1128
- uninstall-hooks Remove only managed SessionStart hooks
1129
- plugin Discover, install, update, or remove optional official plugins
1184
+ ${commandList}
1130
1185
 
1131
1186
  Global flags:
1132
1187
  --dir <path> Override memory directory
@@ -1152,8 +1207,10 @@ Examples:
1152
1207
  agent-memory status --json
1153
1208
  agent-memory completion zsh
1154
1209
  agent-memory install-hooks --yes
1155
- agent-memory plugin status
1156
- agent-memory plugin install`);
1210
+ agent-memory pro status
1211
+ agent-memory pro install
1212
+ agent-memory recall "what did we decide about authentication?"
1213
+ agent-memory dashboard`);
1157
1214
  }
1158
1215
 
1159
1216
  // ---------------------------------------------------------------------------
@@ -1245,18 +1302,22 @@ async function main() {
1245
1302
  case "plugin":
1246
1303
  await cmdPlugin(flags, positional);
1247
1304
  break;
1305
+ case "pro":
1306
+ await cmdPro(flags, positional);
1307
+ break;
1248
1308
  default: {
1249
1309
  const controller = new AbortController();
1250
1310
  const abort = () => controller.abort();
1251
1311
  process.once("SIGINT", abort);
1252
1312
  try {
1253
- const result = await new InstalledPluginRuntimeV1({ coreVersion: VERSION }).run(command, {
1313
+ const pluginCommand = command === "dashboard" ? "web" : command;
1314
+ const result = await new InstalledPluginRuntimeV1({ coreVersion: VERSION }).run(pluginCommand, {
1254
1315
  args: positional,
1255
1316
  flags,
1256
1317
  signal: controller.signal,
1257
1318
  });
1258
1319
  if (!result) exitError(`Unknown command: ${command}. Run 'agent-memory help' for usage.`, json);
1259
- if (!result.ok) exitError(result.error?.message ?? `Plugin command ${command} failed`, json);
1320
+ if (!result.ok) exitError(result.error?.message ?? `Plugin command ${pluginCommand} failed`, json);
1260
1321
  output(result.data ?? { ok: true }, json);
1261
1322
  } finally {
1262
1323
  process.removeListener("SIGINT", abort);
@@ -195,8 +195,8 @@ const MISSING_ENTITLEMENT: PluginEntitlementStatusV1 = {
195
195
  };
196
196
 
197
197
  const OFFICIAL_PLUGINS = [
198
- { id: OFFICIAL_PLUGIN_IDS[0], name: "Session Intelligence" },
199
- { id: OFFICIAL_PLUGIN_IDS[1], name: "Web Console" },
198
+ { id: OFFICIAL_PLUGIN_IDS[0], name: "Coding History Recall" },
199
+ { id: OFFICIAL_PLUGIN_IDS[1], name: "Memory Dashboard" },
200
200
  ] as const;
201
201
 
202
202
  const PACKAGE_MAX_BYTES = 64 * 1024 * 1024;
@@ -131,11 +131,31 @@ export interface PluginStructuredErrorV1 {
131
131
  retryable?: boolean;
132
132
  }
133
133
 
134
+ export interface PluginContextSectionV1 {
135
+ id: string;
136
+ label: string;
137
+ content: string;
138
+ artifactPath?: string;
139
+ metadata?: Record<string, unknown>;
140
+ }
141
+
142
+ export interface PluginContextProviderV1 {
143
+ name: string;
144
+ requiredCapability: string;
145
+ provide(context: {
146
+ host: string;
147
+ cwd?: string;
148
+ query?: string;
149
+ signal: AbortSignal;
150
+ }): Promise<PluginContextSectionV1[]>;
151
+ }
152
+
134
153
  export interface AgentMemoryPluginHostV1 {
135
154
  apiVersion: 1;
136
155
  coreVersion: string;
137
156
  registerCommand(command: PluginCommandV1): void;
138
157
  registerSessionStartHook(hook: PluginSessionStartHookV1): void;
158
+ registerContextProvider?(provider: PluginContextProviderV1): void;
139
159
  getStateDirectory(): string;
140
160
  getMemoryDirectory(): string;
141
161
  getEntitlement(): Promise<PluginEntitlementStatusV1>;
@@ -23,6 +23,8 @@ import {
23
23
  type PluginCommandContextV1,
24
24
  type PluginCommandResultV1,
25
25
  type PluginCommandV1,
26
+ type PluginContextProviderV1,
27
+ type PluginContextSectionV1,
26
28
  type PluginEntitlementStatusV1,
27
29
  type PluginMemoryCorrectionV1,
28
30
  type PluginMemoryWriteV1,
@@ -38,6 +40,11 @@ interface RegisteredCommand {
38
40
  pluginId: string;
39
41
  }
40
42
 
43
+ interface RegisteredContextProvider {
44
+ provider: PluginContextProviderV1;
45
+ pluginId: string;
46
+ }
47
+
41
48
  export interface PluginRuntimeOptionsV1 {
42
49
  coreVersion: string;
43
50
  store?: PluginInstallStoreV1;
@@ -130,6 +137,7 @@ export class InstalledPluginRuntimeV1 {
130
137
  private readonly backend: PluginBootstrapBackendV1;
131
138
  private readonly commands = new Map<string, RegisteredCommand>();
132
139
  private readonly hooks: PluginSessionStartHookV1[] = [];
140
+ private readonly contextProviders: RegisteredContextProvider[] = [];
133
141
  private loaded = false;
134
142
 
135
143
  constructor(private readonly options: PluginRuntimeOptionsV1) {
@@ -202,6 +210,49 @@ export class InstalledPluginRuntimeV1 {
202
210
  }
203
211
  }
204
212
 
213
+ async provideContext(context: {
214
+ host: string;
215
+ cwd?: string;
216
+ query?: string;
217
+ signal: AbortSignal;
218
+ }): Promise<PluginContextSectionV1[]> {
219
+ if (!(await this.load()) || this.contextProviders.length === 0) return [];
220
+ const entitlement = await this.refreshEntitlement();
221
+ const sections: PluginContextSectionV1[] = [];
222
+ for (const registered of this.contextProviders) {
223
+ if (!isPluginCapabilityEnabled(entitlement, registered.provider.requiredCapability)) continue;
224
+ const provided = await registered.provider.provide(context);
225
+ if (!Array.isArray(provided) || provided.length > 16)
226
+ throw new PluginBootstrapFailure(
227
+ "plugin_context_invalid",
228
+ `Plugin ${registered.pluginId} returned invalid context sections`,
229
+ );
230
+ for (const section of provided) {
231
+ if (
232
+ !section ||
233
+ typeof section.id !== "string" ||
234
+ section.id.length === 0 ||
235
+ section.id.length > 256 ||
236
+ typeof section.label !== "string" ||
237
+ section.label.length === 0 ||
238
+ section.label.length > 256 ||
239
+ typeof section.content !== "string" ||
240
+ Buffer.byteLength(section.content, "utf-8") > 64 * 1024 ||
241
+ (section.artifactPath !== undefined &&
242
+ (typeof section.artifactPath !== "string" || section.artifactPath.length > 4_096)) ||
243
+ (section.metadata !== undefined &&
244
+ (!section.metadata || typeof section.metadata !== "object" || Array.isArray(section.metadata)))
245
+ )
246
+ throw new PluginBootstrapFailure(
247
+ "plugin_context_invalid",
248
+ `Plugin ${registered.pluginId} returned an invalid context section`,
249
+ );
250
+ sections.push(structuredClone(section));
251
+ }
252
+ }
253
+ return sections;
254
+ }
255
+
205
256
  private createHost(manifest: AgentMemoryPluginManifestV1): AgentMemoryPluginHostV1 {
206
257
  const descriptors = new Map(manifest.commands.map((command) => [command.name, command]));
207
258
  const stateRoot = path.join(this.store.root, "state");
@@ -242,6 +293,19 @@ export class InstalledPluginRuntimeV1 {
242
293
  );
243
294
  this.hooks.push(hook);
244
295
  },
296
+ registerContextProvider: (provider) => {
297
+ if (!(manifest.capabilities ?? []).includes(provider.requiredCapability))
298
+ throw new PluginBootstrapFailure(
299
+ "plugin_context_invalid",
300
+ `Plugin ${manifest.id} registered a context provider with an undeclared capability`,
301
+ );
302
+ if (!provider.name || this.contextProviders.some((item) => item.provider.name === provider.name))
303
+ throw new PluginBootstrapFailure(
304
+ "plugin_context_invalid",
305
+ `Plugin context provider ${provider.name || "(unnamed)"} is invalid or already registered`,
306
+ );
307
+ this.contextProviders.push({ provider, pluginId: manifest.id });
308
+ },
245
309
  getStateDirectory: () => stateDirectory,
246
310
  getMemoryDirectory: () => {
247
311
  assertPermission(manifest, "memory:read");