From a36fe1427e52eace359c4536ac10e57f864d88fe Mon Sep 17 00:00:00 2001 From: kanishkez Date: Mon, 1 Jun 2026 18:00:28 +0530 Subject: [PATCH 1/4] feat: add xmem-antigravity plugin and groq support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit antigravity is mentioned in the README as a supported export format but there was no actual plugin for it. added plugin/xmem-antigravity with the same structure as xmem-claude — hooks, skills, commands, and scripts. also added a native transcript.jsonl parser in plugin-utils so antigravity sessions don't need the LLM fallback that other formats use. groq was the only major provider missing from the model registry. added src/models/groq.py and wired it into settings, registry, and base.py. default model is llama-3.3-70b-versatile. --- .env.example | 3 + .../.antigravity-plugin/plugin.json | 9 ++ plugin/xmem-antigravity/README.md | 51 +++++++ plugin/xmem-antigravity/commands/index.md | 19 +++ plugin/xmem-antigravity/commands/logout.md | 16 ++ .../commands/project-config.md | 25 ++++ plugin/xmem-antigravity/commands/session.md | 14 ++ plugin/xmem-antigravity/hooks/hooks.json | 27 ++++ plugin/xmem-antigravity/package.json | 17 +++ .../xmem-antigravity/scripts/add-memory.cjs | 29 ++++ .../xmem-antigravity/scripts/context-hook.cjs | 34 +++++ .../scripts/lib/plugin-utils.cjs | 138 ++++++++++++++++++ .../scripts/lib/xmem-client.cjs | 99 +++++++++++++ .../scripts/save-project-memory.cjs | 29 ++++ .../scripts/search-memory.cjs | 24 +++ .../xmem-antigravity/scripts/summary-hook.cjs | 33 +++++ .../skills/xmem-save/SKILL.md | 28 ++++ .../skills/xmem-search/SKILL.md | 17 +++ pyproject.toml | 1 + src/config/settings.py | 14 +- src/models/base.py | 1 + src/models/groq.py | 34 +++++ src/models/registry.py | 3 + 23 files changed, 664 insertions(+), 1 deletion(-) create mode 100644 plugin/xmem-antigravity/.antigravity-plugin/plugin.json create mode 100644 plugin/xmem-antigravity/README.md create mode 100644 plugin/xmem-antigravity/commands/index.md create mode 100644 plugin/xmem-antigravity/commands/logout.md create mode 100644 plugin/xmem-antigravity/commands/project-config.md create mode 100644 plugin/xmem-antigravity/commands/session.md create mode 100644 plugin/xmem-antigravity/hooks/hooks.json create mode 100644 plugin/xmem-antigravity/package.json create mode 100644 plugin/xmem-antigravity/scripts/add-memory.cjs create mode 100644 plugin/xmem-antigravity/scripts/context-hook.cjs create mode 100644 plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs create mode 100644 plugin/xmem-antigravity/scripts/lib/xmem-client.cjs create mode 100644 plugin/xmem-antigravity/scripts/save-project-memory.cjs create mode 100644 plugin/xmem-antigravity/scripts/search-memory.cjs create mode 100644 plugin/xmem-antigravity/scripts/summary-hook.cjs create mode 100644 plugin/xmem-antigravity/skills/xmem-save/SKILL.md create mode 100644 plugin/xmem-antigravity/skills/xmem-search/SKILL.md create mode 100644 src/models/groq.py diff --git a/.env.example b/.env.example index bd83feb..dca76cf 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,9 @@ # Google Gemini GEMINI_API_KEY=your_gemini_api_key_here GEMINI_MODEL=gemini-2.5-flash +# Groq (optional — ultra-low-latency LPU inference) +# GROQ_API_KEY=your_groq_api_key_here +# GROQ_MODEL=llama-3.3-70b-versatile # LLM Settings TEMPERATURE=0.3 FALLBACK_ORDER='["gemini"]' diff --git a/plugin/xmem-antigravity/.antigravity-plugin/plugin.json b/plugin/xmem-antigravity/.antigravity-plugin/plugin.json new file mode 100644 index 0000000..bbcaaee --- /dev/null +++ b/plugin/xmem-antigravity/.antigravity-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "xmem-antigravity", + "version": "0.1.0", + "description": "Persistent XMem memory across Antigravity agent sessions", + "author": { + "name": "XMem", + "email": "support@xmem.in" + } +} diff --git a/plugin/xmem-antigravity/README.md b/plugin/xmem-antigravity/README.md new file mode 100644 index 0000000..3175215 --- /dev/null +++ b/plugin/xmem-antigravity/README.md @@ -0,0 +1,51 @@ +# XMem Antigravity Plugin + +Antigravity plugin for persistent memory through XMem. + +This plugin mirrors the structure of `xmem-claude`, adapted for Antigravity's +agent lifecycle: + +- loads relevant project memory on `SessionStart` +- stores a redacted transcript tail on `Stop` +- provides `xmem-search` and `xmem-save` skills +- includes commands for indexing, config, session checks, and logout + +## Install + +From Antigravity, install this plugin from the local folder once it is published +or linked by the plugin marketplace flow for this repo. + +## Configuration + +Use environment variables. Do not commit secrets. + +```bash +export XMEM_API_KEY="xmem_..." +export XMEM_API_URL="https://api.xmem.in" +export XMEM_USER_ID="your-user-id" +``` + +`XMEM_API_URL` defaults to `https://api.xmem.in`. `XMEM_USER_ID` falls back to the local OS username; production API keys scope requests to the authenticated key owner. + +Optional project config can live at `.antigravity/.xmem-antigravity/config.json`: + +```json +{ + "apiUrl": "https://api.xmem.in", + "userId": "your-user-id" +} +``` + +Prefer environment variables for API keys. + +## Commands + +- `/xmem-antigravity:index` - explore the current repo and save a project summary +- `/xmem-antigravity:project-config` - show configuration options +- `/xmem-antigravity:session` - check whether memory is configured +- `/xmem-antigravity:logout` - remove project-local config + +## Skills + +- `xmem-search` - search prior XMem memories +- `xmem-save` - save durable project knowledge diff --git a/plugin/xmem-antigravity/commands/index.md b/plugin/xmem-antigravity/commands/index.md new file mode 100644 index 0000000..d6c7496 --- /dev/null +++ b/plugin/xmem-antigravity/commands/index.md @@ -0,0 +1,19 @@ +--- +description: Index the current codebase into XMem for future Antigravity context +allowed-tools: ["Read", "Glob", "Grep", "Bash"] +--- + +# Index Codebase Into XMem + +Explore the repository and save a concise architecture summary into XMem. + +1. Read `README.md`, package manifests, config files, and entry points. +2. Identify the stack, runtime commands, major modules, API routes, data stores, and conventions. +3. Skip dependency folders, generated output, lock files, virtual environments, and secrets. +4. Save the final summary: + +```bash +node "${ANTIGRAVITY_PLUGIN_ROOT}/scripts/save-project-memory.cjs" "SUMMARY_HERE" +``` + +Include important files and decisions, but do not save secrets. diff --git a/plugin/xmem-antigravity/commands/logout.md b/plugin/xmem-antigravity/commands/logout.md new file mode 100644 index 0000000..403bdc3 --- /dev/null +++ b/plugin/xmem-antigravity/commands/logout.md @@ -0,0 +1,16 @@ +--- +description: Remove local XMem Antigravity plugin project config +allowed-tools: ["Bash"] +--- + +# XMem Logout + +This plugin does not store credentials by default. It reads `XMEM_API_KEY` from the environment. + +To remove project-local config: + +```bash +node -e "const fs=require('fs'); const p='.antigravity/.xmem-antigravity/config.json'; if(fs.existsSync(p)){fs.rmSync(p); console.log('Removed '+p)}else{console.log('No project config found')}" +``` + +Also unset `XMEM_API_KEY` in your shell if you want to disconnect this terminal session. diff --git a/plugin/xmem-antigravity/commands/project-config.md b/plugin/xmem-antigravity/commands/project-config.md new file mode 100644 index 0000000..aaf3b2a --- /dev/null +++ b/plugin/xmem-antigravity/commands/project-config.md @@ -0,0 +1,25 @@ +--- +description: Show XMem Antigravity plugin configuration options +allowed-tools: ["Read", "Write", "Bash"] +--- + +# XMem Antigravity Configuration + +The plugin reads credentials from environment variables first: + +```bash +export XMEM_API_KEY="xmem_..." +export XMEM_API_URL="https://api.xmem.in" +export XMEM_USER_ID="your-user-id" +``` + +Optional project config lives at `.antigravity/.xmem-antigravity/config.json`: + +```json +{ + "apiUrl": "https://api.xmem.in", + "userId": "your-user-id" +} +``` + +Avoid storing API keys in project config. Prefer environment variables or your shell secret manager. diff --git a/plugin/xmem-antigravity/commands/session.md b/plugin/xmem-antigravity/commands/session.md new file mode 100644 index 0000000..f7a3730 --- /dev/null +++ b/plugin/xmem-antigravity/commands/session.md @@ -0,0 +1,14 @@ +--- +description: Check whether XMem Antigravity memory is configured +allowed-tools: ["Bash"] +--- + +# XMem Session + +Check plugin configuration without printing secrets: + +```bash +node -e "console.log(process.env.XMEM_API_KEY ? 'XMEM_API_KEY is set' : 'XMEM_API_KEY is not set'); console.log('XMEM_API_URL=' + (process.env.XMEM_API_URL || 'https://api.xmem.in'))" +``` + +If the key is missing, set `XMEM_API_KEY` before starting Antigravity. diff --git a/plugin/xmem-antigravity/hooks/hooks.json b/plugin/xmem-antigravity/hooks/hooks.json new file mode 100644 index 0000000..6df2913 --- /dev/null +++ b/plugin/xmem-antigravity/hooks/hooks.json @@ -0,0 +1,27 @@ +{ + "description": "XMem: Persistent memory for Antigravity", + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "node \"${ANTIGRAVITY_PLUGIN_ROOT}/scripts/context-hook.cjs\"", + "timeout": 30 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "node \"${ANTIGRAVITY_PLUGIN_ROOT}/scripts/summary-hook.cjs\"", + "timeout": 30 + } + ] + } + ] + } +} diff --git a/plugin/xmem-antigravity/package.json b/plugin/xmem-antigravity/package.json new file mode 100644 index 0000000..50b24d8 --- /dev/null +++ b/plugin/xmem-antigravity/package.json @@ -0,0 +1,17 @@ +{ + "name": "xmem-antigravity", + "version": "0.1.0", + "description": "Persistent XMem memory for Antigravity agent sessions", + "private": true, + "type": "commonjs", + "engines": { + "node": ">=18.0.0" + }, + "keywords": [ + "antigravity", + "plugin", + "xmem", + "memory" + ], + "license": "MIT" +} diff --git a/plugin/xmem-antigravity/scripts/add-memory.cjs b/plugin/xmem-antigravity/scripts/add-memory.cjs new file mode 100644 index 0000000..c9afd4a --- /dev/null +++ b/plugin/xmem-antigravity/scripts/add-memory.cjs @@ -0,0 +1,29 @@ +#!/usr/bin/env node +const { createClient } = require("./lib/xmem-client.cjs"); +const { projectName, redactSecrets, truncate } = require("./lib/plugin-utils.cjs"); + +async function main() { + const content = process.argv.slice(2).join(" ").trim(); + if (!content) { + console.log('Usage: node add-memory.cjs "content to save"'); + return; + } + + try { + const cwd = process.cwd(); + const client = createClient(cwd); + await client.ingest(truncate(redactSecrets(content)), { + source: "antigravity", + type: "manual", + project: projectName(cwd), + }); + console.log(`Saved memory to XMem for project: ${projectName(cwd)}`); + } catch (error) { + console.log(`XMem save failed: ${error.message}`); + } +} + +main().catch((error) => { + console.error(`Fatal error: ${error.message}`); + process.exit(1); +}); diff --git a/plugin/xmem-antigravity/scripts/context-hook.cjs b/plugin/xmem-antigravity/scripts/context-hook.cjs new file mode 100644 index 0000000..f9b86cd --- /dev/null +++ b/plugin/xmem-antigravity/scripts/context-hook.cjs @@ -0,0 +1,34 @@ +#!/usr/bin/env node +const { createClient, formatResults } = require("./lib/xmem-client.cjs"); +const { projectName, readStdin, writeJson } = require("./lib/plugin-utils.cjs"); + +async function main() { + const input = await readStdin(); + const cwd = input.cwd || process.cwd(); + const project = projectName(cwd); + + try { + const client = createClient(cwd); + const data = await client.search(`Antigravity project context, architecture, decisions, conventions for ${project}`, 6); + const formatted = formatResults(data); + + writeJson({ + hookSpecificOutput: { + hookEventName: "SessionStart", + additionalContext: `\n${formatted}\n`, + }, + }); + } catch (error) { + writeJson({ + hookSpecificOutput: { + hookEventName: "SessionStart", + additionalContext: `\nXMem memory unavailable: ${error.message}\nSet XMEM_API_KEY to enable Antigravity memory.\n`, + }, + }); + } +} + +main().catch((error) => { + console.error(`XMem fatal: ${error.message}`); + process.exit(1); +}); diff --git a/plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs b/plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs new file mode 100644 index 0000000..73ed80d --- /dev/null +++ b/plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs @@ -0,0 +1,138 @@ +const fs = require("node:fs"); +const os = require("node:os"); +const path = require("node:path"); + +const MAX_TEXT_LENGTH = 12000; + +function readStdin() { + return new Promise((resolve) => { + let data = ""; + process.stdin.setEncoding("utf8"); + process.stdin.on("data", (chunk) => { + data += chunk; + }); + process.stdin.on("end", () => { + if (!data.trim()) { + resolve({}); + return; + } + try { + resolve(JSON.parse(data)); + } catch { + resolve({}); + } + }); + }); +} + +function writeJson(value) { + process.stdout.write(`${JSON.stringify(value)}\n`); +} + +function projectName(cwd = process.cwd()) { + return path.basename(path.resolve(cwd)); +} + +function projectConfigPath(cwd = process.cwd()) { + return path.join(path.resolve(cwd), ".antigravity", ".xmem-antigravity", "config.json"); +} + +function globalConfigPath() { + return path.join(os.homedir(), ".xmem-antigravity", "settings.json"); +} + +function readJsonFile(filePath) { + try { + return JSON.parse(fs.readFileSync(filePath, "utf8")); + } catch { + return {}; + } +} + +function loadConfig(cwd = process.cwd()) { + return { + ...readJsonFile(globalConfigPath()), + ...readJsonFile(projectConfigPath(cwd)), + }; +} + +function truncate(text, limit = MAX_TEXT_LENGTH) { + const value = String(text || "").trim(); + if (value.length <= limit) return value; + return `${value.slice(0, limit)}\n\n[truncated]`; +} + +function redactSecrets(text) { + return String(text || "") + .replace(/xmem_[A-Za-z0-9_-]{12,}/g, "[redacted-xmem-key]") + .replace(/sk-[A-Za-z0-9_-]{16,}/g, "[redacted-api-key]") + .replace(/(authorization\s*[:=]\s*bearer\s+)[^\s"'`]+/gi, "$1[redacted]") + .replace(/(bearer\s+)[^\s"'`]+/gi, "$1[redacted]") + .replace(/((?:api[_-]?key|authorization|token)\s*[:=]\s*)[^\s"'`]+/gi, "$1[redacted]"); +} + +function extractText(value) { + if (!value) return ""; + if (typeof value === "string") return value; + if (Array.isArray(value)) return value.map(extractText).filter(Boolean).join("\n"); + if (typeof value === "object") { + if (typeof value.text === "string") return value.text; + if (typeof value.content === "string") return value.content; + if (value.content) return extractText(value.content); + if (value.message) return extractText(value.message); + } + return ""; +} + +/** + * Parse an Antigravity transcript.jsonl file into a list of role: text entries. + * + * Antigravity stores one JSON object per line with the shape: + * { step_index, source, type, content, tool_calls? } + * + * source values: USER_EXPLICIT, MODEL, SYSTEM + * type values: USER_INPUT, PLANNER_RESPONSE, VIEW_FILE, RUN_COMMAND, ... + */ +function parseAntigravityTranscript(transcriptPath) { + if (!transcriptPath || !fs.existsSync(transcriptPath)) return []; + + const lines = fs.readFileSync(transcriptPath, "utf8").split(/\r?\n/).filter(Boolean); + const entries = []; + + for (const line of lines) { + try { + const step = JSON.parse(line); + const source = step.source || "entry"; + const text = extractText(step.content || step.tool_calls || step); + if (text.trim()) entries.push(`${source}: ${text.trim()}`); + } catch { + if (line.trim()) entries.push(line.trim()); + } + } + + return entries; +} + +function transcriptTail(transcriptPath, sessionId, cwd) { + const entries = parseAntigravityTranscript(transcriptPath); + if (!entries.length) return ""; + + const body = entries.slice(-40).join("\n\n"); + if (!body.trim()) return ""; + + return truncate(redactSecrets(`[Antigravity session]\nProject: ${projectName(cwd)}\nSession: ${sessionId || "unknown"}\n\n${body}`)); +} + +module.exports = { + extractText, + globalConfigPath, + loadConfig, + parseAntigravityTranscript, + projectConfigPath, + projectName, + readStdin, + redactSecrets, + transcriptTail, + truncate, + writeJson, +}; diff --git a/plugin/xmem-antigravity/scripts/lib/xmem-client.cjs b/plugin/xmem-antigravity/scripts/lib/xmem-client.cjs new file mode 100644 index 0000000..cc75d60 --- /dev/null +++ b/plugin/xmem-antigravity/scripts/lib/xmem-client.cjs @@ -0,0 +1,99 @@ +const os = require("node:os"); +const { loadConfig } = require("./plugin-utils.cjs"); + +const DEFAULT_API_URL = "https://api.xmem.in"; + +class XMemClient { + constructor(options = {}) { + this.apiUrl = String(options.apiUrl || DEFAULT_API_URL).replace(/\/+$/, ""); + this.apiKey = options.apiKey; + this.userId = options.userId; + } + + async request(path, payload) { + if (!this.apiKey) { + throw new Error("XMEM_API_KEY is not configured."); + } + + const response = await fetch(`${this.apiUrl}${path}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${this.apiKey}`, + }, + body: JSON.stringify(payload), + }); + + const text = await response.text(); + let body = null; + try { + body = JSON.parse(text); + } catch { + body = { error: text }; + } + + if (!response.ok || body?.status === "error") { + throw new Error(body?.error || body?.detail || `XMem request failed with HTTP ${response.status}`); + } + + return body?.data ?? body; + } + + ingest(content, metadata = {}) { + return this.request("/v1/memory/ingest", { + user_query: content, + agent_response: "", + user_id: this.userId, + session_datetime: new Date().toISOString(), + effort_level: "low", + metadata, + }); + } + + search(query, topK = 8) { + return this.request("/v1/memory/search", { + query, + user_id: this.userId, + top_k: topK, + domains: ["profile", "temporal", "summary"], + }); + } +} + +function createClient(cwd = process.cwd()) { + const config = loadConfig(cwd); + const userInfo = (() => { + try { + return os.userInfo().username; + } catch { + return "antigravity"; + } + })(); + + return new XMemClient({ + apiKey: process.env.XMEM_API_KEY || process.env.XMEM_ANTIGRAVITY_API_KEY || config.apiKey, + apiUrl: process.env.XMEM_API_URL || process.env.XMEM_ANTIGRAVITY_API_URL || config.apiUrl || DEFAULT_API_URL, + userId: process.env.XMEM_USER_ID || process.env.XMEM_ANTIGRAVITY_USER_ID || config.userId || userInfo || "antigravity", + }); +} + +function formatResults(data) { + const results = data?.results || []; + if (!results.length) return "No XMem memories matched."; + + return results + .slice(0, 8) + .map((item, index) => { + const score = typeof item.score === "number" ? ` score=${item.score.toFixed(3)}` : ""; + const domain = item.domain ? ` domain=${item.domain}` : ""; + return `${index + 1}.${domain}${score}\n${item.content || ""}`.trim(); + }) + .join("\n\n"); +} + +module.exports = { + DEFAULT_API_URL, + XMemClient, + createClient, + formatResults, +}; diff --git a/plugin/xmem-antigravity/scripts/save-project-memory.cjs b/plugin/xmem-antigravity/scripts/save-project-memory.cjs new file mode 100644 index 0000000..cf605a0 --- /dev/null +++ b/plugin/xmem-antigravity/scripts/save-project-memory.cjs @@ -0,0 +1,29 @@ +#!/usr/bin/env node +const { createClient } = require("./lib/xmem-client.cjs"); +const { projectName, redactSecrets, truncate } = require("./lib/plugin-utils.cjs"); + +async function main() { + const content = process.argv.slice(2).join(" ").trim(); + if (!content) { + console.log('Usage: node save-project-memory.cjs "project knowledge to save"'); + return; + } + + try { + const cwd = process.cwd(); + const client = createClient(cwd); + await client.ingest(truncate(redactSecrets(content)), { + source: "antigravity", + type: "project-knowledge", + project: projectName(cwd), + }); + console.log(`Saved project memory to XMem for: ${projectName(cwd)}`); + } catch (error) { + console.log(`XMem project save failed: ${error.message}`); + } +} + +main().catch((error) => { + console.error(`Fatal error: ${error.message}`); + process.exit(1); +}); diff --git a/plugin/xmem-antigravity/scripts/search-memory.cjs b/plugin/xmem-antigravity/scripts/search-memory.cjs new file mode 100644 index 0000000..d017915 --- /dev/null +++ b/plugin/xmem-antigravity/scripts/search-memory.cjs @@ -0,0 +1,24 @@ +#!/usr/bin/env node +const { createClient, formatResults } = require("./lib/xmem-client.cjs"); +const { redactSecrets } = require("./lib/plugin-utils.cjs"); + +async function main() { + const query = process.argv.slice(2).join(" ").trim(); + if (!query) { + console.log('Usage: node search-memory.cjs "query"'); + return; + } + + try { + const client = createClient(process.cwd()); + const data = await client.search(redactSecrets(query), 10); + console.log(formatResults(data)); + } catch (error) { + console.log(`XMem search failed: ${error.message}`); + } +} + +main().catch((error) => { + console.error(`Fatal error: ${error.message}`); + process.exit(1); +}); diff --git a/plugin/xmem-antigravity/scripts/summary-hook.cjs b/plugin/xmem-antigravity/scripts/summary-hook.cjs new file mode 100644 index 0000000..bd3d250 --- /dev/null +++ b/plugin/xmem-antigravity/scripts/summary-hook.cjs @@ -0,0 +1,33 @@ +#!/usr/bin/env node +const { createClient } = require("./lib/xmem-client.cjs"); +const { projectName, readStdin, transcriptTail, writeJson } = require("./lib/plugin-utils.cjs"); + +async function main() { + const input = await readStdin(); + const cwd = input.cwd || process.cwd(); + const content = transcriptTail(input.transcript_path, input.session_id, cwd); + + if (!content) { + writeJson({ continue: true }); + return; + } + + try { + const client = createClient(cwd); + await client.ingest(content, { + source: "antigravity", + type: "session-summary", + project: projectName(cwd), + session_id: input.session_id || "", + }); + } catch (error) { + console.error(`XMem: ${error.message}`); + } + + writeJson({ continue: true }); +} + +main().catch((error) => { + console.error(`XMem fatal: ${error.message}`); + process.exit(1); +}); diff --git a/plugin/xmem-antigravity/skills/xmem-save/SKILL.md b/plugin/xmem-antigravity/skills/xmem-save/SKILL.md new file mode 100644 index 0000000..142fde3 --- /dev/null +++ b/plugin/xmem-antigravity/skills/xmem-save/SKILL.md @@ -0,0 +1,28 @@ +--- +name: xmem-save +description: Save important project knowledge, decisions, conventions, bug fixes, or implementation context into XMem. +allowed-tools: Bash(node:*) +--- + +# XMem Save + +Use this skill when the user asks to remember or save something for future Antigravity sessions. + +Format the memory with useful context: + +```text +[SAVE:] +Project: +Decision or fact: +Relevant files: +Why it matters: +[/SAVE] +``` + +Then run: + +```bash +node "${ANTIGRAVITY_PLUGIN_ROOT}/scripts/save-project-memory.cjs" "FORMATTED_CONTENT" +``` + +Never include API keys, tokens, passwords, or other secrets in the saved content. diff --git a/plugin/xmem-antigravity/skills/xmem-search/SKILL.md b/plugin/xmem-antigravity/skills/xmem-search/SKILL.md new file mode 100644 index 0000000..1537d67 --- /dev/null +++ b/plugin/xmem-antigravity/skills/xmem-search/SKILL.md @@ -0,0 +1,17 @@ +--- +name: xmem-search +description: Search XMem for prior Antigravity sessions, project decisions, implementation notes, and remembered coding context. +allowed-tools: Bash(node:*) +--- + +# XMem Search + +Use this skill when the user asks to recall previous work, earlier implementation details, project decisions, or saved coding memory. + +Run: + +```bash +node "${ANTIGRAVITY_PLUGIN_ROOT}/scripts/search-memory.cjs" "USER_QUERY_HERE" +``` + +Summarize the returned memories clearly. If nothing useful is returned, ask a sharper follow-up question or try a more specific query. diff --git a/pyproject.toml b/pyproject.toml index 7ba8c03..e223432 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,7 @@ dependencies = [ local = [ "chromadb>=0.5.0", "fastembed>=0.3.0", + "langchain-groq>=0.2.0", "langchain-ollama>=0.1.0", "psycopg[binary]>=3.1.18", "pgvector>=0.2.5", diff --git a/src/config/settings.py b/src/config/settings.py index 80857d6..b3afff5 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -68,6 +68,18 @@ class Settings(BaseSettings): default="deepseek-v4-flash", description="DeepSeek vision model name" ) + groq_api_key: Optional[str] = Field( + default=None, + description="Groq API key" + ) + groq_model: str = Field( + default="llama-3.3-70b-versatile", + description="Groq model name" + ) + groq_vision_model: str = Field( + default="llama-3.2-11b-vision-preview", + description="Groq vision-capable model name" + ) mimo_api_key: Optional[str] = Field( default=None, description="Xiaomi MiMo API key" @@ -493,7 +505,7 @@ class Settings(BaseSettings): @field_validator("fallback_order") @classmethod def validate_fallback_order(cls, v: List[str]) -> List[str]: - valid_providers = {"gemini", "claude", "openai", "deepseek", "mimo", "openrouter", "bedrock", "ollama"} + valid_providers = {"gemini", "claude", "openai", "deepseek", "groq", "mimo", "openrouter", "bedrock", "ollama"} for provider in v: if provider not in valid_providers: raise ValueError( diff --git a/src/models/base.py b/src/models/base.py index f6f65e7..b882392 100644 --- a/src/models/base.py +++ b/src/models/base.py @@ -9,6 +9,7 @@ "claude", "openai", "deepseek", + "groq", "mimo", "openrouter", "bedrock", diff --git a/src/models/groq.py b/src/models/groq.py new file mode 100644 index 0000000..3452f29 --- /dev/null +++ b/src/models/groq.py @@ -0,0 +1,34 @@ +""" +Groq model factory. + +Uses ChatGroq with Groq's LPU (Language Processing Unit) for ultra-low-latency +inference — typically 10–20x faster than GPU-based providers. Ideal for XMem's +memory pipeline where ingest latency is critical. +""" + +from langchain_core.language_models import BaseChatModel + +from src.config import settings + + +def build_groq_model( + model_name: str | None = None, + temperature: float | None = None, +) -> BaseChatModel: + try: + from langchain_groq import ChatGroq + except ImportError as exc: + raise ImportError( + "Groq support requires langchain-groq. " + "Install it with: pip install langchain-groq" + ) from exc + + api_key = settings.groq_api_key + if not api_key: + raise ValueError("GROQ_API_KEY is not set") + + return ChatGroq( + api_key=api_key, + model=model_name or settings.groq_model, + temperature=temperature if temperature is not None else settings.temperature, + ) diff --git a/src/models/registry.py b/src/models/registry.py index 4a2941b..8c3fd16 100644 --- a/src/models/registry.py +++ b/src/models/registry.py @@ -34,6 +34,7 @@ def _build_from_module(module_name: str, func_name: str, **kwargs) -> BaseChatMo "claude": lambda **kw: _build_from_module("claude", "build_claude_model", **kw), "openai": lambda **kw: _build_from_module("openai", "build_openai_model", **kw), "deepseek": lambda **kw: _build_from_module("deepseek", "build_deepseek_model", **kw), + "groq": lambda **kw: _build_from_module("groq", "build_groq_model", **kw), "mimo": lambda **kw: _build_from_module("mimo", "build_mimo_model", **kw), "openrouter": lambda **kw: _build_from_module("openrouter", "build_openrouter_model", **kw), "bedrock": lambda **kw: _build_from_module("bedrock", "build_bedrock_model", **kw), @@ -46,6 +47,7 @@ def _build_from_module(module_name: str, func_name: str, **kwargs) -> BaseChatMo "claude": lambda: settings.claude_api_key, "openai": lambda: settings.openai_api_key, "deepseek": lambda: settings.deepseek_api_key, + "groq": lambda: settings.groq_api_key, "mimo": lambda: settings.mimo_api_key, "openrouter": lambda: settings.openrouter_api_key, "bedrock": lambda: settings.aws_access_key_id, @@ -102,6 +104,7 @@ def get_model( "claude": lambda: settings.claude_vision_model, "openai": lambda: settings.openai_vision_model, "deepseek": lambda: settings.deepseek_vision_model, + "groq": lambda: settings.groq_vision_model, "mimo": lambda: settings.mimo_vision_model, "openrouter": lambda: settings.openrouter_vision_model, "bedrock": lambda: settings.bedrock_vision_model, From ccfc8550ecd35f066220511af4d325c875e1ee8d Mon Sep 17 00:00:00 2001 From: Kanishk Date: Mon, 1 Jun 2026 18:15:06 +0530 Subject: [PATCH 2/4] Update pyproject.toml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e223432..7ba8c03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,6 @@ dependencies = [ local = [ "chromadb>=0.5.0", "fastembed>=0.3.0", - "langchain-groq>=0.2.0", "langchain-ollama>=0.1.0", "psycopg[binary]>=3.1.18", "pgvector>=0.2.5", From c08549000392422cbba5544fde20ef55f5ae4e3b Mon Sep 17 00:00:00 2001 From: Kanishk Date: Mon, 1 Jun 2026 18:15:18 +0530 Subject: [PATCH 3/4] Update src/models/groq.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- src/models/groq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/groq.py b/src/models/groq.py index 3452f29..b5dd9f8 100644 --- a/src/models/groq.py +++ b/src/models/groq.py @@ -20,7 +20,7 @@ def build_groq_model( except ImportError as exc: raise ImportError( "Groq support requires langchain-groq. " - "Install it with: pip install langchain-groq" + "Install it with: pip install -e \".[local]\"" ) from exc api_key = settings.groq_api_key From 9d7483d3f5c9a304eb8ec5b0253637e39ce3e4a8 Mon Sep 17 00:00:00 2001 From: Kanishk Date: Mon, 1 Jun 2026 18:15:29 +0530 Subject: [PATCH 4/4] Update plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs b/plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs index 73ed80d..2ffa1c1 100644 --- a/plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs +++ b/plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs @@ -66,6 +66,7 @@ function redactSecrets(text) { return String(text || "") .replace(/xmem_[A-Za-z0-9_-]{12,}/g, "[redacted-xmem-key]") .replace(/sk-[A-Za-z0-9_-]{16,}/g, "[redacted-api-key]") + .replace(/gsk_[A-Za-z0-9_-]{16,}/g, "[redacted-groq-key]") .replace(/(authorization\s*[:=]\s*bearer\s+)[^\s"'`]+/gi, "$1[redacted]") .replace(/(bearer\s+)[^\s"'`]+/gi, "$1[redacted]") .replace(/((?:api[_-]?key|authorization|token)\s*[:=]\s*)[^\s"'`]+/gi, "$1[redacted]");