mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
feat: add Antigravity CLI (agy) as supported harness
Antigravity CLI is the successor to Gemini CLI, built in Go with parallel subagent support. This adds full MCP + lifecycle hook integration with the correct config format differences: - MCP config lives in a separate mcp_config.json (not settings.json) - Uses serverUrl (not httpUrl) for streamable-HTTP endpoints - Hooks config uses named-groups format with Antigravity-native events (PreToolUse, PostToolUse, PreInvocation, Stop) - Mixed shape: nested for tool events, flat for lifecycle events
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
| Oh My Pi / OMP | Supported | `pi` / `omp` aliases for MCP config + TypeScript extension. |
|
||||
| Claude Desktop | MCP-only | Uses `mcp-remote`; no lifecycle hooks. |
|
||||
| OpenClaw | Supported | MCP config + native plugin lifecycle hooks. |
|
||||
| Antigravity CLI | Supported | MCP config (`serverUrl`) + lifecycle hooks. |
|
||||
| LLM providers | Supported | Anthropic, OpenAI, Gemini, and OpenAI-compatible endpoints. |
|
||||
| Embedding providers | Supported | OpenAI and Voyage. |
|
||||
|
||||
@@ -72,7 +73,7 @@ priors are at the [bottom](#influences-and-prior-art).
|
||||
mode. Mounted on the same axum server as MCP.
|
||||
- **Multi-agent + multi-machine ready.** Supported clients: Claude
|
||||
Code, Codex, OpenCode, Cursor, Claude Desktop (via `mcp-remote`),
|
||||
Gemini CLI, OpenClaw, and Oh My Pi / OMP (`pi` / `omp` aliases).
|
||||
Gemini CLI, Antigravity CLI, OpenClaw, and Oh My Pi / OMP (`pi` / `omp` aliases).
|
||||
Server runs local (loopback) OR on a homelab box (LAN/VPN/cloud)
|
||||
with bearer-token auth.
|
||||
- **Thin-client CLI.** `ai-memory bootstrap`, `purge-project`,
|
||||
@@ -115,7 +116,7 @@ priors are at the [bottom](#influences-and-prior-art).
|
||||
## Quick start
|
||||
|
||||
You need: Docker + an agent CLI (Claude Code, Codex, OpenCode, OMP, Cursor,
|
||||
or anything else that speaks MCP).
|
||||
Antigravity CLI, or anything else that speaks MCP).
|
||||
|
||||
The default quick-start has **no authentication** - the server binds
|
||||
to loopback only, so on a single-user laptop nothing else can reach
|
||||
@@ -187,8 +188,8 @@ overwrites them so future image updates ship updated hooks. Drop
|
||||
- **Upgrades:** run `ai-memory upgrade` to refresh the wrapper, image,
|
||||
and staged hook scripts. Redeploy remote servers separately.
|
||||
|
||||
For Codex, OpenCode, OMP, Cursor, Claude Desktop, Gemini CLI, OpenClaw,
|
||||
curl-based hook installs, source builds, CLI env vars, and the full
|
||||
For Codex, OpenCode, OMP, Cursor, Claude Desktop, Gemini CLI, Antigravity CLI,
|
||||
OpenClaw, curl-based hook installs, source builds, CLI env vars, and the full
|
||||
subcommand reference, see [`docs/install.md`](docs/install.md).
|
||||
|
||||
## Security
|
||||
@@ -308,7 +309,7 @@ diagram, crate breakdown, schema notes, and invariants.
|
||||
| [`docs/usage.md`](docs/usage.md) | Handoffs, proactive memory queries, routing snippet, web UI, raw-wiki inspection, and rules-vs-facts workflow. |
|
||||
| [`docs/marker-file.md`](docs/marker-file.md) | `.ai-memory.toml` workspace/project routing for multi-client trees, mono-repos, and work/personal separation. |
|
||||
| [`docs/windows.md`](docs/windows.md) | Windows install modes: full WSL2, native Windows with Docker Desktop, native source builds, and current hook/MCP harness caveats. |
|
||||
| [`docs/mcp-install.md`](docs/mcp-install.md) | Per-client MCP and lifecycle notes (Cursor, Claude Desktop, Gemini CLI, OpenClaw, OMP). |
|
||||
| [`docs/mcp-install.md`](docs/mcp-install.md) | Per-client MCP and lifecycle notes (Cursor, Claude Desktop, Gemini CLI, Antigravity CLI, OpenClaw, OMP). |
|
||||
| [`docs/deploy.md`](docs/deploy.md) | Homelab deploy: bin/deploy, bearer-token auth, TLS via cloudflared. |
|
||||
| [`docs/lifecycle-ops.md`](docs/lifecycle-ops.md) | **Read before running purge / rename / backup / restore / reset.** Safety matrix for the state-touching commands, per-project disk layout (how isolation actually works), and operator workflows for "fresh start", "snapshot before risky op", "drop one project". |
|
||||
| [`docs/llm-provider-comparison.md`](docs/llm-provider-comparison.md) | Empirical notes behind the recommended LLM defaults. |
|
||||
|
||||
@@ -359,6 +359,9 @@ pub enum AgentChoice {
|
||||
/// OpenClaw personal AI gateway — native plugin package with
|
||||
/// session/tool/compaction hooks.
|
||||
Openclaw,
|
||||
/// Google Antigravity CLI (`agy`) — JSON-config hooks in
|
||||
/// `~/.gemini/antigravity-cli/hooks.json`.
|
||||
AntigravityCli,
|
||||
}
|
||||
|
||||
/// MCP client to render configuration for. Includes both the
|
||||
@@ -389,6 +392,8 @@ pub enum McpClient {
|
||||
/// Oh My Pi (`omp`) / Pi-compatible coding agent — `~/.omp/agent/mcp.json`.
|
||||
#[value(alias = "omp", alias = "oh-my-pi")]
|
||||
Pi,
|
||||
/// Google Antigravity CLI (`agy`) — `~/.gemini/antigravity-cli/mcp_config.json`.
|
||||
AntigravityCli,
|
||||
}
|
||||
|
||||
/// Arguments for `commit`.
|
||||
|
||||
@@ -23,8 +23,9 @@ use crate::cli::{AgentChoice, InstallHooksArgs};
|
||||
use crate::commands::apply_shared::{ApplyOutcome, apply_atomic, mutate_json};
|
||||
use crate::commands::openclaw_plugin;
|
||||
use crate::commands::render_shared::{
|
||||
CURSOR_PROFILE, GEMINI_PROFILE, build_claude_code_payload, build_codex_payload,
|
||||
build_profile_payload, hook_script_for_current_platform, ts_string_literal,
|
||||
CURSOR_PROFILE, GEMINI_PROFILE, build_antigravity_payload, build_claude_code_payload,
|
||||
build_codex_payload, build_profile_payload, hook_script_for_current_platform,
|
||||
ts_string_literal,
|
||||
};
|
||||
use crate::config::Config;
|
||||
|
||||
@@ -58,6 +59,10 @@ pub fn run(config: &Config, args: InstallHooksArgs) -> Result<()> {
|
||||
let hooks_dir = resolve_hooks_dir(args.hooks_dir.as_deref(), args.agent)?;
|
||||
apply_to_gemini_settings(&hooks_dir, &args.server_url, auth, &args)
|
||||
}
|
||||
AgentChoice::AntigravityCli => {
|
||||
let hooks_dir = resolve_hooks_dir(args.hooks_dir.as_deref(), args.agent)?;
|
||||
apply_to_antigravity_settings(&hooks_dir, &args.server_url, auth, &args)
|
||||
}
|
||||
AgentChoice::Openclaw => openclaw_plugin::apply(&args.server_url, auth, &args),
|
||||
};
|
||||
}
|
||||
@@ -80,6 +85,10 @@ pub fn run(config: &Config, args: InstallHooksArgs) -> Result<()> {
|
||||
let hooks_dir = resolve_hooks_dir(args.hooks_dir.as_deref(), args.agent)?;
|
||||
render_agent("gemini-cli", &hooks_dir, &args.server_url, auth)
|
||||
}
|
||||
AgentChoice::AntigravityCli => {
|
||||
let hooks_dir = resolve_hooks_dir(args.hooks_dir.as_deref(), args.agent)?;
|
||||
render_agent("antigravity-cli", &hooks_dir, &args.server_url, auth)
|
||||
}
|
||||
AgentChoice::Openclaw => {
|
||||
openclaw_plugin::render(&args.server_url, auth);
|
||||
Ok(())
|
||||
@@ -394,6 +403,74 @@ fn merge_gemini_hooks(
|
||||
})
|
||||
}
|
||||
|
||||
/// Mutate `~/.gemini/antigravity-cli/hooks.json` so Antigravity CLI (`agy`)
|
||||
/// fires the ai-memory scripts on its lifecycle events.
|
||||
///
|
||||
/// Antigravity CLI uses a named-groups format where hook groups are
|
||||
/// top-level keys (e.g. `"ai-memory"`) containing event arrays. Tool
|
||||
/// events (`PreToolUse`, `PostToolUse`) use nested shape with matcher;
|
||||
/// lifecycle events (`PreInvocation`, `Stop`) use flat shape.
|
||||
///
|
||||
/// Config file: `~/.gemini/antigravity-cli/hooks.json`
|
||||
fn apply_to_antigravity_settings(
|
||||
hooks_dir: &Path,
|
||||
server_url: &str,
|
||||
auth_token: Option<&str>,
|
||||
args: &InstallHooksArgs,
|
||||
) -> Result<()> {
|
||||
let path = match &args.config_file {
|
||||
Some(p) => p.clone(),
|
||||
None => dirs::home_dir()
|
||||
.context("could not locate $HOME for ~/.gemini/antigravity-cli/hooks.json")?
|
||||
.join(".gemini")
|
||||
.join("antigravity-cli")
|
||||
.join("hooks.json"),
|
||||
};
|
||||
let staged = stage_hook_scripts(hooks_dir, "antigravity-cli")?;
|
||||
let command_dir = staged_command_dir(&staged, "antigravity-cli");
|
||||
let outcome = merge_antigravity_hooks(&command_dir, server_url, auth_token, &path)?;
|
||||
println!(
|
||||
"✓ {} {} ({})",
|
||||
outcome.verb(),
|
||||
path.display(),
|
||||
match outcome {
|
||||
ApplyOutcome::Created => "new file",
|
||||
ApplyOutcome::Updated => "backup written next to it",
|
||||
ApplyOutcome::NoOp => "already up to date",
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn merge_antigravity_hooks(
|
||||
staged: &Path,
|
||||
server_url: &str,
|
||||
auth_token: Option<&str>,
|
||||
config_path: &Path,
|
||||
) -> Result<ApplyOutcome> {
|
||||
let payload = build_antigravity_payload(staged, server_url, auth_token);
|
||||
let our_group = payload
|
||||
.get("ai-memory")
|
||||
.and_then(|v| v.as_object())
|
||||
.context("internal: build_antigravity_payload didn't return an ai-memory group")?
|
||||
.clone();
|
||||
apply_atomic(config_path, |existing| {
|
||||
mutate_json(existing, |root| {
|
||||
// Get-or-create the "ai-memory" named group; overlay
|
||||
// our events. Other named groups survive untouched.
|
||||
let group = root
|
||||
.entry("ai-memory")
|
||||
.or_insert_with(|| serde_json::Value::Object(serde_json::Map::new()))
|
||||
.as_object_mut()
|
||||
.context("`ai-memory` is present in hooks.json but not an object")?;
|
||||
for (event, value) in &our_group {
|
||||
group.insert(event.clone(), value.clone());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate an OpenCode plugin at `~/.config/opencode/plugins/ai-memory.ts`.
|
||||
///
|
||||
/// OpenCode's integration surface is a TypeScript plugin, not a JSON
|
||||
@@ -1168,6 +1245,7 @@ fn resolve_hooks_dir(explicit: Option<&Path>, agent: AgentChoice) -> Result<Path
|
||||
AgentChoice::Codex => "codex",
|
||||
AgentChoice::Cursor => "cursor",
|
||||
AgentChoice::GeminiCli => "gemini-cli",
|
||||
AgentChoice::AntigravityCli => "antigravity-cli",
|
||||
AgentChoice::OpenCode | AgentChoice::Omp | AgentChoice::Openclaw => {
|
||||
anyhow::bail!("{agent:?} uses a generated integration, not a hook script directory")
|
||||
}
|
||||
@@ -1279,7 +1357,14 @@ mod tests {
|
||||
"PowerShell hooks require the shared lib helper"
|
||||
);
|
||||
|
||||
for agent_dir in ["claude-code", "codex", "cursor", "gemini-cli", "opencode"] {
|
||||
for agent_dir in [
|
||||
"claude-code",
|
||||
"codex",
|
||||
"cursor",
|
||||
"gemini-cli",
|
||||
"opencode",
|
||||
"antigravity-cli",
|
||||
] {
|
||||
let dir = hooks_root.join(agent_dir);
|
||||
let mut sh = BTreeMap::new();
|
||||
let mut ps1 = BTreeMap::new();
|
||||
@@ -1810,4 +1895,103 @@ mod tests {
|
||||
"PreToolUse must not appear in Gemini config"
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Antigravity tests
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn antigravity_preserves_existing_hooks_and_adds_ours() {
|
||||
let hooks_tmp = TempDir::new().unwrap();
|
||||
stub_scripts(
|
||||
hooks_tmp.path(),
|
||||
&[
|
||||
"session-start.sh",
|
||||
"session-end.sh",
|
||||
"pre-tool-use.sh",
|
||||
"post-tool-use.sh",
|
||||
],
|
||||
);
|
||||
|
||||
let config_tmp = TempDir::new().unwrap();
|
||||
let config_path = config_tmp.path().join("hooks.json");
|
||||
// Pre-existing settings with another named hook group.
|
||||
fs::write(
|
||||
&config_path,
|
||||
r#"{"my-linter":{"PostToolUse":[{"matcher":"run_command","hooks":[{"type":"command","command":"lint.sh"}]}]}}"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
merge_antigravity_hooks(
|
||||
hooks_tmp.path(),
|
||||
"http://127.0.0.1:49374",
|
||||
None,
|
||||
&config_path,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let parsed: serde_json::Value =
|
||||
serde_json::from_str(&fs::read_to_string(&config_path).unwrap()).unwrap();
|
||||
// The pre-existing my-linter group survives.
|
||||
assert!(
|
||||
parsed["my-linter"]["PostToolUse"].is_array(),
|
||||
"my-linter.PostToolUse must survive"
|
||||
);
|
||||
// Our named group "ai-memory" is present.
|
||||
assert!(
|
||||
parsed["ai-memory"]["PreInvocation"].is_array(),
|
||||
"PreInvocation hook should be present"
|
||||
);
|
||||
assert!(
|
||||
parsed["ai-memory"]["PreToolUse"].is_array(),
|
||||
"PreToolUse hook should be present"
|
||||
);
|
||||
assert!(
|
||||
parsed["ai-memory"]["PostToolUse"].is_array(),
|
||||
"PostToolUse hook should be present"
|
||||
);
|
||||
assert!(
|
||||
parsed["ai-memory"]["Stop"].is_array(),
|
||||
"Stop hook should be present"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn antigravity_apply_is_idempotent() {
|
||||
let hooks_tmp = TempDir::new().unwrap();
|
||||
stub_scripts(
|
||||
hooks_tmp.path(),
|
||||
&[
|
||||
"session-start.sh",
|
||||
"session-end.sh",
|
||||
"pre-tool-use.sh",
|
||||
"post-tool-use.sh",
|
||||
],
|
||||
);
|
||||
|
||||
let config_tmp = TempDir::new().unwrap();
|
||||
let config_path = config_tmp.path().join("hooks.json");
|
||||
|
||||
let first = merge_antigravity_hooks(
|
||||
hooks_tmp.path(),
|
||||
"http://127.0.0.1:49374",
|
||||
None,
|
||||
&config_path,
|
||||
)
|
||||
.unwrap();
|
||||
assert_ne!(
|
||||
first,
|
||||
ApplyOutcome::NoOp,
|
||||
"first apply should not be a no-op"
|
||||
);
|
||||
|
||||
let second = merge_antigravity_hooks(
|
||||
hooks_tmp.path(),
|
||||
"http://127.0.0.1:49374",
|
||||
None,
|
||||
&config_path,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(second, ApplyOutcome::NoOp, "second apply must be a no-op");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ fn resolve_targets(explicit: Option<&std::path::PathBuf>) -> Result<Vec<std::pat
|
||||
eprintln!(
|
||||
"note: neither CLAUDE.md nor AGENTS.md exists in {}; \
|
||||
creating CLAUDE.md. If you use Codex / OpenCode / \
|
||||
Cursor / Gemini CLI, re-run with `--target AGENTS.md`.",
|
||||
Cursor / Gemini CLI / Antigravity CLI, re-run with `--target AGENTS.md`.",
|
||||
cwd.display()
|
||||
);
|
||||
Ok(vec![claude_md])
|
||||
|
||||
@@ -55,6 +55,7 @@ pub fn run(config: &Config, args: InstallMcpArgs) -> Result<()> {
|
||||
McpClient::GeminiCli => render_gemini_cli(&args)?,
|
||||
McpClient::Openclaw => render_openclaw(&args)?,
|
||||
McpClient::Pi => render_pi(&args)?,
|
||||
McpClient::AntigravityCli => render_antigravity_cli(&args)?,
|
||||
};
|
||||
println!("{snippet}");
|
||||
Ok(())
|
||||
@@ -108,6 +109,10 @@ fn resolve_config_file(args: &InstallMcpArgs) -> Result<PathBuf> {
|
||||
McpClient::GeminiCli => home.join(".gemini").join("settings.json"),
|
||||
McpClient::Openclaw => home.join(".openclaw").join("config.json"),
|
||||
McpClient::Pi => home.join(".omp").join("agent").join("mcp.json"),
|
||||
McpClient::AntigravityCli => home
|
||||
.join(".gemini")
|
||||
.join("antigravity-cli")
|
||||
.join("mcp_config.json"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -142,7 +147,8 @@ fn json_mcp_location(client: McpClient) -> Option<JsonMcpLocation> {
|
||||
| McpClient::ClaudeDesktop
|
||||
| McpClient::Cursor
|
||||
| McpClient::GeminiCli
|
||||
| McpClient::Pi => Some(JsonMcpLocation::RootMcpServers),
|
||||
| McpClient::Pi
|
||||
| McpClient::AntigravityCli => Some(JsonMcpLocation::RootMcpServers),
|
||||
McpClient::OpenCode => Some(JsonMcpLocation::RootMcp),
|
||||
McpClient::Openclaw => Some(JsonMcpLocation::NestedMcpServers),
|
||||
McpClient::Codex => None,
|
||||
@@ -261,6 +267,13 @@ fn build_mcp_entry(args: &InstallMcpArgs) -> Result<serde_json::Value> {
|
||||
entry.insert("headers".into(), json!({"Authorization": b}));
|
||||
}
|
||||
}
|
||||
McpClient::AntigravityCli => {
|
||||
entry.insert("serverUrl".into(), json!(args.server_url));
|
||||
entry.insert("timeout".into(), json!(GEMINI_MCP_TIMEOUT_MS));
|
||||
if let Some(b) = &bearer {
|
||||
entry.insert("headers".into(), json!({"Authorization": b}));
|
||||
}
|
||||
}
|
||||
_ => bail!("internal: build_mcp_entry called for unsupported client"),
|
||||
}
|
||||
Ok(serde_json::Value::Object(entry))
|
||||
@@ -528,6 +541,17 @@ fn render_pi(args: &InstallMcpArgs) -> Result<String> {
|
||||
))
|
||||
}
|
||||
|
||||
fn render_antigravity_cli(args: &InstallMcpArgs) -> Result<String> {
|
||||
Ok(format!(
|
||||
"# Antigravity CLI (`agy`) — merge into ~/.gemini/antigravity-cli/mcp_config.json:\n\
|
||||
#\n\
|
||||
# Antigravity CLI uses `serverUrl` (not `url` or `httpUrl`) for\n\
|
||||
# streamable-HTTP endpoints. The `timeout` is in milliseconds.\n\
|
||||
{snippet}\n",
|
||||
snippet = render_json_mcp_fragment(args)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -565,6 +589,7 @@ mod tests {
|
||||
McpClient::GeminiCli => render_gemini_cli(&args).unwrap(),
|
||||
McpClient::Openclaw => render_openclaw(&args).unwrap(),
|
||||
McpClient::Pi => render_pi(&args).unwrap(),
|
||||
McpClient::AntigravityCli => render_antigravity_cli(&args).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,6 +606,7 @@ mod tests {
|
||||
McpClient::GeminiCli,
|
||||
McpClient::Openclaw,
|
||||
McpClient::Pi,
|
||||
McpClient::AntigravityCli,
|
||||
] {
|
||||
let out = render_with_token(client);
|
||||
// Every client embeds the token as `Authorization:
|
||||
@@ -611,6 +637,7 @@ mod tests {
|
||||
McpClient::GeminiCli,
|
||||
McpClient::Openclaw,
|
||||
McpClient::Pi,
|
||||
McpClient::AntigravityCli,
|
||||
] {
|
||||
let out = render_for_test(client);
|
||||
assert!(
|
||||
@@ -631,6 +658,7 @@ mod tests {
|
||||
McpClient::GeminiCli => render_gemini_cli(&args).unwrap(),
|
||||
McpClient::Openclaw => render_openclaw(&args).unwrap(),
|
||||
McpClient::Pi => render_pi(&args).unwrap(),
|
||||
McpClient::AntigravityCli => render_antigravity_cli(&args).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,6 +674,7 @@ mod tests {
|
||||
assert!(render_for_test(McpClient::Openclaw).contains("\"streamable-http\""));
|
||||
assert!(render_for_test(McpClient::Codex).contains("[mcp_servers.ai-memory]"));
|
||||
assert!(render_for_test(McpClient::Pi).contains("~/.omp/agent/mcp.json"));
|
||||
assert!(render_for_test(McpClient::AntigravityCli).contains("\"serverUrl\""));
|
||||
}
|
||||
|
||||
/// The Codex apply path must emit block-form `[mcp_servers.<name>]`
|
||||
|
||||
@@ -199,6 +199,87 @@ pub(crate) const GEMINI_PROFILE: HookProfile = HookProfile {
|
||||
shape: HookShape::Nested,
|
||||
};
|
||||
|
||||
/// Antigravity CLI (`agy`) hook-event vocabulary (per
|
||||
/// <https://antigravity.google/docs/hooks>). Named-groups format
|
||||
/// at the top level; events inside each group.
|
||||
/// Tool events use nested shape (matcher + hooks), lifecycle
|
||||
/// events use flat shape (direct handler list).
|
||||
pub(crate) const ANTIGRAVITY_TOOL_EVENTS: [(&str, &str); 2] = [
|
||||
("PreToolUse", "pre-tool-use.sh"),
|
||||
("PostToolUse", "post-tool-use.sh"),
|
||||
];
|
||||
|
||||
pub(crate) const ANTIGRAVITY_LIFECYCLE_EVENTS: [(&str, &str); 2] = [
|
||||
("PreInvocation", "session-start.sh"),
|
||||
("Stop", "session-end.sh"),
|
||||
];
|
||||
|
||||
/// Build the Antigravity CLI (`agy`) `hooks.json` payload.
|
||||
///
|
||||
/// Antigravity CLI uses a named-groups format where the top level
|
||||
/// maps hook-group names to their event configurations. Each group
|
||||
/// can contain any subset of the supported events. Tool events
|
||||
/// (`PreToolUse`, `PostToolUse`) use the nested shape with matcher;
|
||||
/// lifecycle events (`PreInvocation`, `Stop`) use a flat handler
|
||||
/// list where matcher is ignored.
|
||||
///
|
||||
/// The output is `{ "ai-memory": { <events> } }`.
|
||||
#[must_use]
|
||||
pub(crate) fn build_antigravity_payload(
|
||||
emit_root: &Path,
|
||||
server_url: &str,
|
||||
auth_token: Option<&str>,
|
||||
) -> serde_json::Value {
|
||||
build_antigravity_payload_for_platform(
|
||||
emit_root,
|
||||
server_url,
|
||||
auth_token,
|
||||
HookCommandPlatform::current(),
|
||||
)
|
||||
}
|
||||
|
||||
fn build_antigravity_payload_for_platform(
|
||||
emit_root: &Path,
|
||||
server_url: &str,
|
||||
auth_token: Option<&str>,
|
||||
platform: HookCommandPlatform,
|
||||
) -> serde_json::Value {
|
||||
let mut group = serde_json::Map::new();
|
||||
|
||||
// Tool events: nested shape (matcher + inner hooks array)
|
||||
for (event, script) in &ANTIGRAVITY_TOOL_EVENTS {
|
||||
let s = script_for_platform(script, platform);
|
||||
let abs = emit_root.join(s.as_ref());
|
||||
let command = hook_command(&abs, server_url, auth_token, platform);
|
||||
group.insert(
|
||||
(*event).to_string(),
|
||||
json!([{
|
||||
"matcher": "",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": command,
|
||||
}],
|
||||
}]),
|
||||
);
|
||||
}
|
||||
|
||||
// Lifecycle events: flat shape (direct handler list, no matcher)
|
||||
for (event, script) in &ANTIGRAVITY_LIFECYCLE_EVENTS {
|
||||
let s = script_for_platform(script, platform);
|
||||
let abs = emit_root.join(s.as_ref());
|
||||
let command = hook_command(&abs, server_url, auth_token, platform);
|
||||
group.insert(
|
||||
(*event).to_string(),
|
||||
json!([{
|
||||
"type": "command",
|
||||
"command": command,
|
||||
}]),
|
||||
);
|
||||
}
|
||||
|
||||
json!({ "ai-memory": group })
|
||||
}
|
||||
|
||||
/// Build a Codex-flavoured hook payload. Thin alias for back-compat;
|
||||
/// new code should call `build_profile_payload(&CODEX_PROFILE, …)`.
|
||||
pub(crate) fn build_codex_payload(
|
||||
@@ -637,4 +718,63 @@ mod tests {
|
||||
"Windows command must not use sh: {cmd}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn antigravity_payload_uses_named_groups_with_mixed_shape() {
|
||||
let root = PathBuf::from("/host/hooks/antigravity-cli");
|
||||
let v = build_antigravity_payload(&root, "http://localhost:49374", Some("tok"));
|
||||
|
||||
// Top-level key is the named group "ai-memory", not "hooks"
|
||||
let group = v
|
||||
.get("ai-memory")
|
||||
.and_then(|g| g.as_object())
|
||||
.expect("missing top-level 'ai-memory' named group");
|
||||
assert!(
|
||||
!v.as_object().unwrap().contains_key("hooks"),
|
||||
"Antigravity uses named groups, not a 'hooks' wrapper"
|
||||
);
|
||||
|
||||
// Tool events: nested shape (matcher + hooks array)
|
||||
let pre_tool = group
|
||||
.get("PreToolUse")
|
||||
.and_then(|e| e.as_array())
|
||||
.expect("missing PreToolUse");
|
||||
let outer = pre_tool[0].as_object().unwrap();
|
||||
assert!(outer.contains_key("matcher"));
|
||||
let inner = outer.get("hooks").and_then(|h| h.as_array()).unwrap();
|
||||
assert_eq!(inner.len(), 1);
|
||||
let entry = inner[0].as_object().unwrap();
|
||||
assert_eq!(entry.get("type").and_then(|t| t.as_str()), Some("command"));
|
||||
|
||||
// Lifecycle events: flat shape (no matcher, direct handler list)
|
||||
let pre_invocation = group
|
||||
.get("PreInvocation")
|
||||
.and_then(|e| e.as_array())
|
||||
.expect("missing PreInvocation");
|
||||
let handler = pre_invocation[0].as_object().unwrap();
|
||||
assert!(
|
||||
!handler.contains_key("matcher"),
|
||||
"PreInvocation should not have matcher (flat shape)"
|
||||
);
|
||||
assert!(
|
||||
!handler.contains_key("hooks"),
|
||||
"PreInvocation should not have inner hooks array (flat shape)"
|
||||
);
|
||||
assert_eq!(
|
||||
handler.get("type").and_then(|t| t.as_str()),
|
||||
Some("command")
|
||||
);
|
||||
|
||||
// Auth token inlined into commands
|
||||
let cmd = handler.get("command").and_then(|c| c.as_str()).unwrap();
|
||||
assert!(cmd.contains("AI_MEMORY_AUTH_TOKEN=tok"));
|
||||
|
||||
// All expected events present
|
||||
for expected in ["PreToolUse", "PostToolUse", "PreInvocation", "Stop"] {
|
||||
assert!(
|
||||
group.contains_key(expected),
|
||||
"missing Antigravity event {expected}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ pub fn run(config: &Config, args: SetupAgentArgs) -> Result<()> {
|
||||
AgentChoice::Codex => "codex",
|
||||
AgentChoice::Cursor => "cursor",
|
||||
AgentChoice::GeminiCli => "gemini-cli",
|
||||
AgentChoice::AntigravityCli => "antigravity-cli",
|
||||
AgentChoice::OpenCode => unreachable!("opencode handled above"),
|
||||
AgentChoice::Omp => unreachable!("omp handled above"),
|
||||
AgentChoice::Openclaw => unreachable!("openclaw handled above"),
|
||||
@@ -120,7 +121,10 @@ pub fn run(config: &Config, args: SetupAgentArgs) -> Result<()> {
|
||||
|
||||
match args.agent {
|
||||
AgentChoice::ClaudeCode => emit_claude_code(&emit_root, &args)?,
|
||||
AgentChoice::Codex | AgentChoice::Cursor | AgentChoice::GeminiCli => {
|
||||
AgentChoice::Codex
|
||||
| AgentChoice::Cursor
|
||||
| AgentChoice::GeminiCli
|
||||
| AgentChoice::AntigravityCli => {
|
||||
emit_other(&emit_root, agent_sub, &args);
|
||||
}
|
||||
AgentChoice::OpenCode => unreachable!("opencode handled above"),
|
||||
|
||||
@@ -186,6 +186,8 @@ pub enum AgentKind {
|
||||
/// OpenClaw personal AI gateway.
|
||||
#[serde(rename = "openclaw", alias = "open-claw")]
|
||||
OpenClaw,
|
||||
/// Google Antigravity CLI (`agy`).
|
||||
AntigravityCli,
|
||||
/// Oh My Pi (`omp`) / Pi-compatible coding agent.
|
||||
Omp,
|
||||
/// Anything else (manual capture, future agents).
|
||||
@@ -204,6 +206,7 @@ impl AgentKind {
|
||||
Self::GeminiCli => "gemini-cli",
|
||||
Self::ClaudeDesktop => "claude-desktop",
|
||||
Self::OpenClaw => "openclaw",
|
||||
Self::AntigravityCli => "antigravity-cli",
|
||||
Self::Omp => "omp",
|
||||
Self::Other => "other",
|
||||
}
|
||||
@@ -222,6 +225,7 @@ impl AgentKind {
|
||||
"gemini-cli" | "gemini" => Self::GeminiCli,
|
||||
"claude-desktop" | "claude_desktop" => Self::ClaudeDesktop,
|
||||
"openclaw" | "open-claw" => Self::OpenClaw,
|
||||
"antigravity-cli" | "antigravity" | "agy" => Self::AntigravityCli,
|
||||
"omp" | "pi" | "oh-my-pi" => Self::Omp,
|
||||
_ => Self::Other,
|
||||
}
|
||||
|
||||
@@ -792,6 +792,7 @@ impl AiMemoryServer {
|
||||
"opencode": "AGENTS.md",
|
||||
"cursor": "AGENTS.md",
|
||||
"gemini_cli": "AGENTS.md",
|
||||
"antigravity_cli": "AGENTS.md",
|
||||
"default": "AGENTS.md"
|
||||
},
|
||||
"notes": [
|
||||
|
||||
+8
-8
@@ -7,7 +7,7 @@ path (docker + Claude Code). This page covers everything else:
|
||||
(homelab, LAN box, remote server)
|
||||
- [Configuring the CLI URL and auth](#configuring-the-cli-url-and-auth)
|
||||
- [Configuring other agent CLIs](#configuring-other-agent-clis)
|
||||
(Codex, OpenCode, OMP, Cursor, Claude Desktop, Gemini CLI, OpenClaw)
|
||||
(Codex, OpenCode, OMP, Cursor, Claude Desktop, Gemini CLI, Antigravity CLI, OpenClaw)
|
||||
- [Installing hooks without docker](#installing-hooks-without-docker)
|
||||
(curl-based installer)
|
||||
- [Running ai-memory without docker](#running-ai-memory-without-docker)
|
||||
@@ -116,11 +116,11 @@ Each agent CLI needs two things:
|
||||
becomes manual.
|
||||
|
||||
Claude Desktop is MCP-only today. Claude Code, Codex, OpenCode, OMP,
|
||||
Cursor, Gemini CLI, and OpenClaw have lifecycle capture paths through
|
||||
Cursor, Gemini CLI, Antigravity CLI, and OpenClaw have lifecycle capture paths through
|
||||
`install-hooks`.
|
||||
|
||||
> **Two-step hook install pattern.** Claude Code, Codex, Cursor, and
|
||||
> Gemini CLI use shell/PowerShell hook scripts: (1) `docker cp` the
|
||||
> **Two-step hook install pattern.** Claude Code, Codex, Cursor,
|
||||
> Gemini CLI, and Antigravity CLI use shell/PowerShell hook scripts: (1) `docker cp` the
|
||||
> bundled scripts to your home dir, (2) `docker run --rm install-hooks`
|
||||
> to render the config snippet.
|
||||
> OpenClaw, OpenCode, and OMP are different: they use generated
|
||||
@@ -214,7 +214,7 @@ files owned by the user running the command. Prefer it as the
|
||||
default; reach for `setup-agent` only when your docker setup is
|
||||
known not to remap UIDs.
|
||||
|
||||
### Cursor, Gemini CLI, Claude Desktop, OpenClaw
|
||||
### Cursor, Gemini CLI, Claude Desktop, OpenClaw, Antigravity CLI
|
||||
|
||||
See [**`docs/mcp-install.md`**](mcp-install.md) for the per-client MCP
|
||||
config file path and snippet, or one-shot it via:
|
||||
@@ -249,7 +249,7 @@ docker run --rm akitaonrails/ai-memory:latest \
|
||||
--server-url "http://homelab:49374"
|
||||
```
|
||||
|
||||
Cursor, Gemini CLI, and OpenClaw support both `install-mcp` and
|
||||
Cursor, Gemini CLI, Antigravity CLI, and OpenClaw support both `install-mcp` and
|
||||
`install-hooks`. Claude Desktop is MCP-only here, so you'll need to
|
||||
nudge the model to call `memory_query` / `memory_handoff_accept` itself.
|
||||
For clients with `install-hooks` support, the capture path handles
|
||||
@@ -277,7 +277,7 @@ docker run --rm akitaonrails/ai-memory:latest \
|
||||
```
|
||||
|
||||
The curl script installer supports
|
||||
`--agent claude-code|codex|cursor|gemini-cli|opencode|openclaw|omp|pi`
|
||||
`--agent claude-code|codex|cursor|gemini-cli|antigravity-cli|opencode|openclaw|omp|pi`
|
||||
and `--to <dir>`; `--help` prints the full flag list. OpenCode,
|
||||
OpenClaw, and OMP do not need script extraction because `install-hooks`
|
||||
generates TypeScript plugin/extension files for them instead.
|
||||
@@ -584,6 +584,6 @@ write to `~/.local/share/ai-memory/hooks/`.
|
||||
- [`docs/usage.md`](usage.md) - handoffs, proactive querying, web UI,
|
||||
routing snippet, and raw-wiki inspection
|
||||
- [`docs/mcp-install.md`](mcp-install.md) - per-client MCP config
|
||||
reference for Cursor, Claude Desktop, Gemini CLI, OpenClaw, OMP
|
||||
reference for Cursor, Claude Desktop, Gemini CLI, Antigravity CLI, OpenClaw, OMP
|
||||
- [`docs/ARCHITECTURE.md`](ARCHITECTURE.md) - what's actually
|
||||
running inside ai-memory
|
||||
|
||||
+101
-5
@@ -19,9 +19,9 @@
|
||||
This page documents how to register ai-memory as an MCP server with
|
||||
agent CLIs beyond the README quick start.
|
||||
|
||||
Claude Code, OpenAI Codex, Cursor, Gemini CLI, OpenClaw, OpenCode, and
|
||||
Claude Code, OpenAI Codex, Cursor, Gemini CLI, Antigravity CLI, OpenClaw, OpenCode, and
|
||||
OMP have automatic capture integrations (shell/PowerShell hooks for
|
||||
Claude Code / Codex / Cursor / Gemini CLI, TypeScript plugin/extension
|
||||
Claude Code / Codex / Cursor / Gemini CLI / Antigravity CLI, TypeScript plugin/extension
|
||||
files for OpenClaw / OpenCode / OMP) and are covered in the
|
||||
[main README](../README.md#quick-start).
|
||||
|
||||
@@ -41,7 +41,7 @@ the LLM to call `memory_handoff_begin` manually before quitting.
|
||||
> **One-shot tip:** every snippet below is also reachable from the
|
||||
> CLI:
|
||||
> ```bash
|
||||
> ai-memory install-mcp --client gemini-cli # or cursor / claude-desktop / openclaw / pi|omp
|
||||
> ai-memory install-mcp --client gemini-cli # or cursor / claude-desktop / openclaw / pi|omp / antigravity-cli
|
||||
> ```
|
||||
|
||||
---
|
||||
@@ -166,6 +166,102 @@ capture path; `SessionStart` also fetches pending handoffs.
|
||||
|
||||
---
|
||||
|
||||
## Antigravity CLI (`agy`)
|
||||
|
||||
**Status:** ✅ MCP supported. ✅ Lifecycle hooks supported via
|
||||
`ai-memory install-hooks --agent antigravity-cli --apply`.
|
||||
|
||||
**Config file (MCP):** `~/.gemini/antigravity-cli/mcp_config.json`
|
||||
|
||||
Antigravity CLI is the successor to Gemini CLI, built in Go with
|
||||
parallel subagent support. It uses a separate `mcp_config.json`
|
||||
(instead of Gemini CLI's combined `settings.json`) and uses
|
||||
`serverUrl` (not `httpUrl`) for streamable-HTTP endpoints.
|
||||
|
||||
```bash
|
||||
# One-shot via CLI:
|
||||
ai-memory install-mcp --client antigravity-cli
|
||||
```
|
||||
|
||||
The rendered snippet writes to `mcp_config.json` under `mcpServers`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"ai-memory": {
|
||||
"serverUrl": "http://127.0.0.1:49374/mcp",
|
||||
"timeout": 5000
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Config file (hooks):** `~/.gemini/antigravity-cli/hooks.json`
|
||||
|
||||
Antigravity CLI uses a named-groups hook format. Each top-level key
|
||||
is a hook group name; inside, event arrays map to handlers. Tool
|
||||
events (`PreToolUse`, `PostToolUse`) use nested shape with matcher;
|
||||
lifecycle events (`PreInvocation`, `Stop`) use flat shape.
|
||||
|
||||
```bash
|
||||
# One-shot via CLI:
|
||||
ai-memory install-hooks --agent antigravity-cli --apply
|
||||
```
|
||||
|
||||
The rendered hooks config looks like:
|
||||
|
||||
```json
|
||||
{
|
||||
"ai-memory": {
|
||||
"PreInvocation": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "AI_MEMORY_HOOK_URL=http://127.0.0.1:49374 /path/to/session-start.sh"
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "AI_MEMORY_HOOK_URL=http://127.0.0.1:49374 /path/to/pre-tool-use.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "AI_MEMORY_HOOK_URL=http://127.0.0.1:49374 /path/to/post-tool-use.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "AI_MEMORY_HOOK_URL=http://127.0.0.1:49374 /path/to/session-end.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Gotchas:**
|
||||
- Antigravity CLI uses `serverUrl` for HTTP MCP endpoints, not `url`
|
||||
or `httpUrl`. The `--apply` flag writes the correct key.
|
||||
- Hook scripts are staged under `~/.local/share/ai-memory/hooks/antigravity-cli/`.
|
||||
- The `PreInvocation` event fires before each model call (not just at
|
||||
session start). This is the closest equivalent to Gemini CLI's
|
||||
`SessionStart`.
|
||||
- Source: <https://antigravity.google/docs/hooks>
|
||||
|
||||
---
|
||||
|
||||
## OpenClaw
|
||||
|
||||
**Status:** ✅ MCP supported. ✅ Lifecycle hooks supported via a native
|
||||
@@ -300,8 +396,8 @@ that *starts* the next one - to play nicely with ai-memory:
|
||||
|
||||
| Side | What's needed | Covered by |
|
||||
|---|---|---|
|
||||
| **Ending side** | The agent must create a handoff, either through a true session-end hook or by calling `memory_handoff_begin`. | Built-in for Claude Code, Codex, Cursor, Gemini CLI, OpenClaw, and OMP. OpenCode has no true session-end event, so ask it to call `memory_handoff_begin` before quitting when you need a handoff. |
|
||||
| **Starting side** | Either (a) the session-start/plugin path injects the handoff via `/handoff`, OR (b) the model proactively calls `memory_handoff_accept` on first turn. | (a) is built-in for Claude Code / Codex / Cursor / Gemini CLI / OpenClaw / OpenCode / OMP. (b) works for any MCP-capable client if you nudge the model - see [the routing snippet](usage.md#install-the-routing-snippet). |
|
||||
| **Ending side** | The agent must create a handoff, either through a true session-end hook or by calling `memory_handoff_begin`. | Built-in for Claude Code, Codex, Cursor, Gemini CLI, Antigravity CLI, OpenClaw, and OMP. OpenCode has no true session-end event, so ask it to call `memory_handoff_begin` before quitting when you need a handoff. |
|
||||
| **Starting side** | Either (a) the session-start/plugin path injects the handoff via `/handoff`, OR (b) the model proactively calls `memory_handoff_accept` on first turn. | (a) is built-in for Claude Code / Codex / Cursor / Gemini CLI / Antigravity CLI / OpenClaw / OpenCode / OMP. (b) works for any MCP-capable client if you nudge the model - see [the routing snippet](usage.md#install-the-routing-snippet). |
|
||||
|
||||
So a typical mixed workflow looks like:
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
# Sourced by per-agent lifecycle hook scripts. POSIX shell only —
|
||||
# no bash-isms, no external deps (no jq, no toml crate). Keep changes
|
||||
# byte-trivial because every supported agent (claude-code, codex,
|
||||
# cursor, gemini-cli, opencode, omp) sources this same file.
|
||||
# cursor, gemini-cli, antigravity-cli, opencode, omp) sources this same file.
|
||||
|
||||
# Walk up from "$1" toward $HOME (or /) looking for `.ai-memory.toml`.
|
||||
# Prints the absolute path of the first marker found, or nothing.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
. "$PSScriptRoot\..\lib\ai-memory-hook.ps1"
|
||||
Invoke-AiMemoryHook -Event "post-tool-use" -Agent "antigravity-cli"
|
||||
exit 0
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# antigravity-cli post-tool-use hook.
|
||||
# Forwards the event JSON to the ai-memory server, fire-and-forget.
|
||||
_lib_dir="$(dirname "$0")"
|
||||
[ -f "$_lib_dir/_lib.sh" ] || _lib_dir="$_lib_dir/.."
|
||||
. "$_lib_dir/_lib.sh"
|
||||
|
||||
SERVER="${AI_MEMORY_HOOK_URL:-http://127.0.0.1:49374}"
|
||||
PAYLOAD=$(cat)
|
||||
CWD=$(ai_memory_extract_cwd "$PAYLOAD")
|
||||
QS=$(ai_memory_marker_qs "$CWD")
|
||||
|
||||
printf '%s' "$PAYLOAD" \
|
||||
| ai_memory_post_hook "$SERVER/hook?event=post-tool-use&agent=antigravity-cli${QS}" >/dev/null 2>&1 || true
|
||||
exit 0
|
||||
@@ -0,0 +1,3 @@
|
||||
. "$PSScriptRoot\..\lib\ai-memory-hook.ps1"
|
||||
Invoke-AiMemoryHook -Event "pre-compact" -Agent "antigravity-cli"
|
||||
exit 0
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# antigravity-cli pre-compact hook.
|
||||
# Forwards the event JSON to the ai-memory server, fire-and-forget.
|
||||
_lib_dir="$(dirname "$0")"
|
||||
[ -f "$_lib_dir/_lib.sh" ] || _lib_dir="$_lib_dir/.."
|
||||
. "$_lib_dir/_lib.sh"
|
||||
|
||||
SERVER="${AI_MEMORY_HOOK_URL:-http://127.0.0.1:49374}"
|
||||
PAYLOAD=$(cat)
|
||||
CWD=$(ai_memory_extract_cwd "$PAYLOAD")
|
||||
QS=$(ai_memory_marker_qs "$CWD")
|
||||
|
||||
printf '%s' "$PAYLOAD" \
|
||||
| ai_memory_post_hook "$SERVER/hook?event=pre-compact&agent=antigravity-cli${QS}" >/dev/null 2>&1 || true
|
||||
exit 0
|
||||
@@ -0,0 +1,3 @@
|
||||
. "$PSScriptRoot\..\lib\ai-memory-hook.ps1"
|
||||
Invoke-AiMemoryHook -Event "pre-tool-use" -Agent "antigravity-cli"
|
||||
exit 0
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# antigravity-cli pre-tool-use hook.
|
||||
# Forwards the event JSON to the ai-memory server, fire-and-forget.
|
||||
_lib_dir="$(dirname "$0")"
|
||||
[ -f "$_lib_dir/_lib.sh" ] || _lib_dir="$_lib_dir/.."
|
||||
. "$_lib_dir/_lib.sh"
|
||||
|
||||
SERVER="${AI_MEMORY_HOOK_URL:-http://127.0.0.1:49374}"
|
||||
PAYLOAD=$(cat)
|
||||
CWD=$(ai_memory_extract_cwd "$PAYLOAD")
|
||||
QS=$(ai_memory_marker_qs "$CWD")
|
||||
|
||||
printf '%s' "$PAYLOAD" \
|
||||
| ai_memory_post_hook "$SERVER/hook?event=pre-tool-use&agent=antigravity-cli${QS}" >/dev/null 2>&1 || true
|
||||
exit 0
|
||||
@@ -0,0 +1,3 @@
|
||||
. "$PSScriptRoot\..\lib\ai-memory-hook.ps1"
|
||||
Invoke-AiMemoryHook -Event "session-end" -Agent "antigravity-cli"
|
||||
exit 0
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# antigravity-cli session-end hook.
|
||||
# Forwards the event JSON to the ai-memory server, fire-and-forget.
|
||||
_lib_dir="$(dirname "$0")"
|
||||
[ -f "$_lib_dir/_lib.sh" ] || _lib_dir="$_lib_dir/.."
|
||||
. "$_lib_dir/_lib.sh"
|
||||
|
||||
SERVER="${AI_MEMORY_HOOK_URL:-http://127.0.0.1:49374}"
|
||||
PAYLOAD=$(cat)
|
||||
CWD=$(ai_memory_extract_cwd "$PAYLOAD")
|
||||
QS=$(ai_memory_marker_qs "$CWD")
|
||||
|
||||
printf '%s' "$PAYLOAD" \
|
||||
| ai_memory_post_hook "$SERVER/hook?event=session-end&agent=antigravity-cli${QS}" >/dev/null 2>&1 || true
|
||||
exit 0
|
||||
@@ -0,0 +1,3 @@
|
||||
. "$PSScriptRoot\..\lib\ai-memory-hook.ps1"
|
||||
Invoke-AiMemoryHook -Event "session-start" -Agent "antigravity-cli" -FetchHandoff
|
||||
exit 0
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
# antigravity-cli SessionStart hook.
|
||||
# 1. Forwards the event JSON to the ai-memory server (fire-and-forget).
|
||||
# 2. Synchronously fetches any pending cross-agent handoff and prints
|
||||
# it to stdout — agent CLIs prepend session-start hook stdout to
|
||||
# the next session, so the resuming agent sees prior context with
|
||||
# no human in the loop.
|
||||
#
|
||||
_lib_dir="$(dirname "$0")"
|
||||
[ -f "$_lib_dir/_lib.sh" ] || _lib_dir="$_lib_dir/.."
|
||||
. "$_lib_dir/_lib.sh"
|
||||
|
||||
SERVER="${AI_MEMORY_HOOK_URL:-http://127.0.0.1:49374}"
|
||||
PAYLOAD=$(cat)
|
||||
CWD=$(ai_memory_extract_cwd "$PAYLOAD")
|
||||
QS=$(ai_memory_marker_qs "$CWD")
|
||||
|
||||
printf '%s' "$PAYLOAD" \
|
||||
| ai_memory_post_hook "$SERVER/hook?event=session-start&agent=antigravity-cli${QS}" >/dev/null 2>&1 || true
|
||||
ai_memory_get_handoff "$SERVER/handoff?agent=antigravity-cli${QS}" 2>/dev/null || true
|
||||
exit 0
|
||||
@@ -0,0 +1,3 @@
|
||||
. "$PSScriptRoot\..\lib\ai-memory-hook.ps1"
|
||||
Invoke-AiMemoryHook -Event "stop" -Agent "antigravity-cli"
|
||||
exit 0
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# antigravity-cli stop hook.
|
||||
# Forwards the event JSON to the ai-memory server, fire-and-forget.
|
||||
_lib_dir="$(dirname "$0")"
|
||||
[ -f "$_lib_dir/_lib.sh" ] || _lib_dir="$_lib_dir/.."
|
||||
. "$_lib_dir/_lib.sh"
|
||||
|
||||
SERVER="${AI_MEMORY_HOOK_URL:-http://127.0.0.1:49374}"
|
||||
PAYLOAD=$(cat)
|
||||
CWD=$(ai_memory_extract_cwd "$PAYLOAD")
|
||||
QS=$(ai_memory_marker_qs "$CWD")
|
||||
|
||||
printf '%s' "$PAYLOAD" \
|
||||
| ai_memory_post_hook "$SERVER/hook?event=stop&agent=antigravity-cli${QS}" >/dev/null 2>&1 || true
|
||||
exit 0
|
||||
@@ -0,0 +1,3 @@
|
||||
. "$PSScriptRoot\..\lib\ai-memory-hook.ps1"
|
||||
Invoke-AiMemoryHook -Event "user-prompt" -Agent "antigravity-cli"
|
||||
exit 0
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# antigravity-cli user-prompt hook.
|
||||
# Forwards the event JSON to the ai-memory server, fire-and-forget.
|
||||
_lib_dir="$(dirname "$0")"
|
||||
[ -f "$_lib_dir/_lib.sh" ] || _lib_dir="$_lib_dir/.."
|
||||
. "$_lib_dir/_lib.sh"
|
||||
|
||||
SERVER="${AI_MEMORY_HOOK_URL:-http://127.0.0.1:49374}"
|
||||
PAYLOAD=$(cat)
|
||||
CWD=$(ai_memory_extract_cwd "$PAYLOAD")
|
||||
QS=$(ai_memory_marker_qs "$CWD")
|
||||
|
||||
printf '%s' "$PAYLOAD" \
|
||||
| ai_memory_post_hook "$SERVER/hook?event=user-prompt&agent=antigravity-cli${QS}" >/dev/null 2>&1 || true
|
||||
exit 0
|
||||
@@ -10,7 +10,7 @@
|
||||
# | bash -s -- --agent claude-code
|
||||
#
|
||||
# Options:
|
||||
# --agent <claude-code|codex|cursor|gemini-cli|opencode|openclaw|omp|pi>
|
||||
# --agent <claude-code|codex|cursor|gemini-cli|antigravity-cli|opencode|openclaw|omp|pi>
|
||||
# which agent (default: claude-code;
|
||||
# generated-plugin agents print hints)
|
||||
# --to <dir> install root (default: $HOME/.ai-memory/hooks)
|
||||
@@ -46,9 +46,9 @@ while [[ $# -gt 0 ]]; do
|
||||
done
|
||||
|
||||
case "$AGENT" in
|
||||
claude-code|codex|cursor|gemini-cli|opencode|openclaw|omp|pi|oh-my-pi) ;;
|
||||
claude-code|codex|cursor|gemini-cli|antigravity-cli|opencode|openclaw|omp|pi|oh-my-pi) ;;
|
||||
*)
|
||||
echo "unsupported agent: $AGENT (expected claude-code | codex | cursor | gemini-cli | opencode | openclaw | omp | pi | oh-my-pi)" >&2
|
||||
echo "unsupported agent: $AGENT (expected claude-code | codex | cursor | gemini-cli | antigravity-cli | opencode | openclaw | omp | pi | oh-my-pi)" >&2
|
||||
exit 64 ;;
|
||||
esac
|
||||
|
||||
|
||||
Reference in New Issue
Block a user