@goodfoot/agent-hooks 1.0.11 → 1.0.14
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/CHANGELOG.md +12 -0
- package/README.md +42 -74
- package/dist/agents/antigravity/scaffold.js +1 -1
- package/dist/agents/codex/scaffold.js +1 -1
- package/dist/agents/opencode/cli-support.js +43 -30
- package/dist/agents/opencode/events.js +1 -23
- package/dist/agents/opencode/index.js +2 -2
- package/dist/agents/opencode/plugin.js +6 -25
- package/dist/agents/opencode/types.js +16 -36
- package/dist/cli.js +4 -4
- package/dist/core/transport.js +19 -8
- package/dist/scaffold.js +1 -1
- package/package.json +13 -8
- package/types/agents/opencode/cli-support.d.ts +10 -11
- package/types/agents/opencode/events.d.ts +2 -23
- package/types/agents/opencode/index.d.ts +2 -2
- package/types/agents/opencode/plugin.d.ts +4 -25
- package/types/agents/opencode/policy.d.ts +1 -1
- package/types/agents/opencode/types.d.ts +21 -27
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @goodfoot/agent-hooks npm package changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.14
|
|
4
|
+
|
|
5
|
+
Compiles each OpenCode source into an installable plugin directory containing `package.json` and `index.js`. OpenCode 2.1.2 rejects the previous bare `.mjs` configuration paths. Generated packages support discovery under `.opencode/plugins/` and explicit directory entries in `plugins`.
|
|
6
|
+
|
|
7
|
+
## 1.0.13
|
|
8
|
+
|
|
9
|
+
Targets OpenCode 2.1.2 with the `@opencode/plugin` SDK. Plugin definitions now use `id` and `setup`, hook types follow V2 domain registrations with one mutable event, and compilation rejects V1 entrypoints.
|
|
10
|
+
|
|
11
|
+
## 1.0.12
|
|
12
|
+
|
|
13
|
+
Waits for stdout and stderr writes to complete before exiting, preventing truncated hook responses and error diagnostics when output is piped. Asynchronous stdout write failures now follow the existing unexpected-error policy without retrying the payload.
|
|
14
|
+
|
|
3
15
|
## 1.0.11
|
|
4
16
|
|
|
5
17
|
Moves generated-file tracking out of `hooks.json` into a sibling `hooks.meta.json`, because Claude Code rejects a hooks manifest carrying any key besides `hooks`. The manifest is now a pure function of its inputs, so a rebuild that changes nothing writes identical bytes and leaves the host's hook-trust hash intact. A missing or unreadable sidecar is treated as "nothing tracked as generated", which preserves the entries already in the manifest rather than pruning them.
|
package/README.md
CHANGED
|
@@ -104,34 +104,16 @@ the root `plugin.json` required by that install command.
|
|
|
104
104
|
|
|
105
105
|
#### OpenCode
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
Clone this repository and register the built plugin directory in OpenCode V2's
|
|
108
|
+
`opencode.json`, using an absolute path to your checkout:
|
|
109
109
|
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"plugins": ["/absolute/path/to/marketplace/plugins-opencode/agent-hooks"]
|
|
113
|
+
}
|
|
113
114
|
```
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
separator as a path — absolute, `./`-prefixed, and plain relative forms all load on
|
|
117
|
-
OpenCode 1.18.23. A single segment with no separator (`agent-hooks`) is instead resolved as an
|
|
118
|
-
npm package name and silently fails to load, with nothing logged. The absolute form
|
|
119
|
-
above avoids the ambiguity.
|
|
120
|
-
|
|
121
|
-
That is the whole install — no `opencode.json` editing. `opencode plugin` writes only the
|
|
122
|
-
top-level `plugin` key, and OpenCode keeps `plugin` and `skills` as disjoint config keys,
|
|
123
|
-
so registering the module would not by itself surface its skills. The plugin closes that
|
|
124
|
-
gap from the inside: its `config` hook runs against the resolved config before use and
|
|
125
|
-
appends its own bundled `skills/` directory to `skills.paths`, resolved relative to
|
|
126
|
-
wherever the package was installed. Installing is enough.
|
|
127
|
-
|
|
128
|
-
Without flags this installs into the current project's `.opencode/opencode.json`. Add
|
|
129
|
-
`-g` to install into the global config instead.
|
|
130
|
-
|
|
131
|
-
Verify with `opencode debug skill`. Its output is large and embeds whole skill bodies:
|
|
132
|
-
redirect it to a file and search there. Piping it to `head` truncates the JSON mid-string
|
|
133
|
-
and reports a present skill as missing.
|
|
134
|
-
|
|
116
|
+
The plugin contributes its bundled authoring skills through `ctx.skill.transform`.
|
|
135
117
|
Installing this skill is separate from compiling hooks *for* OpenCode, covered in
|
|
136
118
|
[OpenCode](#opencode) below.
|
|
137
119
|
|
|
@@ -235,69 +217,55 @@ or `@goodfoot/agent-hooks/codex`) — do not mix events or output builders acros
|
|
|
235
217
|
|
|
236
218
|
## OpenCode
|
|
237
219
|
|
|
238
|
-
OpenCode
|
|
239
|
-
(
|
|
240
|
-
`@goodfoot/agent-hooks/opencode` does **not** route through `defineHook`/`Transport`/`drive`.
|
|
241
|
-
It ships its own primitives instead:
|
|
220
|
+
OpenCode 2.1.2 loads a long-lived plugin definition with a stable `id` and
|
|
221
|
+
`setup(ctx)`. Register hooks on their owning domains during setup:
|
|
242
222
|
|
|
243
223
|
```typescript
|
|
244
|
-
import { defineOpenCodePlugin, guardAdvisory
|
|
245
|
-
|
|
246
|
-
const sessions = createRootSessionRegistry();
|
|
224
|
+
import { defineOpenCodePlugin, guardAdvisory } from '@goodfoot/agent-hooks/opencode';
|
|
247
225
|
|
|
248
226
|
export default defineOpenCodePlugin({
|
|
249
227
|
id: 'my-plugin',
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (event.
|
|
253
|
-
|
|
254
|
-
} else if (event.type === 'session.updated') {
|
|
255
|
-
sessions.observeResumed(event.properties.info.id, event.properties.info.parentID);
|
|
256
|
-
}
|
|
257
|
-
}, 'continue'),
|
|
258
|
-
|
|
259
|
-
'tool.execute.before': async (input, output) => {
|
|
260
|
-
if (input.tool === 'bash' && sessions.isRoot(input.sessionID)) {
|
|
261
|
-
output.args = { ...output.args, timeout: 30_000 };
|
|
228
|
+
async setup(ctx) {
|
|
229
|
+
await ctx.tool.hook('execute.before', guardAdvisory('tool.execute.before', async (event) => {
|
|
230
|
+
if (event.tool === 'bash') {
|
|
231
|
+
event.input = { ...(event.input as Record<string, unknown>), timeout: 30_000 };
|
|
262
232
|
}
|
|
263
|
-
},
|
|
264
|
-
}
|
|
233
|
+
}, 'continue'));
|
|
234
|
+
},
|
|
265
235
|
});
|
|
266
236
|
```
|
|
267
237
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
answer from that record.
|
|
280
|
-
|
|
281
|
-
`Hooks` also includes `config?: (input: Config) => Promise<void>`, which runs against the
|
|
282
|
-
resolved config before use and can mutate it. That is how a plugin contributes
|
|
283
|
-
configuration it would otherwise need the user to hand-write — the plugins in this
|
|
284
|
-
repository use it to append their own `skills/` directory to `skills.paths`, so installing
|
|
285
|
-
one delivers its skills without any `opencode.json` edit.
|
|
286
|
-
|
|
287
|
-
Compile with the same unified CLI, but `--agent opencode` treats `-o`/`--output` as a
|
|
288
|
-
plugin-artifact **directory**, not a manifest path — OpenCode config references built
|
|
289
|
-
files directly, so there is no `hooks.json`-equivalent manifest or shebang wrapper:
|
|
238
|
+
`guardAdvisory` accepts domain-prefixed V2 hook names and preserves the native
|
|
239
|
+
single mutable event argument. Permission evaluation (`permission.evaluate`)
|
|
240
|
+
always surfaces failures and cannot opt into `continue`.
|
|
241
|
+
`createRootSessionRegistry()` remains available for tracking observed session
|
|
242
|
+
parentage and resumption; consume events with `ctx.event.subscribe()` and abort
|
|
243
|
+
subscriptions in the cleanup function returned by `setup`.
|
|
244
|
+
|
|
245
|
+
Configuration contributions use domain transforms such as `ctx.skill.transform`.
|
|
246
|
+
There is no returned V1 `config` callback or global mutable config object.
|
|
247
|
+
|
|
248
|
+
Compile plugins with the unified CLI:
|
|
290
249
|
|
|
291
250
|
```bash
|
|
292
|
-
npx -y @goodfoot/agent-hooks --agent opencode -i "src/**/*.ts" -o "./.opencode/
|
|
251
|
+
npx -y @goodfoot/agent-hooks --agent opencode -i "src/**/*.ts" -o "./.opencode/plugins"
|
|
293
252
|
```
|
|
294
253
|
|
|
295
|
-
|
|
296
|
-
|
|
254
|
+
Each source emits a plugin package directory: `src/my-plugin.ts` produces
|
|
255
|
+
`.opencode/plugins/my-plugin/package.json` and `index.js`. The compiler validates
|
|
256
|
+
the entrypoint as a V2 `{ id, setup }` definition. OpenCode discovers these
|
|
257
|
+
package directories under `.opencode/plugins/` automatically.
|
|
258
|
+
|
|
259
|
+
For output elsewhere, add each generated package directory to `plugins`:
|
|
260
|
+
|
|
261
|
+
```json
|
|
262
|
+
{ "plugins": ["./build/plugins/my-plugin"] }
|
|
263
|
+
```
|
|
297
264
|
|
|
298
|
-
|
|
299
|
-
`@
|
|
300
|
-
|
|
265
|
+
OpenCode 2.1.2 rejects individual `.js` or `.mjs` paths in that list.
|
|
266
|
+
`@opencode/plugin@^2.1.2` is an optional peer dependency, required only for the
|
|
267
|
+
OpenCode entry point. See the [official V2 plugin migration guide](https://opencode.ai/v2/docs/build/plugins/migrate-v1/)
|
|
268
|
+
for domain hooks, transforms, and cleanup.
|
|
301
269
|
|
|
302
270
|
## Fail-open advisory hooks
|
|
303
271
|
|
|
@@ -50,7 +50,7 @@ function packageJson(projectName, outputPath) {
|
|
|
50
50
|
lint: "biome check .",
|
|
51
51
|
typecheck: "tsc --noEmit",
|
|
52
52
|
},
|
|
53
|
-
dependencies: { [PACKAGE_NAME]: "^1.0.
|
|
53
|
+
dependencies: { [PACKAGE_NAME]: "^1.0.14" },
|
|
54
54
|
devDependencies: {
|
|
55
55
|
"@biomejs/biome": "2.4.9",
|
|
56
56
|
"@types/node": "^24.0.0",
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OpenCode-specific CLI internals: bundling each selected plugin entry to
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* built output — not a hand-rolled AST check, which could pass a shape the
|
|
2
|
+
* OpenCode-specific CLI internals: bundling each selected plugin entry to an
|
|
3
|
+
* ESM plugin directory and validating its V2 `{ id, setup }` contract
|
|
4
|
+
* against a real dynamic import of the built output — not a hand-rolled AST check, which could pass a shape the
|
|
6
5
|
* actual OpenCode loader would still reject.
|
|
7
6
|
*
|
|
8
7
|
* Unlike Codex's `compileCodexHook`, there is no wrapper content and no
|
|
9
8
|
* `execute()` runtime module to link: OpenCode imports the plugin author's
|
|
10
|
-
* own default export directly
|
|
9
|
+
* own default export directly from the package entry point — no
|
|
11
10
|
* stdin/stdout driver, no `hooks.json` manifest, no shebang.
|
|
12
11
|
*
|
|
13
12
|
* Driver-level primitives (loader parsing, glob discovery) are imported from
|
|
@@ -46,41 +45,55 @@ export async function compileOpenCodePlugin(sourcePath, loaders, sourcemap = fal
|
|
|
46
45
|
}
|
|
47
46
|
/**
|
|
48
47
|
* Validates that `outputPath`'s default export matches OpenCode's real
|
|
49
|
-
* loader contract
|
|
50
|
-
*
|
|
48
|
+
* V2 loader contract: an object with a stable non-empty `id` and a
|
|
49
|
+
* `setup` function. Imports the built artifact itself
|
|
51
50
|
* rather than statically analyzing source, so the check exercises exactly
|
|
52
51
|
* what OpenCode's loader will see.
|
|
53
|
-
* @throws Error when the default export
|
|
52
|
+
* @throws Error when the default export is not a V2 plugin definition.
|
|
54
53
|
*/
|
|
55
54
|
export async function validateOpenCodePluginModule(outputPath) {
|
|
56
55
|
const imported = await import(pathToFileURL(outputPath).href);
|
|
57
56
|
const defaultExport = imported.default;
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
typeof
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
const definition = defaultExport;
|
|
58
|
+
if (typeof definition !== "object" ||
|
|
59
|
+
definition === null ||
|
|
60
|
+
typeof definition.id !== "string" ||
|
|
61
|
+
definition.id.trim().length === 0 ||
|
|
62
|
+
typeof definition.setup !== "function") {
|
|
63
|
+
throw new Error(`${outputPath}: default export must be a V2 { id, setup } plugin definition`);
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
async function compileAllOpenCodePlugins(pluginFiles, outputDir, loaders, sourcemap) {
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
const outputPaths = pluginFiles.map((sourcePath) => path.join(outputDir, path.basename(sourcePath, path.extname(sourcePath))));
|
|
68
|
+
if (new Set(outputPaths).size !== outputPaths.length) {
|
|
69
|
+
throw new Error("OpenCode plugin source basenames must be unique within the output directory");
|
|
69
70
|
}
|
|
71
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
72
|
+
const stagingDir = fs.mkdtempSync(path.join(outputDir, ".opencode-build-"));
|
|
70
73
|
const compiled = [];
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
try {
|
|
75
|
+
// Validate fresh modules before replacing any previously built entry points.
|
|
76
|
+
// Unique staging paths also prevent dynamic import's cache hiding rebuild errors.
|
|
77
|
+
for (const [index, sourcePath] of pluginFiles.entries()) {
|
|
78
|
+
const content = await compileOpenCodePlugin(sourcePath, loaders, sourcemap);
|
|
79
|
+
const stagedPath = path.join(stagingDir, String(index));
|
|
80
|
+
fs.mkdirSync(stagedPath);
|
|
81
|
+
fs.writeFileSync(path.join(stagedPath, "package.json"), `${JSON.stringify({ private: true, type: "module", main: "index.js", exports: "./index.js" }, null, 2)}\n`, "utf-8");
|
|
82
|
+
const entryPath = path.join(stagedPath, "index.js");
|
|
83
|
+
fs.writeFileSync(entryPath, content, "utf-8");
|
|
84
|
+
await validateOpenCodePluginModule(entryPath);
|
|
85
|
+
compiled.push({ sourcePath, outputPath: outputPaths[index] });
|
|
78
86
|
}
|
|
79
|
-
|
|
80
|
-
fs.
|
|
81
|
-
|
|
87
|
+
for (const [index, { outputPath }] of compiled.entries()) {
|
|
88
|
+
fs.mkdirSync(outputPath, { recursive: true });
|
|
89
|
+
for (const filename of ["package.json", "index.js"]) {
|
|
90
|
+
fs.renameSync(path.join(stagingDir, String(index), filename), path.join(outputPath, filename));
|
|
91
|
+
}
|
|
82
92
|
}
|
|
83
|
-
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
// Only remove compiler-owned staging data; preserve other files in plugin directories.
|
|
96
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
84
97
|
}
|
|
85
98
|
return compiled;
|
|
86
99
|
}
|
|
@@ -104,9 +117,9 @@ export function validateOpenCodeArgs(args) {
|
|
|
104
117
|
}
|
|
105
118
|
/**
|
|
106
119
|
* Runs the OpenCode build branch of the unified CLI: discovers plugin entry
|
|
107
|
-
* files matching `--input`, bundles each to a
|
|
108
|
-
* `--output
|
|
109
|
-
*
|
|
120
|
+
* files matching `--input`, bundles each to a package directory under
|
|
121
|
+
* `--output`. OpenCode plugin config references these directories; each contains
|
|
122
|
+
* package.json and index.js. Validates each entry point's
|
|
110
123
|
* default export against OpenCode's real loader contract.
|
|
111
124
|
*/
|
|
112
125
|
export async function runOpenCodeCli(args) {
|
|
@@ -1,28 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenCode callback policy split: which lifecycle callbacks may opt into
|
|
3
|
-
* guarded fail-open (`"continue"`) behavior, and which must always surface
|
|
4
|
-
* failures.
|
|
5
|
-
*
|
|
6
|
-
* Unlike Codex's advisory allow-list (transcribed from `codex-hooks/README.md`,
|
|
7
|
-
* an external source-of-truth document), OpenCode has no prior source package
|
|
8
|
-
* to transcribe from — this package *is* the source of truth for its own
|
|
9
|
-
* OpenCode surface. The split below is derived directly from CARD.md's
|
|
10
|
-
* Desired Functionality section (committed to the card repository, itself
|
|
11
|
-
* the authorization for this contract): "Advisory callbacks can opt into
|
|
12
|
-
* guarded fail-open behavior with observable diagnostics; policy-enforcing
|
|
13
|
-
* callbacks default to surfacing failures."
|
|
14
|
-
*
|
|
15
|
-
* `permission.ask` is the only callback whose `output.status` is an
|
|
16
|
-
* allow/deny/ask *security decision* — swallowing a failure there means the
|
|
17
|
-
* decision was silently skipped, exactly the Codex precedent
|
|
18
|
-
* (`PreToolUse`/`PermissionRequest`) this mirrors. Every other lifecycle
|
|
19
|
-
* callback only observes or additively enriches (message/tool mutation,
|
|
20
|
-
* shell env, event notification) and is therefore advisory.
|
|
21
|
-
* @module
|
|
22
|
-
*/
|
|
23
1
|
import { OPENCODE_HOOK_NAMES } from "./types.js";
|
|
24
2
|
/** Callbacks that must always surface failures — never eligible for `"continue"`. */
|
|
25
|
-
export const POLICY_ENFORCING_EVENTS = ["permission.
|
|
3
|
+
export const POLICY_ENFORCING_EVENTS = ["permission.evaluate"];
|
|
26
4
|
/**
|
|
27
5
|
* The advisory allow-list: every OpenCode lifecycle callback except
|
|
28
6
|
* {@link POLICY_ENFORCING_EVENTS}. Only these accept
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@goodfoot/agent-hooks/opencode` — the OpenCode agent surface.
|
|
3
3
|
*
|
|
4
|
-
* Re-exports OpenCode-native types (narrowed from `@opencode
|
|
4
|
+
* Re-exports OpenCode-native types (narrowed from `@opencode/plugin`),
|
|
5
5
|
* the advisory/policy-enforcing callback split, the shared error-policy
|
|
6
6
|
* vocabulary, and the plugin authoring primitives. No default export at this
|
|
7
|
-
* subpath — `defineOpenCodePlugin`'s *return value* is the `{ id,
|
|
7
|
+
* subpath — `defineOpenCodePlugin`'s *return value* is the `{ id, setup }`
|
|
8
8
|
* default export a plugin author's own module produces, not something this
|
|
9
9
|
* barrel exports itself.
|
|
10
10
|
* @module
|
|
@@ -1,32 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin authoring primitives for `@goodfoot/agent-hooks/opencode`.
|
|
3
|
-
*
|
|
4
|
-
* `defineOpenCodePlugin` validates the `{ id, server }` module contract that
|
|
5
|
-
* OpenCode's real loader (`getServerPlugin`, verified live against
|
|
6
|
-
* `sst/opencode`'s `packages/opencode/src/plugin/index.ts`) accepts as a
|
|
7
|
-
* default export. `createRootSessionRegistry` and `guardAdvisory` are
|
|
8
|
-
* optional composition helpers a `server` factory may call — not a required
|
|
9
|
-
* framework, since a plugin author can also return a raw {@link Hooks}
|
|
10
|
-
* object built directly from `./types.js`.
|
|
11
|
-
* @module
|
|
12
|
-
*/
|
|
13
1
|
import { applyOpenCodeErrorPolicy } from "./policy.js";
|
|
14
|
-
/**
|
|
15
|
-
* Validates and returns an OpenCode plugin module definition.
|
|
16
|
-
*
|
|
17
|
-
* @param definition - `id` must be a non-empty string; `server` must be a
|
|
18
|
-
* function matching upstream's {@link Plugin} signature.
|
|
19
|
-
* @returns `definition`, unchanged, once validated.
|
|
20
|
-
* @throws Error when `id` is missing/empty or `server` is not a function.
|
|
21
|
-
*/
|
|
2
|
+
/** Validates a V2 plugin definition before OpenCode loads it. */
|
|
22
3
|
export function defineOpenCodePlugin(definition) {
|
|
23
|
-
if (typeof definition.id !== "string" || definition.id.length === 0) {
|
|
24
|
-
throw new Error(
|
|
4
|
+
if (typeof definition.id !== "string" || definition.id.trim().length === 0) {
|
|
5
|
+
throw new Error('defineOpenCodePlugin: "id" must be a non-empty string');
|
|
25
6
|
}
|
|
26
|
-
if (typeof definition.
|
|
27
|
-
throw new Error(
|
|
7
|
+
if (typeof definition.setup !== "function") {
|
|
8
|
+
throw new Error('defineOpenCodePlugin: "setup" must be a function');
|
|
28
9
|
}
|
|
29
|
-
return
|
|
10
|
+
return definition;
|
|
30
11
|
}
|
|
31
12
|
/** Creates a fresh, empty {@link RootSessionRegistry}. */
|
|
32
13
|
export function createRootSessionRegistry() {
|
|
@@ -1,40 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenCode-native types for `@goodfoot/agent-hooks/opencode`.
|
|
3
|
-
*
|
|
4
|
-
* OpenCode loads a long-lived, in-process plugin module rather than spawning
|
|
5
|
-
* a one-shot command-hook process: {@link PluginModule} (`{ id?, server }`)
|
|
6
|
-
* or a bare {@link Plugin} function is what OpenCode's real loader accepts —
|
|
7
|
-
* verified directly against the installed `@opencode-ai/plugin@1.18.23`
|
|
8
|
-
* `.d.ts`, not the (looser) public docs page. This module re-exports and
|
|
9
|
-
* narrows those upstream types; it never redeclares OpenCode's callback
|
|
10
|
-
* input/output shapes by hand, so this surface can't drift from the SDK it
|
|
11
|
-
* wraps.
|
|
12
|
-
* @module
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* The stable, lifecycle-callback keys of {@link Hooks} — excludes `tool`
|
|
16
|
-
* (custom tool definitions, not a lifecycle callback), `auth` and `provider`
|
|
17
|
-
* (static registration objects), and `dispose` (a no-argument cleanup
|
|
18
|
-
* callback with no policy semantics of its own).
|
|
19
|
-
*
|
|
20
|
-
* Order matches declaration order in the upstream `Hooks` interface.
|
|
21
|
-
*/
|
|
22
1
|
export const OPENCODE_HOOK_NAMES = [
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
2
|
+
"aisdk.sdk",
|
|
3
|
+
"aisdk.language",
|
|
4
|
+
"session.prompt",
|
|
5
|
+
"session.context",
|
|
6
|
+
"session.compaction",
|
|
7
|
+
"session.generate",
|
|
8
|
+
"session.title",
|
|
9
|
+
"session.model.request",
|
|
10
|
+
"session.http.request",
|
|
11
|
+
"session.http.response",
|
|
12
|
+
"session.experimental.ws.handshake",
|
|
13
|
+
"session.experimental.ws.send",
|
|
14
|
+
"session.experimental.ws.receive",
|
|
15
|
+
"session.retry",
|
|
16
|
+
"permission.evaluate",
|
|
17
|
+
"shell.create.before",
|
|
30
18
|
"tool.execute.before",
|
|
31
|
-
"shell.env",
|
|
32
19
|
"tool.execute.after",
|
|
33
|
-
"experimental.chat.messages.transform",
|
|
34
|
-
"experimental.chat.system.transform",
|
|
35
|
-
"experimental.provider.small_model",
|
|
36
|
-
"experimental.session.compacting",
|
|
37
|
-
"experimental.compaction.autocontinue",
|
|
38
|
-
"experimental.text.complete",
|
|
39
|
-
"tool.definition",
|
|
40
20
|
];
|
package/dist/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ import { scaffoldProject } from "./scaffold.js";
|
|
|
26
26
|
// ============================================================================
|
|
27
27
|
// Constants
|
|
28
28
|
// ============================================================================
|
|
29
|
-
const VERSION = "1.0.
|
|
29
|
+
const VERSION = "1.0.14";
|
|
30
30
|
/** Filename of the tracking sidecar written beside hooks.json. */
|
|
31
31
|
const HOOKS_META_FILENAME = "hooks.meta.json";
|
|
32
32
|
const DEFAULT_ESBUILD_LOADERS = {
|
|
@@ -71,8 +71,8 @@ Required Arguments:
|
|
|
71
71
|
The agent runtime the hooks are built for. Determines the hook surface
|
|
72
72
|
validated at build time and the runtime module linked into each compiled
|
|
73
73
|
bundle. There is no default: omitting this flag is an error.
|
|
74
|
-
With --agent opencode, -o/--output
|
|
75
|
-
|
|
74
|
+
With --agent opencode, -o/--output contains one plugin package directory
|
|
75
|
+
per source (package.json and index.js; no hooks.json is generated).
|
|
76
76
|
With --agent antigravity, -o/--output is the hooks.json path at the
|
|
77
77
|
plugin root; bundles are written to a bin/ directory beside it and the
|
|
78
78
|
generated commands are relative to the manifest, which is the working
|
|
@@ -1335,7 +1335,7 @@ async function main() {
|
|
|
1335
1335
|
loaderFlags: args.loaderFlags ?? [],
|
|
1336
1336
|
sourcemap: args.sourcemap,
|
|
1337
1337
|
});
|
|
1338
|
-
process.stdout.write(`Generated ${compiled.length} plugin
|
|
1338
|
+
process.stdout.write(`Generated ${compiled.length} plugin package(s) in ${path.resolve(process.cwd(), args.output)}\n`);
|
|
1339
1339
|
process.exit(0);
|
|
1340
1340
|
}
|
|
1341
1341
|
catch (error) {
|
package/dist/core/transport.js
CHANGED
|
@@ -111,18 +111,29 @@ function classify(error, phase, policy, onUnexpectedError) {
|
|
|
111
111
|
}
|
|
112
112
|
return { kind: "handlerError", error, phase };
|
|
113
113
|
}
|
|
114
|
+
// A successful write return value only describes buffering; its callback confirms delivery.
|
|
115
|
+
function writeStream(stream, content) {
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
stream.write(content, (error) => {
|
|
118
|
+
if (error)
|
|
119
|
+
reject(error);
|
|
120
|
+
else
|
|
121
|
+
resolve();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
114
125
|
/**
|
|
115
126
|
* Writes an unexpected (non-handler, runtime-owned) error's stacktrace to
|
|
116
127
|
* stderr. Used for cleanup/finalize failures under the default `"error"`
|
|
117
128
|
* policy, which exit with code 1 — these are not the handler's own
|
|
118
129
|
* intentional signal.
|
|
119
130
|
*/
|
|
120
|
-
function writeUnexpectedErrorStderr(error) {
|
|
131
|
+
async function writeUnexpectedErrorStderr(error) {
|
|
121
132
|
if (error instanceof Error) {
|
|
122
|
-
process.stderr
|
|
133
|
+
await writeStream(process.stderr, `${error.stack ?? error.message}\n`);
|
|
123
134
|
}
|
|
124
135
|
else {
|
|
125
|
-
process.stderr
|
|
136
|
+
await writeStream(process.stderr, `${String(error)}\n`);
|
|
126
137
|
}
|
|
127
138
|
}
|
|
128
139
|
/**
|
|
@@ -198,7 +209,7 @@ export async function drive(transport, hookFn) {
|
|
|
198
209
|
cleanupQuietly();
|
|
199
210
|
process.exit(FALLBACK_EXIT_SUCCESS);
|
|
200
211
|
}
|
|
201
|
-
writeUnexpectedErrorStderr(error);
|
|
212
|
+
await writeUnexpectedErrorStderr(error);
|
|
202
213
|
cleanupQuietly();
|
|
203
214
|
process.exit(FALLBACK_EXIT_ERROR);
|
|
204
215
|
}
|
|
@@ -206,15 +217,15 @@ export async function drive(transport, hookFn) {
|
|
|
206
217
|
cleanup(policy, onUnexpectedError);
|
|
207
218
|
}
|
|
208
219
|
catch (error) {
|
|
209
|
-
writeUnexpectedErrorStderr(error);
|
|
220
|
+
await writeUnexpectedErrorStderr(error);
|
|
210
221
|
process.exit(FALLBACK_EXIT_ERROR);
|
|
211
222
|
}
|
|
212
223
|
if (finalized.stderr !== undefined) {
|
|
213
|
-
process.stderr
|
|
224
|
+
await writeStream(process.stderr, finalized.stderr);
|
|
214
225
|
}
|
|
215
226
|
if (finalized.stdout !== undefined) {
|
|
216
227
|
try {
|
|
217
|
-
process.stdout
|
|
228
|
+
await writeStream(process.stdout, finalized.stdout);
|
|
218
229
|
}
|
|
219
230
|
catch (error) {
|
|
220
231
|
if (policy === "continue") {
|
|
@@ -222,7 +233,7 @@ export async function drive(transport, hookFn) {
|
|
|
222
233
|
cleanupQuietly();
|
|
223
234
|
process.exit(FALLBACK_EXIT_SUCCESS);
|
|
224
235
|
}
|
|
225
|
-
writeUnexpectedErrorStderr(error);
|
|
236
|
+
await writeUnexpectedErrorStderr(error);
|
|
226
237
|
cleanupQuietly();
|
|
227
238
|
process.exit(FALLBACK_EXIT_ERROR);
|
|
228
239
|
}
|
package/dist/scaffold.js
CHANGED
|
@@ -131,7 +131,7 @@ function generatePackageJson(projectName, outputPath) {
|
|
|
131
131
|
typecheck: "tsc --noEmit",
|
|
132
132
|
},
|
|
133
133
|
dependencies: {
|
|
134
|
-
"@goodfoot/agent-hooks": "^1.0.
|
|
134
|
+
"@goodfoot/agent-hooks": "^1.0.14",
|
|
135
135
|
},
|
|
136
136
|
devDependencies: {
|
|
137
137
|
"@biomejs/biome": "2.4.16",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodfoot/agent-hooks",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "Unified agent hooks library: shared core runtime with per-agent entry points for Claude Code, Codex, and Antigravity",
|
|
5
5
|
"homepage": "https://github.com/goodfoot-io/marketplace/tree/main/packages/agent-hooks",
|
|
6
6
|
"repository": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"typecheck": "tsc --noEmit",
|
|
24
24
|
"build": "tsc --build tsconfig.build.json",
|
|
25
|
-
"lint": "biome check --write --unsafe --max-diagnostics 500 ./src ./tests ./e2e ./scripts && node ./scripts/check-stale-imports.mjs",
|
|
25
|
+
"lint": "biome check --write --unsafe --max-diagnostics 500 ./src ./tests ./e2e ./scripts ./vitest.config.ts && node ./scripts/check-stale-imports.mjs",
|
|
26
26
|
"test": "node ./scripts/check-stale-imports.mjs && node ./scripts/check-agent-duplication.mjs && vitest run",
|
|
27
27
|
"release": "bash ../../scripts/release-package.sh agent-hooks",
|
|
28
28
|
"release:dry-run": "bash ../../scripts/release-package.sh agent-hooks --dry-run"
|
|
@@ -56,24 +56,29 @@
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"esbuild": "^0.
|
|
59
|
+
"esbuild": "^0.28.2",
|
|
60
60
|
"glob": "^11.0.0",
|
|
61
61
|
"typescript": "^5.9.3"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@opencode
|
|
64
|
+
"@opencode/plugin": "^2.1.2"
|
|
65
65
|
},
|
|
66
66
|
"peerDependenciesMeta": {
|
|
67
|
-
"@opencode
|
|
67
|
+
"@opencode/plugin": {
|
|
68
68
|
"optional": true
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@anthropic-ai/claude-agent-sdk": "^0.3.162",
|
|
73
|
+
"@anthropic-ai/sdk": "^0.133.0",
|
|
73
74
|
"@biomejs/biome": "2.4.16",
|
|
74
|
-
"@
|
|
75
|
-
"@
|
|
75
|
+
"@goodfoot/vitest-cache": "workspace:*",
|
|
76
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
77
|
+
"@opencode/plugin": "2.1.2",
|
|
78
|
+
"@types/node": "^26.6.5",
|
|
76
79
|
"tsx": "^4.20.6",
|
|
77
|
-
"
|
|
80
|
+
"vite": "8.3.4",
|
|
81
|
+
"vitest": "patch:vitest@npm%3A5.0.4#~/.yarn/patches/vitest-npm-5.0.4-2ab4bceca5.patch",
|
|
82
|
+
"zod": "^4.6.7"
|
|
78
83
|
}
|
|
79
84
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OpenCode-specific CLI internals: bundling each selected plugin entry to
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* built output — not a hand-rolled AST check, which could pass a shape the
|
|
2
|
+
* OpenCode-specific CLI internals: bundling each selected plugin entry to an
|
|
3
|
+
* ESM plugin directory and validating its V2 `{ id, setup }` contract
|
|
4
|
+
* against a real dynamic import of the built output — not a hand-rolled AST check, which could pass a shape the
|
|
6
5
|
* actual OpenCode loader would still reject.
|
|
7
6
|
*
|
|
8
7
|
* Unlike Codex's `compileCodexHook`, there is no wrapper content and no
|
|
9
8
|
* `execute()` runtime module to link: OpenCode imports the plugin author's
|
|
10
|
-
* own default export directly
|
|
9
|
+
* own default export directly from the package entry point — no
|
|
11
10
|
* stdin/stdout driver, no `hooks.json` manifest, no shebang.
|
|
12
11
|
*
|
|
13
12
|
* Driver-level primitives (loader parsing, glob discovery) are imported from
|
|
@@ -34,11 +33,11 @@ interface CompiledOpenCodePlugin {
|
|
|
34
33
|
export declare function compileOpenCodePlugin(sourcePath: string, loaders: HookLoaderMap, sourcemap?: boolean): Promise<string>;
|
|
35
34
|
/**
|
|
36
35
|
* Validates that `outputPath`'s default export matches OpenCode's real
|
|
37
|
-
* loader contract
|
|
38
|
-
*
|
|
36
|
+
* V2 loader contract: an object with a stable non-empty `id` and a
|
|
37
|
+
* `setup` function. Imports the built artifact itself
|
|
39
38
|
* rather than statically analyzing source, so the check exercises exactly
|
|
40
39
|
* what OpenCode's loader will see.
|
|
41
|
-
* @throws Error when the default export
|
|
40
|
+
* @throws Error when the default export is not a V2 plugin definition.
|
|
42
41
|
*/
|
|
43
42
|
export declare function validateOpenCodePluginModule(outputPath: string): Promise<void>;
|
|
44
43
|
/**
|
|
@@ -48,9 +47,9 @@ export declare function validateOpenCodePluginModule(outputPath: string): Promis
|
|
|
48
47
|
export declare function validateOpenCodeArgs(args: OpenCodeCliArgs): string | undefined;
|
|
49
48
|
/**
|
|
50
49
|
* Runs the OpenCode build branch of the unified CLI: discovers plugin entry
|
|
51
|
-
* files matching `--input`, bundles each to a
|
|
52
|
-
* `--output
|
|
53
|
-
*
|
|
50
|
+
* files matching `--input`, bundles each to a package directory under
|
|
51
|
+
* `--output`. OpenCode plugin config references these directories; each contains
|
|
52
|
+
* package.json and index.js. Validates each entry point's
|
|
54
53
|
* default export against OpenCode's real loader contract.
|
|
55
54
|
*/
|
|
56
55
|
export declare function runOpenCodeCli(args: OpenCodeCliArgs): Promise<CompiledOpenCodePlugin[]>;
|
|
@@ -1,29 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenCode callback policy split: which lifecycle callbacks may opt into
|
|
3
|
-
* guarded fail-open (`"continue"`) behavior, and which must always surface
|
|
4
|
-
* failures.
|
|
5
|
-
*
|
|
6
|
-
* Unlike Codex's advisory allow-list (transcribed from `codex-hooks/README.md`,
|
|
7
|
-
* an external source-of-truth document), OpenCode has no prior source package
|
|
8
|
-
* to transcribe from — this package *is* the source of truth for its own
|
|
9
|
-
* OpenCode surface. The split below is derived directly from CARD.md's
|
|
10
|
-
* Desired Functionality section (committed to the card repository, itself
|
|
11
|
-
* the authorization for this contract): "Advisory callbacks can opt into
|
|
12
|
-
* guarded fail-open behavior with observable diagnostics; policy-enforcing
|
|
13
|
-
* callbacks default to surfacing failures."
|
|
14
|
-
*
|
|
15
|
-
* `permission.ask` is the only callback whose `output.status` is an
|
|
16
|
-
* allow/deny/ask *security decision* — swallowing a failure there means the
|
|
17
|
-
* decision was silently skipped, exactly the Codex precedent
|
|
18
|
-
* (`PreToolUse`/`PermissionRequest`) this mirrors. Every other lifecycle
|
|
19
|
-
* callback only observes or additively enriches (message/tool mutation,
|
|
20
|
-
* shell env, event notification) and is therefore advisory.
|
|
21
|
-
* @module
|
|
22
|
-
*/
|
|
1
|
+
/** V2 permission evaluation always surfaces errors; enrichment hooks may opt into continue. @module */
|
|
23
2
|
import type { UnexpectedErrorPolicy } from "../../core/types.js";
|
|
24
3
|
import { type OpenCodeHookName } from "./types.js";
|
|
25
4
|
/** Callbacks that must always surface failures — never eligible for `"continue"`. */
|
|
26
|
-
export declare const POLICY_ENFORCING_EVENTS: readonly ["permission.
|
|
5
|
+
export declare const POLICY_ENFORCING_EVENTS: readonly ["permission.evaluate"];
|
|
27
6
|
/** The policy-enforcing set as a literal union. */
|
|
28
7
|
export type PolicyEnforcingEventName = (typeof POLICY_ENFORCING_EVENTS)[number];
|
|
29
8
|
/** Type-level membership check: `true` only for policy-enforcing callback names. */
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@goodfoot/agent-hooks/opencode` — the OpenCode agent surface.
|
|
3
3
|
*
|
|
4
|
-
* Re-exports OpenCode-native types (narrowed from `@opencode
|
|
4
|
+
* Re-exports OpenCode-native types (narrowed from `@opencode/plugin`),
|
|
5
5
|
* the advisory/policy-enforcing callback split, the shared error-policy
|
|
6
6
|
* vocabulary, and the plugin authoring primitives. No default export at this
|
|
7
|
-
* subpath — `defineOpenCodePlugin`'s *return value* is the `{ id,
|
|
7
|
+
* subpath — `defineOpenCodePlugin`'s *return value* is the `{ id, setup }`
|
|
8
8
|
* default export a plugin author's own module produces, not something this
|
|
9
9
|
* barrel exports itself.
|
|
10
10
|
* @module
|
|
@@ -1,30 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin authoring primitives for `@goodfoot/agent-hooks/opencode`.
|
|
3
|
-
*
|
|
4
|
-
* `defineOpenCodePlugin` validates the `{ id, server }` module contract that
|
|
5
|
-
* OpenCode's real loader (`getServerPlugin`, verified live against
|
|
6
|
-
* `sst/opencode`'s `packages/opencode/src/plugin/index.ts`) accepts as a
|
|
7
|
-
* default export. `createRootSessionRegistry` and `guardAdvisory` are
|
|
8
|
-
* optional composition helpers a `server` factory may call — not a required
|
|
9
|
-
* framework, since a plugin author can also return a raw {@link Hooks}
|
|
10
|
-
* object built directly from `./types.js`.
|
|
11
|
-
* @module
|
|
12
|
-
*/
|
|
1
|
+
/** OpenCode V2 plugin authoring helpers. @module */
|
|
13
2
|
import type { AdvisoryEventName } from "./events.js";
|
|
14
3
|
import { type UnexpectedErrorHandler, type UnexpectedErrorPolicy } from "./policy.js";
|
|
15
|
-
import type { OpenCodeHookHandler,
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
* @param definition - `id` must be a non-empty string; `server` must be a
|
|
20
|
-
* function matching upstream's {@link Plugin} signature.
|
|
21
|
-
* @returns `definition`, unchanged, once validated.
|
|
22
|
-
* @throws Error when `id` is missing/empty or `server` is not a function.
|
|
23
|
-
*/
|
|
24
|
-
export declare function defineOpenCodePlugin(definition: {
|
|
25
|
-
id: string;
|
|
26
|
-
server: Plugin;
|
|
27
|
-
}): PluginModule;
|
|
4
|
+
import type { OpenCodeHookHandler, PluginInput, PluginModule } from "./types.js";
|
|
5
|
+
/** Validates a V2 plugin definition before OpenCode loads it. */
|
|
6
|
+
export declare function defineOpenCodePlugin(definition: PluginModule): PluginModule;
|
|
28
7
|
/**
|
|
29
8
|
* Tracks OpenCode session parentage and resumption across a plugin's
|
|
30
9
|
* lifetime.
|
|
@@ -27,4 +27,4 @@ export type { UnexpectedErrorHandler, UnexpectedErrorPolicy };
|
|
|
27
27
|
* @param onError - Best-effort diagnostic sink for a swallowed `"continue"` error.
|
|
28
28
|
* @throws Error the handler threw is thrown when `policy` is `"error"`.
|
|
29
29
|
*/
|
|
30
|
-
export declare function applyOpenCodeErrorPolicy<TArgs extends unknown[]>(_name: string, handler: (...args: TArgs) => Promise<void
|
|
30
|
+
export declare function applyOpenCodeErrorPolicy<TArgs extends unknown[]>(_name: string, handler: (...args: TArgs) => Promise<void> | void, policy: UnexpectedErrorPolicy | undefined, onError?: UnexpectedErrorHandler): (...args: TArgs) => Promise<void> | void;
|
|
@@ -1,28 +1,22 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
* Order matches declaration order in the upstream `Hooks` interface.
|
|
23
|
-
*/
|
|
24
|
-
export declare const OPENCODE_HOOK_NAMES: readonly ["event", "config", "chat.message", "chat.params", "chat.headers", "permission.ask", "command.execute.before", "tool.execute.before", "shell.env", "tool.execute.after", "experimental.chat.messages.transform", "experimental.chat.system.transform", "experimental.provider.small_model", "experimental.session.compacting", "experimental.compaction.autocontinue", "experimental.text.complete", "tool.definition"];
|
|
25
|
-
/** Literal union of {@link OPENCODE_HOOK_NAMES}. */
|
|
1
|
+
/** OpenCode V2 native plugin and domain hook types, derived from the installed SDK. @module */
|
|
2
|
+
import type { Plugin as NativePlugin, PluginOptions } from "@opencode/plugin";
|
|
3
|
+
import type { AISDKHooks } from "@opencode/plugin/promise/aisdk";
|
|
4
|
+
import type { PermissionHooks } from "@opencode/plugin/promise/permission";
|
|
5
|
+
import type { Hooks as DomainHooks } from "@opencode/plugin/promise/registration";
|
|
6
|
+
import type { SessionHooks } from "@opencode/plugin/promise/session";
|
|
7
|
+
import type { ShellHooks } from "@opencode/plugin/promise/shell";
|
|
8
|
+
import type { ToolDomain } from "@opencode/plugin/promise/tool";
|
|
9
|
+
type HookSpec<T> = T extends DomainHooks<infer Spec> ? Spec : never;
|
|
10
|
+
type Prefix<Domain extends string, Spec> = {
|
|
11
|
+
[Name in keyof Spec as Name extends string ? `${Domain}.${Name}` : never]: (event: Spec[Name]) => Promise<void> | void;
|
|
12
|
+
};
|
|
13
|
+
/** V2 hook callbacks, named by their owning domain and registration name. */
|
|
14
|
+
export type Hooks = Prefix<"aisdk", AISDKHooks> & Prefix<"session", SessionHooks> & Prefix<"permission", PermissionHooks> & Prefix<"shell", ShellHooks> & Prefix<"tool", HookSpec<ToolDomain["hook"]>>;
|
|
15
|
+
/** The V2 setup callback. */
|
|
16
|
+
export type Plugin = NativePlugin.Plugin["setup"];
|
|
17
|
+
export type PluginInput = NativePlugin.Context;
|
|
18
|
+
export type PluginModule = NativePlugin.Plugin;
|
|
19
|
+
export type { PluginOptions };
|
|
20
|
+
export declare const OPENCODE_HOOK_NAMES: readonly ["aisdk.sdk", "aisdk.language", "session.prompt", "session.context", "session.compaction", "session.generate", "session.title", "session.model.request", "session.http.request", "session.http.response", "session.experimental.ws.handshake", "session.experimental.ws.send", "session.experimental.ws.receive", "session.retry", "permission.evaluate", "shell.create.before", "tool.execute.before", "tool.execute.after"];
|
|
26
21
|
export type OpenCodeHookName = (typeof OPENCODE_HOOK_NAMES)[number];
|
|
27
|
-
|
|
28
|
-
export type OpenCodeHookHandler<TName extends OpenCodeHookName> = NonNullable<Hooks[TName]>;
|
|
22
|
+
export type OpenCodeHookHandler<TName extends OpenCodeHookName> = Hooks[TName];
|