context-mode 0.9.21 → 1.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.
Files changed (102) hide show
  1. package/.claude-plugin/hooks/hooks.json +46 -4
  2. package/.claude-plugin/marketplace.json +2 -2
  3. package/.claude-plugin/plugin.json +4 -4
  4. package/README.md +377 -191
  5. package/build/adapters/claude-code/config.d.ts +8 -0
  6. package/build/adapters/claude-code/config.js +8 -0
  7. package/build/adapters/claude-code/hooks.d.ts +53 -0
  8. package/build/adapters/claude-code/hooks.js +88 -0
  9. package/build/adapters/claude-code/index.d.ts +50 -0
  10. package/build/adapters/claude-code/index.js +523 -0
  11. package/build/adapters/codex/config.d.ts +8 -0
  12. package/build/adapters/codex/config.js +8 -0
  13. package/build/adapters/codex/hooks.d.ts +21 -0
  14. package/build/adapters/codex/hooks.js +27 -0
  15. package/build/adapters/codex/index.d.ts +44 -0
  16. package/build/adapters/codex/index.js +223 -0
  17. package/build/adapters/detect.d.ts +26 -0
  18. package/build/adapters/detect.js +131 -0
  19. package/build/adapters/gemini-cli/config.d.ts +8 -0
  20. package/build/adapters/gemini-cli/config.js +8 -0
  21. package/build/adapters/gemini-cli/hooks.d.ts +44 -0
  22. package/build/adapters/gemini-cli/hooks.js +64 -0
  23. package/build/adapters/gemini-cli/index.d.ts +57 -0
  24. package/build/adapters/gemini-cli/index.js +468 -0
  25. package/build/adapters/opencode/config.d.ts +8 -0
  26. package/build/adapters/opencode/config.js +8 -0
  27. package/build/adapters/opencode/hooks.d.ts +38 -0
  28. package/build/adapters/opencode/hooks.js +50 -0
  29. package/build/adapters/opencode/index.d.ts +52 -0
  30. package/build/adapters/opencode/index.js +386 -0
  31. package/build/adapters/types.d.ts +218 -0
  32. package/build/adapters/types.js +13 -0
  33. package/build/adapters/vscode-copilot/config.d.ts +8 -0
  34. package/build/adapters/vscode-copilot/config.js +8 -0
  35. package/build/adapters/vscode-copilot/hooks.d.ts +49 -0
  36. package/build/adapters/vscode-copilot/hooks.js +76 -0
  37. package/build/adapters/vscode-copilot/index.d.ts +58 -0
  38. package/build/adapters/vscode-copilot/index.js +512 -0
  39. package/build/cli.d.ts +9 -6
  40. package/build/cli.js +133 -423
  41. package/build/db-base.d.ts +84 -0
  42. package/build/db-base.js +128 -0
  43. package/build/executor.d.ts +6 -7
  44. package/build/executor.js +111 -51
  45. package/build/opencode-plugin.d.ts +37 -0
  46. package/build/opencode-plugin.js +118 -0
  47. package/build/runtime.js +1 -1
  48. package/build/server.js +436 -117
  49. package/build/session/db.d.ts +110 -0
  50. package/build/session/db.js +285 -0
  51. package/build/session/extract.d.ts +51 -0
  52. package/build/session/extract.js +407 -0
  53. package/build/session/snapshot.d.ts +70 -0
  54. package/build/session/snapshot.js +309 -0
  55. package/build/store.d.ts +4 -22
  56. package/build/store.js +67 -55
  57. package/build/truncate.d.ts +59 -0
  58. package/build/truncate.js +157 -0
  59. package/build/types.d.ts +101 -0
  60. package/build/types.js +20 -0
  61. package/configs/claude-code/CLAUDE.md +62 -0
  62. package/configs/codex/AGENTS.md +58 -0
  63. package/configs/codex/config.toml +5 -0
  64. package/configs/gemini-cli/GEMINI.md +58 -0
  65. package/configs/gemini-cli/mcp.json +7 -0
  66. package/configs/gemini-cli/settings.json +49 -0
  67. package/configs/opencode/AGENTS.md +58 -0
  68. package/configs/opencode/opencode.json +10 -0
  69. package/configs/vscode-copilot/copilot-instructions.md +58 -0
  70. package/configs/vscode-copilot/hooks.json +16 -0
  71. package/configs/vscode-copilot/mcp.json +8 -0
  72. package/hooks/core/formatters.mjs +86 -0
  73. package/hooks/core/routing.mjs +262 -0
  74. package/hooks/core/stdin.mjs +19 -0
  75. package/hooks/formatters/claude-code.mjs +57 -0
  76. package/hooks/formatters/gemini-cli.mjs +55 -0
  77. package/hooks/formatters/vscode-copilot.mjs +55 -0
  78. package/hooks/gemini-cli/aftertool.mjs +58 -0
  79. package/hooks/gemini-cli/beforetool.mjs +25 -0
  80. package/hooks/gemini-cli/precompress.mjs +51 -0
  81. package/hooks/gemini-cli/sessionstart.mjs +117 -0
  82. package/hooks/hooks.json +46 -4
  83. package/hooks/posttooluse.mjs +53 -0
  84. package/hooks/precompact.mjs +55 -0
  85. package/hooks/pretooluse.mjs +23 -266
  86. package/hooks/routing-block.mjs +19 -6
  87. package/hooks/session-directive.mjs +353 -0
  88. package/hooks/session-helpers.mjs +112 -0
  89. package/hooks/sessionstart.mjs +123 -16
  90. package/hooks/userpromptsubmit.mjs +58 -0
  91. package/hooks/vscode-copilot/posttooluse.mjs +58 -0
  92. package/hooks/vscode-copilot/precompact.mjs +51 -0
  93. package/hooks/vscode-copilot/pretooluse.mjs +25 -0
  94. package/hooks/vscode-copilot/sessionstart.mjs +115 -0
  95. package/package.json +20 -17
  96. package/skills/context-mode/SKILL.md +49 -49
  97. package/skills/{doctor → ctx-doctor}/SKILL.md +3 -3
  98. package/skills/{stats → ctx-stats}/SKILL.md +3 -3
  99. package/skills/{upgrade → ctx-upgrade}/SKILL.md +3 -3
  100. package/start.mjs +47 -0
  101. package/hooks/pretooluse.sh +0 -147
  102. package/server.bundle.mjs +0 -341
