myagentmemory 0.5.2 → 0.5.4
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 +10 -7
- package/dist/cli-spec.d.ts +1 -1
- package/dist/cli-spec.js +16 -4
- package/dist/cli.js +466 -65
- package/dist/completions.d.ts +12 -0
- package/dist/completions.js +51 -0
- package/dist/core.js +11 -0
- package/dist/hooks.d.ts +15 -1
- package/dist/hooks.js +190 -5
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +15 -0
- package/dist/plugin-host.d.ts +7 -1
- package/dist/plugin-runtime.d.ts +2 -0
- package/dist/plugin-runtime.js +13 -1
- package/dist/plugin-service.d.ts +1 -5
- package/dist/plugin-service.js +16 -64
- package/dist/upgrade.d.ts +60 -7
- package/dist/upgrade.js +146 -15
- package/docs/official-plugin-bootstrap.md +3 -4
- package/package.json +1 -2
- package/scripts/install-skills.sh +4 -1
- package/skills/agent/SKILL.md +1 -1
- package/skills/claude-code/SKILL.md +1 -1
- package/skills/codex/SKILL.md +1 -1
- package/skills/cursor/SKILL.md +1 -1
- package/skills/qoder/SKILL.md +164 -0
- package/src/cli-spec.ts +19 -4
- package/src/completions.ts +73 -0
- package/src/core.ts +11 -0
- package/src/hooks.ts +199 -6
- package/src/plugin-host.ts +7 -1
package/dist/cli.js
CHANGED
|
@@ -26,13 +26,13 @@ import * as fs from "node:fs";
|
|
|
26
26
|
import * as os from "node:os";
|
|
27
27
|
import * as path from "node:path";
|
|
28
28
|
import { COMMAND_DESCRIPTIONS, COMMAND_OPTIONS, COMMANDS, GLOBAL_OPTIONS, optionTakesValue, PLUGIN_COMMAND_OPTIONS, renderCommandHelp, SCRATCHPAD_ACTION_OPTIONS, } from "./cli-spec.js";
|
|
29
|
-
import { detectCompletionShell, generateCompletion, installCompletion } from "./completions.js";
|
|
29
|
+
import { detectCompletionShell, generateCompletion, installCompletion, uninstallCompletion, } from "./completions.js";
|
|
30
30
|
import { _setBaseDir, buildDynamicContext, buildMemoryContext, buildStableContext, checkCollection, dailyPath, detectQmd, distilMemories, ensureDirs, ensureQmdAvailableForSync, ensureQmdAvailableForUpdate, getCollectionName, getDailyDir, getMemoryDir, getMemoryFile, getQmdEmbedMode, getQmdHealth, getQmdResultPath, getQmdResultText, getScratchpadFile, getTopicsDir, installSkills, memoryWrite, nowTimestamp, parseScratchpad, probeEmbeddings, readFileSafe, readHookMode, redactSecrets, runQmdEmbedDetached, runQmdSearch, runQmdSync, runQmdUpdateNow, scheduleQmdUpdate, scratchpadAction, searchRelevantMemories, serializeScratchpad, setupQmdCollection, slugifyTopic, todayStr, topicPath, uninstallSkills, } from "./core.js";
|
|
31
|
-
import { detectHookAgents, installHooks, isHookInstalled, isStopHookInstalled, isUserPromptSubmitInstalled, uninstallHooks, } from "./hooks.js";
|
|
31
|
+
import { detectHookAgents, getPiMemoryState, installHooks, isHookInstalled, isStopHookInstalled, isUserPromptSubmitInstalled, uninstallHooks, } from "./hooks.js";
|
|
32
32
|
import { StdioMcpServer } from "./mcp-server.js";
|
|
33
|
-
import { createDefaultPluginBootstrap, PluginBootstrapFailure, } from "./plugin-bootstrap.js";
|
|
33
|
+
import { createDefaultPluginBootstrap, getDefaultPluginInstallRoot, PluginBootstrapFailure, } from "./plugin-bootstrap.js";
|
|
34
34
|
import { InstalledPluginRuntimeV1 } from "./plugin-runtime.js";
|
|
35
|
-
import { checkForUpgrades, detectInstallMethod, formatUpgradeNotice, isCacheFresh, readUpgradeCache, refreshUpgradeCacheBackground, runInstaller, } from "./upgrade.js";
|
|
35
|
+
import { checkForUpgrades, detectInstallMethod, formatUpgradeNotice, isCacheFresh, readUpgradeCache, readUpgradePolicy, refreshUpgradeCacheBackground, runInstaller, writeUpgradeCache, writeUpgradePolicy, } from "./upgrade.js";
|
|
36
36
|
function readPackageVersion() {
|
|
37
37
|
try {
|
|
38
38
|
const packageJson = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
@@ -124,10 +124,6 @@ function levenshtein(a, b) {
|
|
|
124
124
|
}
|
|
125
125
|
return prev[b.length];
|
|
126
126
|
}
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
// Pro plan / cap-exhausted UX
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
|
-
const UPGRADE_URL = "https://agentmemory.paperpilot.me/upgrade";
|
|
131
127
|
function detectCapExhausted(result) {
|
|
132
128
|
if (result.ok !== false)
|
|
133
129
|
return null;
|
|
@@ -194,13 +190,11 @@ function printCapExhaustedBox(command, info) {
|
|
|
194
190
|
"─────────────────────────────────────────────────────────────",
|
|
195
191
|
usedLine,
|
|
196
192
|
"",
|
|
197
|
-
"
|
|
198
|
-
` ${UPGRADE_URL}`,
|
|
193
|
+
" Paid plans are not available yet. Try again after the free-preview allowance resets.",
|
|
199
194
|
"─────────────────────────────────────────────────────────────",
|
|
200
195
|
"",
|
|
201
196
|
];
|
|
202
197
|
console.error(lines.join("\n"));
|
|
203
|
-
openExternalUrl(UPGRADE_URL);
|
|
204
198
|
}
|
|
205
199
|
// Persist the last usage decision to disk so `pro status` can show counters.
|
|
206
200
|
function cacheProUsage(decision) {
|
|
@@ -394,9 +388,9 @@ function printProOverview(installed) {
|
|
|
394
388
|
console.log("Core remembers what you save. Pro learns from what you do.");
|
|
395
389
|
console.log("");
|
|
396
390
|
console.log("AgentMemory Pro:");
|
|
397
|
-
console.log(' Remember past sessions Ask "what did we decide about auth?" across Claude Code, Codex, and
|
|
391
|
+
console.log(' Remember past sessions Ask "what did we decide about auth?" across Claude Code, Codex, and Pi history.');
|
|
398
392
|
console.log(" Learn from your patterns Turn repeated corrections into memory you can inspect and undo.");
|
|
399
|
-
console.log(" Private by default
|
|
393
|
+
console.log(" Private by default AgentMemory services never receive memory or session content.");
|
|
400
394
|
console.log("");
|
|
401
395
|
if (installed) {
|
|
402
396
|
printProUsageCounters();
|
|
@@ -443,7 +437,7 @@ function printPluginResult(result, json, allowBrowser) {
|
|
|
443
437
|
console.log(`AgentMemory Pro${version} has an update available.`);
|
|
444
438
|
break;
|
|
445
439
|
case "uninstalled":
|
|
446
|
-
console.log("AgentMemory Pro executable components were removed. Memory and
|
|
440
|
+
console.log("AgentMemory Pro executable components were removed. Memory and local activation state were preserved.");
|
|
447
441
|
break;
|
|
448
442
|
case "not_installed":
|
|
449
443
|
console.log("AgentMemory Pro is not installed.");
|
|
@@ -674,10 +668,15 @@ const STOP_NAG_REASON = "Before stopping: if this session produced a durable fac
|
|
|
674
668
|
"ignore this and stop normally.";
|
|
675
669
|
/**
|
|
676
670
|
* Stop hook handler — fires at the end of every assistant turn (not once per
|
|
677
|
-
* session).
|
|
678
|
-
* to nudge a memory-write check without being
|
|
679
|
-
*
|
|
680
|
-
*
|
|
671
|
+
* session). Continues the conversation at most once every STOP_NAG_INTERVAL
|
|
672
|
+
* turns per session_id to nudge a memory-write check without being
|
|
673
|
+
* disruptive. Uses `hookSpecificOutput.additionalContext` rather than
|
|
674
|
+
* `decision: "block"` — functionally identical (both go through the same
|
|
675
|
+
* `stop_hook_active` re-entry check and Claude Code's loop-protection cap),
|
|
676
|
+
* but additionalContext renders as "Stop hook feedback" in the transcript
|
|
677
|
+
* instead of the alarming-looking "Stop hook error". Always allows the stop
|
|
678
|
+
* (empty stdout) on missing session_id, `stop_hook_active` (Claude Code's own
|
|
679
|
+
* re-entrancy signal — never nag twice in a row), or any internal error.
|
|
681
680
|
*/
|
|
682
681
|
async function cmdStop(_flags) {
|
|
683
682
|
const TIMEOUT_MS = 3_000;
|
|
@@ -695,7 +694,9 @@ async function cmdStop(_flags) {
|
|
|
695
694
|
if (!sessionId || payload?.stop_hook_active === true)
|
|
696
695
|
return;
|
|
697
696
|
if (shouldNagOnStop(sessionId, Date.now())) {
|
|
698
|
-
process.stdout.write(JSON.stringify({
|
|
697
|
+
process.stdout.write(JSON.stringify({
|
|
698
|
+
hookSpecificOutput: { hookEventName: "Stop", additionalContext: STOP_NAG_REASON },
|
|
699
|
+
}));
|
|
699
700
|
}
|
|
700
701
|
})().catch(() => {
|
|
701
702
|
// Any failure in the Stop hook must be swallowed — never trap the user
|
|
@@ -1070,6 +1071,11 @@ async function cmdInstallHooks(flags) {
|
|
|
1070
1071
|
const json = hasFlag(flags, "json");
|
|
1071
1072
|
const requested = getFlag(flags, "only");
|
|
1072
1073
|
const requestedKeys = requested ? new Set(requested.split(",").map((value) => value.trim())) : null;
|
|
1074
|
+
// Internal-only signal from cmdSetup: pi's action is a real network package
|
|
1075
|
+
// install (`pi install npm:pi-memory`), not a local config-file edit like
|
|
1076
|
+
// every other agent here — so setup's internally-manufactured `yes: true`
|
|
1077
|
+
// must not cover it. Not a public flag; never documented in cli-spec.
|
|
1078
|
+
const deferPi = hasFlag(flags, "_setup-defer-pi");
|
|
1073
1079
|
const modeFlag = getFlag(flags, "mode");
|
|
1074
1080
|
if (modeFlag !== undefined && modeFlag !== "stable" && modeFlag !== "per-turn") {
|
|
1075
1081
|
exitError(`--mode must be 'stable' or 'per-turn' (got ${modeFlag})`, json);
|
|
@@ -1078,11 +1084,19 @@ async function cmdInstallHooks(flags) {
|
|
|
1078
1084
|
const { homeDir, targets } = detectHookAgents();
|
|
1079
1085
|
if (!homeDir)
|
|
1080
1086
|
exitError("Home directory not found.", json);
|
|
1081
|
-
const eligible = targets.filter((target) =>
|
|
1087
|
+
const eligible = targets.filter((target) => {
|
|
1088
|
+
if (!(target.supported && target.detected))
|
|
1089
|
+
return false;
|
|
1090
|
+
if (requestedKeys && !requestedKeys.has(target.key))
|
|
1091
|
+
return false;
|
|
1092
|
+
if (target.key === "pi" && deferPi)
|
|
1093
|
+
return false;
|
|
1094
|
+
return true;
|
|
1095
|
+
});
|
|
1082
1096
|
if (!eligible.length) {
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
return
|
|
1097
|
+
const report = { ok: true, homeDir, results: [] };
|
|
1098
|
+
output(json ? report : "No eligible agents. Nothing to install.", json);
|
|
1099
|
+
return report;
|
|
1086
1100
|
}
|
|
1087
1101
|
// Consider an agent "already installed" only when the wiring matches the requested mode.
|
|
1088
1102
|
// per-turn requires BOTH SessionStart and UserPromptSubmit; stable requires SessionStart AND
|
|
@@ -1112,43 +1126,50 @@ async function cmdInstallHooks(flags) {
|
|
|
1112
1126
|
console.log(`Automatic context already active for: ${labels}.`);
|
|
1113
1127
|
}
|
|
1114
1128
|
if (!pending.length) {
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
return output("Nothing to install.", false);
|
|
1129
|
+
const report = {
|
|
1130
|
+
ok: true,
|
|
1131
|
+
homeDir,
|
|
1132
|
+
results: alreadyInstalled.map((target) => ({
|
|
1133
|
+
key: target.key,
|
|
1134
|
+
label: target.label,
|
|
1135
|
+
installed: false,
|
|
1136
|
+
reason: "already installed",
|
|
1137
|
+
mode,
|
|
1138
|
+
})),
|
|
1139
|
+
};
|
|
1140
|
+
output(json ? report : "Nothing to install.", json);
|
|
1141
|
+
return report;
|
|
1129
1142
|
}
|
|
1130
1143
|
const selected = new Set();
|
|
1131
1144
|
const applyAll = hasFlag(flags, "yes") || hasFlag(flags, "all") || !process.stdin.isTTY;
|
|
1132
1145
|
const hookLabel = mode === "per-turn" ? "SessionStart + UserPromptSubmit hooks" : "SessionStart hook";
|
|
1133
1146
|
for (const target of pending) {
|
|
1134
|
-
|
|
1147
|
+
// pi gets a real package install (`pi install npm:pi-memory`), not a config-file hook edit —
|
|
1148
|
+
// word the prompt accordingly so the confirmation matches what actually happens.
|
|
1149
|
+
const question = target.key === "pi"
|
|
1150
|
+
? `Install pi-memory (native pi extension) for ${target.label}?`
|
|
1151
|
+
: `Install ${hookLabel} for ${target.label}?`;
|
|
1152
|
+
if (applyAll || (await promptYesNo(question, true)))
|
|
1135
1153
|
selected.add(target.key);
|
|
1136
1154
|
}
|
|
1137
1155
|
if (!selected.size) {
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
return
|
|
1156
|
+
const report = { ok: true, homeDir, results: [] };
|
|
1157
|
+
output(json ? report : "Nothing selected. Skipped.", json);
|
|
1158
|
+
return report;
|
|
1141
1159
|
}
|
|
1142
1160
|
const report = installHooks(selected, mode);
|
|
1143
1161
|
if (!report.ok)
|
|
1144
1162
|
exitError(report.error ?? "install failed", json);
|
|
1145
|
-
if (json)
|
|
1146
|
-
|
|
1163
|
+
if (json) {
|
|
1164
|
+
output(report, true);
|
|
1165
|
+
return report;
|
|
1166
|
+
}
|
|
1147
1167
|
for (const result of report.results) {
|
|
1148
1168
|
console.log(result.installed
|
|
1149
1169
|
? `Installed ${result.label} hook (${result.mode ?? mode}): ${result.path}`
|
|
1150
1170
|
: `Skipped ${result.label} (${result.reason ?? "unknown"})`);
|
|
1151
1171
|
}
|
|
1172
|
+
return report;
|
|
1152
1173
|
}
|
|
1153
1174
|
function cmdUninstallHooks(flags) {
|
|
1154
1175
|
const json = hasFlag(flags, "json");
|
|
@@ -1434,15 +1455,14 @@ async function cmdSetup(flags) {
|
|
|
1434
1455
|
const steps = [];
|
|
1435
1456
|
const runQuiet = async (fn) => {
|
|
1436
1457
|
if (!json) {
|
|
1437
|
-
await fn();
|
|
1438
|
-
return;
|
|
1458
|
+
return await fn();
|
|
1439
1459
|
}
|
|
1440
1460
|
const originalLog = console.log;
|
|
1441
1461
|
const originalInfo = console.info;
|
|
1442
1462
|
console.log = () => { };
|
|
1443
1463
|
console.info = () => { };
|
|
1444
1464
|
try {
|
|
1445
|
-
await fn();
|
|
1465
|
+
return await fn();
|
|
1446
1466
|
}
|
|
1447
1467
|
finally {
|
|
1448
1468
|
console.log = originalLog;
|
|
@@ -1473,8 +1493,31 @@ async function cmdSetup(flags) {
|
|
|
1473
1493
|
// Step 3: hooks (uses the improved preflight — silent when all already installed)
|
|
1474
1494
|
if (!skipHooks) {
|
|
1475
1495
|
try {
|
|
1476
|
-
|
|
1477
|
-
|
|
1496
|
+
const userSuppliedYes = hasFlag(flags, "yes");
|
|
1497
|
+
// pi's install is a real network package fetch (`pi install npm:pi-memory`),
|
|
1498
|
+
// unlike every other agent here (local config-file edits) — setup's
|
|
1499
|
+
// manufactured `yes: true` above must not silently cover that unless the
|
|
1500
|
+
// user actually asked for --yes themselves.
|
|
1501
|
+
const { homeDir: detectedHomeDir, targets: detectedTargets } = detectHookAgents();
|
|
1502
|
+
const piTarget = detectedTargets.find((target) => target.key === "pi");
|
|
1503
|
+
const piDetected = Boolean(piTarget?.supported && piTarget?.detected);
|
|
1504
|
+
const piAlreadyActive = piDetected && detectedHomeDir ? isHookInstalled(detectedHomeDir, "pi") : false;
|
|
1505
|
+
const hooksFlags = userSuppliedYes ? subFlags : { ...subFlags, "_setup-defer-pi": true };
|
|
1506
|
+
const report = await runQuiet(() => cmdInstallHooks(hooksFlags));
|
|
1507
|
+
const piFailure = report?.results.find((result) => result.key === "pi" && !result.installed && result.reason && result.reason !== "already installed");
|
|
1508
|
+
if (piFailure) {
|
|
1509
|
+
steps.push({ name: "hooks", ok: false, detail: `pi-memory install failed: ${piFailure.reason}` });
|
|
1510
|
+
}
|
|
1511
|
+
else if (piDetected && !piAlreadyActive && !userSuppliedYes) {
|
|
1512
|
+
steps.push({
|
|
1513
|
+
name: "hooks",
|
|
1514
|
+
ok: true,
|
|
1515
|
+
detail: "pi-memory deferred — re-run with --yes, or agent-memory install-hooks --only pi",
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
else {
|
|
1519
|
+
steps.push({ name: "hooks", ok: true });
|
|
1520
|
+
}
|
|
1478
1521
|
}
|
|
1479
1522
|
catch (error) {
|
|
1480
1523
|
steps.push({ name: "hooks", ok: false, detail: error.message });
|
|
@@ -1656,7 +1699,7 @@ async function cmdSetup(flags) {
|
|
|
1656
1699
|
console.log(colorize("The local plugin is live. Feel the magic now:", "green"));
|
|
1657
1700
|
console.log(` ${colorize('agent-memory recall "what did we decide about auth?"', "cyan")} — search past sessions`);
|
|
1658
1701
|
console.log(` ${colorize("agent-memory learn", "cyan")} — surface repeated corrections`);
|
|
1659
|
-
console.log(` ${colorize("agent-memory
|
|
1702
|
+
console.log(` ${colorize("agent-memory index", "cyan")} — refresh the supported local session index`);
|
|
1660
1703
|
console.log(` ${colorize("agent-memory dashboard", "cyan")} — private local dashboard`);
|
|
1661
1704
|
}
|
|
1662
1705
|
else if (skipPlugin) {
|
|
@@ -1665,6 +1708,138 @@ async function cmdSetup(flags) {
|
|
|
1665
1708
|
}
|
|
1666
1709
|
console.log("");
|
|
1667
1710
|
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Reverse of {@link cmdSetup}: removes every install artifact agent-memory
|
|
1713
|
+
* creates outside of this package — hooks, skills, MCP registrations, shell
|
|
1714
|
+
* completions, and the Pro plugin executables. Memory data under
|
|
1715
|
+
* `getMemoryDir()` (MEMORY.md, daily logs, scratchpad, topics, qmd index) is
|
|
1716
|
+
* left untouched unless `--data` is passed, since that's the one step a user
|
|
1717
|
+
* can't undo. Destructive by nature, so it always requires either an
|
|
1718
|
+
* interactive confirmation or `--yes`.
|
|
1719
|
+
*/
|
|
1720
|
+
async function cmdUninstall(flags) {
|
|
1721
|
+
const json = hasFlag(flags, "json");
|
|
1722
|
+
const yes = hasFlag(flags, "yes");
|
|
1723
|
+
const wipeData = hasFlag(flags, "data");
|
|
1724
|
+
const interactive = !json && Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
1725
|
+
if (!yes) {
|
|
1726
|
+
const message = wipeData
|
|
1727
|
+
? "Re-run with --yes to remove agent-memory's hooks, skills, MCP registrations, completions, and Pro plugin, and permanently delete ~/.agent-memory (MEMORY.md, daily logs, scratchpad, topics, qmd index)."
|
|
1728
|
+
: "Re-run with --yes to remove agent-memory's hooks, skills, MCP registrations, completions, and Pro plugin. Your memory data is left untouched.";
|
|
1729
|
+
if (interactive) {
|
|
1730
|
+
const question = wipeData
|
|
1731
|
+
? "This will also permanently delete your memory data (MEMORY.md, daily logs, scratchpad). Continue?"
|
|
1732
|
+
: "Remove agent-memory's hooks, skills, MCP registrations, completions, and Pro plugin?";
|
|
1733
|
+
if (!(await promptYesNo(question, false))) {
|
|
1734
|
+
console.log("Aborted. Nothing was removed.");
|
|
1735
|
+
return;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
else {
|
|
1739
|
+
if (json)
|
|
1740
|
+
output({ ok: false, error: { code: "confirmation_required", message } }, true);
|
|
1741
|
+
else
|
|
1742
|
+
console.error(`Error: ${message}`);
|
|
1743
|
+
process.exitCode = 1;
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
const steps = [];
|
|
1748
|
+
try {
|
|
1749
|
+
const report = uninstallSkills();
|
|
1750
|
+
if (!report.ok)
|
|
1751
|
+
throw new Error(report.error ?? "failed to remove skills");
|
|
1752
|
+
steps.push({
|
|
1753
|
+
name: "skills",
|
|
1754
|
+
ok: true,
|
|
1755
|
+
detail: report.removed.length ? `removed ${report.removed.length}` : "not installed",
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
1758
|
+
catch (error) {
|
|
1759
|
+
steps.push({ name: "skills", ok: false, detail: error.message });
|
|
1760
|
+
}
|
|
1761
|
+
try {
|
|
1762
|
+
const report = uninstallHooks();
|
|
1763
|
+
if (!report.ok)
|
|
1764
|
+
throw new Error(report.error ?? "failed to remove hooks");
|
|
1765
|
+
const removed = report.results.filter((r) => r.installed).length;
|
|
1766
|
+
// agent-memory never removes pi-memory itself (see uninstallPiMemoryDelegate) — make that
|
|
1767
|
+
// explicit here rather than letting a generic "removed N" detail imply everything is gone.
|
|
1768
|
+
const piResult = report.results.find((r) => r.key === "pi");
|
|
1769
|
+
const piNote = piResult?.reason?.startsWith("pi-memory left installed") ? `; ${piResult.reason}` : "";
|
|
1770
|
+
steps.push({
|
|
1771
|
+
name: "hooks",
|
|
1772
|
+
ok: true,
|
|
1773
|
+
detail: (removed ? `removed ${removed}` : "not installed") + piNote,
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
catch (error) {
|
|
1777
|
+
steps.push({ name: "hooks", ok: false, detail: error.message });
|
|
1778
|
+
}
|
|
1779
|
+
try {
|
|
1780
|
+
const results = unregisterMcpFromAgents(null);
|
|
1781
|
+
const removed = results.filter((r) => r.status === "unregistered").length;
|
|
1782
|
+
steps.push({ name: "mcp", ok: true, detail: removed ? `unregistered ${removed}` : "not registered" });
|
|
1783
|
+
}
|
|
1784
|
+
catch (error) {
|
|
1785
|
+
steps.push({ name: "mcp", ok: false, detail: error.message });
|
|
1786
|
+
}
|
|
1787
|
+
try {
|
|
1788
|
+
const results = uninstallCompletion();
|
|
1789
|
+
const removed = results.filter((r) => r.removed || r.profileUpdated).length;
|
|
1790
|
+
steps.push({ name: "completions", ok: true, detail: removed ? `removed ${removed}` : "not installed" });
|
|
1791
|
+
}
|
|
1792
|
+
catch (error) {
|
|
1793
|
+
steps.push({ name: "completions", ok: false, detail: error.message });
|
|
1794
|
+
}
|
|
1795
|
+
try {
|
|
1796
|
+
const manager = createDefaultPluginBootstrap(VERSION);
|
|
1797
|
+
const pluginResult = await manager.uninstall();
|
|
1798
|
+
steps.push({ name: "plugin", ok: pluginResult.ok, detail: pluginResult.result });
|
|
1799
|
+
}
|
|
1800
|
+
catch (error) {
|
|
1801
|
+
steps.push({ name: "plugin", ok: false, detail: error.message });
|
|
1802
|
+
}
|
|
1803
|
+
if (wipeData) {
|
|
1804
|
+
try {
|
|
1805
|
+
const memoryDir = getMemoryDir();
|
|
1806
|
+
if (fs.existsSync(memoryDir))
|
|
1807
|
+
fs.rmSync(memoryDir, { recursive: true, force: true });
|
|
1808
|
+
const pluginRoot = getDefaultPluginInstallRoot();
|
|
1809
|
+
if (fs.existsSync(pluginRoot))
|
|
1810
|
+
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
1811
|
+
steps.push({ name: "data", ok: true, detail: memoryDir });
|
|
1812
|
+
}
|
|
1813
|
+
catch (error) {
|
|
1814
|
+
steps.push({ name: "data", ok: false, detail: error.message });
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
const allOk = steps.every((step) => step.ok);
|
|
1818
|
+
if (!allOk)
|
|
1819
|
+
process.exitCode = 1;
|
|
1820
|
+
if (json) {
|
|
1821
|
+
output({ ok: allOk, data: wipeData, steps }, true);
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
console.log("");
|
|
1825
|
+
console.log(colorize("agent-memory uninstall", "bold"));
|
|
1826
|
+
for (const step of steps) {
|
|
1827
|
+
const mark = step.ok ? MARK_OK : MARK_FAIL;
|
|
1828
|
+
const detail = step.detail ? colorize(` ${step.detail}`, "dim") : "";
|
|
1829
|
+
console.log(` ${mark} ${step.name}${detail}`);
|
|
1830
|
+
}
|
|
1831
|
+
console.log("");
|
|
1832
|
+
if (!allOk) {
|
|
1833
|
+
console.log(colorize("Some steps failed — see details above.", "yellow"));
|
|
1834
|
+
}
|
|
1835
|
+
else if (wipeData) {
|
|
1836
|
+
console.log(colorize("agent-memory has been fully removed, including your memory data.", "green"));
|
|
1837
|
+
}
|
|
1838
|
+
else {
|
|
1839
|
+
console.log(colorize("agent-memory's install artifacts have been removed.", "green"));
|
|
1840
|
+
console.log(colorize(`Your notes are untouched at ${getMemoryDir()}. Re-run with --data to remove them too.`, "dim"));
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1668
1843
|
/**
|
|
1669
1844
|
* Explain the optional plugin without making a successful core setup feel
|
|
1670
1845
|
* incomplete. Keep the quota line here in sync with `freeEntitlement()`.
|
|
@@ -1678,10 +1853,10 @@ function printProPitch(mode) {
|
|
|
1678
1853
|
else {
|
|
1679
1854
|
console.log(`${colorize("Optional: AgentMemory Pro", "bold")} — ${colorize("memory that learns from your work", "dim")}`);
|
|
1680
1855
|
}
|
|
1681
|
-
console.log(` ${colorize("Recall across sessions", "cyan")} Ask "what did we decide about auth?" across Claude, Codex,
|
|
1856
|
+
console.log(` ${colorize("Recall across sessions", "cyan")} Ask "what did we decide about auth?" across Claude Code, Codex, and Pi history.`);
|
|
1682
1857
|
console.log(` ${colorize("Learn from corrections", "cyan")} Turn repeated fixes into memory you can inspect and undo.`);
|
|
1683
|
-
console.log(` ${colorize("
|
|
1684
|
-
console.log(` ${colorize("Private by default", "cyan")}
|
|
1858
|
+
console.log(` ${colorize("Local session index", "cyan")} Scan supported session history without uploading it to AgentMemory.`);
|
|
1859
|
+
console.log(` ${colorize("Private by default", "cyan")} AgentMemory services never receive memory or session content.`);
|
|
1685
1860
|
console.log(` ${colorize("Included at no cost:", "green")} ${colorize("20 recalls + 5 learning scans per day", "bold")}. Local indexing and dashboard remain free.`);
|
|
1686
1861
|
console.log("");
|
|
1687
1862
|
}
|
|
@@ -1900,6 +2075,14 @@ async function cmdDoctor(flags) {
|
|
|
1900
2075
|
});
|
|
1901
2076
|
}
|
|
1902
2077
|
}
|
|
2078
|
+
// Auto-upgrade policy
|
|
2079
|
+
const upgradePolicy = readUpgradePolicy();
|
|
2080
|
+
rows.push({
|
|
2081
|
+
status: "ok",
|
|
2082
|
+
label: "Auto-upgrade",
|
|
2083
|
+
detail: `CLI: ${upgradePolicy.cli}, Pro: ${upgradePolicy.plugin}`,
|
|
2084
|
+
fix: upgradePolicy.cli === "off" && upgradePolicy.plugin === "off" ? "agent-memory upgrade policy auto" : undefined,
|
|
2085
|
+
});
|
|
1903
2086
|
// Skills + hooks per detected host
|
|
1904
2087
|
const { homeDir, targets } = detectHookAgents();
|
|
1905
2088
|
const detected = targets.filter((target) => target.detected);
|
|
@@ -1915,7 +2098,7 @@ async function cmdDoctor(flags) {
|
|
|
1915
2098
|
rows.push({
|
|
1916
2099
|
status: "warn",
|
|
1917
2100
|
label: "Agent hosts",
|
|
1918
|
-
detail: "no supported agents detected (Claude Code, Codex, Cursor, opencode)",
|
|
2101
|
+
detail: "no supported agents detected (Claude Code, Codex, Cursor, opencode, pi)",
|
|
1919
2102
|
fix: "install one of the agents first, then: agent-memory install-skills",
|
|
1920
2103
|
});
|
|
1921
2104
|
}
|
|
@@ -1927,7 +2110,35 @@ async function cmdDoctor(flags) {
|
|
|
1927
2110
|
});
|
|
1928
2111
|
for (const target of detected) {
|
|
1929
2112
|
if (!target.supported) {
|
|
1930
|
-
// Skip skill/hook rows for hosts we don't yet integrate with
|
|
2113
|
+
// Skip skill/hook rows for hosts we don't yet integrate with.
|
|
2114
|
+
continue;
|
|
2115
|
+
}
|
|
2116
|
+
if (target.key === "pi") {
|
|
2117
|
+
// pi has no SKILL.md — agent-memory delegates entirely to the pi-memory
|
|
2118
|
+
// extension, installed via `pi install npm:pi-memory` rather than a
|
|
2119
|
+
// config-file edit, so it gets its own row instead of the generic
|
|
2120
|
+
// Skill:/Hook: pair below.
|
|
2121
|
+
const installed = homeDir ? isHookInstalled(homeDir, "pi") : false;
|
|
2122
|
+
const state = getPiMemoryState();
|
|
2123
|
+
let detail;
|
|
2124
|
+
if (installed) {
|
|
2125
|
+
detail = "pi-memory extension active (github.com/jayzeng/pi-memory)";
|
|
2126
|
+
}
|
|
2127
|
+
else if (state && !state.ok) {
|
|
2128
|
+
detail = `pi-memory not detected — last install attempt (${state.lastAttemptAt}) failed: ${state.detail}`;
|
|
2129
|
+
}
|
|
2130
|
+
else if (state?.ok) {
|
|
2131
|
+
detail = `pi-memory installed (${state.lastAttemptAt}) but no session has run yet`;
|
|
2132
|
+
}
|
|
2133
|
+
else {
|
|
2134
|
+
detail = "pi-memory not detected — agent-memory can install it";
|
|
2135
|
+
}
|
|
2136
|
+
rows.push({
|
|
2137
|
+
status: installed ? "ok" : "warn",
|
|
2138
|
+
label: "Memory: pi",
|
|
2139
|
+
detail,
|
|
2140
|
+
fix: installed ? undefined : "agent-memory install-hooks --only pi",
|
|
2141
|
+
});
|
|
1931
2142
|
continue;
|
|
1932
2143
|
}
|
|
1933
2144
|
const skillPath = homeDir ? `${target.homeMarker}/skills/agent-memory/SKILL.md` : null;
|
|
@@ -2173,9 +2384,10 @@ Usage:
|
|
|
2173
2384
|
agent-memory plugin manage [--no-browser]
|
|
2174
2385
|
|
|
2175
2386
|
The public core remains fully usable without AgentMemory Pro. Install uses a random
|
|
2176
|
-
installation identifier and requires no account or email. The free tier includes
|
|
2177
|
-
20 recalls and 5 learning scans per local day; indexing and the Memory Dashboard
|
|
2178
|
-
remain available.
|
|
2387
|
+
installation identifier and requires no account or email. The free tier includes
|
|
2388
|
+
20 recalls and 5 learning scans per local day; indexing and the Memory Dashboard
|
|
2389
|
+
remain available. AgentMemory services never receive memory or session content;
|
|
2390
|
+
recall results are subject to the coding agent or model provider you invoke.`);
|
|
2179
2391
|
}
|
|
2180
2392
|
function pluginCommandFailure(command, error) {
|
|
2181
2393
|
return {
|
|
@@ -2482,16 +2694,108 @@ async function resolvePluginLatestHint() {
|
|
|
2482
2694
|
return { latest: null, updateAvailable: false };
|
|
2483
2695
|
}
|
|
2484
2696
|
}
|
|
2485
|
-
async function
|
|
2697
|
+
async function cmdUpgradePolicy(flags, positional) {
|
|
2698
|
+
const json = hasFlag(flags, "json");
|
|
2699
|
+
const value = positional[0];
|
|
2700
|
+
if (!value) {
|
|
2701
|
+
const policy = readUpgradePolicy();
|
|
2702
|
+
if (json)
|
|
2703
|
+
output({ cli: policy.cli, plugin: policy.plugin }, true);
|
|
2704
|
+
else
|
|
2705
|
+
console.log(` cli: ${policy.cli}\n plugin: ${policy.plugin}`);
|
|
2706
|
+
return;
|
|
2707
|
+
}
|
|
2708
|
+
if (value !== "off" && value !== "notify" && value !== "auto") {
|
|
2709
|
+
exitError(`Invalid policy '${value}'. Expected one of: off, notify, auto.`, json);
|
|
2710
|
+
}
|
|
2711
|
+
const onlyCli = hasFlag(flags, "cli");
|
|
2712
|
+
const onlyPlugin = hasFlag(flags, "plugin");
|
|
2713
|
+
const patch = {};
|
|
2714
|
+
if (onlyCli || !onlyPlugin)
|
|
2715
|
+
patch.cli = value;
|
|
2716
|
+
if (onlyPlugin || !onlyCli)
|
|
2717
|
+
patch.plugin = value;
|
|
2718
|
+
const next = writeUpgradePolicy(patch);
|
|
2719
|
+
if (json)
|
|
2720
|
+
output(next, true);
|
|
2721
|
+
else
|
|
2722
|
+
console.log(` cli: ${next.cli}\n plugin: ${next.plugin}`);
|
|
2723
|
+
}
|
|
2724
|
+
/**
|
|
2725
|
+
* `--background`-mode install: only touches a target when its persisted policy
|
|
2726
|
+
* (see `readUpgradePolicy`) is `"auto"`. Always non-interactive — this path is
|
|
2727
|
+
* only ever reached from a detached, non-TTY child spawned by
|
|
2728
|
+
* `refreshUpgradeCacheBackground()`. Failures are recorded, never thrown.
|
|
2729
|
+
*/
|
|
2730
|
+
async function runAutoUpgrade(status, policy, pluginCurrent, quiet) {
|
|
2731
|
+
const now = new Date().toISOString();
|
|
2732
|
+
let cliAuto;
|
|
2733
|
+
let pluginAuto;
|
|
2734
|
+
if (policy.cli === "auto" && status.cli.upgradeAvailable) {
|
|
2735
|
+
const method = detectInstallMethod();
|
|
2736
|
+
if (!quiet)
|
|
2737
|
+
console.log(`Auto-upgrading CLI via: ${method.command.join(" ")}`);
|
|
2738
|
+
try {
|
|
2739
|
+
const result = runInstaller(method);
|
|
2740
|
+
cliAuto = result.ok
|
|
2741
|
+
? { at: now, ok: true, version: status.cli.latest }
|
|
2742
|
+
: {
|
|
2743
|
+
at: now,
|
|
2744
|
+
ok: false,
|
|
2745
|
+
version: status.cli.current,
|
|
2746
|
+
error: (result.stderr || result.stdout || `exit ${result.code}`).trim(),
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
catch (error) {
|
|
2750
|
+
cliAuto = {
|
|
2751
|
+
at: now,
|
|
2752
|
+
ok: false,
|
|
2753
|
+
version: status.cli.current,
|
|
2754
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2755
|
+
};
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
if (policy.plugin === "auto" && status.plugin.upgradeAvailable) {
|
|
2759
|
+
if (!quiet)
|
|
2760
|
+
console.log("Auto-upgrading Pro plugin bundle…");
|
|
2761
|
+
try {
|
|
2762
|
+
const pluginResult = await cmdPlugin({ json: true }, ["update"]);
|
|
2763
|
+
const ok = Boolean(pluginResult?.ok);
|
|
2764
|
+
pluginAuto = ok
|
|
2765
|
+
? { at: now, ok: true, version: status.plugin.latest }
|
|
2766
|
+
: {
|
|
2767
|
+
at: now,
|
|
2768
|
+
ok: false,
|
|
2769
|
+
version: pluginCurrent,
|
|
2770
|
+
error: pluginResult?.error?.message ?? "plugin update failed",
|
|
2771
|
+
};
|
|
2772
|
+
}
|
|
2773
|
+
catch (error) {
|
|
2774
|
+
pluginAuto = {
|
|
2775
|
+
at: now,
|
|
2776
|
+
ok: false,
|
|
2777
|
+
version: pluginCurrent,
|
|
2778
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2779
|
+
};
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
return { cliAuto, pluginAuto };
|
|
2783
|
+
}
|
|
2784
|
+
async function cmdUpgrade(flags, positional = []) {
|
|
2785
|
+
if (positional[0] === "policy") {
|
|
2786
|
+
await cmdUpgradePolicy(flags, positional.slice(1));
|
|
2787
|
+
return;
|
|
2788
|
+
}
|
|
2486
2789
|
const json = hasFlag(flags, "json");
|
|
2487
2790
|
const quiet = hasFlag(flags, "quiet");
|
|
2488
2791
|
const checkOnly = hasFlag(flags, "check");
|
|
2792
|
+
const background = hasFlag(flags, "background");
|
|
2489
2793
|
const refresh = hasFlag(flags, "refresh");
|
|
2490
2794
|
const onlyCli = hasFlag(flags, "cli");
|
|
2491
2795
|
const onlyPlugin = hasFlag(flags, "plugin");
|
|
2492
2796
|
const targetCli = onlyCli || !onlyPlugin;
|
|
2493
2797
|
const targetPlugin = onlyPlugin || !onlyCli;
|
|
2494
|
-
const applyAll = hasFlag(flags, "yes") || !process.stdin.isTTY || !process.stdout.isTTY;
|
|
2798
|
+
const applyAll = hasFlag(flags, "yes") || background || !process.stdin.isTTY || !process.stdout.isTTY;
|
|
2495
2799
|
const pluginCurrent = await readPluginCurrentVersion();
|
|
2496
2800
|
const pluginProbe = targetPlugin ? await resolvePluginLatestHint() : { latest: null, updateAvailable: false };
|
|
2497
2801
|
const status = await checkForUpgrades({
|
|
@@ -2508,6 +2812,30 @@ async function cmdUpgrade(flags) {
|
|
|
2508
2812
|
printUpgradeStatus(status, { targetCli, targetPlugin });
|
|
2509
2813
|
return;
|
|
2510
2814
|
}
|
|
2815
|
+
if (background) {
|
|
2816
|
+
const policy = readUpgradePolicy();
|
|
2817
|
+
const { cliAuto, pluginAuto } = await runAutoUpgrade(status, policy, pluginCurrent, quiet);
|
|
2818
|
+
writeUpgradeCache({
|
|
2819
|
+
checkedAt: status.checkedAt,
|
|
2820
|
+
cliCurrent: VERSION,
|
|
2821
|
+
cliLatest: status.cli.latest,
|
|
2822
|
+
pluginCurrent,
|
|
2823
|
+
pluginLatest: status.plugin.latest,
|
|
2824
|
+
cliAuto,
|
|
2825
|
+
pluginAuto,
|
|
2826
|
+
});
|
|
2827
|
+
if (json)
|
|
2828
|
+
output({ ...status, cliAuto, pluginAuto }, true);
|
|
2829
|
+
else if (!quiet) {
|
|
2830
|
+
if (cliAuto)
|
|
2831
|
+
console.log(` ${cliAuto.ok ? MARK_OK : MARK_FAIL} CLI auto-upgrade: ${cliAuto.ok ? `→ ${cliAuto.version}` : cliAuto.error}`);
|
|
2832
|
+
if (pluginAuto)
|
|
2833
|
+
console.log(` ${pluginAuto.ok ? MARK_OK : MARK_FAIL} Pro auto-upgrade: ${pluginAuto.ok ? `→ ${pluginAuto.version}` : pluginAuto.error}`);
|
|
2834
|
+
}
|
|
2835
|
+
if ((cliAuto && !cliAuto.ok) || (pluginAuto && !pluginAuto.ok))
|
|
2836
|
+
process.exitCode = 1;
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2511
2839
|
const cliNeedsUpgrade = targetCli && status.cli.upgradeAvailable;
|
|
2512
2840
|
const pluginNeedsUpgrade = targetPlugin && status.plugin.upgradeAvailable;
|
|
2513
2841
|
if (!cliNeedsUpgrade && !pluginNeedsUpgrade) {
|
|
@@ -2648,6 +2976,64 @@ function registerMcpInAgents(only) {
|
|
|
2648
2976
|
}
|
|
2649
2977
|
return results;
|
|
2650
2978
|
}
|
|
2979
|
+
/**
|
|
2980
|
+
* Reverse of {@link registerMcpInAgents}: removes the `agent-memory` MCP server
|
|
2981
|
+
* entry from every supported local harness. Missing config files are reported
|
|
2982
|
+
* as `not-installed`, files that never had the entry as `not-registered`.
|
|
2983
|
+
*/
|
|
2984
|
+
function unregisterMcpFromAgents(only) {
|
|
2985
|
+
const home = os.homedir();
|
|
2986
|
+
const want = (key) => !only || only.has(key);
|
|
2987
|
+
const results = [];
|
|
2988
|
+
const unregisterJson = (key, displayName, configFile) => {
|
|
2989
|
+
const p = path.join(home, configFile);
|
|
2990
|
+
if (!fs.existsSync(p)) {
|
|
2991
|
+
results.push({ key, displayName, path: p, status: "not-installed" });
|
|
2992
|
+
return;
|
|
2993
|
+
}
|
|
2994
|
+
let s = {};
|
|
2995
|
+
try {
|
|
2996
|
+
s = JSON.parse(fs.readFileSync(p, "utf8"));
|
|
2997
|
+
}
|
|
2998
|
+
catch { }
|
|
2999
|
+
const servers = (s.mcpServers ?? {});
|
|
3000
|
+
if (!servers["agent-memory"]) {
|
|
3001
|
+
results.push({ key, displayName, path: p, status: "not-registered" });
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
3004
|
+
delete servers["agent-memory"];
|
|
3005
|
+
if (Object.keys(servers).length === 0)
|
|
3006
|
+
delete s.mcpServers;
|
|
3007
|
+
else
|
|
3008
|
+
s.mcpServers = servers;
|
|
3009
|
+
fs.writeFileSync(p, `${JSON.stringify(s, null, 2)}\n`);
|
|
3010
|
+
results.push({ key, displayName, path: p, status: "unregistered" });
|
|
3011
|
+
};
|
|
3012
|
+
if (want("claude"))
|
|
3013
|
+
unregisterJson("claude", "Claude Code", ".claude.json");
|
|
3014
|
+
if (want("cursor"))
|
|
3015
|
+
unregisterJson("cursor", "Cursor", ".cursor/mcp.json");
|
|
3016
|
+
if (want("windsurf"))
|
|
3017
|
+
unregisterJson("windsurf", "Windsurf", ".windsurf/mcp_settings.json");
|
|
3018
|
+
if (want("codex")) {
|
|
3019
|
+
const p = path.join(home, ".codex", "config.toml");
|
|
3020
|
+
if (!fs.existsSync(p)) {
|
|
3021
|
+
results.push({ key: "codex", displayName: "Codex", path: p, status: "not-installed" });
|
|
3022
|
+
}
|
|
3023
|
+
else {
|
|
3024
|
+
const existing = fs.readFileSync(p, "utf8");
|
|
3025
|
+
if (!existing.includes("[mcp_servers.agent-memory]")) {
|
|
3026
|
+
results.push({ key: "codex", displayName: "Codex", path: p, status: "not-registered" });
|
|
3027
|
+
}
|
|
3028
|
+
else {
|
|
3029
|
+
const pattern = /\n?\[mcp_servers\.agent-memory\]\n(?:(?!\[)[^\n]*\n?)*/;
|
|
3030
|
+
fs.writeFileSync(p, existing.replace(pattern, ""), "utf8");
|
|
3031
|
+
results.push({ key: "codex", displayName: "Codex", path: p, status: "unregistered" });
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
return results;
|
|
3036
|
+
}
|
|
2651
3037
|
async function cmdServe(flags) {
|
|
2652
3038
|
const isMcp = hasFlag(flags, "mcp");
|
|
2653
3039
|
const isRegister = hasFlag(flags, "register");
|
|
@@ -2689,7 +3075,7 @@ async function cmdServe(flags) {
|
|
|
2689
3075
|
// Core tools: free tier, available without Pro.
|
|
2690
3076
|
server.addTool({
|
|
2691
3077
|
name: "memory_read",
|
|
2692
|
-
description: "Read the
|
|
3078
|
+
description: "Read ONLY the curated long-term memory (MEMORY.md) and open scratchpad items — a small saved snapshot, not a search. For finding things in daily logs/topics, or recalling past chat sessions, use the `agent-memory` skill (search/recall commands) instead of this tool.",
|
|
2693
3079
|
inputSchema: { type: "object", properties: {} },
|
|
2694
3080
|
}, async () => {
|
|
2695
3081
|
const memFile = getMemoryFile();
|
|
@@ -2739,11 +3125,16 @@ async function cmdServe(flags) {
|
|
|
2739
3125
|
server.addTool({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema }, (input) => runtime.runMcpTool(tool.name, input));
|
|
2740
3126
|
}
|
|
2741
3127
|
server.addStartupHook(() => runtime.runMcpStartup());
|
|
3128
|
+
server.addShutdownHook(() => runtime.runMcpShutdown());
|
|
2742
3129
|
}
|
|
2743
3130
|
catch {
|
|
2744
3131
|
// Pro not installed or failed to load — serve with core tools only.
|
|
2745
3132
|
}
|
|
2746
3133
|
await server.start();
|
|
3134
|
+
// Hard backstop: a Pro plugin's fs.watch handles (or any other resource
|
|
3135
|
+
// that keeps the event loop alive) must never prevent this process from
|
|
3136
|
+
// exiting once stdin has closed.
|
|
3137
|
+
process.exit(0);
|
|
2747
3138
|
}
|
|
2748
3139
|
// ---------------------------------------------------------------------------
|
|
2749
3140
|
// Usage
|
|
@@ -2753,7 +3144,7 @@ function printUsage() {
|
|
|
2753
3144
|
["Do things", ["save", "note", "recall", "search"]],
|
|
2754
3145
|
["See things", ["status", "doctor", "dashboard"]],
|
|
2755
3146
|
["Advanced", ["write", "read", "context", "scratchpad", "distil", "sync"]],
|
|
2756
|
-
["Setup", ["setup", "install-skills", "install-hooks", "completion"]],
|
|
3147
|
+
["Setup", ["setup", "install-skills", "install-hooks", "completion", "uninstall"]],
|
|
2757
3148
|
["Pro", ["pro", "learn"]],
|
|
2758
3149
|
];
|
|
2759
3150
|
const knownGroupCommands = new Set(groups.flatMap(([, list]) => list));
|
|
@@ -2854,7 +3245,7 @@ async function main() {
|
|
|
2854
3245
|
process.stdout.isTTY &&
|
|
2855
3246
|
!json &&
|
|
2856
3247
|
command &&
|
|
2857
|
-
!["init", "help", "version", "doctor", "status", "completion", "hook", "serve"].includes(command) &&
|
|
3248
|
+
!["init", "help", "version", "doctor", "status", "completion", "hook", "serve", "uninstall"].includes(command) &&
|
|
2858
3249
|
!fs.existsSync(getMemoryDir())) {
|
|
2859
3250
|
console.log(colorize("It looks like this is your first run — no memory directory yet.", "yellow"));
|
|
2860
3251
|
if (await promptYesNo("Run agent-memory init to set things up?", true)) {
|
|
@@ -2966,6 +3357,9 @@ async function main() {
|
|
|
2966
3357
|
case "uninstall-hooks":
|
|
2967
3358
|
cmdUninstallHooks(flags);
|
|
2968
3359
|
break;
|
|
3360
|
+
case "uninstall":
|
|
3361
|
+
await cmdUninstall(flags);
|
|
3362
|
+
break;
|
|
2969
3363
|
case "hook": {
|
|
2970
3364
|
const sub = positional[0];
|
|
2971
3365
|
if (sub !== "session-start" && sub !== "user-prompt-submit" && sub !== "stop") {
|
|
@@ -2988,6 +3382,13 @@ async function main() {
|
|
|
2988
3382
|
const layer = readHookMode() === "per-turn" ? "stable" : undefined;
|
|
2989
3383
|
await cmdContext(layer ? { "no-search": true, layer } : { "no-search": true });
|
|
2990
3384
|
try {
|
|
3385
|
+
const policy = readUpgradePolicy();
|
|
3386
|
+
if (!policy.existed) {
|
|
3387
|
+
// Persist the (possibly env-overridden) defaults now, so this notice
|
|
3388
|
+
// only ever fires once — the file's mere existence is the "seen" flag.
|
|
3389
|
+
writeUpgradePolicy({ cli: policy.cli, plugin: policy.plugin });
|
|
3390
|
+
console.error("agent-memory: auto-upgrade is on by default (CLI + Pro plugin). Disable with: agent-memory upgrade policy off");
|
|
3391
|
+
}
|
|
2991
3392
|
const cache = readUpgradeCache();
|
|
2992
3393
|
if (cache) {
|
|
2993
3394
|
const status = await checkForUpgrades({
|
|
@@ -2995,7 +3396,7 @@ async function main() {
|
|
|
2995
3396
|
pluginCurrent: cache.pluginCurrent,
|
|
2996
3397
|
cacheOnly: true,
|
|
2997
3398
|
});
|
|
2998
|
-
const notice = formatUpgradeNotice(status);
|
|
3399
|
+
const notice = formatUpgradeNotice(status, cache);
|
|
2999
3400
|
if (notice)
|
|
3000
3401
|
console.error(notice);
|
|
3001
3402
|
}
|
|
@@ -3022,7 +3423,7 @@ async function main() {
|
|
|
3022
3423
|
capability: "session",
|
|
3023
3424
|
});
|
|
3024
3425
|
if (decision.state === "exhausted") {
|
|
3025
|
-
console.error(`AgentMemory free session allowance resets in ${formatResetTime(decision.resetAt)}.
|
|
3426
|
+
console.error(`AgentMemory free session allowance resets in ${formatResetTime(decision.resetAt)}. Paid plans are not available yet.`);
|
|
3026
3427
|
}
|
|
3027
3428
|
}
|
|
3028
3429
|
}
|
|
@@ -3041,7 +3442,7 @@ async function main() {
|
|
|
3041
3442
|
await cmdPro(flags, positional);
|
|
3042
3443
|
break;
|
|
3043
3444
|
case "upgrade":
|
|
3044
|
-
await cmdUpgrade(flags);
|
|
3445
|
+
await cmdUpgrade(flags, positional);
|
|
3045
3446
|
break;
|
|
3046
3447
|
case "serve":
|
|
3047
3448
|
await cmdServe(flags);
|