feat(cli): agentmemory connect — automate native-plugin install for 8 agents#402
Conversation
…for 8 agents agentmemory connect <agent> wires agentmemory into each supported coding agent: detect install, back up existing config to ~/.agentmemory/backups/, merge agentmemory MCP entry, verify, idempotent on re-run. Working: claude-code, codex, cursor, gemini-cli, openclaw. Stubbed with manual-install hints: hermes (YAML), pi (TS extension copy), openhuman (no integrations folder). Windows prints manual-install message and exits. Argv: agentmemory connect [agent] [--all] [--dry-run] [--force]
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR introduces a comprehensive ChangesAgentMemory connect command for MCP server wiring
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes The changes introduce a multi-adapter wiring infrastructure across 14 new files with varying logic density. While most adapters follow a pattern, Claude Code and Codex adapters use custom JSON/TOML handling logic requiring separate review. The orchestration layer combines interactive selection, platform gating, and flag parsing. Comprehensive test coverage validates the main paths but the infrastructure itself is substantial and heterogeneous.
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
src/cli/connect/index.ts (2)
141-143: ⚡ Quick winSimplify the type narrowing.
The cast
(result as { reason: string }).reasonis fragile. TypeScript should narrowresultautomatically after checkingresult.kind === "skipped". If it doesn't, the type definition intypes.tsmay need adjustment (discriminated union).♻️ Proposed fix to rely on automatic narrowing
const result = await runAdapter(adapter, opts); summarize([{ name: agentName, result }]); - if (result.kind === "skipped" && (result as { reason: string }).reason !== "not-detected") { + if (result.kind === "skipped" && result.reason !== "not-detected") { process.exit(1); }If TypeScript complains, the
ConnectResulttype intypes.tsmay not be properly defined as a discriminated union.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/cli/connect/index.ts` around lines 141 - 143, Remove the unsafe cast by relying on TypeScript's discriminated union: after the existing check if (result.kind === "skipped") use result.reason directly instead of (result as { reason: string }).reason; if the compiler still complains, update the ConnectResult definition in types.ts to be a proper discriminated union where the "skipped" variant includes a string reason (e.g., an interface/type with kind: "skipped" and reason: string) so the check on result.kind narrows the type automatically.
93-93: ⚡ Quick winAdd outro before exit for consistency.
Lines 136 calls
p.outro()beforeprocess.exit(1), but lines 93 and 118 exit without an outro message. This creates an inconsistent UX where some error paths show a closing banner and others don't.♻️ Proposed fix to add outro before exit
if (detected.length === 0) { p.log.error("No supported agents detected on this machine."); p.outro(`Supported: ${knownAgents().join(", ")}`); + // Note: outro already called above process.exit(1); }And for line 118:
if (detected.length === 0) { p.log.error("No supported agents detected on this machine."); + p.outro(`Supported: ${knownAgents().join(", ")}`); process.exit(1); }Also applies to: 118-118
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/cli/connect/index.ts` at line 93, Several error paths in src/cli/connect/index.ts call process.exit(1) without printing the CLI closing banner; before each bare process.exit(1) (the ones next to the existing call at the top-level and the one around the failure at line 118), call p.outro() so the outro is shown consistently on all exits—i.e., locate the places where process.exit(1) is invoked (the same contexts as the existing p.outro() + process.exit(1) combo) and insert a p.outro() immediately before each process.exit(1), guarding against double-calling only if an earlier code path already called p.outro().test/cli-connect.test.ts (1)
94-94: ⚡ Quick winUse the imported
mkdirSyncinstead ofrequire.The file imports
mkdirSyncfromnode:fsat line 2, but lines 94, 116, 132, and 145 userequire("node:fs").mkdirSync(...)instead. This creates unnecessary inconsistency.♻️ Proposed fix to use imported function
Replace all occurrences of
require("node:fs").mkdirSync(...)withmkdirSync(...):- require("node:fs").mkdirSync(claudeDir, { recursive: true }); + mkdirSync(claudeDir, { recursive: true });Apply the same change to lines 116, 132, and 145.
Also applies to: 116-116, 132-132, 145-145
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/cli-connect.test.ts` at line 94, Replace the inline require calls with the imported function: change every occurrence of require("node:fs").mkdirSync(...) to use the already-imported mkdirSync(...) (the calls that create claudeDir and the other three similar directories), ensuring you keep the same options (e.g., { recursive: true }) and argument order; look for the four spots where require("node:fs").mkdirSync is used and swap them to mkdirSync to maintain consistency with the import at the top.src/cli/connect/codex.ts (2)
52-52: 💤 Low valueSimplify confusing no-op regex replace.
The
.replace(/\n{3,}$/, "\n\n")is immediately undone by.trimEnd(), which removes all trailing whitespace including the newlines just added. The net effect isout.join("\n").trimEnd() + "\n". While this produces the correct result, the intermediate replace step is confusing and serves no purpose.♻️ Proposed simplification
- return out.join("\n").replace(/\n{3,}$/, "\n\n").trimEnd() + "\n"; + return out.join("\n").trimEnd() + "\n";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/cli/connect/codex.ts` at line 52, The trailing-newline normalization currently does a no-op replace: change the return that uses out.join("\n").replace(/\n{3,}$/, "\n\n").trimEnd() + "\n" to simply join the lines, trim trailing whitespace, and append a single newline (i.e., use out.join("\n").trimEnd() + "\n") so remove the unnecessary .replace call; locate the expression referencing the variable out in the return statement in codex.ts to update it.
89-91: 💤 Low valueConsider clearer formatting for complex string concatenation.
The nested ternary operators in line 91 make the spacing logic hard to parse at a glance. While correct, extracting the logic or adding intermediate variables would improve readability.
♻️ Example refactor for clarity
const cleaned = wired ? stripExistingBlock(current) : current; - const joiner = cleaned.length === 0 || cleaned.endsWith("\n") ? "" : "\n"; - const next = `${cleaned}${joiner}${cleaned.length > 0 ? "\n" : ""}${TOML_BLOCK}`; + let next = cleaned; + if (!next.endsWith("\n") && next.length > 0) { + next += "\n"; + } + if (next.length > 0) { + next += "\n"; + } + next += TOML_BLOCK; writeFileSync(CODEX_TOML, next, "utf-8");🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/cli/connect/codex.ts` around lines 89 - 91, The string-concatenation that builds next (using cleaned, joiner and nested ternaries) is hard to read; refactor by extracting the spacing logic into clear intermediate variables and a small helper so the expression in next is a single straightforward template. Specifically, compute a prefix (e.g., empty vs "\n"), a suffix (whether to add an extra "\n" when cleaned.length > 0), and reuse joiner, then compose next from cleaned + prefix + suffix + TOML_BLOCK; update usages of cleaned, joiner and next and keep stripExistingBlock, current, wired and TOML_BLOCK names intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/cli/connect/claude-code.ts`:
- Around line 67-69: Remove the redundant intermediate write of an empty JSON
object: keep the directory creation using mkdirSync(CLAUDE_DIR, { recursive:
true }) but delete the writeFileSync(CLAUDE_JSON, "{}\n", "utf-8") call and let
writeJsonAtomic(CLAUDE_JSON, data, ...) perform the initial write atomically;
update any related comments and ensure CLAUDE_DIR and CLAUDE_JSON are still
created/available before calling writeJsonAtomic.
---
Nitpick comments:
In `@src/cli/connect/codex.ts`:
- Line 52: The trailing-newline normalization currently does a no-op replace:
change the return that uses out.join("\n").replace(/\n{3,}$/, "\n\n").trimEnd()
+ "\n" to simply join the lines, trim trailing whitespace, and append a single
newline (i.e., use out.join("\n").trimEnd() + "\n") so remove the unnecessary
.replace call; locate the expression referencing the variable out in the return
statement in codex.ts to update it.
- Around line 89-91: The string-concatenation that builds next (using cleaned,
joiner and nested ternaries) is hard to read; refactor by extracting the spacing
logic into clear intermediate variables and a small helper so the expression in
next is a single straightforward template. Specifically, compute a prefix (e.g.,
empty vs "\n"), a suffix (whether to add an extra "\n" when cleaned.length > 0),
and reuse joiner, then compose next from cleaned + prefix + suffix + TOML_BLOCK;
update usages of cleaned, joiner and next and keep stripExistingBlock, current,
wired and TOML_BLOCK names intact.
In `@src/cli/connect/index.ts`:
- Around line 141-143: Remove the unsafe cast by relying on TypeScript's
discriminated union: after the existing check if (result.kind === "skipped") use
result.reason directly instead of (result as { reason: string }).reason; if the
compiler still complains, update the ConnectResult definition in types.ts to be
a proper discriminated union where the "skipped" variant includes a string
reason (e.g., an interface/type with kind: "skipped" and reason: string) so the
check on result.kind narrows the type automatically.
- Line 93: Several error paths in src/cli/connect/index.ts call process.exit(1)
without printing the CLI closing banner; before each bare process.exit(1) (the
ones next to the existing call at the top-level and the one around the failure
at line 118), call p.outro() so the outro is shown consistently on all
exits—i.e., locate the places where process.exit(1) is invoked (the same
contexts as the existing p.outro() + process.exit(1) combo) and insert a
p.outro() immediately before each process.exit(1), guarding against
double-calling only if an earlier code path already called p.outro().
In `@test/cli-connect.test.ts`:
- Line 94: Replace the inline require calls with the imported function: change
every occurrence of require("node:fs").mkdirSync(...) to use the
already-imported mkdirSync(...) (the calls that create claudeDir and the other
three similar directories), ensuring you keep the same options (e.g., {
recursive: true }) and argument order; look for the four spots where
require("node:fs").mkdirSync is used and swap them to mkdirSync to maintain
consistency with the import at the top.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5adc883b-a4f2-4d17-a29a-04d1c6544296
📒 Files selected for processing (14)
src/cli.tssrc/cli/connect/claude-code.tssrc/cli/connect/codex.tssrc/cli/connect/cursor.tssrc/cli/connect/gemini-cli.tssrc/cli/connect/hermes.tssrc/cli/connect/index.tssrc/cli/connect/json-mcp-adapter.tssrc/cli/connect/openclaw.tssrc/cli/connect/openhuman.tssrc/cli/connect/pi.tssrc/cli/connect/types.tssrc/cli/connect/util.tstest/cli-connect.test.ts
| mkdirSync(CLAUDE_DIR, { recursive: true }); | ||
| writeFileSync(CLAUDE_JSON, "{}\n", "utf-8"); | ||
| } |
There was a problem hiding this comment.
Remove unnecessary intermediate file write.
Line 68 writes an empty JSON object {} to CLAUDE_JSON, but line 73 immediately overwrites it via writeJsonAtomic. The intermediate write serves no purpose—only the directory creation on line 67 is needed before the atomic write.
♻️ Proposed fix
} else {
mkdirSync(CLAUDE_DIR, { recursive: true });
- writeFileSync(CLAUDE_JSON, "{}\n", "utf-8");
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| mkdirSync(CLAUDE_DIR, { recursive: true }); | |
| writeFileSync(CLAUDE_JSON, "{}\n", "utf-8"); | |
| } | |
| mkdirSync(CLAUDE_DIR, { recursive: true }); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/cli/connect/claude-code.ts` around lines 67 - 69, Remove the redundant
intermediate write of an empty JSON object: keep the directory creation using
mkdirSync(CLAUDE_DIR, { recursive: true }) but delete the
writeFileSync(CLAUDE_JSON, "{}\n", "utf-8") call and let
writeJsonAtomic(CLAUDE_JSON, data, ...) perform the initial write atomically;
update any related comments and ensure CLAUDE_DIR and CLAUDE_JSON are still
created/available before calling writeJsonAtomic.
… remove, silent killers) (#407) Patch bump per the established rule: all changes additive, no breaks to MemoryProvider trait or exported types or default behaviour. New top-level subcommands (connect, remove, --reset, --force) are opt-in. PRs included since v0.9.14: #405 — silent killers: viewer port auto-bump, engine version-match warning, stop --force, adopt-on-attach, npx PATH hint #402 — agentmemory connect — automate native-plugin install for 8 agents (claude-code, codex, cursor, gemini-cli, openclaw end-to-end; hermes/pi/openhuman stubbed) #406 — interactive doctor v2 + agentmemory remove (destruction plan + two-confirmation flow) #403 — splash banner + agent grid + provider picker + smart-defaults preferences + bootLog shim (30+ lines of log spam → 10) Files bumped (9): package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json, plugin/.codex-plugin/plugin.json, src/version.ts, src/types.ts, src/functions/export-import.ts, test/export-import.test.ts, CHANGELOG.md
* docs(website): refresh agent-memory.dev for v0.9.15+ DevEx surface Drift accumulated across the website from v0.9.0 through v0.9.15. Each section caught up: CommandCenter: - "iii CONSOLE · OPTIONAL" → "iii CONSOLE · FIRST-CLASS". User pushback: the console gives engine-level visibility (workers, functions, queues, traces) and is now installed inline by the agentmemory CLI on first run from v0.9.16. Not optional. - Bullet "49 HTTP TRIGGERS" → "107 HTTP ENDPOINTS" to match generated-meta.json restEndpoints count. Dropped the stale "33 REGISTERED FUNCTIONS" subhead. - Section lede now mentions both UIs are first-class + inline- installed. Compare: - MCP TOOLS our column 44 → 51 (matches generated-meta.json). - New REST ENDPOINTS row (107) — competitors don't ship a REST shape per their docs, so dashes for mem0/letta/cognee. - New NATIVE PLUGINS row listing the 6 first-party agents + Cursor/Gemini CLI. Agents: - FEATURED expanded from 4 to 6 (added pi + OpenHuman). Codex CLI pitch updated to "6 hooks + MCP · native plugin" reflecting the hooks addition in PR #383. - Section title "FOUR FIRST-PARTY" → "SIX FIRST-PARTY". - Section lede now mentions `agentmemory connect <agent>` (shipped in v0.9.15 PR #402). Hero: - "START IN 60 SECONDS" CTA → "START IN 30 SECONDS". Cold install + engine spawn measured 8-12s on v0.9.15 with the native binary path; 60s was the v0.9.0-era Docker-first claim. generated-meta.json regenerated by the build (auto-derive rule). Website build clean. * chore(website): refresh generated-meta timestamp * feat(website): AS FEATURED IN bar — AlphaSignal, AAIF, Trendshift Wedges between Hero and Stats. Three text-cells in a 3-col row (1-col on mobile), each links to the source article. Matches the existing brutalist mono aesthetic — no logo files, no marquee animation, no JS. Border + small ↗ glyph on each card; gold border on hover. Sources chosen for brand authority + independence: - AlphaSignal (180K technical subscribers) → alphasignalai.substack.com/p/how-agentmemory-works-and-how-to - Agentic AI Foundation (Linux Foundation backed) → aaif.io - Trendshift "NEW 2026 · Position #19" → trendshift.io/repositories/25123 Static, server-rendered, no client bundle impact. * feat(website): real logos in AS FEATURED IN bar Replaced text-only treatment with actual brand marks: - AlphaSignal — github.com/Alpha-Signal org avatar (verified) https://avatars.githubusercontent.com/u/64016073?s=200&v=4 - Agentic AI Foundation — aaif.io's own favicon PNG (verified by fetching their site) - Trendshift — their official badge endpoint trendshift.io/api/badge/repositories/25123 — bakes the repo's live star + position count into the image, so the cell tracks the Trendshift state automatically Layout split: AlphaSignal + AAIF render logo-left / text-right with ↗ arrow. Trendshift renders centered with the full badge image (it's already a wide brand mark with the rank + stars baked in, so making it text-bound looks cramped). next.config.ts adds the three new image hosts to remotePatterns (aaif.io, trendshift.io, raw.githubusercontent.com). Existing unoptimized={true} per image so we don't proxy through Vercel's optimizer. Build clean. No new deps. * feat(website): use real AAIF wordmark logo + center 3-card grid User pushed the actual Agentic AI Foundation wordmark — 838x203 PNG on transparent background, black artwork. Dropped the favicon and self-hosted the wordmark at /featured/aaif-logo.png so the brand reads cleanly on the dark cell. Rendered as a badge-style cell (centered, wide-form) like Trendshift, with a `filter: invert(1) brightness(1.05)` so the black artwork inverts to white-on-dark and matches the site palette without shipping a second asset. Grid template balanced: 1fr / 1.3fr / 1fr — gives AAIF the extra horizontal room its wordmark needs while keeping AlphaSignal and Trendshift consistent on either side. Max width bumped to 1000px. `badgeImg` got `object-fit: contain` and an explicit `max-height` so the AAIF + Trendshift badges sit at the same vertical baseline. * fix(website): coderabbit PR #415 — meta drift + a11y + dead CSS Five findings, all valid against the branch: 1. CommandCenter line 48 hardcoded "107 HTTP ENDPOINTS" but website/lib/generated-meta.json reports restEndpoints: 121. Bumped to 121. The meta file is the source of truth (auto-derived from src/triggers/api.ts at build via scripts/derive-meta.js). 2. Compare.tsx REST ENDPOINTS row same drift: "107" → "121". 3. Compare.tsx NATIVE PLUGINS row claimed "6" in the label but the parenthesized list had 8 names (Cursor + Gemini included). Per README: those two are MCP-server tier, not native plugin. List trimmed to the actual 6 native-plugin agents: Claude/Codex/OpenClaw/Hermes/pi/OpenHuman. 4. FeaturedIn.module.css .cell had :hover only — keyboard users tabbing through saw no focus indicator (WCAG 2.4.7). Added :focus-visible with gold border + outline. 5. .cellBadge declared `grid-template-columns: none` while using display: flex. Dead property removed.
Summary
agentmemory connect <agent>automates wiring agentmemory into each supported coding agent — no more readingintegrations/<agent>/README.mdand copy-pasting JSON by hand. Modeled onskillkit install <skill>.Each adapter: detect → backup → merge → verify → idempotent.
Backups land in
~/.agentmemory/backups/<agent>-<timestamp>.<ext>before any write. Re-running is a no-op unless--forceis passed.Status table
claude-code~/.claude.json(mcpServers merge)codex~/.codex/config.toml(TOML block append/rewrite)cursor~/.cursor/mcp.json(mcpServers merge)gemini-cli~/.gemini/settings.json(mcpServers merge)openclaw~/.openclaw/openclaw.json(mcpServers merge)hermes~/.hermes/config.yamlpi~/.pi/agent/extensions/agentmemory/openhumanintegrations/openhuman/doesn't exist yet in the repoWindows: every adapter prints "Windows: manual install required — see docs" and exits cleanly. Symlink semantics differ enough that I didn't try.
Layout
src/cli/connect/index.ts— dispatcher + argv parsing + interactive pickersrc/cli/connect/types.ts—ConnectAdapterinterface + result typessrc/cli/connect/util.ts— backup, atomic JSON write, log helperssrc/cli/connect/json-mcp-adapter.ts— shared adapter factory for cursor / gemini-cli / openclawsrc/cli/connect/{claude-code,codex,cursor,gemini-cli,openclaw,hermes,pi,openhuman}.ts— one file per agentsrc/cli.ts— wiresconnectinto thecommandsmap +--helptexttest/cli-connect.test.ts— 13 tests (dispatcher resolution, fail-loud on unknown, claude-code mock-fs happy path + idempotency + dry-run + backup, stub adapters)Test plan
npm run build— passes (addeddist/connect-*.mjschunk, 15.98 kB)npx vitest run test/cli-connect.test.ts— 13/13 passtest/mcp-standalone.test.ts(spec called out ~11) — my additions are net-positivenode dist/cli.mjs --help—connectdocumented alongsideinit/status/doctor/demonode dist/cli.mjs connect claude-code --dry-runon live machine — detects~/.claude/, prints planned diff, no file mutatednode dist/cli.mjs connect not-a-real-agent— fails loud with exit 1 + supported listOut of scope
Disconnect / uninstall is sibling work —
agentmemory removewill undo what this wrote (backups already exist for it).Summary by CodeRabbit
New Features
connect [agent]command for integrating AgentMemory with multiple AI agents: Claude Code, Codex, Cursor, Gemini CLI, Hermes, OpenClaw, OpenHuman, and Pi.--all(connect all detected agents),--dry-run, and--forcere-installation flags.Tests