@@ -0,0 +1,223 @@
1
+ /**
2
+ * adapters/codex — Codex CLI platform adapter.
3
+ *
4
+ * Implements HookAdapter for Codex CLI's MCP-only paradigm.
5
+ *
6
+ * Codex CLI hook specifics:
7
+ * - NO hook support (PRs #2904, #9796 were closed without merge)
8
+ * - Only "hook": notify config for agent-turn-complete (very limited)
9
+ * - Config: ~/.codex/config.toml (TOML format, not JSON)
10
+ * - MCP: full support via [mcp_servers] in config.toml
11
+ * - All capabilities are false — MCP is the only integration path
12
+ * - Session dir: ~/.codex/context-mode/sessions/
13
+ */
14
+ import { createHash } from "node:crypto";
15
+ import { readFileSync, writeFileSync, mkdirSync, copyFileSync, accessSync, constants, } from "node:fs";
16
+ import { resolve, join, dirname } from "node:path";
17
+ import { fileURLToPath } from "node:url";
18
+ import { homedir } from "node:os";
19
+ // ─────────────────────────────────────────────────────────
20
+ // Adapter implementation
21
+ // ─────────────────────────────────────────────────────────
22
+ export class CodexAdapter {
23
+ name = "Codex CLI";
24
+ paradigm = "mcp-only";
25
+ capabilities = {
26
+ preToolUse: false,
27
+ postToolUse: false,
28
+ preCompact: false,
29
+ sessionStart: false,
30
+ canModifyArgs: false,
31
+ canModifyOutput: false,
32
+ canInjectSessionContext: false,
33
+ };
34
+ // ── Input parsing ──────────────────────────────────────
35
+ // Codex CLI does not support hooks. These methods exist to satisfy the
36
+ // interface contract but will throw if called.
37
+ parsePreToolUseInput(_raw) {
38
+ throw new Error("Codex CLI does not support hooks");
39
+ }
40
+ parsePostToolUseInput(_raw) {
41
+ throw new Error("Codex CLI does not support hooks");
42
+ }
43
+ parsePreCompactInput(_raw) {
44
+ throw new Error("Codex CLI does not support hooks");
45
+ }
46
+ parseSessionStartInput(_raw) {
47
+ throw new Error("Codex CLI does not support hooks");
48
+ }
49
+ // ── Response formatting ────────────────────────────────
50
+ // Codex CLI does not support hooks. Return undefined for all responses.
51
+ formatPreToolUseResponse(_response) {
52
+ return undefined;
53
+ }
54
+ formatPostToolUseResponse(_response) {
55
+ return undefined;
56
+ }
57
+ formatPreCompactResponse(_response) {
58
+ return undefined;
59
+ }
60
+ formatSessionStartResponse(_response) {
61
+ return undefined;
62
+ }
63
+ // ── Configuration ──────────────────────────────────────
64
+ getSettingsPath() {
65
+ return resolve(homedir(), ".codex", "config.toml");
66
+ }
67
+ getSessionDir() {
68
+ const dir = join(homedir(), ".codex", "context-mode", "sessions");
69
+ mkdirSync(dir, { recursive: true });
70
+ return dir;
71
+ }
72
+ getSessionDBPath(projectDir) {
73
+ const hash = createHash("sha256")
74
+ .update(projectDir)
75
+ .digest("hex")
76
+ .slice(0, 16);
77
+ return join(this.getSessionDir(), `${hash}.db`);
78
+ }
79
+ getSessionEventsPath(projectDir) {
80
+ const hash = createHash("sha256")
81
+ .update(projectDir)
82
+ .digest("hex")
83
+ .slice(0, 16);
84
+ return join(this.getSessionDir(), `${hash}-events.md`);
85
+ }
86
+ generateHookConfig(_pluginRoot) {
87
+ // Codex CLI does not support hooks — return empty registration
88
+ return {};
89
+ }
90
+ readSettings() {
91
+ // Codex CLI uses TOML format. Full TOML parsing is complex;
92
+ // return null for now. MCP configuration should be done manually
93
+ // or via a dedicated TOML library in the upgrade flow.
94
+ try {
95
+ const raw = readFileSync(this.getSettingsPath(), "utf-8");
96
+ // Return raw TOML as a single-key object for inspection
97
+ return { _raw_toml: raw };
98
+ }
99
+ catch {
100
+ return null;
101
+ }
102
+ }
103
+ writeSettings(_settings) {
104
+ // Codex CLI uses TOML format. Writing TOML requires a dedicated
105
+ // serializer. This is a no-op; TOML config should be edited
106
+ // manually or via the `codex` CLI tool.
107
+ }
108
+ // ── Diagnostics (doctor) ─────────────────────────────────
109
+ validateHooks(_pluginRoot) {
110
+ return [
111
+ {
112
+ check: "Hook support",
113
+ status: "warn",
114
+ message: "Codex CLI does not support hooks (PRs #2904, #9796 closed without merge). " +
115
+ "Only MCP integration is available.",
116
+ },
117
+ ];
118
+ }
119
+ checkPluginRegistration() {
120
+ // Check for context-mode in [mcp_servers] section of config.toml
121
+ try {
122
+ const raw = readFileSync(this.getSettingsPath(), "utf-8");
123
+ const hasContextMode = raw.includes("context-mode");
124
+ const hasMcpSection = raw.includes("[mcp_servers]") || raw.includes("[mcp_servers.");
125
+ if (hasContextMode && hasMcpSection) {
126
+ return {
127
+ check: "MCP registration",
128
+ status: "pass",
129
+ message: "context-mode found in [mcp_servers] config",
130
+ };
131
+ }
132
+ if (hasMcpSection) {
133
+ return {
134
+ check: "MCP registration",
135
+ status: "fail",
136
+ message: "[mcp_servers] section exists but context-mode not found",
137
+ fix: 'Add context-mode to [mcp_servers] in ~/.codex/config.toml',
138
+ };
139
+ }
140
+ return {
141
+ check: "MCP registration",
142
+ status: "fail",
143
+ message: "No [mcp_servers] section in config.toml",
144
+ fix: 'Add [mcp_servers.context-mode] to ~/.codex/config.toml',
145
+ };
146
+ }
147
+ catch {
148
+ return {
149
+ check: "MCP registration",
150
+ status: "warn",
151
+ message: "Could not read ~/.codex/config.toml",
152
+ };
153
+ }
154
+ }
155
+ getInstalledVersion() {
156
+ // Codex CLI has no marketplace or plugin system
157
+ return "not installed";
158
+ }
159
+ // ── Upgrade ────────────────────────────────────────────
160
+ configureAllHooks(_pluginRoot) {
161
+ // Codex CLI does not support hooks — nothing to configure
162
+ return [];
163
+ }
164
+ backupSettings() {
165
+ const settingsPath = this.getSettingsPath();
166
+ try {
167
+ accessSync(settingsPath, constants.R_OK);
168
+ const backupPath = settingsPath + ".bak";
169
+ copyFileSync(settingsPath, backupPath);
170
+ return backupPath;
171
+ }
172
+ catch {
173
+ return null;
174
+ }
175
+ }
176
+ setHookPermissions(_pluginRoot) {
177
+ // No hook scripts for Codex CLI
178
+ return [];
179
+ }
180
+ updatePluginRegistry(_pluginRoot, _version) {
181
+ // Codex CLI has no plugin registry
182
+ }
183
+ // ── Routing Instructions (soft enforcement) ────────────
184
+ getRoutingInstructionsConfig() {
185
+ return {
186
+ fileName: "AGENTS.md",
187
+ globalPath: resolve(homedir(), ".codex", "AGENTS.md"),
188
+ projectRelativePath: "AGENTS.md",
189
+ };
190
+ }
191
+ writeRoutingInstructions(projectDir, pluginRoot) {
192
+ const config = this.getRoutingInstructionsConfig();
193
+ const targetPath = resolve(projectDir, config.projectRelativePath);
194
+ const sourcePath = resolve(pluginRoot, "configs", "codex", config.fileName);
195
+ try {
196
+ const content = readFileSync(sourcePath, "utf-8");
197
+ try {
198
+ const existing = readFileSync(targetPath, "utf-8");
199
+ if (existing.includes("context-mode"))
200
+ return null;
201
+ writeFileSync(targetPath, existing.trimEnd() + "\n\n" + content, "utf-8");
202
+ return targetPath;
203
+ }
204
+ catch {
205
+ writeFileSync(targetPath, content, "utf-8");
206
+ return targetPath;
207
+ }
208
+ }
209
+ catch {
210
+ return null;
211
+ }
212
+ }
213
+ getRoutingInstructions() {
214
+ const instructionsPath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "configs", "codex", "AGENTS.md");
215
+ try {
216
+ return readFileSync(instructionsPath, "utf-8");
217
+ }
218
+ catch {
219
+ // Fallback inline instructions
220
+ return "# context-mode\n\nUse context-mode MCP tools (execute, execute_file, batch_execute, fetch_and_index, search) instead of bash/cat/curl for data-heavy operations.";
221
+ }
222
+ }
223
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * adapters/detect — Auto-detect which platform is running.
3
+ *
4
+ * Detection priority:
5
+ * 1. Environment variables (high confidence)
6
+ * 2. Config directory existence (medium confidence)
7
+ * 3. Fallback to Claude Code (low confidence — most common)
8
+ *
9
+ * Each platform sets identifiable env vars or creates config dirs:
10
+ * - Claude Code: CLAUDE_PROJECT_DIR, ~/.claude/
11
+ * - Gemini CLI: GEMINI_PROJECT_DIR, ~/.gemini/
12
+ * - OpenCode: OPENCODE_PROJECT_DIR, .opencode/
13
+ * - Copilot CLI: GITHUB_COPILOT_*, ~/.config/github-copilot/
14
+ * - VS Code: VSCODE_*, ~/.vscode/
15
+ * - Cursor: CURSOR_*, ~/.cursor/
16
+ */
17
+ import type { PlatformId, DetectionSignal, HookAdapter } from "./types.js";
18
+ /**
19
+ * Detect the current platform by checking env vars and config dirs.
20
+ */
21
+ export declare function detectPlatform(): DetectionSignal;
22
+ /**
23
+ * Get the adapter instance for a given platform.
24
+ * Lazily imports platform-specific adapter modules.
25
+ */
26
+ export declare function getAdapter(platform?: PlatformId): Promise<HookAdapter>;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * adapters/detect — Auto-detect which platform is running.
3
+ *
4
+ * Detection priority:
5
+ * 1. Environment variables (high confidence)
6
+ * 2. Config directory existence (medium confidence)
7
+ * 3. Fallback to Claude Code (low confidence — most common)
8
+ *
9
+ * Each platform sets identifiable env vars or creates config dirs:
10
+ * - Claude Code: CLAUDE_PROJECT_DIR, ~/.claude/
11
+ * - Gemini CLI: GEMINI_PROJECT_DIR, ~/.gemini/
12
+ * - OpenCode: OPENCODE_PROJECT_DIR, .opencode/
13
+ * - Copilot CLI: GITHUB_COPILOT_*, ~/.config/github-copilot/
14
+ * - VS Code: VSCODE_*, ~/.vscode/
15
+ * - Cursor: CURSOR_*, ~/.cursor/
16
+ */
17
+ import { existsSync } from "node:fs";
18
+ import { resolve } from "node:path";
19
+ import { homedir } from "node:os";
20
+ /**
21
+ * Detect the current platform by checking env vars and config dirs.
22
+ */
23
+ export function detectPlatform() {
24
+ // ── High confidence: environment variables ─────────────
25
+ if (process.env.CLAUDE_PROJECT_DIR || process.env.CLAUDE_SESSION_ID) {
26
+ return {
27
+ platform: "claude-code",
28
+ confidence: "high",
29
+ reason: "CLAUDE_PROJECT_DIR or CLAUDE_SESSION_ID env var set",
30
+ };
31
+ }
32
+ if (process.env.GEMINI_PROJECT_DIR || process.env.GEMINI_SESSION_ID) {
33
+ return {
34
+ platform: "gemini-cli",
35
+ confidence: "high",
36
+ reason: "GEMINI_PROJECT_DIR or GEMINI_SESSION_ID env var set",
37
+ };
38
+ }
39
+ if (process.env.OPENCODE_PROJECT_DIR || process.env.OPENCODE_SESSION_ID) {
40
+ return {
41
+ platform: "opencode",
42
+ confidence: "high",
43
+ reason: "OPENCODE_PROJECT_DIR or OPENCODE_SESSION_ID env var set",
44
+ };
45
+ }
46
+ if (process.env.GITHUB_COPILOT_AGENT || process.env.COPILOT_SESSION_ID) {
47
+ return {
48
+ platform: "copilot-cli",
49
+ confidence: "high",
50
+ reason: "GITHUB_COPILOT_AGENT or COPILOT_SESSION_ID env var set",
51
+ };
52
+ }
53
+ if (process.env.VSCODE_PID || process.env.VSCODE_CWD) {
54
+ return {
55
+ platform: "vscode-copilot",
56
+ confidence: "high",
57
+ reason: "VSCODE_PID or VSCODE_CWD env var set",
58
+ };
59
+ }
60
+ if (process.env.CURSOR_SESSION_ID || process.env.CURSOR_TRACE_ID) {
61
+ return {
62
+ platform: "cursor",
63
+ confidence: "high",
64
+ reason: "CURSOR_SESSION_ID or CURSOR_TRACE_ID env var set",
65
+ };
66
+ }
67
+ // ── Medium confidence: config directory existence ──────
68
+ const home = homedir();
69
+ if (existsSync(resolve(home, ".claude"))) {
70
+ return {
71
+ platform: "claude-code",
72
+ confidence: "medium",
73
+ reason: "~/.claude/ directory exists",
74
+ };
75
+ }
76
+ if (existsSync(resolve(home, ".gemini"))) {
77
+ return {
78
+ platform: "gemini-cli",
79
+ confidence: "medium",
80
+ reason: "~/.gemini/ directory exists",
81
+ };
82
+ }
83
+ if (existsSync(resolve(home, ".cursor"))) {
84
+ return {
85
+ platform: "cursor",
86
+ confidence: "medium",
87
+ reason: "~/.cursor/ directory exists",
88
+ };
89
+ }
90
+ // ── Low confidence: fallback ───────────────────────────
91
+ return {
92
+ platform: "claude-code",
93
+ confidence: "low",
94
+ reason: "No platform detected, defaulting to Claude Code",
95
+ };
96
+ }
97
+ /**
98
+ * Get the adapter instance for a given platform.
99
+ * Lazily imports platform-specific adapter modules.
100
+ */
101
+ export async function getAdapter(platform) {
102
+ const target = platform ?? detectPlatform().platform;
103
+ switch (target) {
104
+ case "claude-code": {
105
+ const { ClaudeCodeAdapter } = await import("./claude-code/index.js");
106
+ return new ClaudeCodeAdapter();
107
+ }
108
+ case "gemini-cli": {
109
+ const { GeminiCLIAdapter } = await import("./gemini-cli/index.js");
110
+ return new GeminiCLIAdapter();
111
+ }
112
+ case "opencode": {
113
+ const { OpenCodeAdapter } = await import("./opencode/index.js");
114
+ return new OpenCodeAdapter();
115
+ }
116
+ case "codex": {
117
+ const { CodexAdapter } = await import("./codex/index.js");
118
+ return new CodexAdapter();
119
+ }
120
+ case "vscode-copilot": {
121
+ const { VSCodeCopilotAdapter } = await import("./vscode-copilot/index.js");
122
+ return new VSCodeCopilotAdapter();
123
+ }
124
+ default: {
125
+ // Unsupported platform — fall back to Claude Code adapter
126
+ // (MCP server works everywhere, hooks may not)
127
+ const { ClaudeCodeAdapter } = await import("./claude-code/index.js");
128
+ return new ClaudeCodeAdapter();
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * adapters/gemini-cli/config — Thin re-exports from GeminiCLIAdapter.
3
+ *
4
+ * This module exists for backward compatibility. All logic lives in the
5
+ * adapter class (index.ts). New code should use getAdapter() from detect.ts.
6
+ */
7
+ export { GeminiCLIAdapter } from "./index.js";
8
+ export { HOOK_TYPES, HOOK_SCRIPTS, REQUIRED_HOOKS, OPTIONAL_HOOKS } from "./hooks.js";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * adapters/gemini-cli/config — Thin re-exports from GeminiCLIAdapter.
3
+ *
4
+ * This module exists for backward compatibility. All logic lives in the
5
+ * adapter class (index.ts). New code should use getAdapter() from detect.ts.
6
+ */
7
+ export { GeminiCLIAdapter } from "./index.js";
8
+ export { HOOK_TYPES, HOOK_SCRIPTS, REQUIRED_HOOKS, OPTIONAL_HOOKS } from "./hooks.js";
@@ -0,0 +1,44 @@
1
+ /**
2
+ * adapters/gemini-cli/hooks — Gemini CLI hook definitions and matchers.
3
+ *
4
+ * Defines the hook types, matchers, and registration format specific to
5
+ * Gemini CLI's hook system. This module is used by:
6
+ * - CLI setup/upgrade commands (to configure hooks in settings.json)
7
+ * - Doctor command (to validate hook configuration)
8
+ * - Hook config generation
9
+ *
10
+ * Gemini CLI hook system reference:
11
+ * - Hooks are registered in ~/.gemini/settings.json under "hooks" key
12
+ * - Each hook type maps to an array of { matcher, hooks } entries
13
+ * - Hook names: BeforeTool, AfterTool, PreCompress, SessionStart
14
+ * - Input: JSON on stdin
15
+ * - Output: JSON on stdout (or empty for passthrough)
16
+ */
17
+ /** Gemini CLI hook types. */
18
+ export declare const HOOK_TYPES: {
19
+ readonly BEFORE_TOOL: "BeforeTool";
20
+ readonly AFTER_TOOL: "AfterTool";
21
+ readonly PRE_COMPRESS: "PreCompress";
22
+ readonly SESSION_START: "SessionStart";
23
+ };
24
+ export type HookType = (typeof HOOK_TYPES)[keyof typeof HOOK_TYPES];
25
+ /** Map of hook types to their script file names. */
26
+ export declare const HOOK_SCRIPTS: Record<HookType, string>;
27
+ /** Required hooks that must be configured for context-mode to function. */
28
+ export declare const REQUIRED_HOOKS: HookType[];
29
+ /** Optional hooks that enhance functionality but aren't critical. */
30
+ export declare const OPTIONAL_HOOKS: HookType[];
31
+ /**
32
+ * Check if a hook entry points to a context-mode hook script.
33
+ */
34
+ export declare function isContextModeHook(entry: {
35
+ hooks?: Array<{
36
+ command?: string;
37
+ }>;
38
+ }, hookType: HookType): boolean;
39
+ /**
40
+ * Build the hook command string for a given hook type.
41
+ * Uses the CLI dispatcher: `context-mode hook gemini-cli <event>`
42
+ * Requires global install: `npm install -g context-mode`
43
+ */
44
+ export declare function buildHookCommand(hookType: HookType): string;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * adapters/gemini-cli/hooks — Gemini CLI hook definitions and matchers.
3
+ *
4
+ * Defines the hook types, matchers, and registration format specific to
5
+ * Gemini CLI's hook system. This module is used by:
6
+ * - CLI setup/upgrade commands (to configure hooks in settings.json)
7
+ * - Doctor command (to validate hook configuration)
8
+ * - Hook config generation
9
+ *
10
+ * Gemini CLI hook system reference:
11
+ * - Hooks are registered in ~/.gemini/settings.json under "hooks" key
12
+ * - Each hook type maps to an array of { matcher, hooks } entries
13
+ * - Hook names: BeforeTool, AfterTool, PreCompress, SessionStart
14
+ * - Input: JSON on stdin
15
+ * - Output: JSON on stdout (or empty for passthrough)
16
+ */
17
+ // ─────────────────────────────────────────────────────────
18
+ // Hook type constants
19
+ // ─────────────────────────────────────────────────────────
20
+ /** Gemini CLI hook types. */
21
+ export const HOOK_TYPES = {
22
+ BEFORE_TOOL: "BeforeTool",
23
+ AFTER_TOOL: "AfterTool",
24
+ PRE_COMPRESS: "PreCompress",
25
+ SESSION_START: "SessionStart",
26
+ };
27
+ // ─────────────────────────────────────────────────────────
28
+ // Hook script file names
29
+ // ─────────────────────────────────────────────────────────
30
+ /** Map of hook types to their script file names. */
31
+ export const HOOK_SCRIPTS = {
32
+ [HOOK_TYPES.BEFORE_TOOL]: "beforetool.mjs",
33
+ [HOOK_TYPES.AFTER_TOOL]: "aftertool.mjs",
34
+ [HOOK_TYPES.PRE_COMPRESS]: "precompress.mjs",
35
+ [HOOK_TYPES.SESSION_START]: "sessionstart.mjs",
36
+ };
37
+ // ─────────────────────────────────────────────────────────
38
+ // Hook validation
39
+ // ─────────────────────────────────────────────────────────
40
+ /** Required hooks that must be configured for context-mode to function. */
41
+ export const REQUIRED_HOOKS = [
42
+ HOOK_TYPES.BEFORE_TOOL,
43
+ HOOK_TYPES.SESSION_START,
44
+ ];
45
+ /** Optional hooks that enhance functionality but aren't critical. */
46
+ export const OPTIONAL_HOOKS = [
47
+ HOOK_TYPES.AFTER_TOOL,
48
+ HOOK_TYPES.PRE_COMPRESS,
49
+ ];
50
+ /**
51
+ * Check if a hook entry points to a context-mode hook script.
52
+ */
53
+ export function isContextModeHook(entry, hookType) {
54
+ const scriptName = HOOK_SCRIPTS[hookType];
55
+ return (entry.hooks?.some((h) => h.command?.includes(scriptName)) ?? false);
56
+ }
57
+ /**
58
+ * Build the hook command string for a given hook type.
59
+ * Uses the CLI dispatcher: `context-mode hook gemini-cli <event>`
60
+ * Requires global install: `npm install -g context-mode`
61
+ */
62
+ export function buildHookCommand(hookType) {
63
+ return `context-mode hook gemini-cli ${hookType.toLowerCase()}`;
64
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * adapters/gemini-cli — Gemini CLI platform adapter.
3
+ *
4
+ * Implements HookAdapter for Gemini CLI's JSON stdin/stdout hook paradigm.
5
+ *
6
+ * Gemini CLI hook specifics:
7
+ * - I/O: JSON on stdin, JSON on stdout (same paradigm as Claude Code)
8
+ * - Hook names: BeforeTool, AfterTool, PreCompress, SessionStart
9
+ * - Arg modification: `hookSpecificOutput.tool_input` (merged with original)
10
+ * - Blocking: `decision: "deny"` in response (NOT permissionDecision)
11
+ * - Output modification: `decision: "deny"` + reason replaces output,
12
+ * `hookSpecificOutput.additionalContext` appends
13
+ * - PreCompress: advisory only (async, cannot block)
14
+ * - No `decision: "ask"` support
15
+ * - Hooks don't fire for subagents yet
16
+ * - Config: ~/.gemini/settings.json (user), .gemini/settings.json (project)
17
+ * - Session ID: session_id field
18
+ * - Project dir env: GEMINI_PROJECT_DIR (also CLAUDE_PROJECT_DIR alias)
19
+ * - Session dir: ~/.gemini/context-mode/sessions/
20
+ */
21
+ import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse, HookRegistration, RoutingInstructionsConfig } from "../types.js";
22
+ export declare class GeminiCLIAdapter implements HookAdapter {
23
+ readonly name = "Gemini CLI";
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
+ getSessionDir(): string;
36
+ getSessionDBPath(projectDir: string): string;
37
+ getSessionEventsPath(projectDir: string): string;
38
+ generateHookConfig(_pluginRoot: string): HookRegistration;
39
+ readSettings(): Record<string, unknown> | null;
40
+ writeSettings(settings: Record<string, unknown>): void;
41
+ validateHooks(pluginRoot: string): DiagnosticResult[];
42
+ checkPluginRegistration(): DiagnosticResult;
43
+ getInstalledVersion(): string;
44
+ configureAllHooks(_pluginRoot: string): string[];
45
+ backupSettings(): string | null;
46
+ setHookPermissions(pluginRoot: string): string[];
47
+ updatePluginRegistry(pluginRoot: string, version: string): void;
48
+ getRoutingInstructionsConfig(): RoutingInstructionsConfig;
49
+ writeRoutingInstructions(projectDir: string, pluginRoot: string): string | null;
50
+ /** Get the project directory from environment variables. */
51
+ private getProjectDir;
52
+ /**
53
+ * Extract session ID from Gemini CLI hook input.
54
+ * Priority: session_id field > env fallback > ppid fallback.
55
+ */
56
+ private extractSessionId;
57
+ }