@codemieai/code 0.10.1 → 0.12.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/dist/agents/core/AgentCLI.d.ts.map +1 -1
- package/dist/agents/core/AgentCLI.js +11 -3
- package/dist/agents/core/AgentCLI.js.map +1 -1
- package/dist/agents/core/BaseAgentAdapter.d.ts +3 -1
- package/dist/agents/core/BaseAgentAdapter.d.ts.map +1 -1
- package/dist/agents/core/BaseAgentAdapter.js +34 -2
- package/dist/agents/core/BaseAgentAdapter.js.map +1 -1
- package/dist/agents/core/config-redaction.d.ts +7 -0
- package/dist/agents/core/config-redaction.d.ts.map +1 -0
- package/dist/agents/core/config-redaction.js +20 -0
- package/dist/agents/core/config-redaction.js.map +1 -0
- package/dist/agents/core/print-config.d.ts +8 -0
- package/dist/agents/core/print-config.d.ts.map +1 -0
- package/dist/agents/core/print-config.js +17 -0
- package/dist/agents/core/print-config.js.map +1 -0
- package/dist/agents/core/session/BaseSessionAdapter.d.ts +24 -1
- package/dist/agents/core/session/BaseSessionAdapter.d.ts.map +1 -1
- package/dist/agents/core/types.d.ts +44 -4
- package/dist/agents/core/types.d.ts.map +1 -1
- package/dist/agents/plugins/claude/claude.plugin.d.ts.map +1 -1
- package/dist/agents/plugins/claude/claude.plugin.js +73 -0
- package/dist/agents/plugins/claude/claude.plugin.js.map +1 -1
- package/dist/agents/plugins/claude/session/processors/claude.conversations-processor.d.ts.map +1 -1
- package/dist/agents/plugins/claude/session/processors/claude.conversations-processor.js +115 -32
- package/dist/agents/plugins/claude/session/processors/claude.conversations-processor.js.map +1 -1
- package/dist/agents/plugins/claude/settings-conflict.d.ts +8 -0
- package/dist/agents/plugins/claude/settings-conflict.d.ts.map +1 -0
- package/dist/agents/plugins/claude/settings-conflict.js +35 -0
- package/dist/agents/plugins/claude/settings-conflict.js.map +1 -0
- package/dist/agents/plugins/codemie-code-hooks/shell-hooks-source.d.ts +14 -2
- package/dist/agents/plugins/codemie-code-hooks/shell-hooks-source.d.ts.map +1 -1
- package/dist/agents/plugins/codemie-code-hooks/shell-hooks-source.js +218 -263
- package/dist/agents/plugins/codemie-code-hooks/shell-hooks-source.js.map +1 -1
- package/dist/agents/plugins/copilot-cli/copilot-cli-event-types.d.ts +146 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli-event-types.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli-event-types.js +12 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli-event-types.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.constants.d.ts +11 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.constants.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.constants.js +11 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.constants.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.paths.d.ts +11 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.paths.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.paths.js +21 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.paths.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.plugin.d.ts +30 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.plugin.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.plugin.js +64 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.plugin.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.session.d.ts +43 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.session.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.session.js +331 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.session.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.storage-utils.d.ts +10 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.storage-utils.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.storage-utils.js +41 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.storage-utils.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.usage.d.ts +37 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.usage.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.usage.js +96 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.usage.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.workspace.d.ts +17 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.workspace.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.workspace.js +58 -0
- package/dist/agents/plugins/copilot-cli/copilot-cli.workspace.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/index.d.ts +2 -0
- package/dist/agents/plugins/copilot-cli/index.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/index.js +2 -0
- package/dist/agents/plugins/copilot-cli/index.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.conversations-processor.d.ts +15 -0
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.conversations-processor.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.conversations-processor.js +20 -0
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.conversations-processor.js.map +1 -0
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.metrics-processor.d.ts +16 -0
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.metrics-processor.d.ts.map +1 -0
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.metrics-processor.js +22 -0
- package/dist/agents/plugins/copilot-cli/session/processors/copilot-cli.metrics-processor.js.map +1 -0
- package/dist/agents/plugins/opencode/opencode-message-types.d.ts +13 -0
- package/dist/agents/plugins/opencode/opencode-message-types.d.ts.map +1 -1
- package/dist/agents/plugins/opencode/opencode-message-types.js.map +1 -1
- package/dist/agents/plugins/opencode/opencode.incremental-sync.d.ts +38 -0
- package/dist/agents/plugins/opencode/opencode.incremental-sync.d.ts.map +1 -0
- package/dist/agents/plugins/opencode/opencode.incremental-sync.js +153 -0
- package/dist/agents/plugins/opencode/opencode.incremental-sync.js.map +1 -0
- package/dist/agents/plugins/opencode/opencode.paths.d.ts +18 -2
- package/dist/agents/plugins/opencode/opencode.paths.d.ts.map +1 -1
- package/dist/agents/plugins/opencode/opencode.paths.js +39 -6
- package/dist/agents/plugins/opencode/opencode.paths.js.map +1 -1
- package/dist/agents/plugins/opencode/opencode.plugin.d.ts.map +1 -1
- package/dist/agents/plugins/opencode/opencode.plugin.js +241 -75
- package/dist/agents/plugins/opencode/opencode.plugin.js.map +1 -1
- package/dist/agents/plugins/opencode/opencode.session.d.ts +18 -1
- package/dist/agents/plugins/opencode/opencode.session.d.ts.map +1 -1
- package/dist/agents/plugins/opencode/opencode.session.js +58 -17
- package/dist/agents/plugins/opencode/opencode.session.js.map +1 -1
- package/dist/agents/plugins/opencode/opencode.sqlite-reader.d.ts +8 -2
- package/dist/agents/plugins/opencode/opencode.sqlite-reader.d.ts.map +1 -1
- package/dist/agents/plugins/opencode/opencode.sqlite-reader.js +67 -42
- package/dist/agents/plugins/opencode/opencode.sqlite-reader.js.map +1 -1
- package/dist/agents/plugins/opencode/opencode.storage-utils.d.ts +19 -0
- package/dist/agents/plugins/opencode/opencode.storage-utils.d.ts.map +1 -1
- package/dist/agents/plugins/opencode/opencode.storage-utils.js +51 -0
- package/dist/agents/plugins/opencode/opencode.storage-utils.js.map +1 -1
- package/dist/agents/plugins/opencode/session/processors/opencode.conversations-processor.d.ts +13 -19
- package/dist/agents/plugins/opencode/session/processors/opencode.conversations-processor.d.ts.map +1 -1
- package/dist/agents/plugins/opencode/session/processors/opencode.conversations-processor.js +339 -82
- package/dist/agents/plugins/opencode/session/processors/opencode.conversations-processor.js.map +1 -1
- package/dist/agents/plugins/opencode/session/processors/opencode.metrics-processor.d.ts +37 -33
- package/dist/agents/plugins/opencode/session/processors/opencode.metrics-processor.d.ts.map +1 -1
- package/dist/agents/plugins/opencode/session/processors/opencode.metrics-processor.js +204 -246
- package/dist/agents/plugins/opencode/session/processors/opencode.metrics-processor.js.map +1 -1
- package/dist/agents/registry.d.ts +20 -0
- package/dist/agents/registry.d.ts.map +1 -1
- package/dist/agents/registry.js +29 -2
- package/dist/agents/registry.js.map +1 -1
- package/dist/cli/commands/analytics/agent-labels.d.ts +3 -0
- package/dist/cli/commands/analytics/agent-labels.d.ts.map +1 -0
- package/dist/cli/commands/analytics/agent-labels.js +19 -0
- package/dist/cli/commands/analytics/agent-labels.js.map +1 -0
- package/dist/cli/commands/analytics/cost/cost-enricher.d.ts.map +1 -1
- package/dist/cli/commands/analytics/cost/cost-enricher.js +12 -0
- package/dist/cli/commands/analytics/cost/cost-enricher.js.map +1 -1
- package/dist/cli/commands/analytics/cost/types.d.ts +10 -0
- package/dist/cli/commands/analytics/cost/types.d.ts.map +1 -1
- package/dist/cli/commands/analytics/cost/usage-readers.d.ts.map +1 -1
- package/dist/cli/commands/analytics/cost/usage-readers.js +54 -0
- package/dist/cli/commands/analytics/cost/usage-readers.js.map +1 -1
- package/dist/cli/commands/analytics/formatter.d.ts.map +1 -1
- package/dist/cli/commands/analytics/formatter.js +10 -2
- package/dist/cli/commands/analytics/formatter.js.map +1 -1
- package/dist/cli/commands/analytics/index.d.ts.map +1 -1
- package/dist/cli/commands/analytics/index.js +21 -0
- package/dist/cli/commands/analytics/index.js.map +1 -1
- package/dist/cli/commands/analytics/native-loader.d.ts.map +1 -1
- package/dist/cli/commands/analytics/native-loader.js +28 -3
- package/dist/cli/commands/analytics/native-loader.js.map +1 -1
- package/dist/cli/commands/analytics/report/client/app.js +32 -14
- package/dist/cli/commands/analytics/report/payload-builder.d.ts.map +1 -1
- package/dist/cli/commands/analytics/report/payload-builder.js +7 -0
- package/dist/cli/commands/analytics/report/payload-builder.js.map +1 -1
- package/dist/cli/commands/analytics/report/types.d.ts +10 -0
- package/dist/cli/commands/analytics/report/types.d.ts.map +1 -1
- package/dist/cli/commands/install.js +2 -2
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/list.js +1 -1
- package/dist/cli/commands/list.js.map +1 -1
- package/dist/cli/commands/proxy/connectors/desktop.d.ts.map +1 -1
- package/dist/cli/commands/proxy/connectors/desktop.js +6 -0
- package/dist/cli/commands/proxy/connectors/desktop.js.map +1 -1
- package/dist/cli/commands/proxy/connectors/vscode-models.d.ts +1 -0
- package/dist/cli/commands/proxy/connectors/vscode-models.d.ts.map +1 -1
- package/dist/cli/commands/proxy/connectors/vscode-models.js +36 -12
- package/dist/cli/commands/proxy/connectors/vscode-models.js.map +1 -1
- package/dist/cli/commands/proxy/connectors/vscode.d.ts.map +1 -1
- package/dist/cli/commands/proxy/connectors/vscode.js +3 -0
- package/dist/cli/commands/proxy/connectors/vscode.js.map +1 -1
- package/dist/cli/commands/proxy/health-check.d.ts.map +1 -1
- package/dist/cli/commands/proxy/health-check.js +9 -0
- package/dist/cli/commands/proxy/health-check.js.map +1 -1
- package/dist/cli/commands/setup.d.ts +33 -0
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +151 -35
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/uninstall.js +1 -1
- package/dist/cli/commands/uninstall.js.map +1 -1
- package/dist/cli/commands/update.js +2 -2
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/first-time.js +3 -3
- package/dist/cli/first-time.js.map +1 -1
- package/dist/providers/core/types.d.ts +28 -1
- package/dist/providers/core/types.d.ts.map +1 -1
- package/dist/providers/core/types.js.map +1 -1
- package/dist/providers/plugins/bedrock/bedrock.template.d.ts.map +1 -1
- package/dist/providers/plugins/bedrock/bedrock.template.js +21 -2
- package/dist/providers/plugins/bedrock/bedrock.template.js.map +1 -1
- package/dist/providers/plugins/litellm/litellm.setup-steps.d.ts.map +1 -1
- package/dist/providers/plugins/litellm/litellm.setup-steps.js +21 -5
- package/dist/providers/plugins/litellm/litellm.setup-steps.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/codex-encrypted-content-sanitizer.plugin.d.ts +7 -6
- package/dist/providers/plugins/sso/proxy/plugins/codex-encrypted-content-sanitizer.plugin.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/codex-encrypted-content-sanitizer.plugin.js +13 -7
- package/dist/providers/plugins/sso/proxy/plugins/codex-encrypted-content-sanitizer.plugin.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.js +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.js.map +1 -1
- package/dist/providers/plugins/sso/session/processors/conversations/syncProcessor.js +3 -0
- package/dist/providers/plugins/sso/session/processors/conversations/syncProcessor.js.map +1 -1
- package/dist/providers/plugins/sso/sso.setup-steps.d.ts.map +1 -1
- package/dist/providers/plugins/sso/sso.setup-steps.js +39 -22
- package/dist/providers/plugins/sso/sso.setup-steps.js.map +1 -1
- package/dist/utils/config.d.ts +6 -2
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +30 -8
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/extensions-scan.d.ts.map +1 -1
- package/dist/utils/extensions-scan.js +59 -22
- package/dist/utils/extensions-scan.js.map +1 -1
- package/dist/utils/mcp-config.d.ts.map +1 -1
- package/dist/utils/mcp-config.js +11 -6
- package/dist/utils/mcp-config.js.map +1 -1
- package/package.json +1 -2
- package/scripts/release.sh +54 -0
|
@@ -8,8 +8,20 @@
|
|
|
8
8
|
* (Anthropic/Claude Code format) and maps them to OpenCode plugin lifecycle hooks.
|
|
9
9
|
*
|
|
10
10
|
* Why a string constant: The plugin uses `import type { Plugin } from "@opencode-ai/plugin"`
|
|
11
|
-
* which doesn't exist in
|
|
11
|
+
* which doesn't exist in this package's dependencies. Embedding as a string avoids
|
|
12
12
|
* TypeScript compilation issues. Bun strips the type import at runtime.
|
|
13
|
+
*
|
|
14
|
+
* PLUGIN CONTRACT — see reasoning-sanitizer-source.ts for the reference shape:
|
|
15
|
+
* a Plugin is an async factory returning a FLAT map of dotted-key handlers, and
|
|
16
|
+
* each handler takes `(input, output)` and MUTATES `output`. Returned values are
|
|
17
|
+
* discarded. An earlier version of this file exported a plain object with nested
|
|
18
|
+
* `hooks: { tool: { execute: { before } } }` keys and returned values from every
|
|
19
|
+
* handler, so none of it ever ran.
|
|
20
|
+
*
|
|
21
|
+
* Session id: taken from the payload OpenCode passes in (`input.sessionID`, or
|
|
22
|
+
* `input.event.properties.sessionID` for bus events). `codemie hook` rejects an
|
|
23
|
+
* empty session_id with exit code 2, so a hook with no resolvable session id is
|
|
24
|
+
* skipped rather than invoked.
|
|
13
25
|
*/
|
|
14
26
|
export const SHELL_HOOKS_PLUGIN_SOURCE = `
|
|
15
27
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
@@ -35,7 +47,6 @@ type HookEventName =
|
|
|
35
47
|
| "PreToolUse"
|
|
36
48
|
| "PostToolUse"
|
|
37
49
|
| "UserPromptSubmit"
|
|
38
|
-
| "PermissionRequest"
|
|
39
50
|
| "PreCompact"
|
|
40
51
|
| "SessionStart"
|
|
41
52
|
| "SessionEnd"
|
|
@@ -80,7 +91,7 @@ function matchesPattern(pattern: string, toolName: string): boolean {
|
|
|
80
91
|
// ─── Config Loading ──────────────────────────────────────────────────────────
|
|
81
92
|
|
|
82
93
|
function loadHooksConfig(): HooksConfig {
|
|
83
|
-
// Priority 1: OPENCODE_HOOKS env var (set by
|
|
94
|
+
// Priority 1: OPENCODE_HOOKS env var (set by the CodeMie CLI)
|
|
84
95
|
const envHooks = process.env.OPENCODE_HOOKS;
|
|
85
96
|
if (envHooks) {
|
|
86
97
|
try {
|
|
@@ -140,7 +151,26 @@ function getMatchingCommands(
|
|
|
140
151
|
return result;
|
|
141
152
|
}
|
|
142
153
|
|
|
143
|
-
// ───
|
|
154
|
+
// ─── Session / Payload Helpers ───────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
// OPENCODE_SESSION_ID is a last-resort fallback only: nothing sets it today, and
|
|
157
|
+
// relying on it is what made every previous hook invocation ship an empty
|
|
158
|
+
// session_id and get rejected with exit code 2.
|
|
159
|
+
function resolveSessionId(input: any): string {
|
|
160
|
+
return (
|
|
161
|
+
input?.sessionID ||
|
|
162
|
+
input?.event?.properties?.sessionID ||
|
|
163
|
+
input?.event?.properties?.info?.id ||
|
|
164
|
+
process.env.OPENCODE_SESSION_ID ||
|
|
165
|
+
""
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// codemie hook needs a transcript path to re-parse the session; for OpenCode
|
|
170
|
+
// that is the SQLite database, exported by the CLI before spawning.
|
|
171
|
+
function transcriptPath(): string {
|
|
172
|
+
return process.env.CODEMIE_OPENCODE_TRANSCRIPT || "";
|
|
173
|
+
}
|
|
144
174
|
|
|
145
175
|
function buildEnvVars(sessionId: string, event: string): Record<string, string> {
|
|
146
176
|
const projectDir = process.env.OPENCODE_PROJECT_DIR || process.cwd();
|
|
@@ -152,6 +182,18 @@ function buildEnvVars(sessionId: string, event: string): Record<string, string>
|
|
|
152
182
|
};
|
|
153
183
|
}
|
|
154
184
|
|
|
185
|
+
function basePayload(event: HookEventName, sessionId: string): HookStdinPayload {
|
|
186
|
+
return {
|
|
187
|
+
hook_event_name: event,
|
|
188
|
+
session_id: sessionId,
|
|
189
|
+
cwd: process.cwd(),
|
|
190
|
+
permission_mode: "default",
|
|
191
|
+
transcript_path: transcriptPath(),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ─── Shell Execution ─────────────────────────────────────────────────────────
|
|
196
|
+
|
|
155
197
|
interface ExecResult {
|
|
156
198
|
stdout: string;
|
|
157
199
|
stderr: string;
|
|
@@ -193,7 +235,13 @@ function execCommandAsync(
|
|
|
193
235
|
stdio: ["pipe", "ignore", "ignore"],
|
|
194
236
|
detached: true,
|
|
195
237
|
});
|
|
238
|
+
// A ChildProcess or stdin 'error' with no listener is an UNHANDLED error
|
|
239
|
+
// event, which throws inside the host OpenCode process. Stop is configured
|
|
240
|
+
// async, so this is the production path: a failed spawn (ENOENT for sh,
|
|
241
|
+
// EAGAIN) or a broken pipe must degrade telemetry, never kill the session.
|
|
242
|
+
child.on("error", () => {});
|
|
196
243
|
if (child.stdin) {
|
|
244
|
+
child.stdin.on("error", () => {});
|
|
197
245
|
child.stdin.write(stdin);
|
|
198
246
|
child.stdin.end();
|
|
199
247
|
}
|
|
@@ -206,7 +254,6 @@ interface ParsedResponse {
|
|
|
206
254
|
blocked: boolean;
|
|
207
255
|
reason?: string;
|
|
208
256
|
updatedInput?: Record<string, unknown>;
|
|
209
|
-
permissionDecision?: "allow" | "deny" | "ask";
|
|
210
257
|
additionalContext?: string;
|
|
211
258
|
}
|
|
212
259
|
|
|
@@ -225,39 +272,23 @@ function parseResponse(
|
|
|
225
272
|
return { blocked: false };
|
|
226
273
|
}
|
|
227
274
|
|
|
228
|
-
// Parse stdout JSON
|
|
229
275
|
const trimmed = result.stdout.trim();
|
|
230
276
|
if (!trimmed) return { blocked: false };
|
|
231
277
|
|
|
232
278
|
try {
|
|
233
279
|
const json = JSON.parse(trimmed);
|
|
234
280
|
|
|
235
|
-
// PreToolUse:
|
|
281
|
+
// PreToolUse: hookSpecificOutput.updatedInput, or a bare object → merged args
|
|
236
282
|
if (event === "PreToolUse") {
|
|
237
283
|
const updated = json.hookSpecificOutput?.updatedInput || json.updatedInput;
|
|
238
284
|
if (updated && typeof updated === "object") {
|
|
239
285
|
return { blocked: false, updatedInput: updated };
|
|
240
286
|
}
|
|
241
|
-
// If it's a plain object without known keys, treat as updatedInput
|
|
242
287
|
if (typeof json === "object" && !json.hookSpecificOutput && !json.decision) {
|
|
243
288
|
return { blocked: false, updatedInput: json };
|
|
244
289
|
}
|
|
245
290
|
}
|
|
246
291
|
|
|
247
|
-
// PermissionRequest: check for permissionDecision
|
|
248
|
-
if (event === "PermissionRequest") {
|
|
249
|
-
const decision =
|
|
250
|
-
json.hookSpecificOutput?.permissionDecision || json.permissionDecision;
|
|
251
|
-
if (decision && ["allow", "deny", "ask"].includes(decision)) {
|
|
252
|
-
return { blocked: false, permissionDecision: decision };
|
|
253
|
-
}
|
|
254
|
-
// Plain string
|
|
255
|
-
if (typeof json === "string" && ["allow", "deny", "ask"].includes(json)) {
|
|
256
|
-
return { blocked: false, permissionDecision: json };
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// PreCompact: additionalContext
|
|
261
292
|
if (event === "PreCompact") {
|
|
262
293
|
const context = json.additionalContext || json.context;
|
|
263
294
|
if (typeof context === "string") {
|
|
@@ -272,251 +303,175 @@ function parseResponse(
|
|
|
272
303
|
}
|
|
273
304
|
}
|
|
274
305
|
|
|
306
|
+
/** Run every configured command for an event. Returns the parsed responses. */
|
|
307
|
+
function runCommands(
|
|
308
|
+
event: HookEventName,
|
|
309
|
+
sessionId: string,
|
|
310
|
+
payload: HookStdinPayload,
|
|
311
|
+
commands: Array<{ command: string; timeout: number; isAsync: boolean }>,
|
|
312
|
+
swallowErrors: boolean,
|
|
313
|
+
): ParsedResponse[] {
|
|
314
|
+
const env = buildEnvVars(sessionId, event);
|
|
315
|
+
const stdin = JSON.stringify(payload);
|
|
316
|
+
const responses: ParsedResponse[] = [];
|
|
317
|
+
|
|
318
|
+
for (const cmd of commands) {
|
|
319
|
+
if (cmd.isAsync) {
|
|
320
|
+
execCommandAsync(cmd.command, stdin, env);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (swallowErrors) {
|
|
324
|
+
try {
|
|
325
|
+
responses.push(parseResponse(execCommand(cmd.command, stdin, env, cmd.timeout), event));
|
|
326
|
+
} catch {
|
|
327
|
+
// Fire-and-forget events must never break the session.
|
|
328
|
+
}
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
responses.push(parseResponse(execCommand(cmd.command, stdin, env, cmd.timeout), event));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return responses;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ─── Idle Deduplication ──────────────────────────────────────────────────────
|
|
338
|
+
|
|
339
|
+
// OpenCode publishes both the deprecated \`session.idle\` and the newer
|
|
340
|
+
// \`session.status\` {type:"idle"} back to back. Subscribing to only one risks
|
|
341
|
+
// silently losing active-time accounting if it is removed upstream, so both are
|
|
342
|
+
// handled and collapsed on the busy→idle transition.
|
|
343
|
+
const idleState = new Map<string, boolean>();
|
|
344
|
+
|
|
345
|
+
function shouldEmitIdle(sessionId: string, isIdle: boolean): boolean {
|
|
346
|
+
const wasIdle = idleState.get(sessionId) === true;
|
|
347
|
+
idleState.set(sessionId, isIdle);
|
|
348
|
+
return isIdle && !wasIdle;
|
|
349
|
+
}
|
|
350
|
+
|
|
275
351
|
// ─── Plugin Definition ───────────────────────────────────────────────────────
|
|
276
352
|
|
|
277
353
|
const config = loadHooksConfig();
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
chat: {
|
|
363
|
-
message: async (input) => {
|
|
364
|
-
const commands = getMatchingCommands(config, "UserPromptSubmit");
|
|
365
|
-
if (commands.length === 0) return input;
|
|
366
|
-
|
|
367
|
-
const sessionId = process.env.OPENCODE_SESSION_ID || "";
|
|
368
|
-
const env = buildEnvVars(sessionId, "UserPromptSubmit");
|
|
369
|
-
const payload: HookStdinPayload = {
|
|
370
|
-
hook_event_name: "UserPromptSubmit",
|
|
371
|
-
session_id: sessionId,
|
|
372
|
-
cwd: process.cwd(),
|
|
373
|
-
permission_mode: "default",
|
|
374
|
-
transcript_path: "",
|
|
375
|
-
prompt: Array.isArray(input.parts)
|
|
376
|
-
? input.parts
|
|
377
|
-
.filter((p: any) => p.type === "text")
|
|
378
|
-
.map((p: any) => p.text)
|
|
379
|
-
.join("\\n")
|
|
380
|
-
: String(input.parts),
|
|
381
|
-
};
|
|
382
|
-
const stdin = JSON.stringify(payload);
|
|
383
|
-
|
|
384
|
-
for (const cmd of commands) {
|
|
385
|
-
if (cmd.isAsync) {
|
|
386
|
-
execCommandAsync(cmd.command, stdin, env);
|
|
387
|
-
continue;
|
|
388
|
-
}
|
|
389
|
-
const result = execCommand(cmd.command, stdin, env, cmd.timeout);
|
|
390
|
-
const parsed = parseResponse(result, "UserPromptSubmit");
|
|
391
|
-
if (parsed.blocked) {
|
|
392
|
-
// Clear message parts to block submission
|
|
393
|
-
return { ...input, parts: [] };
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
return input;
|
|
397
|
-
},
|
|
398
|
-
},
|
|
399
|
-
|
|
400
|
-
// PermissionRequest → permission.ask (blocking)
|
|
401
|
-
permission: {
|
|
402
|
-
ask: async (input) => {
|
|
403
|
-
const commands = getMatchingCommands(config, "PermissionRequest", input.tool);
|
|
404
|
-
if (commands.length === 0) return input;
|
|
405
|
-
|
|
406
|
-
const sessionId = process.env.OPENCODE_SESSION_ID || "";
|
|
407
|
-
const env = buildEnvVars(sessionId, "PermissionRequest");
|
|
408
|
-
const payload: HookStdinPayload = {
|
|
409
|
-
hook_event_name: "PermissionRequest",
|
|
410
|
-
session_id: sessionId,
|
|
411
|
-
cwd: process.cwd(),
|
|
412
|
-
permission_mode: "default",
|
|
413
|
-
transcript_path: "",
|
|
414
|
-
tool_name: input.tool,
|
|
415
|
-
tool_input: input.args as Record<string, unknown>,
|
|
416
|
-
};
|
|
417
|
-
const stdin = JSON.stringify(payload);
|
|
418
|
-
|
|
419
|
-
for (const cmd of commands) {
|
|
420
|
-
if (cmd.isAsync) {
|
|
421
|
-
execCommandAsync(cmd.command, stdin, env);
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
const result = execCommand(cmd.command, stdin, env, cmd.timeout);
|
|
425
|
-
const parsed = parseResponse(result, "PermissionRequest");
|
|
426
|
-
if (parsed.permissionDecision === "allow") {
|
|
427
|
-
return { ...input, allowed: true };
|
|
428
|
-
}
|
|
429
|
-
if (parsed.permissionDecision === "deny") {
|
|
430
|
-
return { ...input, allowed: false };
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
return input;
|
|
434
|
-
},
|
|
435
|
-
},
|
|
436
|
-
|
|
437
|
-
// PreCompact → experimental.session.compacting (non-blocking)
|
|
438
|
-
experimental: {
|
|
439
|
-
session: {
|
|
440
|
-
compacting: async (input) => {
|
|
441
|
-
const commands = getMatchingCommands(config, "PreCompact");
|
|
442
|
-
if (commands.length === 0) return input;
|
|
443
|
-
|
|
444
|
-
const sessionId = process.env.OPENCODE_SESSION_ID || "";
|
|
445
|
-
const env = buildEnvVars(sessionId, "PreCompact");
|
|
446
|
-
const payload: HookStdinPayload = {
|
|
447
|
-
hook_event_name: "PreCompact",
|
|
448
|
-
session_id: sessionId,
|
|
449
|
-
cwd: process.cwd(),
|
|
450
|
-
permission_mode: "default",
|
|
451
|
-
transcript_path: "",
|
|
452
|
-
};
|
|
453
|
-
const stdin = JSON.stringify(payload);
|
|
454
|
-
|
|
455
|
-
for (const cmd of commands) {
|
|
456
|
-
if (cmd.isAsync) {
|
|
457
|
-
execCommandAsync(cmd.command, stdin, env);
|
|
458
|
-
continue;
|
|
459
|
-
}
|
|
460
|
-
try {
|
|
461
|
-
const result = execCommand(cmd.command, stdin, env, cmd.timeout);
|
|
462
|
-
const parsed = parseResponse(result, "PreCompact");
|
|
463
|
-
if (parsed.additionalContext) {
|
|
464
|
-
return {
|
|
465
|
-
...input,
|
|
466
|
-
context: ((input as any).context || "") + "\\n" + parsed.additionalContext,
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
} catch {
|
|
470
|
-
// Non-blocking
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
return input;
|
|
474
|
-
},
|
|
475
|
-
},
|
|
476
|
-
},
|
|
477
|
-
|
|
478
|
-
// SessionStart/SessionEnd/Stop/Notification → event (non-blocking)
|
|
479
|
-
event: async (input) => {
|
|
480
|
-
let hookEvent: HookEventName | undefined;
|
|
481
|
-
const eventType = (input as any).type || (input as any).event;
|
|
482
|
-
if (eventType === "session.created") hookEvent = "SessionStart";
|
|
483
|
-
else if (eventType === "session.deleted") hookEvent = "SessionEnd";
|
|
484
|
-
else if (eventType === "session.idle") hookEvent = "Stop";
|
|
485
|
-
else if (eventType === "session.error") hookEvent = "Notification";
|
|
486
|
-
|
|
487
|
-
if (!hookEvent) return;
|
|
488
|
-
|
|
489
|
-
const commands = getMatchingCommands(config, hookEvent);
|
|
490
|
-
if (commands.length === 0) return;
|
|
491
|
-
|
|
492
|
-
const sessionId = process.env.OPENCODE_SESSION_ID || "";
|
|
493
|
-
const env = buildEnvVars(sessionId, hookEvent);
|
|
494
|
-
const payload: HookStdinPayload = {
|
|
495
|
-
hook_event_name: hookEvent,
|
|
496
|
-
session_id: sessionId,
|
|
497
|
-
cwd: process.cwd(),
|
|
498
|
-
permission_mode: "default",
|
|
499
|
-
transcript_path: "",
|
|
500
|
-
};
|
|
501
|
-
const stdin = JSON.stringify(payload);
|
|
502
|
-
|
|
503
|
-
for (const cmd of commands) {
|
|
504
|
-
if (cmd.isAsync) {
|
|
505
|
-
execCommandAsync(cmd.command, stdin, env);
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
try {
|
|
509
|
-
execCommand(cmd.command, stdin, env, cmd.timeout);
|
|
510
|
-
} catch {
|
|
511
|
-
// Event hooks are non-blocking
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
},
|
|
515
|
-
},
|
|
354
|
+
|
|
355
|
+
const ShellHooksPlugin: Plugin = async (_input) => ({
|
|
356
|
+
// PreToolUse → tool.execute.before (blocking; mutate output.args)
|
|
357
|
+
"tool.execute.before": async (input: any, output: any) => {
|
|
358
|
+
const commands = getMatchingCommands(config, "PreToolUse", input?.tool);
|
|
359
|
+
if (commands.length === 0) return;
|
|
360
|
+
|
|
361
|
+
const sessionId = resolveSessionId(input);
|
|
362
|
+
if (!sessionId) return;
|
|
363
|
+
|
|
364
|
+
const payload = basePayload("PreToolUse", sessionId);
|
|
365
|
+
payload.tool_name = input?.tool;
|
|
366
|
+
payload.tool_input = output?.args as Record<string, unknown>;
|
|
367
|
+
|
|
368
|
+
for (const parsed of runCommands("PreToolUse", sessionId, payload, commands, false)) {
|
|
369
|
+
if (parsed.blocked) {
|
|
370
|
+
throw new Error(parsed.reason || "Hook blocked tool execution");
|
|
371
|
+
}
|
|
372
|
+
if (parsed.updatedInput && output) {
|
|
373
|
+
output.args = { ...(output.args || {}), ...parsed.updatedInput };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
// PostToolUse → tool.execute.after (fire-and-forget)
|
|
379
|
+
"tool.execute.after": async (input: any, output: any) => {
|
|
380
|
+
const commands = getMatchingCommands(config, "PostToolUse", input?.tool);
|
|
381
|
+
if (commands.length === 0) return;
|
|
382
|
+
|
|
383
|
+
const sessionId = resolveSessionId(input);
|
|
384
|
+
if (!sessionId) return;
|
|
385
|
+
|
|
386
|
+
const payload = basePayload("PostToolUse", sessionId);
|
|
387
|
+
payload.tool_name = input?.tool;
|
|
388
|
+
payload.tool_output =
|
|
389
|
+
typeof output?.output === "string" ? output.output : JSON.stringify(output?.output ?? null);
|
|
390
|
+
|
|
391
|
+
runCommands("PostToolUse", sessionId, payload, commands, true);
|
|
392
|
+
},
|
|
393
|
+
|
|
394
|
+
// UserPromptSubmit → chat.message. Marks the start of an active period;
|
|
395
|
+
// codemie hook forwards it to SessionStore.startActivityTracking.
|
|
396
|
+
"chat.message": async (input: any, output: any) => {
|
|
397
|
+
const sessionId = resolveSessionId(input);
|
|
398
|
+
if (!sessionId) return;
|
|
399
|
+
|
|
400
|
+
// A new prompt opens a new active period, so the session is busy again.
|
|
401
|
+
// Clearing the flag here is what lets the NEXT idle emit a Stop: on a build
|
|
402
|
+
// that publishes session.idle without session.status, nothing else ever
|
|
403
|
+
// resets it, so every turn after the first would be suppressed and
|
|
404
|
+
// active_duration_ms would count only turn one.
|
|
405
|
+
idleState.set(sessionId, false);
|
|
406
|
+
|
|
407
|
+
const commands = getMatchingCommands(config, "UserPromptSubmit");
|
|
408
|
+
if (commands.length === 0) return;
|
|
409
|
+
|
|
410
|
+
// The message parts live on output, not input.
|
|
411
|
+
const parts = output?.parts;
|
|
412
|
+
const payload = basePayload("UserPromptSubmit", sessionId);
|
|
413
|
+
payload.prompt = Array.isArray(parts)
|
|
414
|
+
? parts
|
|
415
|
+
.filter((p: any) => p?.type === "text")
|
|
416
|
+
.map((p: any) => p.text)
|
|
417
|
+
.join("\\n")
|
|
418
|
+
: "";
|
|
419
|
+
|
|
420
|
+
runCommands("UserPromptSubmit", sessionId, payload, commands, true);
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
// PreCompact → experimental.session.compacting (non-blocking)
|
|
424
|
+
"experimental.session.compacting": async (input: any, output: any) => {
|
|
425
|
+
const commands = getMatchingCommands(config, "PreCompact");
|
|
426
|
+
if (commands.length === 0) return;
|
|
427
|
+
|
|
428
|
+
const sessionId = resolveSessionId(input);
|
|
429
|
+
if (!sessionId) return;
|
|
430
|
+
|
|
431
|
+
const responses = runCommands(
|
|
432
|
+
"PreCompact", sessionId, basePayload("PreCompact", sessionId), commands, true
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
for (const parsed of responses) {
|
|
436
|
+
if (parsed.additionalContext && output) {
|
|
437
|
+
output.context = (output.context || "") + "\\n" + parsed.additionalContext;
|
|
516
438
|
}
|
|
517
|
-
|
|
518
|
-
}
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
// Stop / Notification → event bus (non-blocking).
|
|
443
|
+
// SessionStart and SessionEnd are deliberately NOT mapped here: session.created
|
|
444
|
+
// and session.deleted do not correspond to CLI process start/exit, and the
|
|
445
|
+
// CodeMie CLI raises both lifecycle events in-process instead.
|
|
446
|
+
"event": async (input: any) => {
|
|
447
|
+
const eventType = input?.event?.type;
|
|
448
|
+
if (typeof eventType !== "string") return;
|
|
449
|
+
|
|
450
|
+
const sessionId = resolveSessionId(input);
|
|
451
|
+
if (!sessionId) return;
|
|
452
|
+
|
|
453
|
+
let hookEvent: HookEventName | undefined;
|
|
454
|
+
|
|
455
|
+
if (eventType === "session.idle") {
|
|
456
|
+
if (!shouldEmitIdle(sessionId, true)) return;
|
|
457
|
+
hookEvent = "Stop";
|
|
458
|
+
} else if (eventType === "session.status") {
|
|
459
|
+
const isIdle = input?.event?.properties?.status?.type === "idle";
|
|
460
|
+
if (!shouldEmitIdle(sessionId, isIdle)) return;
|
|
461
|
+
hookEvent = "Stop";
|
|
462
|
+
} else if (eventType === "session.error") {
|
|
463
|
+
hookEvent = "Notification";
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (!hookEvent) return;
|
|
467
|
+
|
|
468
|
+
const commands = getMatchingCommands(config, hookEvent);
|
|
469
|
+
if (commands.length === 0) return;
|
|
470
|
+
|
|
471
|
+
runCommands(hookEvent, sessionId, basePayload(hookEvent, sessionId), commands, true);
|
|
472
|
+
},
|
|
473
|
+
});
|
|
519
474
|
|
|
520
|
-
export default
|
|
475
|
+
export default ShellHooksPlugin;
|
|
521
476
|
`;
|
|
522
477
|
//# sourceMappingURL=shell-hooks-source.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-hooks-source.js","sourceRoot":"","sources":["../../../../src/agents/plugins/codemie-code-hooks/shell-hooks-source.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"shell-hooks-source.js","sourceRoot":"","sources":["../../../../src/agents/plugins/codemie-code-hooks/shell-hooks-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkcxC,CAAC"}
|