@mxalbert/context-mode 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +33 -0
- package/.claude-plugin/plugin.json +31 -0
- package/.codex-plugin/hooks.json +65 -0
- package/.codex-plugin/mcp.json +13 -0
- package/.codex-plugin/plugin.json +31 -0
- package/.openclaw-plugin/index.ts +12 -0
- package/.openclaw-plugin/openclaw.plugin.json +38 -0
- package/.openclaw-plugin/package.json +28 -0
- package/LICENSE +94 -0
- package/README.md +1619 -0
- package/bin/statusline.mjs +378 -0
- package/build/adapters/antigravity/index.d.ts +51 -0
- package/build/adapters/antigravity/index.js +169 -0
- package/build/adapters/antigravity-cli/index.d.ts +51 -0
- package/build/adapters/antigravity-cli/index.js +342 -0
- package/build/adapters/base.d.ts +92 -0
- package/build/adapters/base.js +135 -0
- package/build/adapters/claude-code/config.d.ts +8 -0
- package/build/adapters/claude-code/config.js +8 -0
- package/build/adapters/claude-code/hooks.d.ts +106 -0
- package/build/adapters/claude-code/hooks.js +183 -0
- package/build/adapters/claude-code/index.d.ts +80 -0
- package/build/adapters/claude-code/index.js +569 -0
- package/build/adapters/claude-code-base.d.ts +49 -0
- package/build/adapters/claude-code-base.js +113 -0
- package/build/adapters/client-map.d.ts +10 -0
- package/build/adapters/client-map.js +42 -0
- package/build/adapters/codex/config.d.ts +8 -0
- package/build/adapters/codex/config.js +8 -0
- package/build/adapters/codex/hooks.d.ts +55 -0
- package/build/adapters/codex/hooks.js +64 -0
- package/build/adapters/codex/index.d.ts +87 -0
- package/build/adapters/codex/index.js +995 -0
- package/build/adapters/codex/paths.d.ts +1 -0
- package/build/adapters/codex/paths.js +12 -0
- package/build/adapters/codex/usage.d.ts +107 -0
- package/build/adapters/codex/usage.js +227 -0
- package/build/adapters/copilot-base.d.ts +94 -0
- package/build/adapters/copilot-base.js +297 -0
- package/build/adapters/copilot-cli/hooks.d.ts +33 -0
- package/build/adapters/copilot-cli/hooks.js +64 -0
- package/build/adapters/copilot-cli/index.d.ts +48 -0
- package/build/adapters/copilot-cli/index.js +341 -0
- package/build/adapters/cursor/config.d.ts +4 -0
- package/build/adapters/cursor/config.js +4 -0
- package/build/adapters/cursor/hooks.d.ts +55 -0
- package/build/adapters/cursor/hooks.js +82 -0
- package/build/adapters/cursor/index.d.ts +64 -0
- package/build/adapters/cursor/index.js +517 -0
- package/build/adapters/detect.d.ts +136 -0
- package/build/adapters/detect.js +623 -0
- package/build/adapters/gemini-cli/config.d.ts +8 -0
- package/build/adapters/gemini-cli/config.js +8 -0
- package/build/adapters/gemini-cli/hooks.d.ts +83 -0
- package/build/adapters/gemini-cli/hooks.js +115 -0
- package/build/adapters/gemini-cli/index.d.ts +80 -0
- package/build/adapters/gemini-cli/index.js +483 -0
- package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
- package/build/adapters/jetbrains-copilot/config.js +8 -0
- package/build/adapters/jetbrains-copilot/hooks.d.ts +59 -0
- package/build/adapters/jetbrains-copilot/hooks.js +87 -0
- package/build/adapters/jetbrains-copilot/index.d.ts +31 -0
- package/build/adapters/jetbrains-copilot/index.js +131 -0
- package/build/adapters/kimi/config.d.ts +8 -0
- package/build/adapters/kimi/config.js +8 -0
- package/build/adapters/kimi/hooks.d.ts +28 -0
- package/build/adapters/kimi/hooks.js +34 -0
- package/build/adapters/kimi/index.d.ts +66 -0
- package/build/adapters/kimi/index.js +537 -0
- package/build/adapters/kimi/paths.d.ts +21 -0
- package/build/adapters/kimi/paths.js +52 -0
- package/build/adapters/kimi/usage.d.ts +82 -0
- package/build/adapters/kimi/usage.js +217 -0
- package/build/adapters/kiro/hooks.d.ts +61 -0
- package/build/adapters/kiro/hooks.js +94 -0
- package/build/adapters/kiro/index.d.ts +61 -0
- package/build/adapters/kiro/index.js +301 -0
- package/build/adapters/omp/index.d.ts +65 -0
- package/build/adapters/omp/index.js +182 -0
- package/build/adapters/omp/plugin.d.ts +81 -0
- package/build/adapters/omp/plugin.js +331 -0
- package/build/adapters/omp/usage.d.ts +49 -0
- package/build/adapters/omp/usage.js +110 -0
- package/build/adapters/openclaw/config.d.ts +8 -0
- package/build/adapters/openclaw/config.js +8 -0
- package/build/adapters/openclaw/hooks.d.ts +50 -0
- package/build/adapters/openclaw/hooks.js +61 -0
- package/build/adapters/openclaw/index.d.ts +74 -0
- package/build/adapters/openclaw/index.js +439 -0
- package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
- package/build/adapters/openclaw/mcp-tools.js +207 -0
- package/build/adapters/openclaw/plugin.d.ts +141 -0
- package/build/adapters/openclaw/plugin.js +818 -0
- package/build/adapters/openclaw/session-db.d.ts +55 -0
- package/build/adapters/openclaw/session-db.js +88 -0
- package/build/adapters/openclaw/usage.d.ts +34 -0
- package/build/adapters/openclaw/usage.js +52 -0
- package/build/adapters/openclaw/workspace-router.d.ts +29 -0
- package/build/adapters/openclaw/workspace-router.js +64 -0
- package/build/adapters/opencode/config.d.ts +8 -0
- package/build/adapters/opencode/config.js +8 -0
- package/build/adapters/opencode/hooks.d.ts +38 -0
- package/build/adapters/opencode/hooks.js +50 -0
- package/build/adapters/opencode/index.d.ts +144 -0
- package/build/adapters/opencode/index.js +638 -0
- package/build/adapters/opencode/plugin.d.ts +249 -0
- package/build/adapters/opencode/plugin.js +1536 -0
- package/build/adapters/opencode/v2.d.ts +117 -0
- package/build/adapters/opencode/v2.js +224 -0
- package/build/adapters/opencode/zod3tov4.d.ts +1 -0
- package/build/adapters/opencode/zod3tov4.js +132 -0
- package/build/adapters/pi/extension.d.ts +81 -0
- package/build/adapters/pi/extension.js +861 -0
- package/build/adapters/pi/index.d.ts +57 -0
- package/build/adapters/pi/index.js +173 -0
- package/build/adapters/pi/mcp-bridge.d.ts +268 -0
- package/build/adapters/pi/mcp-bridge.js +894 -0
- package/build/adapters/qwen-code/hooks.d.ts +26 -0
- package/build/adapters/qwen-code/hooks.js +29 -0
- package/build/adapters/qwen-code/index.d.ts +40 -0
- package/build/adapters/qwen-code/index.js +361 -0
- package/build/adapters/qwen-code/usage.d.ts +90 -0
- package/build/adapters/qwen-code/usage.js +222 -0
- package/build/adapters/types.d.ts +360 -0
- package/build/adapters/types.js +118 -0
- package/build/adapters/vscode-copilot/config.d.ts +8 -0
- package/build/adapters/vscode-copilot/config.js +8 -0
- package/build/adapters/vscode-copilot/hooks.d.ts +71 -0
- package/build/adapters/vscode-copilot/hooks.js +98 -0
- package/build/adapters/vscode-copilot/index.d.ts +32 -0
- package/build/adapters/vscode-copilot/index.js +222 -0
- package/build/adapters/zed/index.d.ts +40 -0
- package/build/adapters/zed/index.js +163 -0
- package/build/cli.d.ts +34 -0
- package/build/cli.js +1828 -0
- package/build/db-base.d.ts +250 -0
- package/build/db-base.js +756 -0
- package/build/executor.d.ts +72 -0
- package/build/executor.js +678 -0
- package/build/exit-classify.d.ts +19 -0
- package/build/exit-classify.js +12 -0
- package/build/fetch/blocks.d.ts +145 -0
- package/build/fetch/blocks.js +244 -0
- package/build/fetch/extract.d.ts +68 -0
- package/build/fetch/extract.js +123 -0
- package/build/fetch/page-store.d.ts +66 -0
- package/build/fetch/page-store.js +174 -0
- package/build/fetch-cache.d.ts +13 -0
- package/build/fetch-cache.js +15 -0
- package/build/lifecycle.d.ts +124 -0
- package/build/lifecycle.js +278 -0
- package/build/package-identity.d.ts +26 -0
- package/build/package-identity.js +28 -0
- package/build/runPool.d.ts +36 -0
- package/build/runPool.js +51 -0
- package/build/runtime.d.ts +71 -0
- package/build/runtime.js +659 -0
- package/build/search/auto-memory.d.ts +47 -0
- package/build/search/auto-memory.js +170 -0
- package/build/search/ctx-search-schema.d.ts +90 -0
- package/build/search/ctx-search-schema.js +135 -0
- package/build/search/flood-guard.d.ts +57 -0
- package/build/search/flood-guard.js +80 -0
- package/build/search/unified.d.ts +56 -0
- package/build/search/unified.js +104 -0
- package/build/security.d.ts +206 -0
- package/build/security.js +780 -0
- package/build/server.d.ts +240 -0
- package/build/server.js +5034 -0
- package/build/session/analytics.d.ts +773 -0
- package/build/session/analytics.js +2420 -0
- package/build/session/db.d.ts +468 -0
- package/build/session/db.js +1330 -0
- package/build/session/error-classifier.d.ts +87 -0
- package/build/session/error-classifier.js +303 -0
- package/build/session/event-emit.d.ts +48 -0
- package/build/session/event-emit.js +101 -0
- package/build/session/extract.d.ts +333 -0
- package/build/session/extract.js +2688 -0
- package/build/session/model-prices.json +429 -0
- package/build/session/persist-tool-calls.d.ts +54 -0
- package/build/session/persist-tool-calls.js +105 -0
- package/build/session/pricing.d.ts +64 -0
- package/build/session/pricing.js +151 -0
- package/build/session/project-attribution.d.ts +73 -0
- package/build/session/project-attribution.js +245 -0
- package/build/session/purge.d.ts +138 -0
- package/build/session/purge.js +240 -0
- package/build/session/retrieval-marker.d.ts +39 -0
- package/build/session/retrieval-marker.js +65 -0
- package/build/session/snapshot.d.ts +46 -0
- package/build/session/snapshot.js +532 -0
- package/build/store-directory.d.ts +56 -0
- package/build/store-directory.js +254 -0
- package/build/store.d.ts +147 -0
- package/build/store.js +1714 -0
- package/build/truncate.d.ts +57 -0
- package/build/truncate.js +142 -0
- package/build/types.d.ts +117 -0
- package/build/types.js +20 -0
- package/build/util/claude-config.d.ts +32 -0
- package/build/util/claude-config.js +84 -0
- package/build/util/hook-config.d.ts +27 -0
- package/build/util/hook-config.js +76 -0
- package/build/util/jsonc.d.ts +14 -0
- package/build/util/jsonc.js +104 -0
- package/build/util/plugin-cache-integrity.d.ts +51 -0
- package/build/util/plugin-cache-integrity.js +146 -0
- package/build/util/project-dir.d.ts +149 -0
- package/build/util/project-dir.js +340 -0
- package/build/util/sibling-mcp.d.ts +79 -0
- package/build/util/sibling-mcp.js +181 -0
- package/cli.bundle.mjs +1495 -0
- package/configs/antigravity/GEMINI.md +68 -0
- package/configs/antigravity/mcp_config.json +7 -0
- package/configs/antigravity-cli/hooks/hooks.json +37 -0
- package/configs/antigravity-cli/hooks.json +37 -0
- package/configs/antigravity-cli/mcp_config.json +10 -0
- package/configs/antigravity-cli/plugin.json +14 -0
- package/configs/antigravity-cli/rules/context-mode.md +77 -0
- package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
- package/configs/claude-code/CLAUDE.md +91 -0
- package/configs/codex/AGENTS.md +99 -0
- package/configs/codex/config.toml +8 -0
- package/configs/codex/hooks.json +47 -0
- package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
- package/configs/copilot-cli/.mcp.json +12 -0
- package/configs/copilot-cli/README.md +47 -0
- package/configs/copilot-cli/hooks.json +41 -0
- package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
- package/configs/cursor/context-mode.mdc +76 -0
- package/configs/cursor/hooks.json +21 -0
- package/configs/cursor/mcp.json +7 -0
- package/configs/gemini-cli/GEMINI.md +88 -0
- package/configs/gemini-cli/mcp.json +7 -0
- package/configs/gemini-cli/settings.json +60 -0
- package/configs/jetbrains-copilot/copilot-instructions.md +80 -0
- package/configs/jetbrains-copilot/hooks.json +16 -0
- package/configs/jetbrains-copilot/mcp.json +7 -0
- package/configs/kilo/AGENTS.md +84 -0
- package/configs/kilo/kilo.json +6 -0
- package/configs/kimi/hooks.json +54 -0
- package/configs/kiro/KIRO.md +84 -0
- package/configs/kiro/agent.json +18 -0
- package/configs/kiro/mcp.json +7 -0
- package/configs/omp/SYSTEM.md +85 -0
- package/configs/omp/mcp.json +7 -0
- package/configs/openclaw/AGENTS.md +87 -0
- package/configs/openclaw/openclaw.json +13 -0
- package/configs/opencode/AGENTS.md +84 -0
- package/configs/opencode/opencode.json +6 -0
- package/configs/pi/AGENTS.md +3 -0
- package/configs/qwen-code/QWEN.md +91 -0
- package/configs/vscode-copilot/copilot-instructions.md +80 -0
- package/configs/vscode-copilot/hooks.json +16 -0
- package/configs/vscode-copilot/mcp.json +7 -0
- package/configs/zed/AGENTS.md +68 -0
- package/hooks/antigravity-cli/payload.mjs +98 -0
- package/hooks/antigravity-cli/posttooluse.mjs +138 -0
- package/hooks/antigravity-cli/pretooluse.mjs +78 -0
- package/hooks/antigravity-cli/stop.mjs +58 -0
- package/hooks/auto-injection.mjs +102 -0
- package/hooks/cache-heal-utils.mjs +379 -0
- package/hooks/codex/platform.mjs +1 -0
- package/hooks/codex/posttooluse.mjs +66 -0
- package/hooks/codex/precompact.mjs +70 -0
- package/hooks/codex/pretooluse.mjs +42 -0
- package/hooks/codex/sessionstart.mjs +121 -0
- package/hooks/codex/stop.mjs +138 -0
- package/hooks/codex/userpromptsubmit.mjs +75 -0
- package/hooks/copilot-cli/posttooluse.mjs +79 -0
- package/hooks/copilot-cli/precompact.mjs +66 -0
- package/hooks/copilot-cli/pretooluse.mjs +41 -0
- package/hooks/copilot-cli/sessionstart.mjs +121 -0
- package/hooks/copilot-cli/stop.mjs +59 -0
- package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
- package/hooks/core/codex-caps.mjs +112 -0
- package/hooks/core/formatters.mjs +356 -0
- package/hooks/core/mcp-ready.mjs +109 -0
- package/hooks/core/platform-detect.mjs +49 -0
- package/hooks/core/routing.mjs +1050 -0
- package/hooks/core/stdin.mjs +90 -0
- package/hooks/core/tool-naming.mjs +57 -0
- package/hooks/cursor/afteragentresponse.mjs +74 -0
- package/hooks/cursor/hooks.json +31 -0
- package/hooks/cursor/posttooluse.mjs +72 -0
- package/hooks/cursor/pretooluse.mjs +26 -0
- package/hooks/cursor/sessionstart.mjs +97 -0
- package/hooks/cursor/stop.mjs +48 -0
- package/hooks/ensure-deps.mjs +254 -0
- package/hooks/formatters/claude-code.mjs +113 -0
- package/hooks/formatters/cursor.mjs +37 -0
- package/hooks/formatters/gemini-cli.mjs +55 -0
- package/hooks/formatters/vscode-copilot.mjs +55 -0
- package/hooks/gemini-cli/aftermodel.mjs +70 -0
- package/hooks/gemini-cli/aftertool.mjs +61 -0
- package/hooks/gemini-cli/beforeagent.mjs +99 -0
- package/hooks/gemini-cli/beforetool.mjs +27 -0
- package/hooks/gemini-cli/precompress.mjs +55 -0
- package/hooks/gemini-cli/sessionstart.mjs +137 -0
- package/hooks/heal-partial-install.mjs +712 -0
- package/hooks/hooks.json +143 -0
- package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
- package/hooks/jetbrains-copilot/precompact.mjs +55 -0
- package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
- package/hooks/jetbrains-copilot/sessionstart.mjs +124 -0
- package/hooks/kimi/platform.mjs +1 -0
- package/hooks/kimi/posttooluse.mjs +72 -0
- package/hooks/kimi/precompact.mjs +80 -0
- package/hooks/kimi/pretooluse.mjs +42 -0
- package/hooks/kimi/sessionend.mjs +61 -0
- package/hooks/kimi/sessionstart.mjs +113 -0
- package/hooks/kimi/stop.mjs +132 -0
- package/hooks/kimi/userpromptsubmit.mjs +90 -0
- package/hooks/kiro/agentspawn.mjs +97 -0
- package/hooks/kiro/posttooluse.mjs +51 -0
- package/hooks/kiro/pretooluse.mjs +64 -0
- package/hooks/kiro/userpromptsubmit.mjs +88 -0
- package/hooks/normalize-hooks.mjs +323 -0
- package/hooks/platform-bridge.mjs +340 -0
- package/hooks/posttooluse.mjs +225 -0
- package/hooks/precompact.mjs +100 -0
- package/hooks/pretooluse.mjs +227 -0
- package/hooks/qwen-code/platform.mjs +1 -0
- package/hooks/qwen-code/stop.mjs +168 -0
- package/hooks/routing-block.mjs +105 -0
- package/hooks/run-hook.mjs +95 -0
- package/hooks/security.bundle.mjs +2 -0
- package/hooks/session-attribution.bundle.mjs +1 -0
- package/hooks/session-db.bundle.mjs +145 -0
- package/hooks/session-directive.mjs +520 -0
- package/hooks/session-extract.bundle.mjs +3 -0
- package/hooks/session-helpers.mjs +428 -0
- package/hooks/session-loaders.mjs +448 -0
- package/hooks/session-snapshot.bundle.mjs +32 -0
- package/hooks/sessionstart.mjs +464 -0
- package/hooks/stop.mjs +82 -0
- package/hooks/suppress-stderr.mjs +25 -0
- package/hooks/userpromptsubmit.mjs +108 -0
- package/hooks/vscode-copilot/posttooluse.mjs +61 -0
- package/hooks/vscode-copilot/precompact.mjs +55 -0
- package/hooks/vscode-copilot/pretooluse.mjs +27 -0
- package/hooks/vscode-copilot/sessionstart.mjs +129 -0
- package/openclaw.plugin.json +38 -0
- package/package.json +132 -0
- package/scripts/heal-better-sqlite3.mjs +430 -0
- package/scripts/heal-installed-plugins.mjs +635 -0
- package/scripts/plugin-cache-integrity.mjs +248 -0
- package/scripts/postinstall.mjs +397 -0
- package/server.bundle.mjs +1421 -0
- package/server.js +5 -0
- package/skills/.ignore +7 -0
- package/skills/context-mode/SKILL.md +300 -0
- package/skills/context-mode/references/anti-patterns.md +283 -0
- package/skills/context-mode/references/patterns-javascript.md +298 -0
- package/skills/context-mode/references/patterns-python.md +304 -0
- package/skills/context-mode/references/patterns-shell.md +277 -0
- package/skills/ctx-doctor/SKILL.md +22 -0
- package/skills/ctx-index/SKILL.md +46 -0
- package/skills/ctx-insight/SKILL.md +24 -0
- package/skills/ctx-purge/SKILL.md +49 -0
- package/skills/ctx-search/SKILL.md +35 -0
- package/skills/ctx-stats/SKILL.md +26 -0
- package/skills/ctx-upgrade/SKILL.md +31 -0
- package/start.mjs +616 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* context-mode status line — Claude Code statusLine integration.
|
|
4
|
+
*
|
|
5
|
+
* Reads stats DIRECTLY from SessionDB (`session_events` + `session_resume`),
|
|
6
|
+
* mirroring the `ctx_stats` MCP handler at src/server.ts:2807-2891 so the
|
|
7
|
+
* statusline and ctx_stats never drift. The legacy per-PID sidecar JSON
|
|
8
|
+
* (`stats-pid-*.json`) is no longer the source of truth — sidecars were
|
|
9
|
+
* eventually-consistent (500ms+30s throttles) and PID-scoped (multiple
|
|
10
|
+
* Claude sessions colliding on the same shell ppid).
|
|
11
|
+
*
|
|
12
|
+
* Discipline (Datadog / Stripe / Vercel pattern):
|
|
13
|
+
* - "context-mode" full brand label, never abbreviated
|
|
14
|
+
* - ONE chromatic accent (status dot ●), everything else monochrome
|
|
15
|
+
* - Bold for KPI numbers ($, %), dim for context
|
|
16
|
+
* - No counts (calls / tokens / events) — only $ and % pass the
|
|
17
|
+
* value-per-pixel test
|
|
18
|
+
*
|
|
19
|
+
* Wire it up in ~/.claude/settings.json:
|
|
20
|
+
* {
|
|
21
|
+
* "statusLine": {
|
|
22
|
+
* "type": "command",
|
|
23
|
+
* "command": "context-mode statusline"
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
29
|
+
import { dirname, resolve } from "node:path";
|
|
30
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
31
|
+
import { execFileSync } from "node:child_process";
|
|
32
|
+
import {
|
|
33
|
+
ensureWritableStorageDir,
|
|
34
|
+
resolveDefaultSessionDir,
|
|
35
|
+
resolveSessionStorageDir,
|
|
36
|
+
} from "../hooks/session-db.bundle.mjs";
|
|
37
|
+
|
|
38
|
+
// ── Analytics import — resolved relative to this script ─────────────────
|
|
39
|
+
// statusline.mjs ships in `bin/`; the compiled analytics module lives in
|
|
40
|
+
// `build/session/analytics.js`. Import lazily so a missing build doesn't
|
|
41
|
+
// crash the renderer — degrade to the substantiated headline instead.
|
|
42
|
+
//
|
|
43
|
+
// The dynamic import target MUST be a `file://` URL on Windows. Node's
|
|
44
|
+
// ESM loader rejects absolute drive-letter paths (`C:\...`) with
|
|
45
|
+
// ERR_UNSUPPORTED_ESM_URL_SCHEME — which the catch below silently
|
|
46
|
+
// swallows, leaving `_analytics = null` and rendering the empty-state
|
|
47
|
+
// headline forever. Convert to a file URL so Windows accepts it.
|
|
48
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
49
|
+
const __dirname = dirname(__filename);
|
|
50
|
+
const ANALYTICS_PATH = resolve(__dirname, "..", "build", "session", "analytics.js");
|
|
51
|
+
const ANALYTICS_URL = pathToFileURL(ANALYTICS_PATH).href;
|
|
52
|
+
|
|
53
|
+
let _analytics = null;
|
|
54
|
+
async function loadAnalytics() {
|
|
55
|
+
if (_analytics) return _analytics;
|
|
56
|
+
try {
|
|
57
|
+
_analytics = await import(ANALYTICS_URL);
|
|
58
|
+
} catch {
|
|
59
|
+
_analytics = null;
|
|
60
|
+
}
|
|
61
|
+
return _analytics;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Test seams — keep production behaviour identical when env vars unset.
|
|
65
|
+
// CTX_TEST_PLATFORM — override process.platform for cross-OS resolver tests
|
|
66
|
+
// CTX_TEST_PROC_DIR — override /proc base dir for Linux PID-walk tests
|
|
67
|
+
const TEST_PLATFORM = process.env.CTX_TEST_PLATFORM;
|
|
68
|
+
const PROC_DIR = process.env.CTX_TEST_PROC_DIR || "/proc";
|
|
69
|
+
function platform() {
|
|
70
|
+
return TEST_PLATFORM || process.platform;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Single-shot stderr warning latch — keep noise out of Claude Code's
|
|
74
|
+
// statusline output even when our parent runs us repeatedly per session.
|
|
75
|
+
const __warnedKeys = new Set();
|
|
76
|
+
function warnOnce(key, msg) {
|
|
77
|
+
if (__warnedKeys.has(key)) return;
|
|
78
|
+
__warnedKeys.add(key);
|
|
79
|
+
try { process.stderr.write(`context-mode statusline: ${msg}\n`); } catch { /* ignore */ }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── ANSI palette (single chromatic accent on the status dot) ────────────
|
|
83
|
+
const NO_COLOR = process.env.NO_COLOR || !process.stdout.isTTY;
|
|
84
|
+
const ansi = (code, text) => (NO_COLOR ? text : `\x1b[${code}m${text}\x1b[0m`);
|
|
85
|
+
const brand = (t) => ansi("1;36", t); // bold cyan — brand presence
|
|
86
|
+
const bold = (t) => ansi("1", t); // bold default fg — KPI numbers
|
|
87
|
+
const dim = (t) => ansi("2", t); // dim default fg — context
|
|
88
|
+
const green = (t) => ansi("32", t); // healthy dot
|
|
89
|
+
const yellow = (t) => ansi("33", t); // degraded dot
|
|
90
|
+
const red = (t) => ansi("31", t); // stale dot
|
|
91
|
+
const SEP = dim("·");
|
|
92
|
+
|
|
93
|
+
// ── Stdin drain ─────────────────────────────────────────────────────────
|
|
94
|
+
function readStdinJson() {
|
|
95
|
+
try {
|
|
96
|
+
const raw = readFileSync(0, "utf-8");
|
|
97
|
+
if (!raw.trim()) return {};
|
|
98
|
+
return JSON.parse(raw);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
// The payload is load-bearing — it carries session_id, which resolves the
|
|
101
|
+
// per-session KPI. Empty stdin (normal first render) returned above and
|
|
102
|
+
// stays silent; a non-empty payload that fails to parse is a real anomaly
|
|
103
|
+
// worth one latched stderr line (never pollutes the statusline's stdout).
|
|
104
|
+
warnOnce("stdin-parse", `failed to parse statusline stdin JSON: ${err?.message ?? err}`);
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function resolveSessionDir() {
|
|
110
|
+
return ensureWritableStorageDir(
|
|
111
|
+
resolveSessionStorageDir(() => resolveDefaultSessionDir({
|
|
112
|
+
configDir: ".claude",
|
|
113
|
+
configDirEnv: "CLAUDE_CONFIG_DIR",
|
|
114
|
+
legacySessionDirEnv: "CONTEXT_MODE_SESSION_DIR",
|
|
115
|
+
onLegacySessionDir: () => {
|
|
116
|
+
warnOnce(
|
|
117
|
+
"legacy-session-dir",
|
|
118
|
+
"CONTEXT_MODE_SESSION_DIR is deprecated; set CONTEXT_MODE_DIR to the parent context-mode root.",
|
|
119
|
+
);
|
|
120
|
+
},
|
|
121
|
+
})),
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Walk up the parent process chain to find the Claude Code PID.
|
|
127
|
+
*
|
|
128
|
+
* Claude Code spawns the status line through a shell, so process.ppid is
|
|
129
|
+
* the intermediate shell, not Claude Code itself. We walk up until we find
|
|
130
|
+
* a process whose name matches /claude/i.
|
|
131
|
+
*
|
|
132
|
+
* Per-OS resolver:
|
|
133
|
+
* - linux: read PPid + Name from /proc/<pid>/status
|
|
134
|
+
* - darwin: ps -o ppid=,comm= -p <pid> (BSD ps; works without /proc)
|
|
135
|
+
* - win32: degraded — process.ppid only, with a one-shot stderr warning
|
|
136
|
+
*
|
|
137
|
+
* Without this walk, multiple concurrent Claude sessions all see the same
|
|
138
|
+
* shell ppid and collide on per-PID stats lookup.
|
|
139
|
+
*/
|
|
140
|
+
function findClaudePid() {
|
|
141
|
+
const plat = platform();
|
|
142
|
+
if (plat === "linux") return findClaudePidLinux();
|
|
143
|
+
if (plat === "darwin") return findClaudePidDarwin();
|
|
144
|
+
if (plat === "win32") {
|
|
145
|
+
warnOnce(
|
|
146
|
+
"win",
|
|
147
|
+
"Windows process-tree walk unsupported; multiple concurrent Claude sessions may collide. Set CLAUDE_SESSION_ID for deterministic resolution.",
|
|
148
|
+
);
|
|
149
|
+
return process.ppid;
|
|
150
|
+
}
|
|
151
|
+
return process.ppid;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function findClaudePidLinux() {
|
|
155
|
+
let pid = process.ppid;
|
|
156
|
+
for (let i = 0; i < 8 && pid && pid > 1; i++) {
|
|
157
|
+
try {
|
|
158
|
+
const status = readFileSync(`${PROC_DIR}/${pid}/status`, "utf-8");
|
|
159
|
+
const nameMatch = status.match(/^Name:\s+(.+)$/m);
|
|
160
|
+
const ppidMatch = status.match(/^PPid:\s+(\d+)/m);
|
|
161
|
+
const name = nameMatch?.[1]?.trim() ?? "";
|
|
162
|
+
if (/claude/i.test(name)) return pid;
|
|
163
|
+
pid = ppidMatch ? Number(ppidMatch[1]) : 0;
|
|
164
|
+
} catch {
|
|
165
|
+
return process.ppid;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return process.ppid;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function findClaudePidDarwin() {
|
|
172
|
+
let pid = process.ppid;
|
|
173
|
+
for (let i = 0; i < 8 && pid && pid > 1; i++) {
|
|
174
|
+
try {
|
|
175
|
+
const out = execFileSync(
|
|
176
|
+
"ps",
|
|
177
|
+
["-o", "ppid=,comm=", "-p", String(pid)],
|
|
178
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] },
|
|
179
|
+
).trim();
|
|
180
|
+
if (!out) return process.ppid;
|
|
181
|
+
const m = out.match(/^\s*(\d+)\s+(.+)$/);
|
|
182
|
+
if (!m) return process.ppid;
|
|
183
|
+
const parentPid = Number(m[1]);
|
|
184
|
+
const comm = m[2].trim();
|
|
185
|
+
const base = comm.split("/").pop() || comm;
|
|
186
|
+
if (/claude/i.test(base)) return pid;
|
|
187
|
+
pid = parentPid;
|
|
188
|
+
} catch {
|
|
189
|
+
return process.ppid;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return process.ppid;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function resolveSessionId(payload) {
|
|
196
|
+
// PRIMARY: the session_id Claude Code delivers in the statusLine stdin
|
|
197
|
+
// payload. This is the SAME id the recording hooks key session_events by,
|
|
198
|
+
// so it's the only source that reliably matches stored per-session data.
|
|
199
|
+
//
|
|
200
|
+
// Claude Code does NOT export a CLAUDE_SESSION_ID env var — session_id is
|
|
201
|
+
// delivered only in the stdin JSON (statusline.md "Available data"). And the
|
|
202
|
+
// /proc PID walk yields `pid-<n>`, which never matches a UUID-keyed session.
|
|
203
|
+
// So without reading the payload, the per-session KPI is unreachable and the
|
|
204
|
+
// bar falls back to the global lifetime aggregate — identical in every
|
|
205
|
+
// session and seemingly "frozen".
|
|
206
|
+
const fromPayload = payload?.session_id;
|
|
207
|
+
if (typeof fromPayload === "string" && fromPayload) return fromPayload;
|
|
208
|
+
// Fallback when the payload carries no session_id (and how test fixtures
|
|
209
|
+
// pin a deterministic id). NOT an override — payload wins when present.
|
|
210
|
+
if (process.env.CLAUDE_SESSION_ID) return process.env.CLAUDE_SESSION_ID;
|
|
211
|
+
// Last resort: walk the process tree (only matches pid-keyed events).
|
|
212
|
+
return `pid-${findClaudePid()}`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Formatters ───────────────────────────────────────────────────────────
|
|
216
|
+
function fmtUsd(n) {
|
|
217
|
+
const safe = Number.isFinite(n) && n >= 0 ? n : 0;
|
|
218
|
+
if (safe >= 100) return `$${safe.toFixed(0)}`;
|
|
219
|
+
return `$${safe.toFixed(2)}`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── Status dot — the ONE accent ──────────────────────────────────────────
|
|
223
|
+
function statusDot(pct) {
|
|
224
|
+
if (pct >= 50) return green("●");
|
|
225
|
+
if (pct >= 1) return yellow("●");
|
|
226
|
+
return green("●");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ── Main render ──────────────────────────────────────────────────────────
|
|
230
|
+
async function main() {
|
|
231
|
+
const payload = readStdinJson(); // canonical source of session_id
|
|
232
|
+
const sessionsDir = resolveSessionDir();
|
|
233
|
+
const sessionId = resolveSessionId(payload);
|
|
234
|
+
|
|
235
|
+
const analytics = await loadAnalytics();
|
|
236
|
+
|
|
237
|
+
// BRAND-NEW / build missing — substantiated headline only
|
|
238
|
+
if (!analytics) {
|
|
239
|
+
process.stdout.write(
|
|
240
|
+
`${brand("context-mode")} ${green("●")} ${dim("saves ~98% of context window")}`,
|
|
241
|
+
);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const {
|
|
246
|
+
getRealBytesStats,
|
|
247
|
+
getMultiAdapterLifetimeStats,
|
|
248
|
+
kb,
|
|
249
|
+
} = analytics;
|
|
250
|
+
|
|
251
|
+
// Sessions dir doesn't exist yet — first ever launch
|
|
252
|
+
if (!existsSync(sessionsDir)) {
|
|
253
|
+
process.stdout.write(
|
|
254
|
+
`${brand("context-mode")} ${green("●")} ${dim("saves ~98% of context window")}`,
|
|
255
|
+
);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Lifetime real-bytes across this adapter's sessions dir.
|
|
260
|
+
// Mirrors src/server.ts:2860 — the same call ctx_stats uses.
|
|
261
|
+
let lifetime;
|
|
262
|
+
try {
|
|
263
|
+
lifetime = getRealBytesStats({ sessionsDir });
|
|
264
|
+
} catch {
|
|
265
|
+
lifetime = null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Per-conversation real-bytes for the session $ KPI.
|
|
269
|
+
// Statusline doesn't know the worktree hash, so scan every db in the
|
|
270
|
+
// dir and let getRealBytesStats filter by sessionId.
|
|
271
|
+
let conversation;
|
|
272
|
+
try {
|
|
273
|
+
conversation = getRealBytesStats({ sessionsDir, sessionId });
|
|
274
|
+
} catch {
|
|
275
|
+
conversation = null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Cross-adapter lifetime — drives the "across N tools" headline when
|
|
279
|
+
// 2+ real adapters are present. Mirrors src/server.ts:2840.
|
|
280
|
+
let multi;
|
|
281
|
+
try {
|
|
282
|
+
multi = getMultiAdapterLifetimeStats();
|
|
283
|
+
} catch {
|
|
284
|
+
multi = null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// v1.0.118: drop the $ math — ctx_stats's narrative renderer is the source
|
|
288
|
+
// of truth and uses byte-based metrics. Statusline mirrors the same
|
|
289
|
+
// formulas so the two displays never diverge again.
|
|
290
|
+
//
|
|
291
|
+
// Lifetime bytes — multi-adapter aggregate when present, else local-DB
|
|
292
|
+
// real bytes. Mirrors src/session/analytics.ts:1684 narrative renderer.
|
|
293
|
+
const lifetimeBytes = (multi?.totalBytes && multi.totalBytes > 0)
|
|
294
|
+
? multi.totalBytes
|
|
295
|
+
: (lifetime?.totalSavedTokens ?? 0) * 4;
|
|
296
|
+
|
|
297
|
+
// This-chat bytes — real bytes accounting (data + bytes-avoided + snapshot).
|
|
298
|
+
const sessionBytes = conversation
|
|
299
|
+
? ((conversation.eventDataBytes ?? 0)
|
|
300
|
+
+ (conversation.bytesAvoided ?? 0)
|
|
301
|
+
+ (conversation.snapshotBytes ?? 0))
|
|
302
|
+
: 0;
|
|
303
|
+
|
|
304
|
+
// Per-day average — same lifetime-day computation ctx_stats opener uses.
|
|
305
|
+
const sinceMs = lifetime?.firstEventMs ?? multi?.perAdapter?.[0]?.firstMs ?? 0;
|
|
306
|
+
const lifetimeDays = sinceMs > 0
|
|
307
|
+
? Math.max(1, Math.round((Date.now() - sinceMs) / 86_400_000))
|
|
308
|
+
: 0;
|
|
309
|
+
const perDayBytes = lifetimeDays > 0 ? lifetimeBytes / lifetimeDays : 0;
|
|
310
|
+
|
|
311
|
+
// Reduction % — same as before (bytes-avoided + snapshot vs returned).
|
|
312
|
+
const totalReturned = lifetime?.bytesReturned ?? 0;
|
|
313
|
+
const totalKept =
|
|
314
|
+
(lifetime?.bytesAvoided ?? 0)
|
|
315
|
+
+ (lifetime?.snapshotBytes ?? 0)
|
|
316
|
+
+ (lifetime?.eventDataBytes ?? 0);
|
|
317
|
+
const totalProcessed = totalKept + totalReturned;
|
|
318
|
+
const pct = totalProcessed > 0
|
|
319
|
+
? Math.round((totalKept / totalProcessed) * 100)
|
|
320
|
+
: 0;
|
|
321
|
+
|
|
322
|
+
const dot = statusDot(pct);
|
|
323
|
+
|
|
324
|
+
// Cross-tool count — used in the headline when 2+ real adapters detected.
|
|
325
|
+
const realAdapters = (multi?.perAdapter ?? []).filter((a) => a?.isReal);
|
|
326
|
+
const showMultiAdapter = realAdapters.length >= 2;
|
|
327
|
+
|
|
328
|
+
// BRAND-NEW: no data at all → marketing headline.
|
|
329
|
+
if (lifetimeBytes === 0 && sessionBytes === 0) {
|
|
330
|
+
process.stdout.write(
|
|
331
|
+
`${brand("context-mode")} ${green("●")} ${dim("saves ~98% of context window")}`,
|
|
332
|
+
);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// FRESH session, no this-chat data yet — lead with lifetime number.
|
|
337
|
+
if (sessionBytes === 0 && lifetimeBytes > 0) {
|
|
338
|
+
const blocks = [`${bold(kb(lifetimeBytes))} ${dim("kept out")}`];
|
|
339
|
+
if (perDayBytes > 0) {
|
|
340
|
+
blocks.push(`${bold(kb(perDayBytes) + "/day")}`);
|
|
341
|
+
}
|
|
342
|
+
if (showMultiAdapter) {
|
|
343
|
+
blocks.push(`${dim(`across ${realAdapters.length} tools`)}`);
|
|
344
|
+
}
|
|
345
|
+
blocks.push(dim("preserved across compact, restart & upgrade"));
|
|
346
|
+
process.stdout.write(
|
|
347
|
+
`${brand("context-mode")} ${dot} ${blocks.join(` ${SEP} `)}`,
|
|
348
|
+
);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// ACTIVE: this-chat · lifetime · [N tools] · % efficient
|
|
353
|
+
const valueBlocks = [
|
|
354
|
+
`${bold(kb(sessionBytes))} ${dim("this chat")}`,
|
|
355
|
+
];
|
|
356
|
+
if (lifetimeBytes > 0) {
|
|
357
|
+
valueBlocks.push(`${bold(kb(lifetimeBytes))} ${dim("lifetime")}`);
|
|
358
|
+
}
|
|
359
|
+
if (showMultiAdapter) {
|
|
360
|
+
valueBlocks.push(`${dim(`across ${realAdapters.length} tools`)}`);
|
|
361
|
+
}
|
|
362
|
+
if (pct > 0) {
|
|
363
|
+
valueBlocks.push(`${bold(`${pct}%`)} ${dim("kept out")}`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const head = `${brand("context-mode")} ${dot} `;
|
|
367
|
+
const tail = valueBlocks.join(` ${SEP} `);
|
|
368
|
+
process.stdout.write(head + tail);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
main().catch(() => {
|
|
372
|
+
// Last-resort fallback — a thrown error must never produce a blank statusline.
|
|
373
|
+
try {
|
|
374
|
+
process.stdout.write(
|
|
375
|
+
`${brand("context-mode")} ${green("●")} ${dim("saves ~98% of context window")}`,
|
|
376
|
+
);
|
|
377
|
+
} catch { /* ignore */ }
|
|
378
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/antigravity — Google Antigravity platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Implements HookAdapter for Antigravity's MCP-only paradigm.
|
|
5
|
+
*
|
|
6
|
+
* Antigravity hook specifics:
|
|
7
|
+
* - NO hook support (MCP-only, same as Codex CLI)
|
|
8
|
+
* - Config: ~/.gemini/antigravity/mcp_config.json (JSON format)
|
|
9
|
+
* - MCP: full support via mcpServers in mcp_config.json
|
|
10
|
+
* - All capabilities are false — MCP is the only integration path
|
|
11
|
+
* - Session dir: ~/.gemini/context-mode/sessions/
|
|
12
|
+
* - Routing file: GEMINI.md (shared with Gemini CLI filename, different content)
|
|
13
|
+
*
|
|
14
|
+
* Sources:
|
|
15
|
+
* - Config path: https://github.com/google-gemini/gemini-cli/issues/16058
|
|
16
|
+
* - MCP support: https://antigravity.google/docs/mcp
|
|
17
|
+
* - Tool list: System prompt leak (21 verified tools)
|
|
18
|
+
*/
|
|
19
|
+
import { BaseAdapter } from "../base.js";
|
|
20
|
+
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse, HookRegistration } from "../types.js";
|
|
21
|
+
export declare class AntigravityAdapter extends BaseAdapter implements HookAdapter {
|
|
22
|
+
constructor();
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly paradigm: HookParadigm;
|
|
25
|
+
readonly capabilities: PlatformCapabilities;
|
|
26
|
+
parsePreToolUseInput(_raw: unknown): PreToolUseEvent;
|
|
27
|
+
parsePostToolUseInput(_raw: unknown): PostToolUseEvent;
|
|
28
|
+
parsePreCompactInput(_raw: unknown): PreCompactEvent;
|
|
29
|
+
parseSessionStartInput(_raw: unknown): SessionStartEvent;
|
|
30
|
+
formatPreToolUseResponse(_response: PreToolUseResponse): unknown;
|
|
31
|
+
formatPostToolUseResponse(_response: PostToolUseResponse): unknown;
|
|
32
|
+
formatPreCompactResponse(_response: PreCompactResponse): unknown;
|
|
33
|
+
formatSessionStartResponse(_response: SessionStartResponse): unknown;
|
|
34
|
+
getSettingsPath(): string;
|
|
35
|
+
/**
|
|
36
|
+
* Antigravity nests under ~/.gemini/antigravity/. Always absolute.
|
|
37
|
+
* `_projectDir` accepted for interface symmetry but unused — home-rooted.
|
|
38
|
+
*/
|
|
39
|
+
getConfigDir(_projectDir?: string): string;
|
|
40
|
+
getInstructionFiles(): string[];
|
|
41
|
+
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
42
|
+
readSettings(): Record<string, unknown> | null;
|
|
43
|
+
writeSettings(settings: Record<string, unknown>): void;
|
|
44
|
+
validateHooks(_pluginRoot: string): DiagnosticResult[];
|
|
45
|
+
checkPluginRegistration(): DiagnosticResult;
|
|
46
|
+
getInstalledVersion(): string;
|
|
47
|
+
configureAllHooks(_pluginRoot: string): string[];
|
|
48
|
+
setHookPermissions(_pluginRoot: string): string[];
|
|
49
|
+
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
50
|
+
getRoutingInstructions(): string;
|
|
51
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/antigravity — Google Antigravity platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Implements HookAdapter for Antigravity's MCP-only paradigm.
|
|
5
|
+
*
|
|
6
|
+
* Antigravity hook specifics:
|
|
7
|
+
* - NO hook support (MCP-only, same as Codex CLI)
|
|
8
|
+
* - Config: ~/.gemini/antigravity/mcp_config.json (JSON format)
|
|
9
|
+
* - MCP: full support via mcpServers in mcp_config.json
|
|
10
|
+
* - All capabilities are false — MCP is the only integration path
|
|
11
|
+
* - Session dir: ~/.gemini/context-mode/sessions/
|
|
12
|
+
* - Routing file: GEMINI.md (shared with Gemini CLI filename, different content)
|
|
13
|
+
*
|
|
14
|
+
* Sources:
|
|
15
|
+
* - Config path: https://github.com/google-gemini/gemini-cli/issues/16058
|
|
16
|
+
* - MCP support: https://antigravity.google/docs/mcp
|
|
17
|
+
* - Tool list: System prompt leak (21 verified tools)
|
|
18
|
+
*/
|
|
19
|
+
import { readFileSync, writeFileSync, mkdirSync, } from "node:fs";
|
|
20
|
+
import { resolve, dirname } from "node:path";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
import { homedir } from "node:os";
|
|
23
|
+
import { BaseAdapter } from "../base.js";
|
|
24
|
+
// ─────────────────────────────────────────────────────────
|
|
25
|
+
// Adapter implementation
|
|
26
|
+
// ─────────────────────────────────────────────────────────
|
|
27
|
+
export class AntigravityAdapter extends BaseAdapter {
|
|
28
|
+
constructor() {
|
|
29
|
+
super([".gemini"]);
|
|
30
|
+
}
|
|
31
|
+
name = "Antigravity";
|
|
32
|
+
paradigm = "mcp-only";
|
|
33
|
+
capabilities = {
|
|
34
|
+
preToolUse: false,
|
|
35
|
+
postToolUse: false,
|
|
36
|
+
preCompact: false,
|
|
37
|
+
sessionStart: false,
|
|
38
|
+
canModifyArgs: false,
|
|
39
|
+
canModifyOutput: false,
|
|
40
|
+
canInjectSessionContext: false,
|
|
41
|
+
};
|
|
42
|
+
// ── Input parsing ──────────────────────────────────────
|
|
43
|
+
// Antigravity does not support hooks. These methods exist to satisfy the
|
|
44
|
+
// interface contract but will throw if called.
|
|
45
|
+
parsePreToolUseInput(_raw) {
|
|
46
|
+
throw new Error("Antigravity does not support hooks");
|
|
47
|
+
}
|
|
48
|
+
parsePostToolUseInput(_raw) {
|
|
49
|
+
throw new Error("Antigravity does not support hooks");
|
|
50
|
+
}
|
|
51
|
+
parsePreCompactInput(_raw) {
|
|
52
|
+
throw new Error("Antigravity does not support hooks");
|
|
53
|
+
}
|
|
54
|
+
parseSessionStartInput(_raw) {
|
|
55
|
+
throw new Error("Antigravity does not support hooks");
|
|
56
|
+
}
|
|
57
|
+
// ── Response formatting ────────────────────────────────
|
|
58
|
+
// Antigravity does not support hooks. Return undefined for all responses.
|
|
59
|
+
formatPreToolUseResponse(_response) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
formatPostToolUseResponse(_response) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
formatPreCompactResponse(_response) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
formatSessionStartResponse(_response) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
// ── Configuration ──────────────────────────────────────
|
|
72
|
+
getSettingsPath() {
|
|
73
|
+
return resolve(homedir(), ".gemini", "antigravity", "mcp_config.json");
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Antigravity nests under ~/.gemini/antigravity/. Always absolute.
|
|
77
|
+
* `_projectDir` accepted for interface symmetry but unused — home-rooted.
|
|
78
|
+
*/
|
|
79
|
+
getConfigDir(_projectDir) {
|
|
80
|
+
return resolve(homedir(), ".gemini", "antigravity");
|
|
81
|
+
}
|
|
82
|
+
getInstructionFiles() {
|
|
83
|
+
return ["GEMINI.md"];
|
|
84
|
+
}
|
|
85
|
+
generateHookConfig(_pluginRoot) {
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
readSettings() {
|
|
89
|
+
try {
|
|
90
|
+
const raw = readFileSync(this.getSettingsPath(), "utf-8");
|
|
91
|
+
return JSON.parse(raw);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
writeSettings(settings) {
|
|
98
|
+
const settingsPath = this.getSettingsPath();
|
|
99
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
100
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
101
|
+
}
|
|
102
|
+
// ── Diagnostics (doctor) ─────────────────────────────────
|
|
103
|
+
validateHooks(_pluginRoot) {
|
|
104
|
+
return [
|
|
105
|
+
{
|
|
106
|
+
check: "Hook support",
|
|
107
|
+
status: "warn",
|
|
108
|
+
message: "Antigravity does not support hooks. " +
|
|
109
|
+
"Only MCP integration is available.",
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
checkPluginRegistration() {
|
|
114
|
+
try {
|
|
115
|
+
const raw = readFileSync(this.getSettingsPath(), "utf-8");
|
|
116
|
+
const config = JSON.parse(raw);
|
|
117
|
+
const mcpServers = config?.mcpServers ?? {};
|
|
118
|
+
if ("context-mode" in mcpServers) {
|
|
119
|
+
return {
|
|
120
|
+
check: "MCP registration",
|
|
121
|
+
status: "pass",
|
|
122
|
+
message: "context-mode found in mcpServers config",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
check: "MCP registration",
|
|
127
|
+
status: "fail",
|
|
128
|
+
message: "context-mode not found in mcpServers",
|
|
129
|
+
fix: "Add context-mode to mcpServers in ~/.gemini/antigravity/mcp_config.json",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return {
|
|
134
|
+
check: "MCP registration",
|
|
135
|
+
status: "warn",
|
|
136
|
+
message: "Could not read ~/.gemini/antigravity/mcp_config.json",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
getInstalledVersion() {
|
|
141
|
+
try {
|
|
142
|
+
const pkgPath = resolve(homedir(), ".gemini", "extensions", "context-mode", "package.json");
|
|
143
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
144
|
+
return pkg.version ?? "unknown";
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return "not installed";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// ── Upgrade ────────────────────────────────────────────
|
|
151
|
+
configureAllHooks(_pluginRoot) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
setHookPermissions(_pluginRoot) {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
updatePluginRegistry(_pluginRoot, _version) {
|
|
158
|
+
// Antigravity plugin registry is managed via mcp_config.json
|
|
159
|
+
}
|
|
160
|
+
getRoutingInstructions() {
|
|
161
|
+
const instructionsPath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "configs", "antigravity", "GEMINI.md");
|
|
162
|
+
try {
|
|
163
|
+
return readFileSync(instructionsPath, "utf-8");
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return "# context-mode\n\nUse context-mode MCP tools (execute, execute_file, batch_execute, fetch_and_index, search) instead of run_command/view_file for data-heavy operations.";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/antigravity-cli — Google Antigravity CLI (`agy`) adapter.
|
|
3
|
+
*
|
|
4
|
+
* Integration: MCP tools plus agy's Claude-compatible hook surface. `agy`
|
|
5
|
+
* reads its global MCP profile from `~/.gemini/config/mcp_config.json`
|
|
6
|
+
* (distinct from the Antigravity IDE's `~/.gemini/antigravity/mcp_config.json`)
|
|
7
|
+
* and hooks from `~/.gemini/config/hooks.json`, or from an installed agy
|
|
8
|
+
* plugin's root `hooks.json`.
|
|
9
|
+
*
|
|
10
|
+
* context-mode wires only the surfaces that have a verified mapping:
|
|
11
|
+
* - PreToolUse for bounded routing enforcement on Bash/Read/Grep/WebFetch
|
|
12
|
+
* - PostToolUse capture for executed tool calls
|
|
13
|
+
* - best-effort Stop capture for session-end continuity when agy emits it
|
|
14
|
+
*
|
|
15
|
+
* PreInvocation/PostInvocation are intentionally not registered here: there is
|
|
16
|
+
* no verified payload/response contract or shared context-mode pipeline target
|
|
17
|
+
* for those agy events yet.
|
|
18
|
+
*/
|
|
19
|
+
import { AntigravityAdapter } from "../antigravity/index.js";
|
|
20
|
+
import type { DiagnosticResult, HookParadigm, PlatformCapabilities, PostToolUseEvent, PostToolUseResponse, PreToolUseEvent, PreToolUseResponse } from "../types.js";
|
|
21
|
+
export declare function antigravityCliMcpConfigPath(): string;
|
|
22
|
+
export declare function antigravityCliConfigDir(): string;
|
|
23
|
+
/** agy reads user hooks from ~/.gemini/config/hooks.json (sibling of mcp_config.json). */
|
|
24
|
+
export declare function antigravityCliHooksPath(): string;
|
|
25
|
+
/**
|
|
26
|
+
* `agy plugin install <bundle>` registers MCP + hook + skill into agy's plugin
|
|
27
|
+
* profile under ~/.gemini/config/plugins/<name>/ (verified on agy 1.0.6, re-verified 1.0.10) — the
|
|
28
|
+
* canonical install. The global mcp_config.json / hooks.json paths above are the
|
|
29
|
+
* manual (no-plugin) fallback. doctor must recognize BOTH.
|
|
30
|
+
*/
|
|
31
|
+
export declare function antigravityCliPluginDir(): string;
|
|
32
|
+
export declare class AntigravityCliAdapter extends AntigravityAdapter {
|
|
33
|
+
readonly name = "Antigravity CLI";
|
|
34
|
+
readonly paradigm: HookParadigm;
|
|
35
|
+
readonly capabilities: PlatformCapabilities;
|
|
36
|
+
getSettingsPath(): string;
|
|
37
|
+
getConfigDir(_projectDir?: string): string;
|
|
38
|
+
parsePreToolUseInput(raw: unknown): PreToolUseEvent;
|
|
39
|
+
parsePostToolUseInput(raw: unknown): PostToolUseEvent;
|
|
40
|
+
formatPreToolUseResponse(response: PreToolUseResponse): unknown;
|
|
41
|
+
formatPostToolUseResponse(_response: PostToolUseResponse): unknown;
|
|
42
|
+
checkPluginRegistration(): DiagnosticResult;
|
|
43
|
+
getInstalledVersion(): string;
|
|
44
|
+
/**
|
|
45
|
+
* Write/merge the agy hooks into ~/.gemini/config/hooks.json (manual,
|
|
46
|
+
* non-plugin path). Idempotent — unrelated hook entries are preserved.
|
|
47
|
+
*/
|
|
48
|
+
configureAllHooks(_pluginRoot: string): string[];
|
|
49
|
+
/** Report agy hook status across plugin and manual profiles. */
|
|
50
|
+
validateHooks(_pluginRoot: string): DiagnosticResult[];
|
|
51
|
+
}
|