context-mode 1.0.162 → 1.0.164
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 +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +149 -30
- package/bin/statusline.mjs +24 -4
- package/build/adapters/antigravity/index.d.ts +1 -1
- package/build/adapters/antigravity-cli/index.d.ts +51 -0
- package/build/adapters/antigravity-cli/index.js +342 -0
- package/build/adapters/claude-code/hooks.d.ts +1 -0
- package/build/adapters/claude-code/hooks.js +3 -0
- package/build/adapters/claude-code/index.js +24 -5
- package/build/adapters/client-map.js +5 -0
- package/build/adapters/codex/hooks.d.ts +5 -1
- package/build/adapters/codex/hooks.js +5 -1
- package/build/adapters/codex/index.d.ts +9 -1
- package/build/adapters/codex/index.js +87 -5
- 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/detect.d.ts +1 -1
- package/build/adapters/detect.js +71 -3
- package/build/adapters/openclaw/mcp-tools.js +1 -1
- package/build/adapters/opencode/index.js +31 -17
- package/build/adapters/opencode/zod3tov4.js +27 -6
- package/build/adapters/pi/extension.d.ts +2 -12
- package/build/adapters/pi/extension.js +128 -109
- package/build/adapters/types.d.ts +5 -4
- package/build/adapters/types.js +4 -3
- package/build/cache-heal.d.ts +48 -0
- package/build/cache-heal.js +150 -0
- package/build/cli.js +37 -97
- package/build/executor.d.ts +25 -0
- package/build/executor.js +143 -22
- package/build/lifecycle.d.ts +48 -0
- package/build/lifecycle.js +111 -0
- package/build/opencode-plugin.js +5 -2
- package/build/routing-block.d.ts +8 -0
- package/build/routing-block.js +86 -0
- package/build/runtime.d.ts +0 -36
- package/build/runtime.js +107 -27
- package/build/search/flood-guard.d.ts +57 -0
- package/build/search/flood-guard.js +80 -0
- package/build/security.d.ts +73 -3
- package/build/security.js +293 -33
- package/build/server.d.ts +14 -0
- package/build/server.js +441 -354
- package/build/session/analytics.d.ts +1 -1
- package/build/session/analytics.js +5 -1
- package/build/session/db.js +23 -3
- package/build/session/extract.js +78 -0
- package/build/store.d.ts +1 -1
- package/build/store.js +139 -25
- package/build/tool-naming.d.ts +4 -0
- package/build/tool-naming.js +24 -0
- package/build/util/jsonc.d.ts +14 -0
- package/build/util/jsonc.js +104 -0
- package/cli.bundle.mjs +253 -250
- package/configs/antigravity/GEMINI.md +2 -2
- 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 +2 -2
- package/configs/codex/AGENTS.md +2 -2
- 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/gemini-cli/GEMINI.md +2 -2
- package/configs/jetbrains-copilot/copilot-instructions.md +2 -2
- package/configs/kilo/AGENTS.md +2 -2
- package/configs/kiro/KIRO.md +2 -2
- package/configs/omp/SYSTEM.md +2 -2
- package/configs/openclaw/AGENTS.md +2 -2
- package/configs/opencode/AGENTS.md +2 -2
- package/configs/qwen-code/QWEN.md +2 -2
- package/configs/vscode-copilot/copilot-instructions.md +2 -2
- package/configs/zed/AGENTS.md +2 -2
- 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/codex/pretooluse.mjs +14 -4
- package/hooks/codex/stop.mjs +12 -4
- 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 +158 -7
- package/hooks/core/mcp-ready.mjs +37 -8
- package/hooks/core/routing.mjs +94 -8
- package/hooks/core/tool-naming.mjs +3 -0
- package/hooks/hooks.json +12 -1
- package/hooks/pretooluse.mjs +6 -2
- package/hooks/routing-block.mjs +3 -4
- package/hooks/security.bundle.mjs +2 -1
- package/hooks/session-db.bundle.mjs +5 -5
- package/hooks/session-directive.mjs +88 -20
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-helpers.mjs +21 -0
- package/hooks/sessionstart.mjs +37 -5
- package/hooks/stop.mjs +49 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -10
- package/server.bundle.mjs +206 -200
- package/skills/ctx-insight/SKILL.md +12 -17
- package/build/util/db-lock.d.ts +0 -65
- package/build/util/db-lock.js +0 -166
- package/insight/index.html +0 -13
- package/insight/package.json +0 -55
- package/insight/server.mjs +0 -1265
- package/insight/src/components/analytics.tsx +0 -112
- package/insight/src/components/ui/badge.tsx +0 -52
- package/insight/src/components/ui/button.tsx +0 -58
- package/insight/src/components/ui/card.tsx +0 -103
- package/insight/src/components/ui/chart.tsx +0 -371
- package/insight/src/components/ui/collapsible.tsx +0 -19
- package/insight/src/components/ui/input.tsx +0 -20
- package/insight/src/components/ui/progress.tsx +0 -83
- package/insight/src/components/ui/scroll-area.tsx +0 -55
- package/insight/src/components/ui/separator.tsx +0 -23
- package/insight/src/components/ui/table.tsx +0 -114
- package/insight/src/components/ui/tabs.tsx +0 -82
- package/insight/src/components/ui/tooltip.tsx +0 -64
- package/insight/src/lib/api.ts +0 -144
- package/insight/src/lib/utils.ts +0 -6
- package/insight/src/main.tsx +0 -22
- package/insight/src/routeTree.gen.ts +0 -189
- package/insight/src/router.tsx +0 -19
- package/insight/src/routes/__root.tsx +0 -55
- package/insight/src/routes/enterprise.tsx +0 -316
- package/insight/src/routes/index.tsx +0 -1482
- package/insight/src/routes/knowledge.tsx +0 -221
- package/insight/src/routes/knowledge_.$dbHash.$sourceId.tsx +0 -137
- package/insight/src/routes/search.tsx +0 -97
- package/insight/src/routes/sessions.tsx +0 -179
- package/insight/src/routes/sessions_.$dbHash.$sessionId.tsx +0 -181
- package/insight/src/styles.css +0 -104
- package/insight/tsconfig.json +0 -29
- package/insight/vite.config.ts +0 -19
|
@@ -0,0 +1,342 @@
|
|
|
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 { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { dirname, resolve } from "node:path";
|
|
21
|
+
import { homedir } from "node:os";
|
|
22
|
+
import { AntigravityAdapter } from "../antigravity/index.js";
|
|
23
|
+
import { parseJsonc } from "../../util/jsonc.js";
|
|
24
|
+
export function antigravityCliMcpConfigPath() {
|
|
25
|
+
return resolve(homedir(), ".gemini", "config", "mcp_config.json");
|
|
26
|
+
}
|
|
27
|
+
export function antigravityCliConfigDir() {
|
|
28
|
+
return resolve(homedir(), ".gemini", "antigravity-cli");
|
|
29
|
+
}
|
|
30
|
+
/** agy reads user hooks from ~/.gemini/config/hooks.json (sibling of mcp_config.json). */
|
|
31
|
+
export function antigravityCliHooksPath() {
|
|
32
|
+
return resolve(homedir(), ".gemini", "config", "hooks.json");
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* `agy plugin install <bundle>` registers MCP + hook + skill into agy's plugin
|
|
36
|
+
* profile under ~/.gemini/config/plugins/<name>/ (verified on agy 1.0.6, re-verified 1.0.10) — the
|
|
37
|
+
* canonical install. The global mcp_config.json / hooks.json paths above are the
|
|
38
|
+
* manual (no-plugin) fallback. doctor must recognize BOTH.
|
|
39
|
+
*/
|
|
40
|
+
export function antigravityCliPluginDir() {
|
|
41
|
+
return resolve(homedir(), ".gemini", "config", "plugins", "context-mode");
|
|
42
|
+
}
|
|
43
|
+
function antigravityCliPluginMcpPath() {
|
|
44
|
+
return resolve(antigravityCliPluginDir(), "mcp_config.json");
|
|
45
|
+
}
|
|
46
|
+
function antigravityCliPluginHooksPath() {
|
|
47
|
+
return resolve(antigravityCliPluginDir(), "hooks.json");
|
|
48
|
+
}
|
|
49
|
+
/** True if context-mode's MCP is registered in any agy profile (plugin or global). */
|
|
50
|
+
function readMcpRegistered(paths) {
|
|
51
|
+
for (const path of paths) {
|
|
52
|
+
try {
|
|
53
|
+
const config = parseJsonc(readFileSync(path, "utf-8")) ?? {};
|
|
54
|
+
const mcpServers = config?.mcpServers ?? {};
|
|
55
|
+
if ("context-mode" in mcpServers)
|
|
56
|
+
return { ok: true, where: path };
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
/* unreadable/missing — try next */
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return { ok: false };
|
|
63
|
+
}
|
|
64
|
+
function asRecord(value) {
|
|
65
|
+
return value && typeof value === "object" ? value : {};
|
|
66
|
+
}
|
|
67
|
+
function agyProjectDir(raw) {
|
|
68
|
+
// Refs-backed FIRST: the only real upstream hook-payload example
|
|
69
|
+
// (refs/platforms/antigravity-cli/examples/title/title.sh:10, README.md:11)
|
|
70
|
+
// reads the working directory from `workspace.current_dir` — an OBJECT field,
|
|
71
|
+
// not an array. Prefer it.
|
|
72
|
+
const workspace = asRecord(raw.workspace);
|
|
73
|
+
if (typeof workspace.current_dir === "string" && workspace.current_dir) {
|
|
74
|
+
return workspace.current_dir;
|
|
75
|
+
}
|
|
76
|
+
// Fallback: `workspacePaths[0]` is empirically-derived/unverified (no upstream
|
|
77
|
+
// doc or example confirms this shape) — retained as a defensive fallback only.
|
|
78
|
+
const workspacePaths = raw.workspacePaths;
|
|
79
|
+
return Array.isArray(workspacePaths) && workspacePaths.length > 0
|
|
80
|
+
? String(workspacePaths[0])
|
|
81
|
+
: undefined;
|
|
82
|
+
}
|
|
83
|
+
function agySessionId(raw) {
|
|
84
|
+
// `conversationId` is empirically-derived/unverified — no upstream agy doc or
|
|
85
|
+
// example confirms a session-id field in the hook payload. Kept as the
|
|
86
|
+
// best-available identifier; falls back to the process id when absent.
|
|
87
|
+
return typeof raw.conversationId === "string" && raw.conversationId
|
|
88
|
+
? raw.conversationId
|
|
89
|
+
: `pid-${process.ppid}`;
|
|
90
|
+
}
|
|
91
|
+
function hookEntryHasCommand(entry, command) {
|
|
92
|
+
const e = asRecord(entry);
|
|
93
|
+
const nested = Array.isArray(e.hooks) ? e.hooks : [];
|
|
94
|
+
return nested.some((hook) => asRecord(hook).command === command);
|
|
95
|
+
}
|
|
96
|
+
function hookEntryJsonHasCommand(entry, command) {
|
|
97
|
+
return JSON.stringify(entry).includes(command);
|
|
98
|
+
}
|
|
99
|
+
function matcherCoversAgyPreToolUse(matcher) {
|
|
100
|
+
if (typeof matcher !== "string")
|
|
101
|
+
return false;
|
|
102
|
+
return ["run_command", "view_file", "grep_search", "web_fetch", "read_url_content"].every((tool) => matcher.includes(tool));
|
|
103
|
+
}
|
|
104
|
+
function hookGroupHasCommand(group, command) {
|
|
105
|
+
return Array.isArray(group) && group.some((entry) => hookEntryHasCommand(entry, command));
|
|
106
|
+
}
|
|
107
|
+
function hookGroupHasPreToolUse(group) {
|
|
108
|
+
return Array.isArray(group) && group.some((entry) => {
|
|
109
|
+
const e = asRecord(entry);
|
|
110
|
+
return matcherCoversAgyPreToolUse(e.matcher) && hookEntryHasCommand(entry, PRE_HOOK_COMMAND);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/** True if all context-mode agy hooks are registered in plugin/global profiles. */
|
|
114
|
+
function readRegisteredHooks(paths) {
|
|
115
|
+
const found = { preOk: false, postOk: false, stopOk: false, where: undefined };
|
|
116
|
+
for (const path of paths) {
|
|
117
|
+
try {
|
|
118
|
+
const config = parseJsonc(readFileSync(path, "utf-8")) ?? {};
|
|
119
|
+
const hooks = config.hooks ?? {};
|
|
120
|
+
const preOk = hookGroupHasPreToolUse(hooks.PreToolUse);
|
|
121
|
+
const postOk = hookGroupHasCommand(hooks.PostToolUse, POST_HOOK_COMMAND);
|
|
122
|
+
const stopOk = hookGroupHasCommand(hooks.Stop, STOP_HOOK_COMMAND);
|
|
123
|
+
if ((preOk || postOk || stopOk) && !found.where)
|
|
124
|
+
found.where = path;
|
|
125
|
+
found.preOk ||= preOk;
|
|
126
|
+
found.postOk ||= postOk;
|
|
127
|
+
found.stopOk ||= stopOk;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
/* unreadable/missing — try next */
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Stop is registered when possible, but agy 1.0.6 `-p` probes did not emit it
|
|
134
|
+
// (no Stop-hook change in agy's changelog through 1.0.10). Treat it as
|
|
135
|
+
// best-effort so doctor does not mark a working Pre/Post install
|
|
136
|
+
// as degraded solely because Stop is absent.
|
|
137
|
+
return { ...found, ok: found.preOk && found.postOk };
|
|
138
|
+
}
|
|
139
|
+
/** Dispatcher commands agy invokes from hooks.json. */
|
|
140
|
+
const PRE_HOOK_COMMAND = "context-mode hook antigravity-cli pretooluse";
|
|
141
|
+
const PRE_HOOK_MATCHER = "run_command|view_file|grep_search|web_fetch|read_url_content";
|
|
142
|
+
const POST_HOOK_COMMAND = "context-mode hook antigravity-cli posttooluse";
|
|
143
|
+
const STOP_HOOK_COMMAND = "context-mode hook antigravity-cli stop";
|
|
144
|
+
// Keep in sync with the identical agyContextReason in hooks/core/formatters.mjs:
|
|
145
|
+
// two copies exist because the bundled .mjs formatter (runtime hook path) and
|
|
146
|
+
// this TS adapter are separate layers; the deny-reason text must not drift.
|
|
147
|
+
function agyContextReason(additionalContext) {
|
|
148
|
+
const text = String(additionalContext ?? "")
|
|
149
|
+
.replace(/<\/?context_guidance>/g, " ")
|
|
150
|
+
.replace(/<\/?tip>/g, " ")
|
|
151
|
+
.replace(/\s+/g, " ")
|
|
152
|
+
.trim();
|
|
153
|
+
return text
|
|
154
|
+
? `context-mode: use the context-mode MCP tools instead of this native tool. ${text}`
|
|
155
|
+
: "context-mode: use the context-mode MCP tools instead of this native tool so raw bytes stay out of the conversation.";
|
|
156
|
+
}
|
|
157
|
+
function configureHookEntry(hooks, type, desired, command) {
|
|
158
|
+
const current = Array.isArray(hooks[type]) ? hooks[type] : [];
|
|
159
|
+
const desiredJson = JSON.stringify(desired);
|
|
160
|
+
const alreadyExact = current.some((entry) => JSON.stringify(entry) === desiredJson) &&
|
|
161
|
+
current.every((entry) => !hookEntryJsonHasCommand(entry, command) || JSON.stringify(entry) === desiredJson);
|
|
162
|
+
if (alreadyExact)
|
|
163
|
+
return false;
|
|
164
|
+
hooks[type] = [
|
|
165
|
+
...current.filter((entry) => !hookEntryJsonHasCommand(entry, command)),
|
|
166
|
+
desired,
|
|
167
|
+
];
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
export class AntigravityCliAdapter extends AntigravityAdapter {
|
|
171
|
+
name = "Antigravity CLI";
|
|
172
|
+
paradigm = "json-stdio";
|
|
173
|
+
capabilities = {
|
|
174
|
+
preToolUse: true,
|
|
175
|
+
postToolUse: true,
|
|
176
|
+
preCompact: false,
|
|
177
|
+
sessionStart: false,
|
|
178
|
+
canModifyArgs: false,
|
|
179
|
+
canModifyOutput: false,
|
|
180
|
+
canInjectSessionContext: false,
|
|
181
|
+
};
|
|
182
|
+
getSettingsPath() {
|
|
183
|
+
return antigravityCliMcpConfigPath();
|
|
184
|
+
}
|
|
185
|
+
getConfigDir(_projectDir) {
|
|
186
|
+
return antigravityCliConfigDir();
|
|
187
|
+
}
|
|
188
|
+
parsePreToolUseInput(raw) {
|
|
189
|
+
const payload = asRecord(raw);
|
|
190
|
+
const toolCall = asRecord(payload.toolCall);
|
|
191
|
+
return {
|
|
192
|
+
toolName: typeof toolCall.name === "string" ? toolCall.name : "",
|
|
193
|
+
toolInput: asRecord(toolCall.args),
|
|
194
|
+
sessionId: agySessionId(payload),
|
|
195
|
+
projectDir: agyProjectDir(payload),
|
|
196
|
+
raw,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
parsePostToolUseInput(raw) {
|
|
200
|
+
const payload = asRecord(raw);
|
|
201
|
+
const toolCall = asRecord(payload.toolCall);
|
|
202
|
+
const error = typeof payload.error === "string" ? payload.error : "";
|
|
203
|
+
return {
|
|
204
|
+
toolName: typeof toolCall.name === "string" ? toolCall.name : "",
|
|
205
|
+
toolInput: asRecord(toolCall.args),
|
|
206
|
+
toolOutput: error,
|
|
207
|
+
isError: error.length > 0,
|
|
208
|
+
sessionId: agySessionId(payload),
|
|
209
|
+
projectDir: agyProjectDir(payload),
|
|
210
|
+
raw,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
formatPreToolUseResponse(response) {
|
|
214
|
+
if (response.decision === "deny") {
|
|
215
|
+
return { decision: "deny", reason: response.reason ?? "Denied by context-mode" };
|
|
216
|
+
}
|
|
217
|
+
if (response.decision === "ask") {
|
|
218
|
+
// Fallback reason so a security-policy ask is never a bare prompt
|
|
219
|
+
// (mirrors hooks/core/formatters.mjs antigravity-cli.ask).
|
|
220
|
+
return {
|
|
221
|
+
decision: "ask",
|
|
222
|
+
reason: response.reason ?? "Action requires user confirmation",
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (response.decision === "context" && response.additionalContext) {
|
|
226
|
+
return {
|
|
227
|
+
decision: "deny",
|
|
228
|
+
reason: agyContextReason(response.additionalContext),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
formatPostToolUseResponse(_response) {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
checkPluginRegistration() {
|
|
237
|
+
// Accept the plugin profile (the canonical `agy plugin install` location,
|
|
238
|
+
// ~/.gemini/config/plugins/context-mode/mcp_config.json) OR the global
|
|
239
|
+
// mcp_config.json (the manual no-plugin fallback).
|
|
240
|
+
const { ok, where } = readMcpRegistered([
|
|
241
|
+
antigravityCliPluginMcpPath(),
|
|
242
|
+
this.getSettingsPath(),
|
|
243
|
+
]);
|
|
244
|
+
if (ok) {
|
|
245
|
+
return {
|
|
246
|
+
check: "MCP registration",
|
|
247
|
+
status: "pass",
|
|
248
|
+
message: `context-mode found in Antigravity CLI mcpServers (${where})`,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
check: "MCP registration",
|
|
253
|
+
status: "fail",
|
|
254
|
+
message: "context-mode not found in Antigravity CLI mcpServers",
|
|
255
|
+
fix: "agy plugin install https://github.com/mksglu/context-mode/tree/main/configs/antigravity-cli",
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
getInstalledVersion() {
|
|
259
|
+
// Plugin install: read the real version from the installed plugin manifest so
|
|
260
|
+
// the doctor compares a true semver against npm latest (PASS when current,
|
|
261
|
+
// a meaningful "outdated bundle" WARN otherwise) — not the literal "configured"
|
|
262
|
+
// which produced the bogus "vconfigured, latest vX" line.
|
|
263
|
+
try {
|
|
264
|
+
const manifest = parseJsonc(readFileSync(resolve(antigravityCliPluginDir(), "plugin.json"), "utf-8"));
|
|
265
|
+
if (manifest && typeof manifest.version === "string" && manifest.version) {
|
|
266
|
+
return manifest.version;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
/* not plugin-installed — fall through */
|
|
271
|
+
}
|
|
272
|
+
// Manual (global-profile) registration has no plugin manifest: report the
|
|
273
|
+
// version-less "standalone" MCP mode so doctor shows INFO, not a false WARN.
|
|
274
|
+
if (readMcpRegistered([this.getSettingsPath()]).ok)
|
|
275
|
+
return "standalone";
|
|
276
|
+
return "not installed";
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Write/merge the agy hooks into ~/.gemini/config/hooks.json (manual,
|
|
280
|
+
* non-plugin path). Idempotent — unrelated hook entries are preserved.
|
|
281
|
+
*/
|
|
282
|
+
configureAllHooks(_pluginRoot) {
|
|
283
|
+
const changes = [];
|
|
284
|
+
const hooksPath = antigravityCliHooksPath();
|
|
285
|
+
let config = {};
|
|
286
|
+
try {
|
|
287
|
+
config = parseJsonc(readFileSync(hooksPath, "utf-8")) ?? {};
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
/* fresh file */
|
|
291
|
+
}
|
|
292
|
+
const hooks = config.hooks ?? {};
|
|
293
|
+
const desiredPre = {
|
|
294
|
+
matcher: PRE_HOOK_MATCHER,
|
|
295
|
+
hooks: [{ type: "command", command: PRE_HOOK_COMMAND }],
|
|
296
|
+
};
|
|
297
|
+
const desiredPost = {
|
|
298
|
+
matcher: "",
|
|
299
|
+
hooks: [{ type: "command", command: POST_HOOK_COMMAND }],
|
|
300
|
+
};
|
|
301
|
+
const desiredStop = {
|
|
302
|
+
matcher: "",
|
|
303
|
+
hooks: [{ type: "command", command: STOP_HOOK_COMMAND }],
|
|
304
|
+
};
|
|
305
|
+
const changedPre = configureHookEntry(hooks, "PreToolUse", desiredPre, PRE_HOOK_COMMAND);
|
|
306
|
+
const changedPost = configureHookEntry(hooks, "PostToolUse", desiredPost, POST_HOOK_COMMAND);
|
|
307
|
+
const changedStop = configureHookEntry(hooks, "Stop", desiredStop, STOP_HOOK_COMMAND);
|
|
308
|
+
const changed = changedPre || changedPost || changedStop;
|
|
309
|
+
if (changed) {
|
|
310
|
+
config.hooks = hooks;
|
|
311
|
+
mkdirSync(dirname(hooksPath), { recursive: true });
|
|
312
|
+
writeFileSync(hooksPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
313
|
+
changes.push(`Configured Antigravity CLI PreToolUse/PostToolUse hooks and best-effort Stop hook in ${hooksPath}`);
|
|
314
|
+
}
|
|
315
|
+
return changes;
|
|
316
|
+
}
|
|
317
|
+
/** Report agy hook status across plugin and manual profiles. */
|
|
318
|
+
validateHooks(_pluginRoot) {
|
|
319
|
+
// Accept the plugin profile (the canonical `agy plugin install` location,
|
|
320
|
+
// ~/.gemini/config/plugins/context-mode/hooks.json) OR the global hooks.json
|
|
321
|
+
// (the manual `context-mode upgrade` fallback).
|
|
322
|
+
const { ok, where, preOk, postOk, stopOk } = readRegisteredHooks([
|
|
323
|
+
antigravityCliPluginHooksPath(),
|
|
324
|
+
antigravityCliHooksPath(),
|
|
325
|
+
]);
|
|
326
|
+
const missing = [
|
|
327
|
+
preOk ? null : "PreToolUse",
|
|
328
|
+
postOk ? null : "PostToolUse",
|
|
329
|
+
// Stop is best-effort; do not include it in the health gate.
|
|
330
|
+
].filter(Boolean).join(", ");
|
|
331
|
+
return [
|
|
332
|
+
{
|
|
333
|
+
check: "Antigravity CLI hooks",
|
|
334
|
+
status: ok ? "pass" : "warn",
|
|
335
|
+
message: ok
|
|
336
|
+
? `PreToolUse guard and PostToolUse capture configured in ${where}${stopOk ? "; best-effort Stop hook also configured" : ""}`
|
|
337
|
+
: `Antigravity CLI hooks incomplete (${missing || "none found"} missing) — MCP tools still work, but bounded routing enforcement and session capture are degraded. Run \`agy plugin install https://github.com/mksglu/context-mode/tree/main/configs/antigravity-cli\` or \`context-mode upgrade\` to repair hooks.`,
|
|
338
|
+
...(ok ? {} : { fix: "agy plugin install https://github.com/mksglu/context-mode/tree/main/configs/antigravity-cli" }),
|
|
339
|
+
},
|
|
340
|
+
];
|
|
341
|
+
}
|
|
342
|
+
}
|
|
@@ -22,6 +22,7 @@ export declare const HOOK_TYPES: {
|
|
|
22
22
|
readonly PRE_COMPACT: "PreCompact";
|
|
23
23
|
readonly SESSION_START: "SessionStart";
|
|
24
24
|
readonly USER_PROMPT_SUBMIT: "UserPromptSubmit";
|
|
25
|
+
readonly STOP: "Stop";
|
|
25
26
|
};
|
|
26
27
|
export type HookType = (typeof HOOK_TYPES)[keyof typeof HOOK_TYPES];
|
|
27
28
|
/**
|
|
@@ -26,6 +26,7 @@ export const HOOK_TYPES = {
|
|
|
26
26
|
PRE_COMPACT: "PreCompact",
|
|
27
27
|
SESSION_START: "SessionStart",
|
|
28
28
|
USER_PROMPT_SUBMIT: "UserPromptSubmit",
|
|
29
|
+
STOP: "Stop",
|
|
29
30
|
};
|
|
30
31
|
// ─────────────────────────────────────────────────────────
|
|
31
32
|
// PreToolUse matchers
|
|
@@ -102,6 +103,7 @@ export const HOOK_SCRIPTS = {
|
|
|
102
103
|
PreCompact: "precompact.mjs",
|
|
103
104
|
SessionStart: "sessionstart.mjs",
|
|
104
105
|
UserPromptSubmit: "userpromptsubmit.mjs",
|
|
106
|
+
Stop: "stop.mjs",
|
|
105
107
|
};
|
|
106
108
|
// ─────────────────────────────────────────────────────────
|
|
107
109
|
// Hook validation
|
|
@@ -116,6 +118,7 @@ export const OPTIONAL_HOOKS = [
|
|
|
116
118
|
HOOK_TYPES.POST_TOOL_USE,
|
|
117
119
|
HOOK_TYPES.PRE_COMPACT,
|
|
118
120
|
HOOK_TYPES.USER_PROMPT_SUBMIT,
|
|
121
|
+
HOOK_TYPES.STOP,
|
|
119
122
|
];
|
|
120
123
|
/**
|
|
121
124
|
* Check if a hook entry points to a context-mode hook script.
|
|
@@ -87,7 +87,7 @@ export class ClaudeCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
87
87
|
// available, else `process.execPath` (#369 PATH resolution on Git
|
|
88
88
|
// Bash, #738 bun cold-start win).
|
|
89
89
|
// Pre-D3 we hand-rolled `node "${pluginRoot}/hooks/X.mjs"` for all
|
|
90
|
-
//
|
|
90
|
+
// six events; bare `node` made claude-code the lone outlier and
|
|
91
91
|
// dropping the execPath swap re-opened the Windows class. Algo-D3.5
|
|
92
92
|
// (CI invariant in tests/adapters/claude-code.test.ts) locks this in
|
|
93
93
|
// for adapter #16.
|
|
@@ -142,6 +142,17 @@ export class ClaudeCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
142
142
|
],
|
|
143
143
|
},
|
|
144
144
|
],
|
|
145
|
+
Stop: [
|
|
146
|
+
{
|
|
147
|
+
matcher: "",
|
|
148
|
+
hooks: [
|
|
149
|
+
{
|
|
150
|
+
type: "command",
|
|
151
|
+
command: buildHookRuntimeCommand(`${pluginRoot}/hooks/stop.mjs`),
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
],
|
|
145
156
|
};
|
|
146
157
|
}
|
|
147
158
|
readSettings() {
|
|
@@ -396,10 +407,18 @@ export class ClaudeCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
396
407
|
changes.push(`Removed ${removed} stale ${hookType} hook(s)`);
|
|
397
408
|
}
|
|
398
409
|
}
|
|
399
|
-
// If plugin hooks.json already covers all required hooks
|
|
400
|
-
//
|
|
401
|
-
//
|
|
402
|
-
|
|
410
|
+
// If plugin hooks.json already covers all required hooks AND context-mode is
|
|
411
|
+
// actually installed as a Claude Code plugin (present in enabledPlugins), skip
|
|
412
|
+
// settings.json registration — hooks.json with ${CLAUDE_PLUGIN_ROOT} is the
|
|
413
|
+
// source of truth for plugin installs (Issue #198).
|
|
414
|
+
//
|
|
415
|
+
// Standalone / MacPorts installs are NOT in enabledPlugins. For those, the
|
|
416
|
+
// hooks/hooks.json shipped in the npm package is never consulted by Claude Code
|
|
417
|
+
// (it uses ${CLAUDE_PLUGIN_ROOT} which is only set in plugin mode). We must
|
|
418
|
+
// always write absolute-path hook commands to settings.json in that case.
|
|
419
|
+
const pluginRegistration = this.checkPluginRegistration();
|
|
420
|
+
const isPluginInstall = pluginRegistration.status === "pass";
|
|
421
|
+
const pluginHooks = isPluginInstall ? this.readPluginHooks(pluginRoot) : undefined;
|
|
403
422
|
if (pluginHooks) {
|
|
404
423
|
const allCovered = REQUIRED_HOOKS.every((ht) => this.checkHookType(undefined, pluginHooks, ht));
|
|
405
424
|
if (allCovered) {
|
|
@@ -10,8 +10,13 @@ export const CLIENT_NAME_TO_PLATFORM = {
|
|
|
10
10
|
"claude-code": "claude-code",
|
|
11
11
|
"gemini-cli-mcp-client": "gemini-cli",
|
|
12
12
|
"antigravity-client": "antigravity",
|
|
13
|
+
"antigravity-cli": "antigravity-cli",
|
|
14
|
+
"agy": "antigravity-cli",
|
|
13
15
|
"cursor-vscode": "cursor",
|
|
14
16
|
"Visual-Studio-Code": "vscode-copilot",
|
|
17
|
+
"copilot-cli": "copilot-cli",
|
|
18
|
+
"GitHub Copilot CLI": "copilot-cli",
|
|
19
|
+
"github-copilot-cli": "copilot-cli",
|
|
15
20
|
"JetBrains Client": "jetbrains-copilot",
|
|
16
21
|
"IntelliJ IDEA": "jetbrains-copilot",
|
|
17
22
|
"PyCharm": "jetbrains-copilot",
|
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
* MCP: full support via [mcp_servers] in $CODEX_HOME/config.toml.
|
|
14
14
|
*
|
|
15
15
|
* Known limitations:
|
|
16
|
-
* - PreToolUse: deny works
|
|
16
|
+
* - PreToolUse: deny works on all builds. permissionDecision:"allow" +
|
|
17
|
+
* updatedInput (command rewrite) and additionalContext are honored on
|
|
18
|
+
* codex-cli >= 0.141.0 (#845), detected at runtime by
|
|
19
|
+
* hooks/core/codex-caps.mjs; older builds fail closed (redirect → deny).
|
|
20
|
+
* `ask` remains unsupported.
|
|
17
21
|
* - PostToolUse: updatedMCPToolOutput parsed but logged as unsupported
|
|
18
22
|
* - PostToolUse does not fire on failing Bash calls (upstream bug)
|
|
19
23
|
*/
|
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
* MCP: full support via [mcp_servers] in $CODEX_HOME/config.toml.
|
|
14
14
|
*
|
|
15
15
|
* Known limitations:
|
|
16
|
-
* - PreToolUse: deny works
|
|
16
|
+
* - PreToolUse: deny works on all builds. permissionDecision:"allow" +
|
|
17
|
+
* updatedInput (command rewrite) and additionalContext are honored on
|
|
18
|
+
* codex-cli >= 0.141.0 (#845), detected at runtime by
|
|
19
|
+
* hooks/core/codex-caps.mjs; older builds fail closed (redirect → deny).
|
|
20
|
+
* `ask` remains unsupported.
|
|
17
21
|
* - PostToolUse: updatedMCPToolOutput parsed but logged as unsupported
|
|
18
22
|
* - PostToolUse does not fire on failing Bash calls (upstream bug)
|
|
19
23
|
*/
|
|
@@ -20,9 +20,14 @@ type CodexVersionRunner = (file: string, args: string[], options: {
|
|
|
20
20
|
stdio: ["ignore", "pipe", "ignore"];
|
|
21
21
|
timeout: number;
|
|
22
22
|
}) => string | Buffer;
|
|
23
|
+
interface CodexAdapterOptions {
|
|
24
|
+
codexPluginListRunner?: CodexVersionRunner;
|
|
25
|
+
}
|
|
23
26
|
export declare function probeCodexCliVersion(runCommand?: CodexVersionRunner): string | null;
|
|
27
|
+
export declare function parseCodexContextModePluginRoot(raw: string): string | null;
|
|
24
28
|
export declare class CodexAdapter extends BaseAdapter implements HookAdapter {
|
|
25
|
-
|
|
29
|
+
private readonly codexPluginListRunner;
|
|
30
|
+
constructor(options?: CodexAdapterOptions);
|
|
26
31
|
readonly name = "Codex CLI";
|
|
27
32
|
readonly paradigm: HookParadigm;
|
|
28
33
|
readonly capabilities: PlatformCapabilities;
|
|
@@ -65,6 +70,9 @@ export declare class CodexAdapter extends BaseAdapter implements HookAdapter {
|
|
|
65
70
|
private upsertManagedHookEntry;
|
|
66
71
|
private removeManagedHookEntries;
|
|
67
72
|
private hasCodexPluginHookManifest;
|
|
73
|
+
private getCodexPluginHookStatus;
|
|
74
|
+
private probeCodexContextModePluginRoot;
|
|
75
|
+
private samePath;
|
|
68
76
|
private pruneStaleUserHookTrustState;
|
|
69
77
|
private isExpectedHookEntry;
|
|
70
78
|
private isManagedContextModeEntry;
|
|
@@ -75,6 +75,14 @@ export function probeCodexCliVersion(runCommand = execFileSync) {
|
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
+
export function parseCodexContextModePluginRoot(raw) {
|
|
79
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
80
|
+
const match = line.match(/^\s*context-mode@context-mode\s+installed,\s+enabled\s+\S+\s+(.+?)\s*$/);
|
|
81
|
+
if (match?.[1])
|
|
82
|
+
return match[1].trim();
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
78
86
|
function getTomlSection(raw, sectionName) {
|
|
79
87
|
const lines = raw.split(/\r?\n/);
|
|
80
88
|
let inSection = false;
|
|
@@ -190,8 +198,10 @@ function parseTomlQuotedString(raw) {
|
|
|
190
198
|
// Adapter implementation
|
|
191
199
|
// ─────────────────────────────────────────────────────────
|
|
192
200
|
export class CodexAdapter extends BaseAdapter {
|
|
193
|
-
|
|
201
|
+
codexPluginListRunner;
|
|
202
|
+
constructor(options = {}) {
|
|
194
203
|
super([".codex"]);
|
|
204
|
+
this.codexPluginListRunner = options.codexPluginListRunner ?? execFileSync;
|
|
195
205
|
}
|
|
196
206
|
name = "Codex CLI";
|
|
197
207
|
paradigm = "json-stdio";
|
|
@@ -479,13 +489,35 @@ export class CodexAdapter extends BaseAdapter {
|
|
|
479
489
|
});
|
|
480
490
|
}
|
|
481
491
|
const expected = this.generateHookConfig("");
|
|
482
|
-
const
|
|
483
|
-
const
|
|
492
|
+
const pluginHookStatus = this.getCodexPluginHookStatus(pluginRoot, settingsRaw, settingsReadable);
|
|
493
|
+
const codexPluginEnabled = pluginHookStatus.enabled;
|
|
494
|
+
const codexPluginHooksAvailable = pluginHookStatus.hooksAvailable;
|
|
495
|
+
if (codexPluginEnabled && pluginHookStatus.runtimeRoot) {
|
|
496
|
+
results.push({
|
|
497
|
+
check: "Codex plugin root",
|
|
498
|
+
status: pluginHookStatus.rootMismatch ? "warn" : "pass",
|
|
499
|
+
message: pluginHookStatus.rootMismatch
|
|
500
|
+
? `context-mode doctor is running from ${pluginHookStatus.configuredRoot}, but Codex plugin manager reports ${pluginHookStatus.runtimeRoot}`
|
|
501
|
+
: `Codex plugin manager reports ${pluginHookStatus.runtimeRoot}`,
|
|
502
|
+
...(pluginHookStatus.rootMismatch
|
|
503
|
+
? { fix: "Restart Codex after upgrade; run context-mode upgrade to keep native user-hook fallback until the plugin root converges" }
|
|
504
|
+
: {}),
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
else if (codexPluginEnabled) {
|
|
508
|
+
results.push({
|
|
509
|
+
check: "Codex plugin root",
|
|
510
|
+
status: "warn",
|
|
511
|
+
message: "context-mode@context-mode is enabled, but `codex plugin list` did not report its runtime root",
|
|
512
|
+
fix: "Restart Codex or verify `codex plugin list` shows context-mode@context-mode installed and enabled",
|
|
513
|
+
});
|
|
514
|
+
}
|
|
484
515
|
if (codexPluginEnabled && !codexPluginHooksAvailable) {
|
|
516
|
+
const expectedRoot = pluginHookStatus.runtimeRoot ?? pluginRoot;
|
|
485
517
|
results.push({
|
|
486
518
|
check: "Codex plugin hooks",
|
|
487
519
|
status: "fail",
|
|
488
|
-
message: `context-mode Codex plugin is enabled, but ${join(
|
|
520
|
+
message: `context-mode Codex plugin is enabled, but ${join(expectedRoot, ".codex-plugin", "hooks.json")} is missing`,
|
|
489
521
|
fix: "Reinstall or upgrade the context-mode Codex plugin",
|
|
490
522
|
});
|
|
491
523
|
}
|
|
@@ -659,7 +691,8 @@ export class CodexAdapter extends BaseAdapter {
|
|
|
659
691
|
catch {
|
|
660
692
|
settingsRaw = "";
|
|
661
693
|
}
|
|
662
|
-
const
|
|
694
|
+
const pluginHookStatus = this.getCodexPluginHookStatus(pluginRoot, settingsRaw, settingsRaw.length > 0);
|
|
695
|
+
const codexPluginOwnsHooks = pluginHookStatus.ownsHooksForUpgrade;
|
|
663
696
|
let hookFile;
|
|
664
697
|
if (hookConfig.ok) {
|
|
665
698
|
hookFile = hookConfig.config;
|
|
@@ -839,6 +872,55 @@ export class CodexAdapter extends BaseAdapter {
|
|
|
839
872
|
hasCodexPluginHookManifest(pluginRoot) {
|
|
840
873
|
return existsSync(join(pluginRoot, ".codex-plugin", "hooks.json"));
|
|
841
874
|
}
|
|
875
|
+
getCodexPluginHookStatus(pluginRoot, settingsRaw, settingsReadable) {
|
|
876
|
+
const enabled = settingsReadable && hasCodexPluginEnabled(settingsRaw);
|
|
877
|
+
const configuredRoot = resolve(pluginRoot);
|
|
878
|
+
const configuredManifestAvailable = this.hasCodexPluginHookManifest(configuredRoot);
|
|
879
|
+
const runtimeRoot = enabled ? this.probeCodexContextModePluginRoot() : null;
|
|
880
|
+
const runtimeManifestAvailable = runtimeRoot
|
|
881
|
+
? this.hasCodexPluginHookManifest(runtimeRoot)
|
|
882
|
+
: false;
|
|
883
|
+
const rootMismatch = runtimeRoot
|
|
884
|
+
? !this.samePath(configuredRoot, runtimeRoot)
|
|
885
|
+
: false;
|
|
886
|
+
const hooksAvailable = enabled && (runtimeManifestAvailable
|
|
887
|
+
|| (!runtimeRoot && configuredManifestAvailable));
|
|
888
|
+
return {
|
|
889
|
+
enabled,
|
|
890
|
+
configuredRoot,
|
|
891
|
+
configuredManifestAvailable,
|
|
892
|
+
runtimeRoot,
|
|
893
|
+
runtimeManifestAvailable,
|
|
894
|
+
rootMismatch,
|
|
895
|
+
hooksAvailable,
|
|
896
|
+
ownsHooksForUpgrade: enabled
|
|
897
|
+
&& runtimeRoot !== null
|
|
898
|
+
&& runtimeManifestAvailable
|
|
899
|
+
&& !rootMismatch,
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
probeCodexContextModePluginRoot() {
|
|
903
|
+
try {
|
|
904
|
+
const output = process.platform === "win32"
|
|
905
|
+
? this.codexPluginListRunner("cmd.exe", ["/d", "/s", "/c", "codex plugin list"], {
|
|
906
|
+
encoding: "utf-8",
|
|
907
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
908
|
+
timeout: 5000,
|
|
909
|
+
})
|
|
910
|
+
: this.codexPluginListRunner("codex", ["plugin", "list"], {
|
|
911
|
+
encoding: "utf-8",
|
|
912
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
913
|
+
timeout: 5000,
|
|
914
|
+
});
|
|
915
|
+
return parseCodexContextModePluginRoot(String(output));
|
|
916
|
+
}
|
|
917
|
+
catch {
|
|
918
|
+
return null;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
samePath(left, right) {
|
|
922
|
+
return this.normalizeCommand(resolve(left)) === this.normalizeCommand(resolve(right));
|
|
923
|
+
}
|
|
842
924
|
pruneStaleUserHookTrustState(settingsRaw, hooks) {
|
|
843
925
|
const hooksPath = this.normalizeCommand(this.getHooksPath());
|
|
844
926
|
const eventNames = {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/copilot-cli/hooks — GitHub Copilot CLI hook definitions.
|
|
3
|
+
*
|
|
4
|
+
* GitHub Copilot CLI's native hook events are the camelCase names
|
|
5
|
+
* preToolUse / postToolUse / sessionStart / userPromptSubmitted / agentStop /
|
|
6
|
+
* preCompact. Per the copilot-cli changelog, PascalCase event names are ALSO
|
|
7
|
+
* accepted and fire — the CLI loads hook configs across VS Code, Claude Code,
|
|
8
|
+
* and the CLI by accepting PascalCase event names alongside camelCase
|
|
9
|
+
* (copilot-cli changelog.md:1065; see also :811 and :1081). We register the
|
|
10
|
+
* camelCase KEYS below because they are the CLI's native names, not because
|
|
11
|
+
* PascalCase would fail. The CLI dispatch token (the `.mjs` script base, e.g.
|
|
12
|
+
* `pretooluse`) is independent and stays lowercase via buildHookCommand, so
|
|
13
|
+
* changing the event casing does not change the dispatcher.
|
|
14
|
+
*/
|
|
15
|
+
export declare const HOOK_TYPES: {
|
|
16
|
+
readonly PRE_TOOL_USE: "preToolUse";
|
|
17
|
+
readonly POST_TOOL_USE: "postToolUse";
|
|
18
|
+
readonly PRE_COMPACT: "preCompact";
|
|
19
|
+
readonly SESSION_START: "sessionStart";
|
|
20
|
+
readonly USER_PROMPT_SUBMIT: "userPromptSubmitted";
|
|
21
|
+
readonly STOP: "agentStop";
|
|
22
|
+
};
|
|
23
|
+
export type HookType = (typeof HOOK_TYPES)[keyof typeof HOOK_TYPES];
|
|
24
|
+
export declare const HOOK_SCRIPTS: Record<string, string>;
|
|
25
|
+
export declare const REQUIRED_HOOKS: HookType[];
|
|
26
|
+
export declare const OPTIONAL_HOOKS: HookType[];
|
|
27
|
+
export declare function isContextModeHook(entry: {
|
|
28
|
+
command?: string;
|
|
29
|
+
hooks?: Array<{
|
|
30
|
+
command?: string;
|
|
31
|
+
}>;
|
|
32
|
+
}, hookType: HookType): boolean;
|
|
33
|
+
export declare function buildHookCommand(hookType: HookType, _pluginRoot?: string): string;
|