Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ The agentmemory entry is the **same MCP server block** across every host that us
| **Hermes Agent** | `~/.hermes/config.yaml` | Use the deeper [memory provider plugin](integrations/hermes/) with `memory.provider: agentmemory`. |
| **Qwen Code** | `~/.qwen/settings.json` | `agentmemory connect qwen` writes the standard `mcpServers` block. Hook payload is field-compatible with Claude Code, so the existing 12-hook scripts work without modification — wire them via the `hooks` section in the same `settings.json`. |
| **Antigravity** (replaces Gemini CLI) | `mcp_config.json` (in Antigravity's User dir) | `agentmemory connect antigravity` writes the standard `mcpServers` block. macOS: `~/Library/Application Support/Antigravity/User/`. Linux: `~/.config/Antigravity/User/`. Use after the 2026-06-18 Gemini CLI sunset. |
| **Antigravity CLI** (`agy`) | `~/.gemini/config/mcp_config.json` | `agentmemory connect antigravity-cli` — the `agy` CLI keeps its own config under `~/.gemini/`, separate from the Antigravity IDE above. Pass `--with-hooks` for native auto-capture via `~/.gemini/config/hooks.json`. |
| **Kiro** | `~/.kiro/settings/mcp.json` | `agentmemory connect kiro` writes the user-level config. Workspace overrides go in `.kiro/settings/mcp.json` next to your code. |
| **Warp** | `~/.warp/.mcp.json` | `agentmemory connect warp` writes the standard `mcpServers` block. Warp also auto-discovers skills from `.claude/skills/` — once the Claude Code plugin is installed the 8 agentmemory skills (`remember`, `recall`, `recap`, `handoff`, `forget`, `commit-context`, `commit-history`, `session-history`) appear natively in Warp's slash-command palette. |
| **Cline (CLI)** | `~/.cline/mcp.json` | `agentmemory connect cline` writes the standard `mcpServers` block. VS Code extension users: paste the same block via Cline Settings → MCP Servers → Edit JSON. |
Expand Down
43 changes: 43 additions & 0 deletions plugin/hooks/hooks.antigravity.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"agentmemory": {
"enabled": true,
"PreInvocation": [
{
"type": "command",
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/antigravity-bridge.mjs PreInvocation",
"timeout": 10
}
],
"PreToolUse": [
{
"matcher": "view_file|view_code_item|read_file|edit_file|replace_file_content|write_to_file|create_file|grep_search|codebase_search|find_by_name|list_dir",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/antigravity-bridge.mjs PreToolUse",
"timeout": 10
}
]
}
],
"PostToolUse": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/antigravity-bridge.mjs PostToolUse",
"timeout": 10
}
]
}
],
"Stop": [
{
"type": "command",
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/antigravity-bridge.mjs Stop",
"timeout": 10
}
]
}
}
114 changes: 114 additions & 0 deletions plugin/scripts/antigravity-bridge.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env node
import { spawnSync } from "node:child_process";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
//#region src/hooks/antigravity-bridge.ts
const SCRIPTS_DIR = dirname(fileURLToPath(import.meta.url));
const TOOL_NAME_MAP = {
view_file: "read",
view_line_range: "read",
view_code_item: "read",
read_file: "read",
read_url_content: "read",
edit_file: "edit",
replace_file_content: "edit",
propose_code: "edit",
write_to_file: "write",
create_file: "write",
grep_search: "grep",
codebase_search: "grep",
find_by_name: "glob",
list_dir: "glob"
};
const ARG_KEY_MAP = {
AbsolutePath: "file_path",
TargetFile: "file_path",
DirectoryPath: "path",
SearchDirectory: "path",
Pattern: "pattern",
Query: "pattern",
CommandLine: "command"
};
function asObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
}
function firstString(...values) {
for (const v of values) if (typeof v === "string" && v.length > 0) return v;
}
function normalizeToolArgs(args) {
if (!args) return {};
const out = { ...args };
for (const [from, to] of Object.entries(ARG_KEY_MAP)) if (out[to] === void 0 && args[from] !== void 0) out[to] = args[from];
return out;
}
function normalizePayload(event, raw) {
const toolCall = asObject(raw["toolCall"]);
const workspacePaths = Array.isArray(raw["workspacePaths"]) ? raw["workspacePaths"] : [];
const sessionId = firstString(raw["conversationId"], raw["session_id"], raw["sessionId"]) ?? "unknown";
const cwd = firstString(raw["cwd"], workspacePaths[0]) ?? process.cwd();
const out = {
...raw,
session_id: sessionId,
cwd,
hook_event_name: event
};
const transcriptPath = firstString(raw["transcript_path"], raw["transcriptPath"]);
if (transcriptPath) out["transcript_path"] = transcriptPath;
if (toolCall) {
const args = normalizeToolArgs(asObject(toolCall["args"]) ?? asObject(toolCall["toolArgs"]));
const rawName = firstString(toolCall["name"], toolCall["toolName"], args["ToolName"], args["toolName"]);
if (rawName) {
out["tool_name"] = TOOL_NAME_MAP[rawName] ?? rawName;
out["native_tool_name"] = rawName;
}
out["tool_input"] = args;
const result = toolCall["result"] ?? raw["toolResult"] ?? raw["result"];
if (result !== void 0) out["tool_result"] = result;
}
return out;
}
function targetsFor(event, raw) {
switch (event) {
case "PreInvocation": {
const n = raw["invocationNum"];
return typeof n !== "number" || n <= 1 ? ["session-start.mjs", "prompt-submit.mjs"] : ["prompt-submit.mjs"];
}
case "PreToolUse": return ["pre-tool-use.mjs"];
case "PostToolUse": return ["post-tool-use.mjs"];
case "Stop": return ["stop.mjs", "session-end.mjs"];
default: return [];
}
}
function responseFor(event) {
return event === "PreToolUse" ? "{\"decision\":\"allow\"}" : "{}";
}
async function main() {
const event = process.argv[2];
if (!event) return;
let input = "";
for await (const chunk of process.stdin) input += chunk;
let raw;
try {
raw = JSON.parse(input);
} catch {
return;
}
if (!raw || typeof raw !== "object") return;
const payload = JSON.stringify(normalizePayload(event, raw));
for (const script of targetsFor(event, raw)) spawnSync(process.execPath, [join(SCRIPTS_DIR, script)], {
input: payload,
stdio: [
"pipe",
"ignore",
"ignore"
]
});
}
if (process.argv[1] !== void 0 && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main().catch(() => {}).finally(() => {
process.stdout.write(responseFor(process.argv[2] ?? ""));
process.exit(0);
});
//#endregion
export { normalizePayload, responseFor, targetsFor };

//# sourceMappingURL=antigravity-bridge.mjs.map
3 changes: 2 additions & 1 deletion plugin/skills/agentmemory-agents/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
Generated from `src/cli/connect/index.ts`. Do not edit the block below by hand; run `npm run skills:gen` after adding or removing an adapter.

<!-- AUTOGEN:agents START - generated by scripts/skills/generate.ts, do not edit by hand -->
`agentmemory connect <agent>` wires the memory server into a host agent. 18 adapters:
`agentmemory connect <agent>` wires the memory server into a host agent. 19 adapters:

| Agent | Name | Protocol |
| --- | --- | --- |
| Antigravity | `antigravity` | Using MCP via mcp_config.json. Antigravity replaces Gemini CLI (sunset 2026-06-18). |
| Antigravity CLI (agy) | `antigravity-cli` | Using MCP via ~/.gemini/config/mcp_config.json (the agy CLI, not the Antigravity IDE, that one is `connect antigravity`). The `/mcp` slash command inside agy lists configured servers. Pass --with-hooks to also install the native ~/.gemini/config/hooks.json auto-capture hooks. |
| Claude Code | `claude-code` | Using MCP. Hooks are also available, see https://github.com/rohitg00/agentmemory#claude-code-one-block-paste-it. |
| Cline | `cline` | Using MCP via ~/.cline/mcp.json (CLI). VS Code users: add the same block via Cline Settings → MCP Servers → Edit JSON. |
| Codex CLI | `codex` | Using MCP. Hooks ship via the Codex plugin; on Codex Desktop, also pass --with-hooks to install the global hooks.json workaround for openai/codex#16430. |
Expand Down
2 changes: 1 addition & 1 deletion plugin/skills/agentmemory-rest-api/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Generated from `src/triggers/api.ts`. Do not edit the block below by hand; run `
<!-- AUTOGEN:rest START - generated by scripts/skills/generate.ts, do not edit by hand -->
The REST API is the primary surface. All paths are under `http://localhost:3111` (override with `--port`). When `AGENTMEMORY_SECRET` is set, send `Authorization: Bearer $AGENTMEMORY_SECRET`; localhost is otherwise open.

118 registered endpoints:
119 registered endpoints:

| Method | Path |
| --- | --- |
Expand Down
97 changes: 97 additions & 0 deletions src/cli/connect/antigravity-cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { existsSync, mkdirSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import * as p from "@clack/prompts";
import { createJsonMcpAdapter } from "./json-mcp-adapter.js";
import type { ConnectOptions, ConnectResult } from "./types.js";
import {
buildMergedAntigravityHooks,
containsSpaces,
type AntigravityHookManifest,
} from "./antigravity-hooks.js";
import { findPluginRoot } from "./codex-hooks.js";
import {
backupFile,
logBackup,
logInstalled,
readJsonSafe,
writeJsonAtomic,
} from "./util.js";

// The `agy` CLI shares no configuration with the Antigravity IDE that
// `antigravity.ts` wires — it reads MCP from ~/.gemini/config/mcp_config.json
// and hooks from ~/.gemini/config/hooks.json (per-workspace overrides in
// <repo>/.agents/hooks.json). Detection keys off ~/.gemini/antigravity-cli/,
// which only the CLI creates; ~/.gemini/ alone would also match Gemini CLI.
// Sources: antigravity.google/docs/hooks, antigravity.google/docs/cli/using
const GEMINI_DIR = join(homedir(), ".gemini");
const ANTIGRAVITY_CLI_DIR = join(GEMINI_DIR, "antigravity-cli");
const CUSTOMIZATION_DIR = join(GEMINI_DIR, "config");
const ANTIGRAVITY_CLI_HOOKS = join(CUSTOMIZATION_DIR, "hooks.json");

export const adapter = createJsonMcpAdapter({
name: "antigravity-cli",
displayName: "Antigravity CLI (agy)",
detectDir: ANTIGRAVITY_CLI_DIR,
configPath: join(CUSTOMIZATION_DIR, "mcp_config.json"),
docs: "https://github.com/rohitg00/agentmemory#other-agents",
protocolNote:
"→ Using MCP via ~/.gemini/config/mcp_config.json (the agy CLI, not the Antigravity IDE — that one is `connect antigravity`). The `/mcp` slash command inside agy lists configured servers. Pass --with-hooks to also install the native ~/.gemini/config/hooks.json auto-capture hooks.",
installHooks: installAntigravityCliHooks,
});

/**

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lot of similar comments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimmed in 3a095ba. The file header and the per-function block were stating the same things twice — kept one statement of each and dropped the rest. Comment lines: antigravity-cli.ts 26 → 12, antigravity-hooks.ts 65 → 46, antigravity-bridge.ts 70 → 41, which puts them in line with droid.ts.

What I deliberately kept is the agy behaviour I verified against a live 1.0.15 — the two event shapes, the no-quoting rule, the PreToolUse decision contract. That part isn't derivable from the code, and each of those was a real defect here, so a future reader changing the manifest needs the reason. Happy to cut further if you'd rather that lived only in the PR description.

* Merge the bundled `plugin/hooks/hooks.antigravity.json` into
* `~/.gemini/config/hooks.json`, replacing only the bundle agentmemory owns.
*/
function installAntigravityCliHooks(opts: ConnectOptions): ConnectResult {
let pluginRoot: string;
try {
pluginRoot = findPluginRoot();
} catch (err) {
return {
kind: "skipped",
reason: err instanceof Error ? err.message : String(err),
};
}

// agy honours no quoting, so a space in the path yields hooks that load
// but never run. Refuse rather than install something that can only fail.
if (containsSpaces(pluginRoot)) {
return {
kind: "skipped",
reason: `Antigravity CLI cannot run hook commands whose path contains spaces, and agentmemory is installed at ${pluginRoot}. Reinstall it under a space-free path to use --with-hooks; MCP works either way.`,
};
}

const existing = readJsonSafe<AntigravityHookManifest>(ANTIGRAVITY_CLI_HOOKS);
const merged = buildMergedAntigravityHooks(existing, pluginRoot);

if (opts.dryRun) {
p.log.info(
`[dry-run] Would ${existing ? "merge" : "create"} ${ANTIGRAVITY_CLI_HOOKS} with ${Object.keys(merged).length} hook bundle(s)`,
);
return { kind: "installed", mutatedPath: ANTIGRAVITY_CLI_HOOKS };
}

let backupPath: string | undefined;
if (existsSync(ANTIGRAVITY_CLI_HOOKS)) {
backupPath = backupFile(ANTIGRAVITY_CLI_HOOKS, "antigravity-cli-hooks", "json");
logBackup(backupPath);
} else {
mkdirSync(CUSTOMIZATION_DIR, { recursive: true });
}

writeJsonAtomic(ANTIGRAVITY_CLI_HOOKS, merged);

logInstalled("Antigravity CLI hooks", ANTIGRAVITY_CLI_HOOKS);
p.log.info(
"User-scope hooks reference absolute paths under the bundled plugin/ dir. Re-run `agentmemory connect antigravity-cli --with-hooks` after upgrading agentmemory to refresh them.",
);

return {
kind: "installed",
mutatedPath: ANTIGRAVITY_CLI_HOOKS,
...(backupPath !== undefined && { backupPath }),
};
}
Loading
Loading