@agentmemory/agentmemory 0.9.27 → 0.9.29
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/.env.example +15 -6
- package/AGENTS.md +7 -7
- package/README.md +292 -164
- package/dist/.env.example +15 -6
- package/dist/cli.d.mts.map +1 -1
- package/dist/cli.mjs +427 -177
- package/dist/cli.mjs.map +1 -1
- package/dist/{connect-bmZ5eqYN.mjs → connect-Qaeq15TQ.mjs} +717 -65
- package/dist/connect-Qaeq15TQ.mjs.map +1 -0
- package/dist/docker-compose.yml +2 -2
- package/dist/hooks/antigravity-bridge.d.mts +8 -0
- package/dist/hooks/antigravity-bridge.d.mts.map +1 -0
- package/dist/hooks/antigravity-bridge.mjs +114 -0
- package/dist/hooks/antigravity-bridge.mjs.map +1 -0
- package/dist/hooks/notification.mjs +21 -5
- package/dist/hooks/notification.mjs.map +1 -1
- package/dist/hooks/post-commit.mjs +15 -2
- package/dist/hooks/post-commit.mjs.map +1 -1
- package/dist/hooks/post-tool-failure.mjs +16 -4
- package/dist/hooks/post-tool-failure.mjs.map +1 -1
- package/dist/hooks/post-tool-use.mjs +16 -4
- package/dist/hooks/post-tool-use.mjs.map +1 -1
- package/dist/hooks/pre-compact.mjs +14 -3
- package/dist/hooks/pre-compact.mjs.map +1 -1
- package/dist/hooks/pre-tool-use.mjs +3 -2
- package/dist/hooks/pre-tool-use.mjs.map +1 -1
- package/dist/hooks/prompt-submit.mjs +16 -4
- package/dist/hooks/prompt-submit.mjs.map +1 -1
- package/dist/hooks/session-end.mjs +84 -19
- package/dist/hooks/session-end.mjs.map +1 -1
- package/dist/hooks/session-start.mjs +24 -5
- package/dist/hooks/session-start.mjs.map +1 -1
- package/dist/hooks/stop.mjs +3 -8
- package/dist/hooks/stop.mjs.map +1 -1
- package/dist/hooks/subagent-start.mjs +16 -4
- package/dist/hooks/subagent-start.mjs.map +1 -1
- package/dist/hooks/subagent-stop.mjs +16 -4
- package/dist/hooks/subagent-stop.mjs.map +1 -1
- package/dist/hooks/task-completed.mjs +16 -4
- package/dist/hooks/task-completed.mjs.map +1 -1
- package/dist/{image-refs-B5Zf_Djo.mjs → image-refs-BKLmp_sD.mjs} +4 -22
- package/dist/image-refs-BKLmp_sD.mjs.map +1 -0
- package/dist/image-refs-CJgZmb9X.mjs +34 -0
- package/dist/image-refs-CJgZmb9X.mjs.map +1 -0
- package/dist/index.mjs +1681 -911
- package/dist/index.mjs.map +1 -1
- package/dist/{image-store-Gpo2mgM9.mjs → keyed-mutex-DTHtcZrt.mjs} +14 -11
- package/dist/keyed-mutex-DTHtcZrt.mjs.map +1 -0
- package/dist/{logger-yHTcEBAI.mjs → logger-BEB7pCI9.mjs} +1 -1
- package/dist/{logger-yHTcEBAI.mjs.map → logger-BEB7pCI9.mjs.map} +1 -1
- package/dist/schema-BFZhNTF7.mjs +197 -0
- package/dist/schema-BFZhNTF7.mjs.map +1 -0
- package/dist/{src-MOg2zKJs.mjs → src-G7yt8gGm.mjs} +1521 -854
- package/dist/src-G7yt8gGm.mjs.map +1 -0
- package/dist/{standalone-BORXC71-.mjs → standalone-BqNJDJ04.mjs} +30 -16
- package/dist/standalone-BqNJDJ04.mjs.map +1 -0
- package/dist/standalone.d.mts.map +1 -1
- package/dist/standalone.mjs +55 -15
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-D8XVVPbJ.mjs → tools-registry-BV_2bu7z.mjs} +55 -17
- package/dist/tools-registry-BV_2bu7z.mjs.map +1 -0
- package/dist/viewer/favicon.svg +35 -1
- package/dist/viewer/index.html +473 -83
- package/docker-compose.yml +2 -2
- package/integrations/pi/README.md +77 -0
- package/integrations/pi/index.ts +403 -0
- package/integrations/pi/package.json +32 -0
- package/integrations/pi/security.ts +35 -0
- package/package.json +6 -4
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/.codex-plugin/plugin.json +2 -2
- package/plugin/.devin-plugin/plugin.json +32 -0
- package/plugin/cursor/hooks.json +41 -0
- package/plugin/cursor/mcp.json +12 -0
- package/plugin/hooks/hooks.antigravity.json +43 -0
- package/plugin/hooks/hooks.devin.json +67 -0
- package/plugin/hooks/hooks.droid.json +55 -0
- package/plugin/opencode/README.md +1 -1
- package/plugin/opencode/agentmemory-capture.ts +73 -13
- package/plugin/opencode/plugin.json +1 -1
- package/plugin/plugin.json +2 -2
- package/plugin/scripts/antigravity-bridge.d.mts +8 -0
- package/plugin/scripts/antigravity-bridge.d.mts.map +1 -0
- package/plugin/scripts/antigravity-bridge.mjs +114 -0
- package/plugin/scripts/antigravity-bridge.mjs.map +1 -0
- package/plugin/scripts/notification.mjs +21 -5
- package/plugin/scripts/notification.mjs.map +1 -1
- package/plugin/scripts/post-commit.mjs +15 -2
- package/plugin/scripts/post-commit.mjs.map +1 -1
- package/plugin/scripts/post-tool-failure.mjs +16 -4
- package/plugin/scripts/post-tool-failure.mjs.map +1 -1
- package/plugin/scripts/post-tool-use.mjs +16 -4
- package/plugin/scripts/post-tool-use.mjs.map +1 -1
- package/plugin/scripts/pre-compact.mjs +14 -3
- package/plugin/scripts/pre-compact.mjs.map +1 -1
- package/plugin/scripts/pre-tool-use.mjs +3 -2
- package/plugin/scripts/pre-tool-use.mjs.map +1 -1
- package/plugin/scripts/prompt-submit.mjs +16 -4
- package/plugin/scripts/prompt-submit.mjs.map +1 -1
- package/plugin/scripts/session-end.mjs +84 -19
- package/plugin/scripts/session-end.mjs.map +1 -1
- package/plugin/scripts/session-start.mjs +24 -5
- package/plugin/scripts/session-start.mjs.map +1 -1
- package/plugin/scripts/stop.mjs +3 -8
- package/plugin/scripts/stop.mjs.map +1 -1
- package/plugin/scripts/subagent-start.mjs +16 -4
- package/plugin/scripts/subagent-start.mjs.map +1 -1
- package/plugin/scripts/subagent-stop.mjs +16 -4
- package/plugin/scripts/subagent-stop.mjs.map +1 -1
- package/plugin/scripts/task-completed.mjs +16 -4
- package/plugin/scripts/task-completed.mjs.map +1 -1
- package/plugin/skills/_shared/TROUBLESHOOTING.md +38 -0
- package/plugin/skills/agentmemory-agents/REFERENCE.md +31 -0
- package/plugin/skills/agentmemory-agents/SKILL.md +34 -0
- package/plugin/skills/agentmemory-architecture/SKILL.md +33 -0
- package/plugin/skills/agentmemory-config/REFERENCE.md +45 -0
- package/plugin/skills/agentmemory-config/SKILL.md +37 -0
- package/plugin/skills/agentmemory-hooks/REFERENCE.md +20 -0
- package/plugin/skills/agentmemory-hooks/SKILL.md +39 -0
- package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
- package/plugin/skills/agentmemory-mcp-tools/SKILL.md +39 -0
- package/plugin/skills/agentmemory-rest-api/REFERENCE.md +142 -0
- package/plugin/skills/agentmemory-rest-api/SKILL.md +43 -0
- package/plugin/skills/commit-context/EXAMPLES.md +71 -0
- package/plugin/skills/commit-context/SKILL.md +53 -8
- package/plugin/skills/commit-history/EXAMPLES.md +71 -0
- package/plugin/skills/commit-history/SKILL.md +51 -9
- package/plugin/skills/forget/EXAMPLES.md +75 -0
- package/plugin/skills/forget/SKILL.md +57 -14
- package/plugin/skills/handoff/EXAMPLES.md +56 -0
- package/plugin/skills/handoff/SKILL.md +56 -9
- package/plugin/skills/lesson/SKILL.md +64 -0
- package/plugin/skills/memory-discipline/SKILL.md +57 -0
- package/plugin/skills/recall/EXAMPLES.md +74 -0
- package/plugin/skills/recall/SKILL.md +53 -11
- package/plugin/skills/recap/EXAMPLES.md +60 -0
- package/plugin/skills/recap/SKILL.md +51 -13
- package/plugin/skills/remember/EXAMPLES.md +67 -0
- package/plugin/skills/remember/SKILL.md +58 -17
- package/plugin/skills/session-history/EXAMPLES.md +59 -0
- package/plugin/skills/session-history/SKILL.md +54 -11
- package/plugin/skills/write-agentmemory-skill/SKILL.md +40 -0
- package/dist/connect-bmZ5eqYN.mjs.map +0 -1
- package/dist/image-refs-B5Zf_Djo.mjs.map +0 -1
- package/dist/image-store-Gpo2mgM9.mjs.map +0 -1
- package/dist/rolldown-runtime-twds-ZHy.mjs +0 -14
- package/dist/schema-CNiCWzV9.mjs +0 -78
- package/dist/schema-CNiCWzV9.mjs.map +0 -1
- package/dist/src-MOg2zKJs.mjs.map +0 -1
- package/dist/standalone-BORXC71-.mjs.map +0 -1
- package/dist/tools-registry-D8XVVPbJ.mjs.map +0 -1
- package/dist/version-9DNrRyph.mjs +0 -6
- package/dist/version-9DNrRyph.mjs.map +0 -1
- package/plugin/scripts/diagnostics.d.mts +0 -17
- package/plugin/scripts/diagnostics.d.mts.map +0 -1
- package/plugin/scripts/diagnostics.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as
|
|
3
|
-
import {
|
|
4
|
-
import { r as setBootVerbose } from "./logger-
|
|
5
|
-
import {
|
|
2
|
+
import { a as runAdapter, i as resolveAdapter, r as knownAgents, t as ADAPTERS } from "./connect-Qaeq15TQ.mjs";
|
|
3
|
+
import { i as generateId } from "./schema-BFZhNTF7.mjs";
|
|
4
|
+
import { r as setBootVerbose } from "./logger-BEB7pCI9.mjs";
|
|
5
|
+
import { i as VERSION, n as getAllTools, p as hydrateProcessEnvFromFile, t as ESSENTIAL_TOOLS } from "./tools-registry-BV_2bu7z.mjs";
|
|
6
6
|
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
7
|
-
import { closeSync, constants, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
8
|
-
import { delimiter, dirname, join } from "node:path";
|
|
7
|
+
import { appendFileSync, closeSync, constants, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
8
|
+
import { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { homedir, platform } from "node:os";
|
|
11
11
|
import * as p from "@clack/prompts";
|
|
12
|
+
import pc from "picocolors";
|
|
12
13
|
import { copyFile, mkdir } from "node:fs/promises";
|
|
13
14
|
//#region src/cli/doctor-diagnostics.ts
|
|
14
15
|
/** Common placeholder values shipped in .env.example. */
|
|
@@ -436,7 +437,8 @@ const DEFAULTS = {
|
|
|
436
437
|
skipNpxHint: false,
|
|
437
438
|
skipGlobalInstall: false,
|
|
438
439
|
skipConsoleInstall: false,
|
|
439
|
-
firstRunAt: null
|
|
440
|
+
firstRunAt: null,
|
|
441
|
+
injectContextChosen: false
|
|
440
442
|
};
|
|
441
443
|
function prefsDir() {
|
|
442
444
|
return join(homedir(), ".agentmemory");
|
|
@@ -492,95 +494,14 @@ function isFirstRun() {
|
|
|
492
494
|
//#endregion
|
|
493
495
|
//#region src/cli/onboarding.ts
|
|
494
496
|
const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
label: "GitHub Copilot CLI",
|
|
504
|
-
glyph: "◈"
|
|
505
|
-
},
|
|
506
|
-
{
|
|
507
|
-
value: "codex",
|
|
508
|
-
label: "Codex",
|
|
509
|
-
glyph: "◎"
|
|
510
|
-
},
|
|
511
|
-
{
|
|
512
|
-
value: "openhuman",
|
|
513
|
-
label: "OpenHuman",
|
|
514
|
-
glyph: "◇"
|
|
515
|
-
},
|
|
516
|
-
{
|
|
517
|
-
value: "openclaw",
|
|
518
|
-
label: "OpenClaw",
|
|
519
|
-
glyph: "◇"
|
|
520
|
-
},
|
|
521
|
-
{
|
|
522
|
-
value: "hermes",
|
|
523
|
-
label: "Hermes",
|
|
524
|
-
glyph: "◇"
|
|
525
|
-
},
|
|
526
|
-
{
|
|
527
|
-
value: "pi",
|
|
528
|
-
label: "Pi",
|
|
529
|
-
glyph: "◇"
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
value: "cursor",
|
|
533
|
-
label: "Cursor",
|
|
534
|
-
glyph: "◫"
|
|
535
|
-
},
|
|
536
|
-
{
|
|
537
|
-
value: "gemini-cli",
|
|
538
|
-
label: "Gemini CLI",
|
|
539
|
-
glyph: "✦"
|
|
540
|
-
}
|
|
541
|
-
];
|
|
542
|
-
const MCP_AGENTS = [
|
|
543
|
-
{
|
|
544
|
-
value: "opencode",
|
|
545
|
-
label: "OpenCode",
|
|
546
|
-
glyph: "⬡"
|
|
547
|
-
},
|
|
548
|
-
{
|
|
549
|
-
value: "cline",
|
|
550
|
-
label: "Cline",
|
|
551
|
-
glyph: "◇"
|
|
552
|
-
},
|
|
553
|
-
{
|
|
554
|
-
value: "goose",
|
|
555
|
-
label: "Goose",
|
|
556
|
-
glyph: "◇"
|
|
557
|
-
},
|
|
558
|
-
{
|
|
559
|
-
value: "kilo",
|
|
560
|
-
label: "Kilo",
|
|
561
|
-
glyph: "◇"
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
value: "aider",
|
|
565
|
-
label: "Aider",
|
|
566
|
-
glyph: "◇"
|
|
567
|
-
},
|
|
568
|
-
{
|
|
569
|
-
value: "claude-desktop",
|
|
570
|
-
label: "Claude Desktop",
|
|
571
|
-
glyph: "⟁"
|
|
572
|
-
},
|
|
573
|
-
{
|
|
574
|
-
value: "windsurf",
|
|
575
|
-
label: "Windsurf",
|
|
576
|
-
glyph: "◇"
|
|
577
|
-
},
|
|
578
|
-
{
|
|
579
|
-
value: "roo",
|
|
580
|
-
label: "Roo",
|
|
581
|
-
glyph: "◇"
|
|
582
|
-
}
|
|
583
|
-
];
|
|
497
|
+
const AGENT_GLYPH = {
|
|
498
|
+
"claude-code": "⟁",
|
|
499
|
+
"copilot-cli": "◈",
|
|
500
|
+
codex: "◎",
|
|
501
|
+
cursor: "◫",
|
|
502
|
+
"gemini-cli": "✦",
|
|
503
|
+
opencode: "⬡"
|
|
504
|
+
};
|
|
584
505
|
const PROVIDERS = [
|
|
585
506
|
{
|
|
586
507
|
value: "anthropic",
|
|
@@ -604,7 +525,7 @@ const PROVIDERS = [
|
|
|
604
525
|
},
|
|
605
526
|
{
|
|
606
527
|
value: "minimax",
|
|
607
|
-
label: "MiniMax —
|
|
528
|
+
label: "MiniMax — MiniMax-M3",
|
|
608
529
|
envKey: "MINIMAX_API_KEY"
|
|
609
530
|
},
|
|
610
531
|
{
|
|
@@ -613,16 +534,20 @@ const PROVIDERS = [
|
|
|
613
534
|
envKey: null
|
|
614
535
|
}
|
|
615
536
|
];
|
|
537
|
+
const PROVIDER_COST_HINTS = {
|
|
538
|
+
anthropic: "rough cost: a fast Haiku-class model keeps compress/consolidate at fractions of a cent per session.",
|
|
539
|
+
openai: "rough cost: a mini-class model keeps compress/consolidate at fractions of a cent per session.",
|
|
540
|
+
gemini: "rough cost: a Flash-class model keeps compress/consolidate at fractions of a cent per session.",
|
|
541
|
+
openrouter: "rough cost: pick a small model; spend tracks your chosen model's per-token price.",
|
|
542
|
+
minimax: "rough cost: scales with the MiniMax model price per token."
|
|
543
|
+
};
|
|
616
544
|
function buildAgentOptions() {
|
|
617
|
-
|
|
618
|
-
value: a.
|
|
619
|
-
label: `${a.
|
|
620
|
-
hint: "native plugin"
|
|
621
|
-
}))
|
|
622
|
-
|
|
623
|
-
label: `${a.glyph} ${a.label}`,
|
|
624
|
-
hint: "MCP server"
|
|
625
|
-
}))];
|
|
545
|
+
const options = ADAPTERS.map((a) => ({
|
|
546
|
+
value: a.name,
|
|
547
|
+
label: `${AGENT_GLYPH[a.name] ?? "◇"} ${a.displayName}`,
|
|
548
|
+
hint: a.category === "native" ? "native plugin" : "MCP server"
|
|
549
|
+
}));
|
|
550
|
+
return [...options.filter((o) => o.hint === "native plugin"), ...options.filter((o) => o.hint === "MCP server")];
|
|
626
551
|
}
|
|
627
552
|
function getInitialAgentValues(env = process.env) {
|
|
628
553
|
if (env["COPILOT_CLI"] === "1" || env["COPILOT_AGENT_SESSION_ID"]) return ["copilot-cli"];
|
|
@@ -715,7 +640,12 @@ async function runOnboarding() {
|
|
|
715
640
|
}
|
|
716
641
|
const provider = providerPicked === "skip" ? null : providerPicked;
|
|
717
642
|
const agents = agentsPicked ?? [];
|
|
643
|
+
if (provider) {
|
|
644
|
+
const hint = PROVIDER_COST_HINTS[provider];
|
|
645
|
+
if (hint) p.log.info(hint);
|
|
646
|
+
}
|
|
718
647
|
const envPath = await seedEnvFile(provider);
|
|
648
|
+
await maybePromptContextInjection(envPath);
|
|
719
649
|
writePrefs({
|
|
720
650
|
lastAgent: agents[0] ?? null,
|
|
721
651
|
lastAgents: agents,
|
|
@@ -737,6 +667,33 @@ async function runOnboarding() {
|
|
|
737
667
|
provider
|
|
738
668
|
};
|
|
739
669
|
}
|
|
670
|
+
function enableInjectContextInEnv(envPath) {
|
|
671
|
+
if (!envPath || !existsSync(envPath)) return false;
|
|
672
|
+
try {
|
|
673
|
+
const current = readFileSync(envPath, "utf-8");
|
|
674
|
+
if (/^\s*AGENTMEMORY_INJECT_CONTEXT\s*=\s*true\b/m.test(current)) return true;
|
|
675
|
+
appendFileSync(envPath, `${current.length > 0 && !current.endsWith("\n") ? "\n" : ""}AGENTMEMORY_INJECT_CONTEXT=true\n`, { mode: 384 });
|
|
676
|
+
return true;
|
|
677
|
+
} catch {
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
async function maybePromptContextInjection(envPath) {
|
|
682
|
+
if (readPrefs().injectContextChosen) return;
|
|
683
|
+
const enable = await p.confirm({
|
|
684
|
+
message: "Enable automatic context injection so the agent recalls past sessions without being asked? [y/N]",
|
|
685
|
+
initialValue: false
|
|
686
|
+
});
|
|
687
|
+
if (p.isCancel(enable)) {
|
|
688
|
+
p.cancel("Setup cancelled. Re-run any time with: agentmemory --reset");
|
|
689
|
+
process.exit(0);
|
|
690
|
+
}
|
|
691
|
+
p.log.info("Cost note: injection spends session tokens proportional to tool-call frequency. Default is off.");
|
|
692
|
+
writePrefs({ injectContextChosen: true });
|
|
693
|
+
if (enable === true) if (enableInjectContextInEnv(envPath)) p.log.success("Context injection enabled (AGENTMEMORY_INJECT_CONTEXT=true).");
|
|
694
|
+
else p.log.warn("Could not update ~/.agentmemory/.env. Set AGENTMEMORY_INJECT_CONTEXT=true there to enable it.");
|
|
695
|
+
else p.log.info("Context injection left off. Set AGENTMEMORY_INJECT_CONTEXT=true later to enable.");
|
|
696
|
+
}
|
|
740
697
|
async function wireSelectedAgents(agents) {
|
|
741
698
|
p.note("Wire selected agents now?", "next step");
|
|
742
699
|
const confirmed = await p.confirm({
|
|
@@ -808,13 +765,107 @@ async function wireSelectedAgents(agents) {
|
|
|
808
765
|
p.note(summary.join("\n"), "wire summary");
|
|
809
766
|
}
|
|
810
767
|
//#endregion
|
|
768
|
+
//#region src/cli-data-dir.ts
|
|
769
|
+
function argValue(args, name) {
|
|
770
|
+
const equals = `${name}=`;
|
|
771
|
+
const inline = args.find((arg) => arg.startsWith(equals));
|
|
772
|
+
if (inline) return inline.slice(equals.length);
|
|
773
|
+
const idx = args.indexOf(name);
|
|
774
|
+
if (idx !== -1) return args[idx + 1];
|
|
775
|
+
}
|
|
776
|
+
function expandHome(pathValue, home) {
|
|
777
|
+
if (pathValue === "~") return home;
|
|
778
|
+
if (pathValue.startsWith("~/") || pathValue.startsWith("~\\")) return join(home, pathValue.slice(2));
|
|
779
|
+
return pathValue;
|
|
780
|
+
}
|
|
781
|
+
function absoluteDataDir(pathValue, cwd, home) {
|
|
782
|
+
const expanded = expandHome(pathValue, home);
|
|
783
|
+
return isAbsolute(expanded) ? expanded : resolve(cwd, expanded);
|
|
784
|
+
}
|
|
785
|
+
function platformDefaultDataDir(env, home, nodePlatform, useXdg = true) {
|
|
786
|
+
if (nodePlatform === "darwin") return join(home, "Library", "Application Support", "agentmemory");
|
|
787
|
+
if (nodePlatform === "win32") {
|
|
788
|
+
const appData = env["APPDATA"];
|
|
789
|
+
return appData ? join(appData, "agentmemory") : join(home, ".agentmemory");
|
|
790
|
+
}
|
|
791
|
+
const xdgDataHome = env["XDG_DATA_HOME"];
|
|
792
|
+
if (useXdg && xdgDataHome && isAbsolute(xdgDataHome)) return join(xdgDataHome, "agentmemory");
|
|
793
|
+
return join(home, ".local", "share", "agentmemory");
|
|
794
|
+
}
|
|
795
|
+
function nearestGitParent(pathValue) {
|
|
796
|
+
let current = pathValue;
|
|
797
|
+
while (true) {
|
|
798
|
+
if (existsSync(join(current, ".git"))) return current;
|
|
799
|
+
const parent = dirname(current);
|
|
800
|
+
if (parent === current) return void 0;
|
|
801
|
+
current = parent;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
function isWithin(parent, child) {
|
|
805
|
+
const pathFromParent = relative(parent, child);
|
|
806
|
+
return pathFromParent === "" || pathFromParent !== ".." && !pathFromParent.startsWith(`..${sep}`) && !isAbsolute(pathFromParent);
|
|
807
|
+
}
|
|
808
|
+
function resolveDataDir(options = {}) {
|
|
809
|
+
const args = options.args ?? process.argv.slice(2);
|
|
810
|
+
const env = options.env ?? process.env;
|
|
811
|
+
const cwd = options.cwd ?? process.cwd();
|
|
812
|
+
const home = options.home ?? homedir();
|
|
813
|
+
const nodePlatform = options.platform ?? platform();
|
|
814
|
+
const parsedInstance = parseInt(argValue(args, "--instance") ?? "0", 10);
|
|
815
|
+
const instance = parsedInstance > 0 && parsedInstance <= 50 ? parsedInstance : 0;
|
|
816
|
+
const forInstance = (dataDir) => instance ? join(dataDir, `instance-${instance}`) : dataDir;
|
|
817
|
+
const flagValue = argValue(args, "--data-dir");
|
|
818
|
+
if (flagValue) return {
|
|
819
|
+
dataDir: forInstance(absoluteDataDir(flagValue, cwd, home)),
|
|
820
|
+
source: "flag"
|
|
821
|
+
};
|
|
822
|
+
const envValue = env["AGENTMEMORY_DATA_DIR"];
|
|
823
|
+
if (envValue) return {
|
|
824
|
+
dataDir: forInstance(absoluteDataDir(envValue, cwd, home)),
|
|
825
|
+
source: "env"
|
|
826
|
+
};
|
|
827
|
+
const legacyDir = resolve(cwd, "data");
|
|
828
|
+
const hasLegacyStore = existsSync(join(legacyDir, "state_store.db")) || existsSync(join(legacyDir, "iii-config.yaml"));
|
|
829
|
+
if (instance === 0 && hasLegacyStore) return {
|
|
830
|
+
dataDir: legacyDir,
|
|
831
|
+
source: "default"
|
|
832
|
+
};
|
|
833
|
+
const defaultDir = platformDefaultDataDir(env, home, nodePlatform);
|
|
834
|
+
const gitParent = nearestGitParent(cwd);
|
|
835
|
+
if (gitParent && isWithin(gitParent, defaultDir)) {
|
|
836
|
+
const relocated = platformDefaultDataDir(env, home, nodePlatform, false);
|
|
837
|
+
if (relocated !== defaultDir) return {
|
|
838
|
+
dataDir: forInstance(relocated),
|
|
839
|
+
source: "default",
|
|
840
|
+
relocatedFrom: defaultDir
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
return {
|
|
844
|
+
dataDir: forInstance(defaultDir),
|
|
845
|
+
source: "default"
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
//#endregion
|
|
811
849
|
//#region src/cli.ts
|
|
850
|
+
const c = {
|
|
851
|
+
url: pc.cyan,
|
|
852
|
+
ok: pc.green,
|
|
853
|
+
warn: pc.yellow,
|
|
854
|
+
err: pc.red,
|
|
855
|
+
cmd: (s) => pc.bold(pc.cyan(s)),
|
|
856
|
+
label: pc.bold,
|
|
857
|
+
dim: pc.dim,
|
|
858
|
+
accent: (s) => pc.bold(pc.yellow(s))
|
|
859
|
+
};
|
|
860
|
+
const ALL_TOOLS_COUNT = getAllTools().length;
|
|
861
|
+
const CORE_TOOLS_COUNT = getAllTools().filter((t) => ESSENTIAL_TOOLS.has(t.name)).length;
|
|
812
862
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
813
863
|
const args = process.argv.slice(2);
|
|
814
864
|
const IS_WINDOWS = platform() === "win32";
|
|
815
865
|
const IS_VERBOSE = args.includes("--verbose") || args.includes("-v") || process.env["AGENTMEMORY_VERBOSE"] === "1" || process.env["AGENTMEMORY_VERBOSE"] === "true";
|
|
816
866
|
setBootVerbose(IS_VERBOSE);
|
|
817
867
|
const IS_RESET = args.includes("--reset");
|
|
868
|
+
hydrateProcessEnvFromFile();
|
|
818
869
|
if (args.includes("--version") || args.includes("-V")) {
|
|
819
870
|
process.stdout.write(`${VERSION}\n`);
|
|
820
871
|
process.exit(0);
|
|
@@ -840,6 +891,19 @@ function iiiReleaseUrl() {
|
|
|
840
891
|
function vlog(msg) {
|
|
841
892
|
if (IS_VERBOSE) p.log.info(`[verbose] ${msg}`);
|
|
842
893
|
}
|
|
894
|
+
function wrapList(items, indent, width = 78) {
|
|
895
|
+
const lines = [];
|
|
896
|
+
let line = "";
|
|
897
|
+
for (const item of items) {
|
|
898
|
+
const joined = line ? `${line}, ${item}` : item;
|
|
899
|
+
if (line && indent + joined.length > width) {
|
|
900
|
+
lines.push(`${line},`);
|
|
901
|
+
line = item;
|
|
902
|
+
} else line = joined;
|
|
903
|
+
}
|
|
904
|
+
lines.push(line);
|
|
905
|
+
return lines.join(`\n${" ".repeat(indent)}`);
|
|
906
|
+
}
|
|
843
907
|
if (args.includes("--help") || args.includes("-h")) {
|
|
844
908
|
console.log(`
|
|
845
909
|
agentmemory — persistent memory for AI coding agents
|
|
@@ -849,9 +913,8 @@ Usage: agentmemory [command] [options]
|
|
|
849
913
|
Commands:
|
|
850
914
|
(default) Start agentmemory worker
|
|
851
915
|
init Copy bundled .env.example to ~/.agentmemory/.env if absent
|
|
852
|
-
connect [agent] Wire agentmemory into an installed agent
|
|
853
|
-
|
|
854
|
-
hermes, pi, openhuman).
|
|
916
|
+
connect [agent] Wire agentmemory into an installed agent
|
|
917
|
+
(${wrapList(knownAgents(), 21)}).
|
|
855
918
|
No arg = interactive picker. --all wires every detected agent.
|
|
856
919
|
--dry-run shows what would change. --force re-installs.
|
|
857
920
|
status Show connection status, memory count, flags, and health
|
|
@@ -860,7 +923,9 @@ Commands:
|
|
|
860
923
|
--dry-run: show what each fix would do, don't execute
|
|
861
924
|
remove Cleanly uninstall agentmemory (pidfile, state, .env, binaries).
|
|
862
925
|
--force: skip confirmations · --keep-data: keep memory data
|
|
863
|
-
demo
|
|
926
|
+
demo [--serve] Seed sample sessions and show recall in action.
|
|
927
|
+
--serve boots the server, runs the demo, and stops it
|
|
928
|
+
in one command (no second terminal).
|
|
864
929
|
upgrade Upgrade local deps + iii runtime (best effort)
|
|
865
930
|
stop [--force] Stop the running iii-engine started by this CLI.
|
|
866
931
|
--force bypasses the Docker-heuristic guard and signals
|
|
@@ -876,7 +941,7 @@ Options:
|
|
|
876
941
|
--help, -h Show this help
|
|
877
942
|
--verbose, -v Show engine stderr, boot log, and diagnostic info
|
|
878
943
|
--reset Wipe ~/.agentmemory/preferences.json and re-run onboarding
|
|
879
|
-
--tools all|core Tool visibility (default: all =
|
|
944
|
+
--tools all|core Tool visibility (default: all = ${ALL_TOOLS_COUNT} tools; core = ${CORE_TOOLS_COUNT} essentials)
|
|
880
945
|
--no-engine Skip auto-starting iii-engine
|
|
881
946
|
--port <N> Override REST port (default: 3111). Streams (N+1), viewer
|
|
882
947
|
(N+2), and iii engine (N+46023) auto-derive from N so a
|
|
@@ -884,10 +949,12 @@ Options:
|
|
|
884
949
|
--instance <N> Shortcut for --port (3111 + N*100) to run multiple
|
|
885
950
|
daemons side-by-side without env gymnastics.
|
|
886
951
|
--instance 1 -> 3211/3212/3213/49234, etc. (max N=50)
|
|
952
|
+
--data-dir <path> Store iii-engine state outside the current repo
|
|
887
953
|
|
|
888
954
|
Environment:
|
|
889
955
|
AGENTMEMORY_URL Full REST base URL (e.g. http://localhost:3111).
|
|
890
956
|
Honored by status, doctor, and MCP shim commands.
|
|
957
|
+
AGENTMEMORY_DATA_DIR State directory fallback when --data-dir is not set.
|
|
891
958
|
AGENTMEMORY_USE_DOCKER=1 Prefer the bundled docker-compose path over the
|
|
892
959
|
native iii-engine binary on first run.
|
|
893
960
|
AGENTMEMORY_III_VERSION Override pinned iii-engine version (default ${IIPINNED_VERSION}).
|
|
@@ -907,7 +974,11 @@ Quick start:
|
|
|
907
974
|
process.exit(0);
|
|
908
975
|
}
|
|
909
976
|
const toolsIdx = args.indexOf("--tools");
|
|
910
|
-
if (toolsIdx !== -1 && args[toolsIdx + 1])
|
|
977
|
+
if (toolsIdx !== -1 && args[toolsIdx + 1]) {
|
|
978
|
+
const toolsMode = args[toolsIdx + 1];
|
|
979
|
+
if (toolsMode !== "all" && toolsMode !== "core") p.log.warn(`Unknown --tools value "${toolsMode}" (valid: all, core); falling back to all.`);
|
|
980
|
+
process.env["AGENTMEMORY_TOOLS"] = toolsMode;
|
|
981
|
+
}
|
|
911
982
|
const portIdx = args.indexOf("--port");
|
|
912
983
|
if (portIdx !== -1 && args[portIdx + 1]) process.env["III_REST_PORT"] = args[portIdx + 1];
|
|
913
984
|
const instanceIdx = args.indexOf("--instance");
|
|
@@ -918,6 +989,8 @@ if (instanceIdx !== -1 && args[instanceIdx + 1]) {
|
|
|
918
989
|
if (!process.env["III_REST_PORT"]) process.env["III_REST_PORT"] = String(base);
|
|
919
990
|
}
|
|
920
991
|
}
|
|
992
|
+
const dataDirResolution = resolveDataDir({ args });
|
|
993
|
+
if (dataDirResolution.source !== "default") process.env["AGENTMEMORY_DATA_DIR"] = dataDirResolution.dataDir;
|
|
921
994
|
const skipEngine = args.includes("--no-engine");
|
|
922
995
|
function getRestPort() {
|
|
923
996
|
const url = process.env["AGENTMEMORY_URL"];
|
|
@@ -1012,6 +1085,30 @@ function findIiiConfig() {
|
|
|
1012
1085
|
for (const c of candidates) if (existsSync(c)) return c;
|
|
1013
1086
|
return "";
|
|
1014
1087
|
}
|
|
1088
|
+
function yamlSingleQuoted(value) {
|
|
1089
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
1090
|
+
}
|
|
1091
|
+
function renderIiiConfig(template, dataDir) {
|
|
1092
|
+
return template.replace("file_path: ./data/state_store.db", `file_path: ${yamlSingleQuoted(join(dataDir, "state_store.db"))}`).replace("file_path: ./data/stream_store", `file_path: ${yamlSingleQuoted(join(dataDir, "stream_store"))}`);
|
|
1093
|
+
}
|
|
1094
|
+
function writeRuntimeIiiConfig(configPath, dataDir) {
|
|
1095
|
+
mkdirSync(dataDir, { recursive: true });
|
|
1096
|
+
const runtimeConfigPath = join(dataDir, "iii-config.yaml");
|
|
1097
|
+
writeFileSync(runtimeConfigPath, renderIiiConfig(readFileSync(configPath, "utf8"), dataDir));
|
|
1098
|
+
return runtimeConfigPath;
|
|
1099
|
+
}
|
|
1100
|
+
function warnIfRelocatedDataDir() {
|
|
1101
|
+
if (!dataDirResolution.relocatedFrom) return;
|
|
1102
|
+
try {
|
|
1103
|
+
mkdirSync(dataDirResolution.dataDir, { recursive: true });
|
|
1104
|
+
const marker = join(dataDirResolution.dataDir, ".cwd-relocation-warning");
|
|
1105
|
+
if (existsSync(marker)) return;
|
|
1106
|
+
p.log.warn(`Default data dir ${dataDirResolution.relocatedFrom} is inside a git worktree; using ${dataDirResolution.dataDir} instead.`);
|
|
1107
|
+
writeFileSync(marker, (/* @__PURE__ */ new Date()).toISOString());
|
|
1108
|
+
} catch {
|
|
1109
|
+
p.log.warn(`Default data dir ${dataDirResolution.relocatedFrom} is inside a git worktree; using ${dataDirResolution.dataDir} instead.`);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1015
1112
|
function whichBinary(name) {
|
|
1016
1113
|
const cmd = IS_WINDOWS ? "where" : "which";
|
|
1017
1114
|
try {
|
|
@@ -1188,8 +1285,8 @@ async function maybeOfferGlobalInstall() {
|
|
|
1188
1285
|
if (runCommand(npmBin, [
|
|
1189
1286
|
"install",
|
|
1190
1287
|
"-g",
|
|
1191
|
-
`@agentmemory/agentmemory@0.9.
|
|
1192
|
-
], { label: `Installing @agentmemory/agentmemory@0.9.
|
|
1288
|
+
`@agentmemory/agentmemory@0.9.29`
|
|
1289
|
+
], { label: `Installing @agentmemory/agentmemory@0.9.29 globally` })) {
|
|
1193
1290
|
p.log.success("Installed globally. `agentmemory stop` etc. will now work in new shells.");
|
|
1194
1291
|
writePrefs({ skipGlobalInstall: true });
|
|
1195
1292
|
} else p.log.warn("Global install failed. Try manually: npm install -g @agentmemory/agentmemory");
|
|
@@ -1244,13 +1341,20 @@ function adoptRunningEngine() {
|
|
|
1244
1341
|
const existingPid = readEnginePidfile();
|
|
1245
1342
|
if (existingState && existingPid) return;
|
|
1246
1343
|
const enginePid = findEnginePidsByPort(getRestPort())[0];
|
|
1344
|
+
if (enginePid) {
|
|
1345
|
+
const comm = pidCommand(enginePid);
|
|
1346
|
+
if (isForeignPortHolder(comm)) {
|
|
1347
|
+
vlog(`adoptRunningEngine: refusing to adopt pid ${enginePid} (${comm}) — not the iii engine binary`);
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1247
1351
|
if (enginePid && !existingPid) writeEnginePidfile(enginePid);
|
|
1248
1352
|
if (!existingState) writeEngineState({
|
|
1249
1353
|
kind: "native",
|
|
1250
1354
|
configPath: findIiiConfig() || "",
|
|
1251
1355
|
attached: true
|
|
1252
1356
|
});
|
|
1253
|
-
if (enginePid && !existingPid) p.log.info(`Attached to existing iii-engine (pid ${enginePid})`);
|
|
1357
|
+
if (enginePid && !existingPid) p.log.info(c.ok(`Attached to existing iii-engine (pid ${enginePid})`));
|
|
1254
1358
|
} catch (err) {
|
|
1255
1359
|
vlog(`adoptRunningEngine: ${err instanceof Error ? err.message : String(err)}`);
|
|
1256
1360
|
}
|
|
@@ -1352,7 +1456,7 @@ function startIiiBin(iiiBin, configPath) {
|
|
|
1352
1456
|
binPath: iiiBin
|
|
1353
1457
|
});
|
|
1354
1458
|
spawnEngineBackground(iiiBin, ["--config", configPath], "iii-engine");
|
|
1355
|
-
s.stop("iii-engine process started");
|
|
1459
|
+
s.stop(c.ok("iii-engine process started"));
|
|
1356
1460
|
return true;
|
|
1357
1461
|
}
|
|
1358
1462
|
function pickCompatibleIii(candidates) {
|
|
@@ -1365,17 +1469,19 @@ function pickCompatibleIii(candidates) {
|
|
|
1365
1469
|
}
|
|
1366
1470
|
async function startEngine() {
|
|
1367
1471
|
const configPath = findIiiConfig();
|
|
1472
|
+
warnIfRelocatedDataDir();
|
|
1473
|
+
const runtimeConfigPath = configPath ? writeRuntimeIiiConfig(configPath, dataDirResolution.dataDir) : "";
|
|
1368
1474
|
const pathIii = whichBinary("iii");
|
|
1369
|
-
vlog(`iii binary: ${pathIii ?? "(not on PATH)"}, config: ${
|
|
1475
|
+
vlog(`iii binary: ${pathIii ?? "(not on PATH)"}, config: ${runtimeConfigPath || "(not found)"}`);
|
|
1370
1476
|
const fallbacks = fallbackIiiPaths().filter((p) => existsSync(p));
|
|
1371
1477
|
for (const f of fallbacks) vlog(`fallback iii at ${f} reports version: ${iiiBinVersion(f) ?? "unknown"}`);
|
|
1372
1478
|
let iiiBin = pickCompatibleIii([pathIii, ...fallbacks]);
|
|
1373
|
-
if (iiiBin &&
|
|
1479
|
+
if (iiiBin && runtimeConfigPath) {
|
|
1374
1480
|
if (iiiBin !== pathIii) {
|
|
1375
|
-
p.log.info(`Using iii at: ${iiiBin} (v${IIPINNED_VERSION})`);
|
|
1481
|
+
p.log.info(`Using iii at: ${c.dim(iiiBin)} (v${c.accent(IIPINNED_VERSION)})`);
|
|
1376
1482
|
process.env["PATH"] = `${dirname(iiiBin)}${delimiter}${process.env["PATH"] ?? ""}`;
|
|
1377
1483
|
}
|
|
1378
|
-
return startIiiBin(iiiBin,
|
|
1484
|
+
return startIiiBin(iiiBin, runtimeConfigPath);
|
|
1379
1485
|
}
|
|
1380
1486
|
if (pathIii && !iiiBin) vlog(`iii on PATH is v${iiiBinVersion(pathIii) ?? "unknown"}, pin is v${IIPINNED_VERSION}. Will install pinned engine to ${privateIiiPath()}.`);
|
|
1381
1487
|
if (!configPath) {
|
|
@@ -1384,6 +1490,7 @@ async function startEngine() {
|
|
|
1384
1490
|
}
|
|
1385
1491
|
const dockerBin = whichBinary("docker");
|
|
1386
1492
|
vlog(`docker binary: ${dockerBin ?? "(not on PATH)"}`);
|
|
1493
|
+
mkdirSync(dataDirResolution.dataDir, { recursive: true });
|
|
1387
1494
|
const composeFile = [
|
|
1388
1495
|
join(__dirname, "..", "docker-compose.yml"),
|
|
1389
1496
|
join(__dirname, "docker-compose.yml"),
|
|
@@ -1545,19 +1652,25 @@ function printReadyHint(consoleState) {
|
|
|
1545
1652
|
const engineHost = getEngineHost();
|
|
1546
1653
|
const streamUrl = `ws://${engineHost}:${getStreamPort()}`;
|
|
1547
1654
|
const engineUrl = `ws://${engineHost}:${getEnginePort()}`;
|
|
1548
|
-
const consoleLine = consoleState.kind === "installed" ?
|
|
1655
|
+
const consoleLine = consoleState.kind === "installed" ? `${c.label("iii console")} ${c.dim(consoleState.binPath)} ${c.dim(`(run: ${consoleState.binPath} -p <port>)`)}` : `${c.label("iii console")} ${c.dim(`(install: ${iiiConsoleInstallHint()})`)}`;
|
|
1549
1656
|
const lines = [
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1657
|
+
`${c.label("REST API")} ${c.url(restUrl)}`,
|
|
1658
|
+
`${c.label("Viewer")} ${c.url(viewerUrl)}`,
|
|
1659
|
+
`${c.label("Streams")} ${c.url(streamUrl)}`,
|
|
1660
|
+
`${c.label("Engine")} ${c.url(engineUrl)}`,
|
|
1554
1661
|
consoleLine
|
|
1555
1662
|
];
|
|
1556
|
-
p.note(lines.join("\n"), `agentmemory v${VERSION}`);
|
|
1663
|
+
p.note(lines.join("\n"), `agentmemory v${c.accent(VERSION)}`);
|
|
1557
1664
|
const demoCommand = isInvokedViaNpx() ? "npx @agentmemory/agentmemory demo" : "agentmemory demo";
|
|
1558
|
-
process.stdout.write(`\
|
|
1665
|
+
process.stdout.write(`\n${c.dim("Try:")} ${c.cmd(demoCommand)}\n`);
|
|
1559
1666
|
}
|
|
1560
1667
|
async function main() {
|
|
1668
|
+
try {
|
|
1669
|
+
if ((await fetch(`${getBaseUrl()}/agentmemory/livez`, { signal: AbortSignal.timeout(1500) })).ok) {
|
|
1670
|
+
p.log.error(`agentmemory is already running on port ${getRestPort()}. Starting a second instance here would corrupt the running daemon's REST routing. Use the REST API (or the MCP tools) against the running instance, run a different --instance, or stop it first with \`agentmemory stop\`.`);
|
|
1671
|
+
process.exit(1);
|
|
1672
|
+
}
|
|
1673
|
+
} catch {}
|
|
1561
1674
|
if (IS_RESET) resetPrefs();
|
|
1562
1675
|
const firstRun = isFirstRun();
|
|
1563
1676
|
const prefs = readPrefs();
|
|
@@ -1565,7 +1678,7 @@ async function main() {
|
|
|
1565
1678
|
if (firstRun || IS_RESET) await runOnboarding();
|
|
1566
1679
|
if (skipEngine) {
|
|
1567
1680
|
if (IS_VERBOSE) p.log.info("Skipping engine check (--no-engine)");
|
|
1568
|
-
await import("./src-
|
|
1681
|
+
await import("./src-G7yt8gGm.mjs");
|
|
1569
1682
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
1570
1683
|
const consoleState = await ensureIiiConsole();
|
|
1571
1684
|
await maybeOfferGlobalInstall();
|
|
@@ -1577,21 +1690,38 @@ async function main() {
|
|
|
1577
1690
|
if (IS_VERBOSE) p.log.success("iii-engine is running");
|
|
1578
1691
|
const persisted = readEngineState();
|
|
1579
1692
|
const attachedBin = (persisted?.kind === "native" && persisted.binPath && existsSync(persisted.binPath) ? persisted.binPath : null) ?? whichBinary("iii") ?? fallbackIiiPaths().find((p) => existsSync(p)) ?? null;
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1693
|
+
const detected = attachedBin ? iiiBinVersion(attachedBin) : null;
|
|
1694
|
+
if (detected === IIPINNED_VERSION) {
|
|
1695
|
+
adoptRunningEngine();
|
|
1696
|
+
await import("./src-G7yt8gGm.mjs");
|
|
1697
|
+
if (await waitForAgentmemoryReady(15e3)) {
|
|
1698
|
+
const consoleState = await ensureIiiConsole();
|
|
1699
|
+
await maybeOfferGlobalInstall();
|
|
1700
|
+
printReadyHint(consoleState);
|
|
1585
1701
|
}
|
|
1702
|
+
return;
|
|
1586
1703
|
}
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1704
|
+
const detectedLabel = detected ? `v${detected}` : "an unverified version";
|
|
1705
|
+
const base = isInvokedViaNpx() ? "npx @agentmemory/agentmemory" : "agentmemory";
|
|
1706
|
+
p.log.error(`Another iii-engine (${detectedLabel}) is running on port ${getEnginePort()}, and agentmemory needs its own pinned v${IIPINNED_VERSION}.`);
|
|
1707
|
+
p.note([
|
|
1708
|
+
`agentmemory only supports iii-engine v${IIPINNED_VERSION}. It will not adopt or change the running engine (${detectedLabel}).`,
|
|
1709
|
+
"",
|
|
1710
|
+
c.label("Switch to the pinned engine in two steps:"),
|
|
1711
|
+
"",
|
|
1712
|
+
` 1. Stop the running engine:`,
|
|
1713
|
+
` ${c.cmd(`${base} stop --force`)}`,
|
|
1714
|
+
` ${c.dim(`(or stop your own iii however you started it — agentmemory leaves your global iii untouched)`)}`,
|
|
1715
|
+
"",
|
|
1716
|
+
` 2. Start agentmemory. It downloads and runs the pinned`,
|
|
1717
|
+
` v${IIPINNED_VERSION} into ~/.agentmemory/bin automatically:`,
|
|
1718
|
+
` ${c.cmd(base)}`,
|
|
1719
|
+
"",
|
|
1720
|
+
c.dim(`Step 2 needs no manual install. To install iii v${IIPINNED_VERSION} yourself (replaces your global iii), curl:`),
|
|
1721
|
+
` ${c.cmd(III_CONSOLE_INSTALL_CMD)}`,
|
|
1722
|
+
` ${c.dim("or download the release:")} ${c.url(`https://github.com/iii-hq/iii/releases/tag/iii%2Fv${IIPINNED_VERSION}`)}`
|
|
1723
|
+
].join("\n"), "engine conflict");
|
|
1724
|
+
process.exit(1);
|
|
1595
1725
|
}
|
|
1596
1726
|
if (!await startEngine()) {
|
|
1597
1727
|
p.log.error("Could not start iii-engine.");
|
|
@@ -1633,8 +1763,8 @@ async function main() {
|
|
|
1633
1763
|
}
|
|
1634
1764
|
process.exit(1);
|
|
1635
1765
|
}
|
|
1636
|
-
s.stop("iii-engine is ready");
|
|
1637
|
-
await import("./src-
|
|
1766
|
+
s.stop(c.ok("iii-engine is ready"));
|
|
1767
|
+
await import("./src-G7yt8gGm.mjs");
|
|
1638
1768
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
1639
1769
|
const consoleState = await ensureIiiConsole();
|
|
1640
1770
|
await maybeOfferGlobalInstall();
|
|
@@ -1692,7 +1822,7 @@ async function runStatus() {
|
|
|
1692
1822
|
const pctSaved = estFullTokens > 0 ? Math.round(tokensSaved / estFullTokens * 100) : 0;
|
|
1693
1823
|
p.log.success(`Connected — v${version} at ${base}`);
|
|
1694
1824
|
const lines = [
|
|
1695
|
-
`Health: ${status === "healthy" ? "✓ healthy" : status}`,
|
|
1825
|
+
`Health: ${status === "healthy" ? pc.green("✓ healthy") : pc.yellow(status)}`,
|
|
1696
1826
|
`Sessions: ${sessions}`,
|
|
1697
1827
|
`Observations: ${obsCount}`,
|
|
1698
1828
|
`Memories: ${memCount}`,
|
|
@@ -1700,7 +1830,7 @@ async function runStatus() {
|
|
|
1700
1830
|
`Circuit: ${cb}`,
|
|
1701
1831
|
`Heap: ${heapMB} MB`,
|
|
1702
1832
|
`Uptime: ${uptime}s`,
|
|
1703
|
-
`Viewer: ${getViewerUrl()}`
|
|
1833
|
+
`Viewer: ${c.url(getViewerUrl())}`
|
|
1704
1834
|
];
|
|
1705
1835
|
if (obsCount > 0) {
|
|
1706
1836
|
lines.push("");
|
|
@@ -1709,9 +1839,9 @@ async function runStatus() {
|
|
|
1709
1839
|
lines.push(` Injected: ~${estInjectedTokens.toLocaleString()} tokens`);
|
|
1710
1840
|
}
|
|
1711
1841
|
if (flagsRes) {
|
|
1712
|
-
const provider = flagsRes.provider === "llm" ? "✓ llm" : "✗ noop (no key)";
|
|
1713
|
-
const embed = flagsRes.embeddingProvider === "embeddings" ? "✓ embeddings" : "bm25-only";
|
|
1714
|
-
const flagRows = (flagsRes.flags || []).map((f) => ` ${f.enabled ? "✓" : "✗"} ${f.key.padEnd(32)} ${f.label}`);
|
|
1842
|
+
const provider = flagsRes.provider === "llm" ? pc.green("✓ llm") : pc.yellow("✗ noop (no key)");
|
|
1843
|
+
const embed = flagsRes.embeddingProvider === "embeddings" ? pc.green("✓ embeddings") : pc.dim("bm25-only");
|
|
1844
|
+
const flagRows = (flagsRes.flags || []).map((f) => ` ${f.enabled ? pc.green("✓") : pc.dim("✗")} ${pc.bold(f.key.padEnd(32))} ${f.label}`);
|
|
1715
1845
|
lines.push("");
|
|
1716
1846
|
lines.push(`Provider: ${provider}`);
|
|
1717
1847
|
lines.push(`Embeddings: ${embed}`);
|
|
@@ -1732,7 +1862,7 @@ async function runStatus() {
|
|
|
1732
1862
|
}
|
|
1733
1863
|
}
|
|
1734
1864
|
function formatChecks(checks) {
|
|
1735
|
-
return checks.map((c) => `${c.ok ? "✓" : "✗"} ${c.name}${c.hint ? `\n ${c.hint}` : ""}`).join("\n");
|
|
1865
|
+
return checks.map((c) => `${c.ok ? pc.green("✓") : pc.red("✗")} ${c.name}${c.hint ? `\n ${c.hint}` : ""}`).join("\n");
|
|
1736
1866
|
}
|
|
1737
1867
|
function findLatestDebugLog(debugDir) {
|
|
1738
1868
|
const latestLink = join(debugDir, "latest");
|
|
@@ -2268,15 +2398,63 @@ async function runInit() {
|
|
|
2268
2398
|
].join("\n"), "Next steps");
|
|
2269
2399
|
p.outro(`Edit ${target} and you're set.`);
|
|
2270
2400
|
}
|
|
2401
|
+
async function startServerForDemo() {
|
|
2402
|
+
if (await isAgentmemoryReady()) return async () => {};
|
|
2403
|
+
const startedEngine = !await isEngineRunning();
|
|
2404
|
+
if (startedEngine) {
|
|
2405
|
+
if (!await startEngine()) {
|
|
2406
|
+
p.log.error("Could not start iii-engine for the demo.");
|
|
2407
|
+
p.note(installInstructions().join("\n"), "Setup required");
|
|
2408
|
+
process.exit(1);
|
|
2409
|
+
}
|
|
2410
|
+
if (!await waitForEngine(15e3)) {
|
|
2411
|
+
p.log.error("iii-engine did not become ready within 15s.");
|
|
2412
|
+
process.exit(1);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
await import("./src-G7yt8gGm.mjs");
|
|
2416
|
+
if (!await waitForAgentmemoryReady(15e3)) {
|
|
2417
|
+
p.log.error("agentmemory worker did not become ready within 15s.");
|
|
2418
|
+
process.exit(1);
|
|
2419
|
+
}
|
|
2420
|
+
return async () => {
|
|
2421
|
+
if (!startedEngine) return;
|
|
2422
|
+
const port = getRestPort();
|
|
2423
|
+
const state = readEngineState();
|
|
2424
|
+
if (state?.kind === "docker") {
|
|
2425
|
+
await stopDockerEngine(state.composeFile, port).catch(() => {});
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
const pids = new Set(findEnginePidsByPort(port));
|
|
2429
|
+
const pidfilePid = readEnginePidfile();
|
|
2430
|
+
if (pidfilePid) pids.add(pidfilePid);
|
|
2431
|
+
for (const pid of pids) await signalAndWait(pid, "SIGTERM", 3e3).catch(() => {});
|
|
2432
|
+
clearEnginePidfile();
|
|
2433
|
+
clearEngineState();
|
|
2434
|
+
clearWorkerPidfile();
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2271
2437
|
async function runDemo() {
|
|
2272
2438
|
const port = getRestPort();
|
|
2273
2439
|
const base = `http://localhost:${port}`;
|
|
2274
2440
|
p.intro("agentmemory demo");
|
|
2275
|
-
|
|
2441
|
+
const serve = args.includes("--serve");
|
|
2442
|
+
let teardown = async () => {};
|
|
2443
|
+
if (serve) teardown = await startServerForDemo();
|
|
2444
|
+
else if (!await isAgentmemoryReady()) {
|
|
2276
2445
|
p.log.error(`agentmemory worker not reachable on port ${port} (livez probe failed). Something may be on the port but it isn't serving /agentmemory/*.`);
|
|
2277
2446
|
p.log.info("Start it with: npx @agentmemory/agentmemory");
|
|
2447
|
+
p.log.info("Or run a one-command demo with: npx @agentmemory/agentmemory demo --serve");
|
|
2278
2448
|
process.exit(1);
|
|
2279
2449
|
}
|
|
2450
|
+
try {
|
|
2451
|
+
await runDemoBody(base);
|
|
2452
|
+
} finally {
|
|
2453
|
+
await teardown();
|
|
2454
|
+
}
|
|
2455
|
+
if (serve) process.exit(0);
|
|
2456
|
+
}
|
|
2457
|
+
async function runDemoBody(base) {
|
|
2280
2458
|
const demoProject = "/tmp/agentmemory-demo";
|
|
2281
2459
|
const sessions = buildDemoSessions();
|
|
2282
2460
|
const sSeed = p.spinner();
|
|
@@ -2294,18 +2472,22 @@ async function runDemo() {
|
|
|
2294
2472
|
const results = [];
|
|
2295
2473
|
for (const query of queries) results.push(await runDemoSearch(base, query));
|
|
2296
2474
|
sQuery.stop("Search complete");
|
|
2475
|
+
const semanticHits = results.find((r) => r.query === "database performance optimization")?.hits ?? 0;
|
|
2297
2476
|
const lines = [
|
|
2298
2477
|
`Project: ${demoProject}`,
|
|
2299
2478
|
`Sessions: ${sessions.length} seeded (${totalObs} observations)`,
|
|
2300
2479
|
"",
|
|
2301
|
-
"Search results:",
|
|
2302
|
-
...results.flatMap((r) => [` "${r.query}"`, ` → ${r.hits} hit(s), top: ${r.topTitle.slice(0, 60)}`]),
|
|
2480
|
+
c.label("Search results:"),
|
|
2481
|
+
...results.flatMap((r) => [` ${c.label(`"${r.query}"`)}`, ` ${c.dim("→")} ${c.ok(`${r.hits} hit(s)`)}, top: ${r.topTitle.slice(0, 60)}`]),
|
|
2303
2482
|
"",
|
|
2304
|
-
`Notice: searching "database performance optimization"
|
|
2305
|
-
|
|
2483
|
+
...semanticHits > 0 ? [c.accent(`Notice: searching "database performance optimization"`), c.accent(`found the N+1 query fix — keyword matching can't do that.`)] : [
|
|
2484
|
+
c.dim(`Note: "database performance optimization" found nothing —`),
|
|
2485
|
+
c.dim(`semantic recall needs an embedding provider key (e.g.`),
|
|
2486
|
+
c.dim(`OPENAI_API_KEY or GEMINI_API_KEY in ~/.agentmemory/.env).`)
|
|
2487
|
+
],
|
|
2306
2488
|
"",
|
|
2307
|
-
`Viewer: ${getViewerUrl()}`,
|
|
2308
|
-
`Clean up with: curl -X DELETE "${base}/agentmemory/sessions?project=${demoProject}"`
|
|
2489
|
+
`Viewer: ${c.url(getViewerUrl())}`,
|
|
2490
|
+
`Clean up with: ${c.dim(`curl -X DELETE "${base}/agentmemory/sessions?project=${demoProject}"`)}`
|
|
2309
2491
|
];
|
|
2310
2492
|
p.note(lines.join("\n"), "demo complete");
|
|
2311
2493
|
p.log.success("agentmemory is working. Point your agent at it and get back to coding.");
|
|
@@ -2319,7 +2501,7 @@ function runCommand(command, commandArgs, options = { label: "command" }) {
|
|
|
2319
2501
|
encoding: "utf-8"
|
|
2320
2502
|
});
|
|
2321
2503
|
if (result.status === 0) {
|
|
2322
|
-
spinner.stop(`${options.label}
|
|
2504
|
+
spinner.stop(`${options.label} ${pc.green("✓")}`);
|
|
2323
2505
|
return true;
|
|
2324
2506
|
}
|
|
2325
2507
|
const stderr = (result.stderr || "").toString().trim();
|
|
@@ -2330,7 +2512,7 @@ function runCommand(command, commandArgs, options = { label: "command" }) {
|
|
|
2330
2512
|
p.log.warn(msg.slice(0, 300));
|
|
2331
2513
|
return false;
|
|
2332
2514
|
}
|
|
2333
|
-
spinner.stop(`${options.label}
|
|
2515
|
+
spinner.stop(`${options.label} ${pc.red("✗")}`);
|
|
2334
2516
|
p.log.error(msg.slice(0, 300));
|
|
2335
2517
|
return false;
|
|
2336
2518
|
}
|
|
@@ -2424,6 +2606,38 @@ async function signalAndWait(pid, initialSignal, timeoutMs) {
|
|
|
2424
2606
|
await new Promise((r) => setTimeout(r, 200));
|
|
2425
2607
|
return !pidAlive(pid);
|
|
2426
2608
|
}
|
|
2609
|
+
async function stopWorkerPid(pid, graceMs) {
|
|
2610
|
+
const s = p.spinner();
|
|
2611
|
+
s.start(`Stopping agentmemory worker (pid ${pid})... [flushing state]`);
|
|
2612
|
+
const ok = await signalAndWait(pid, "SIGTERM", graceMs);
|
|
2613
|
+
s.stop(ok ? `Stopped worker pid ${pid}` : `Failed to stop worker pid ${pid}`);
|
|
2614
|
+
return ok;
|
|
2615
|
+
}
|
|
2616
|
+
function pidCommand(pid) {
|
|
2617
|
+
if (IS_WINDOWS) return "";
|
|
2618
|
+
try {
|
|
2619
|
+
return execFileSync("ps", [
|
|
2620
|
+
"-p",
|
|
2621
|
+
String(pid),
|
|
2622
|
+
"-o",
|
|
2623
|
+
"comm="
|
|
2624
|
+
], {
|
|
2625
|
+
encoding: "utf-8",
|
|
2626
|
+
stdio: [
|
|
2627
|
+
"ignore",
|
|
2628
|
+
"pipe",
|
|
2629
|
+
"ignore"
|
|
2630
|
+
]
|
|
2631
|
+
}).trim();
|
|
2632
|
+
} catch {
|
|
2633
|
+
return "";
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
function isForeignPortHolder(comm) {
|
|
2637
|
+
if (!comm) return false;
|
|
2638
|
+
const base = comm.split("/").pop() || comm;
|
|
2639
|
+
return base !== "iii" && !base.startsWith("iii-");
|
|
2640
|
+
}
|
|
2427
2641
|
function findEnginePidsByPort(port) {
|
|
2428
2642
|
if (IS_WINDOWS) return [];
|
|
2429
2643
|
const lsof = whichBinary("lsof");
|
|
@@ -2458,17 +2672,35 @@ async function stopDockerEngine(composeFile, port) {
|
|
|
2458
2672
|
p.log.error(`Engine state references ${composeFile}, but the file is gone. Stop it manually:\n docker compose down (from the dir holding the original docker-compose.yml)`);
|
|
2459
2673
|
process.exit(1);
|
|
2460
2674
|
}
|
|
2675
|
+
const workerPid = readWorkerPidfile();
|
|
2676
|
+
let workerStopped = true;
|
|
2677
|
+
if (workerPid) workerStopped = await stopWorkerPid(workerPid, 5e3);
|
|
2678
|
+
let composeText = "";
|
|
2679
|
+
try {
|
|
2680
|
+
composeText = readFileSync(composeFile, "utf-8");
|
|
2681
|
+
} catch {
|
|
2682
|
+
composeText = "";
|
|
2683
|
+
}
|
|
2684
|
+
const ownServices = ["iii-engine", "iii-init"].filter((svc) => new RegExp(`^\\s+${svc}:`, "m").test(composeText));
|
|
2685
|
+
if (ownServices.length === 0) {
|
|
2686
|
+
p.log.error(`${composeFile} does not define the agentmemory services (iii-engine/iii-init). Refusing to run an unscoped \`docker compose down\` against it — that would tear down every service in the file.\n\nStop the engine service manually:\n docker compose -f ${composeFile} stop <service>`);
|
|
2687
|
+
process.exit(1);
|
|
2688
|
+
}
|
|
2461
2689
|
const ok = runCommand(dockerBin, [
|
|
2462
2690
|
"compose",
|
|
2463
2691
|
"-f",
|
|
2464
2692
|
composeFile,
|
|
2465
|
-
"
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
if (
|
|
2471
|
-
|
|
2693
|
+
"rm",
|
|
2694
|
+
"-s",
|
|
2695
|
+
"-f",
|
|
2696
|
+
...ownServices
|
|
2697
|
+
], { label: `docker compose -f ${composeFile} rm -s -f ${ownServices.join(" ")}` });
|
|
2698
|
+
if (workerStopped) clearWorkerPidfile();
|
|
2699
|
+
if (ok) {
|
|
2700
|
+
clearEnginePidfile();
|
|
2701
|
+
clearEngineState();
|
|
2702
|
+
} else {
|
|
2703
|
+
p.log.error(`docker compose rm failed. The engine may still be running on :${port}. Inspect with:\n docker compose -f ${composeFile} ps`);
|
|
2472
2704
|
process.exit(1);
|
|
2473
2705
|
}
|
|
2474
2706
|
p.outro("Stopped. Memories persisted to disk; restart anytime with: npx @agentmemory/agentmemory");
|
|
@@ -2542,15 +2774,18 @@ async function runStop() {
|
|
|
2542
2774
|
process.exit(1);
|
|
2543
2775
|
}
|
|
2544
2776
|
let allStopped = true;
|
|
2545
|
-
for (const pid of workerCandidates)
|
|
2546
|
-
|
|
2547
|
-
s.start(`Stopping agentmemory worker (pid ${pid})... [flushing state]`);
|
|
2548
|
-
const ok = await signalAndWait(pid, "SIGTERM", 5e3);
|
|
2549
|
-
s.stop(ok ? `Stopped worker pid ${pid}` : `Failed to stop worker pid ${pid}`);
|
|
2550
|
-
if (!ok) allStopped = false;
|
|
2551
|
-
}
|
|
2777
|
+
for (const pid of workerCandidates) if (!await stopWorkerPid(pid, 5e3)) allStopped = false;
|
|
2778
|
+
const skippedForeign = [];
|
|
2552
2779
|
for (const pid of candidates) {
|
|
2553
2780
|
if (workerCandidates.has(pid)) continue;
|
|
2781
|
+
const comm = pidCommand(pid);
|
|
2782
|
+
if (!force && isForeignPortHolder(comm)) {
|
|
2783
|
+
skippedForeign.push({
|
|
2784
|
+
pid,
|
|
2785
|
+
comm
|
|
2786
|
+
});
|
|
2787
|
+
continue;
|
|
2788
|
+
}
|
|
2554
2789
|
const s = p.spinner();
|
|
2555
2790
|
s.start(`Stopping iii-engine (pid ${pid})...`);
|
|
2556
2791
|
const ok = await signalAndWait(pid, "SIGTERM", 3e3);
|
|
@@ -2560,6 +2795,11 @@ async function runStop() {
|
|
|
2560
2795
|
clearEnginePidfile();
|
|
2561
2796
|
clearEngineState();
|
|
2562
2797
|
clearWorkerPidfile();
|
|
2798
|
+
if (skippedForeign.length > 0) {
|
|
2799
|
+
const list = skippedForeign.map((sf) => ` pid ${sf.pid} ${sf.comm}`).join("\n");
|
|
2800
|
+
p.log.error(`Refused to signal process(es) holding :${port} that are not the iii engine:\n${list}\n\nIf the engine runs in Docker, stop it there:\n docker compose ps && docker compose rm -s -f <service>\n\nOr re-run with --force to signal them anyway.`);
|
|
2801
|
+
process.exit(1);
|
|
2802
|
+
}
|
|
2563
2803
|
if (!allStopped) {
|
|
2564
2804
|
p.log.error("One or more processes survived SIGKILL. Investigate with `ps`.");
|
|
2565
2805
|
process.exit(1);
|
|
@@ -2567,14 +2807,18 @@ async function runStop() {
|
|
|
2567
2807
|
p.outro("Stopped. Memories persisted to disk; restart anytime with: npx @agentmemory/agentmemory");
|
|
2568
2808
|
}
|
|
2569
2809
|
async function runMcp() {
|
|
2570
|
-
await import("./standalone-
|
|
2810
|
+
await import("./standalone-BqNJDJ04.mjs");
|
|
2571
2811
|
}
|
|
2572
2812
|
async function runConnectCmd() {
|
|
2573
|
-
const { runConnect } = await import("./connect-
|
|
2813
|
+
const { runConnect } = await import("./connect-Qaeq15TQ.mjs").then((n) => n.n);
|
|
2574
2814
|
await runConnect(args.slice(1));
|
|
2575
2815
|
}
|
|
2576
2816
|
async function runImportJsonl() {
|
|
2577
|
-
const VALUE_FLAGS = new Set([
|
|
2817
|
+
const VALUE_FLAGS = new Set([
|
|
2818
|
+
"--port",
|
|
2819
|
+
"--tools",
|
|
2820
|
+
"--data-dir"
|
|
2821
|
+
]);
|
|
2578
2822
|
let maxFiles;
|
|
2579
2823
|
const tail = args.slice(1);
|
|
2580
2824
|
const positional = [];
|
|
@@ -2786,7 +3030,7 @@ async function runRemove() {
|
|
|
2786
3030
|
}
|
|
2787
3031
|
p.outro("Done. agentmemory cleanly removed. The npm package itself: npm uninstall -g @agentmemory/agentmemory");
|
|
2788
3032
|
}
|
|
2789
|
-
|
|
3033
|
+
const commands = {
|
|
2790
3034
|
init: runInit,
|
|
2791
3035
|
connect: runConnectCmd,
|
|
2792
3036
|
status: runStatus,
|
|
@@ -2797,7 +3041,13 @@ async function runRemove() {
|
|
|
2797
3041
|
remove: runRemove,
|
|
2798
3042
|
mcp: runMcp,
|
|
2799
3043
|
"import-jsonl": runImportJsonl
|
|
2800
|
-
}
|
|
3044
|
+
};
|
|
3045
|
+
const first = args[0] ?? "";
|
|
3046
|
+
async function unknownCommand() {
|
|
3047
|
+
p.log.error(`Unknown command: ${first}. Supported: ${Object.keys(commands).join(", ")}. Run \`agentmemory\` with no arguments to start the memory server, or \`agentmemory --help\` for usage.`);
|
|
3048
|
+
process.exit(1);
|
|
3049
|
+
}
|
|
3050
|
+
(commands[first] ?? (first && !first.startsWith("-") ? unknownCommand : main))().catch((err) => {
|
|
2801
3051
|
p.log.error(err instanceof Error ? err.message : String(err));
|
|
2802
3052
|
process.exit(1);
|
|
2803
3053
|
});
|