@dougbots/avenor-opencode 0.1.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/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # @dougbots/avenor-opencode
2
+
3
+ OpenCode plugin that registers avenor agent runs as tools in your OpenCode session. When you need a sub-agent to handle a well-defined task — write code, run tests, search a codebase — avenor operates in the background while you keep your session focused.
4
+
5
+ ## Setup
6
+
7
+ Install the package alongside its peer dependency:
8
+
9
+ ```bash
10
+ bun add @dougbots/avenor-opencode @opencode-ai/plugin@1.15.3
11
+ ```
12
+
13
+ You also need the `avenor` binary. Either add it to your `PATH` or set the `AVENOR_BIN` environment variable to its location.
14
+
15
+ ```bash
16
+ # Option 1 — add to PATH (e.g. via mise, brew, or manual install)
17
+ which avenor
18
+
19
+ # Option 2 — explicit path
20
+ export AVENOR_BIN=~/.botfiles/bin/avenor
21
+ ```
22
+
23
+ ## Register the plugin
24
+
25
+ Add `@dougbots/avenor-opencode` to your OpenCode configuration. If you use a global config:
26
+
27
+ ```json
28
+ // ~/.opencode.json
29
+ {
30
+ "plugins": ["@dougbots/avenor-opencode"]
31
+ }
32
+ ```
33
+
34
+ For a project-level config, add the same entry to `.opencode.json` in your repo root.
35
+
36
+ Once registered, the plugin exposes six tools that your OpenCode agent can call directly — no extra configuration required.
37
+
38
+ ## Tools
39
+
40
+ ### `avenor_spawn`
41
+
42
+ Dispatch an agent run in the current working directory. Returns a `run_id` you can poll with `avenor_status`.
43
+
44
+ | Argument | Required | Description |
45
+ |---|---|---|
46
+ | `agent` | yes | Agent name (no default — must be specified) |
47
+ | `prompt` | no | Prompt text |
48
+ | `prompt_file` | no | Path to a prompt file |
49
+ | `label` | no | Human-readable label for the run |
50
+ | `timeout` | no | Timeout duration (e.g. `3600s`, `30m`) |
51
+ | `model` | no | Model override |
52
+ | `supervisor_id` | no | Reuse an existing supervisor by socket path |
53
+
54
+ The working directory (`dir`) is injected automatically from your OpenCode session context — it is not a user-facing argument.
55
+
56
+ ### `avenor_status`
57
+
58
+ Get status of a specific run or all active runs. Surfaces any pending permission requests the sub-agent is blocked on.
59
+
60
+ | Argument | Required | Description |
61
+ |---|---|---|
62
+ | `run_id` | no | Specific run ID to query (omit for all runs) |
63
+ | `supervisor_id` | no | Reuse an existing supervisor by socket path |
64
+
65
+ ### `avenor_answer_permission`
66
+
67
+ Answer a pending permission request. The `option_id` comes from the `pending_permission.options` array returned by `avenor_status`.
68
+
69
+ | Argument | Required | Description |
70
+ |---|---|---|
71
+ | `run_id` | yes | Run ID with the pending permission |
72
+ | `option_id` | yes | Option ID from `pending_permission.options` |
73
+ | `request_id` | no | Request ID (auto-discovered if omitted) |
74
+ | `supervisor_id` | no | Reuse an existing supervisor by socket path |
75
+
76
+ ### `avenor_follow_up`
77
+
78
+ Resume a completed run with a follow-up message. Useful for iterating on results or asking the sub-agent to refine its output.
79
+
80
+ | Argument | Required | Description |
81
+ |---|---|---|
82
+ | `run_id` | yes | Completed run ID to resume |
83
+ | `message` | yes | Follow-up message text |
84
+ | `label` | no | Override label (defaults to `<original>-followup`) |
85
+ | `supervisor_id` | no | Reuse an existing supervisor by socket path |
86
+
87
+ ### `avenor_events`
88
+
89
+ Read events from a run, optionally filtered by type. Returns the last N events.
90
+
91
+ | Argument | Required | Description |
92
+ |---|---|---|
93
+ | `run_id` | yes | Run ID to read events from |
94
+ | `types` | no | Filter by event type (array of strings) |
95
+ | `limit` | no | Max events to return (default 50) |
96
+ | `supervisor_id` | no | Reuse an existing supervisor by socket path |
97
+
98
+ ### `avenor_shutdown`
99
+
100
+ Shut down the avenor supervisor process and clean up temp files. Call this when you are done delegating work or if the supervisor has crashed.
101
+
102
+ | Argument | Required | Description |
103
+ |---|---|---|
104
+ | `supervisor_id` | no | Supervisor to shut down (defaults to the singleton) |
105
+ | `force` | no | Force shutdown instead of graceful |
106
+
107
+ ## Typical workflow
108
+
109
+ ```
110
+ 1. avenor_spawn → receive run_id
111
+ 2. avenor_status → check if still running, spot pending permissions
112
+ 3. avenor_answer_permission → unblock if a permission is requested
113
+ 4. avenor_events → inspect output once complete
114
+ 5. avenor_follow_up → optionally send a follow-up
115
+ 6. avenor_shutdown → clean up when finished
116
+ ```
117
+
118
+ ## Dependencies
119
+
120
+ - **Peer:** `@opencode-ai/plugin` v1.15.3 (the runtime that host agents use to mount plugins)
121
+ - **Binary:** `avenor` must be available on `PATH` or at `AVENOR_BIN` (checked at first tool invocation, not at plugin load time)
@@ -0,0 +1,6 @@
1
+ import { Plugin } from "@opencode-ai/plugin";
2
+
3
+ //#region src/plugin.d.ts
4
+ declare const AvenorPlugin: Plugin;
5
+ //#endregion
6
+ export { AvenorPlugin };
package/dist/index.js ADDED
@@ -0,0 +1,109 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { z } from "zod";
3
+ import { answerPermissionTool, eventsTool, followUpTool, shutdownTool, spawnTool, statusTool } from "@dougbots/avenor-core";
4
+ //#region src/plugin.ts
5
+ const AvenorPlugin = async (_ctx) => ({ tool: {
6
+ avenor_spawn: tool({
7
+ description: "Dispatch an agent run via avenor. Returns run_id for status polling.",
8
+ args: {
9
+ agent: z.string().describe("Agent name (required, no default)"),
10
+ prompt: z.string().optional().describe("Prompt text"),
11
+ prompt_file: z.string().optional().describe("Path to prompt file"),
12
+ label: z.string().optional().describe("Human-readable label for the run"),
13
+ timeout: z.string().optional().describe("Timeout duration (e.g. 3600s)"),
14
+ model: z.string().optional().describe("Model override"),
15
+ supervisor_id: z.string().optional().describe("Reuse an existing supervisor by socket path")
16
+ },
17
+ async execute(args, context) {
18
+ return spawnTool({
19
+ agent: args.agent,
20
+ prompt: args.prompt,
21
+ promptFile: args.prompt_file,
22
+ label: args.label,
23
+ dir: context.directory,
24
+ timeout: args.timeout,
25
+ model: args.model,
26
+ supervisorId: args.supervisor_id
27
+ });
28
+ }
29
+ }),
30
+ avenor_status: tool({
31
+ description: "Get status of a run or all runs. Surfaces pending permission requests.",
32
+ args: {
33
+ run_id: z.string().optional().describe("Specific run ID to query"),
34
+ supervisor_id: z.string().optional().describe("Reuse an existing supervisor by socket path")
35
+ },
36
+ async execute(args, _context) {
37
+ return statusTool({
38
+ runId: args.run_id,
39
+ supervisorId: args.supervisor_id
40
+ });
41
+ }
42
+ }),
43
+ avenor_answer_permission: tool({
44
+ description: "Answer a pending permission request. Pass option_id from avenor_status pending_permission.options.",
45
+ args: {
46
+ run_id: z.string().describe("Run ID with the pending permission"),
47
+ option_id: z.string().describe("Option ID from pending_permission.options array"),
48
+ request_id: z.string().optional().describe("Request ID (auto-discovered if omitted)"),
49
+ supervisor_id: z.string().optional().describe("Reuse an existing supervisor by socket path")
50
+ },
51
+ async execute(args, _context) {
52
+ return answerPermissionTool({
53
+ runId: args.run_id,
54
+ optionId: args.option_id,
55
+ requestId: args.request_id,
56
+ supervisorId: args.supervisor_id
57
+ });
58
+ }
59
+ }),
60
+ avenor_follow_up: tool({
61
+ description: "Resume a completed run with a follow-up message.",
62
+ args: {
63
+ run_id: z.string().describe("Completed run ID to resume"),
64
+ message: z.string().describe("Follow-up message text"),
65
+ label: z.string().optional().describe("Override label (defaults to <original>-followup)"),
66
+ supervisor_id: z.string().optional().describe("Reuse an existing supervisor by socket path")
67
+ },
68
+ async execute(args, _context) {
69
+ return followUpTool({
70
+ runId: args.run_id,
71
+ message: args.message,
72
+ label: args.label,
73
+ supervisorId: args.supervisor_id
74
+ });
75
+ }
76
+ }),
77
+ avenor_events: tool({
78
+ description: "Read events from a run. Filter by type. Returns last N events.",
79
+ args: {
80
+ run_id: z.string().describe("Run ID to read events from"),
81
+ types: z.array(z.string()).optional().describe("Filter by event types"),
82
+ limit: z.number().optional().describe("Max events to return (default 50)"),
83
+ supervisor_id: z.string().optional().describe("Reuse an existing supervisor by socket path")
84
+ },
85
+ async execute(args, _context) {
86
+ return eventsTool({
87
+ runId: args.run_id,
88
+ types: args.types,
89
+ limit: args.limit,
90
+ supervisorId: args.supervisor_id
91
+ });
92
+ }
93
+ }),
94
+ avenor_shutdown: tool({
95
+ description: "Shut down the avenor supervisor and clean up temp files.",
96
+ args: {
97
+ supervisor_id: z.string().optional().describe("Supervisor to shut down (defaults to singleton)"),
98
+ force: z.boolean().optional().describe("Force shutdown instead of graceful")
99
+ },
100
+ async execute(args, _context) {
101
+ return shutdownTool({
102
+ supervisorId: args.supervisor_id,
103
+ force: args.force
104
+ });
105
+ }
106
+ })
107
+ } });
108
+ //#endregion
109
+ export { AvenorPlugin };
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@dougbots/avenor-opencode",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "files": [
6
+ "dist"
7
+ ],
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "scripts": {
20
+ "build": "tsdown",
21
+ "test": "bun test",
22
+ "dev": "tsdown --watch"
23
+ },
24
+ "dependencies": {
25
+ "@dougbots/avenor-core": "workspace:*",
26
+ "zod": "^4.4.3"
27
+ },
28
+ "peerDependencies": {
29
+ "@opencode-ai/plugin": "1.15.3"
30
+ },
31
+ "devDependencies": {
32
+ "@opencode-ai/plugin": "1.15.3",
33
+ "@types/bun": "^1.3.12",
34
+ "@types/node": "^25.8.0",
35
+ "tsdown": "^0.22.0",
36
+ "typescript": "^6.0.3",
37
+ "unrun": "^0.3.0"
38
+ }
39
+ }