@snarkipus/opencode-beads 0.9.4 → 0.10.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 +24 -4
- package/package.json +1 -1
- package/src/plugin-core.ts +30 -6
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Add the version-pinned plugin to `~/.config/opencode/opencode.json`:
|
|
|
30
30
|
|
|
31
31
|
```json
|
|
32
32
|
{
|
|
33
|
-
"plugin": ["@snarkipus/opencode-beads@0.
|
|
33
|
+
"plugin": ["@snarkipus/opencode-beads@0.10.0"]
|
|
34
34
|
}
|
|
35
35
|
```
|
|
36
36
|
|
|
@@ -38,13 +38,13 @@ Restart OpenCode. Update the pin explicitly when upgrading; an unpinned package
|
|
|
38
38
|
|
|
39
39
|
### 0.9.3 recovery notice
|
|
40
40
|
|
|
41
|
-
Version 0.9.3 contained command guidance from the withdrawn upstream Beads v1.2.1 release. This plugin did not bundle the `bd` binary or implement database migrations, but it could invoke a consumer-installed v1.2.1 binary through `bd prime`. Even one v1.2.1 execution could migrate the database. Upgrade the plugin package to 0.9.4; if v1.2.1 was installed and may have run, also follow the [upstream database recovery guidance](https://github.com/gastownhall/beads/blob/v1.2.2/docs/RECOVERY-1.2.1.md).
|
|
41
|
+
Version 0.9.3 contained command guidance from the withdrawn upstream Beads v1.2.1 release. This plugin did not bundle the `bd` binary or implement database migrations, but it could invoke a consumer-installed v1.2.1 binary through `bd prime`. Even one v1.2.1 execution could migrate the database. Upgrade the plugin package to 0.9.4 or later; if v1.2.1 was installed and may have run, also follow the [upstream database recovery guidance](https://github.com/gastownhall/beads/blob/v1.2.2/docs/RECOVERY-1.2.1.md).
|
|
42
42
|
|
|
43
43
|
## Context injection
|
|
44
44
|
|
|
45
45
|
The plugin runs full `bd prime` when a primary-agent session first receives a message and after compaction. Prime supplies the canonical current workflow, command guidance, and persistent memories; the plugin adds only compact OpenCode-specific safety and delegation guidance.
|
|
46
46
|
|
|
47
|
-
Known regular subagents remain excluded, including `explore` and `general`, while `beads-task-agent`
|
|
47
|
+
Known regular subagents remain excluded, including `explore` and `general`, while `beads-task-agent` and `beads-task-agent-luna` remain eligible. If agent lookup fails, context injection fails open to preserve compatibility with unknown custom primary agents.
|
|
48
48
|
|
|
49
49
|
If `bd` is unavailable, the project is uninitialized, or prime fails or returns no content, injection is silently skipped and remains retryable. Vendored commands stay available, and the task agent retains a bounded standalone quick reference for `ready`, `show`, atomic claim, discovered follow-up, validate before closure, and close.
|
|
50
50
|
|
|
@@ -52,7 +52,7 @@ If `bd` is unavailable, the project is uninitialized, or prime fails or returns
|
|
|
52
52
|
|
|
53
53
|
Vendored workflows are registered under `/beads:*`, including `/beads:ready`, `/beads:create`, and `/beads:show`. The plugin does not generate a command for every `bd` subcommand; use the CLI and the generated [Beads CLI reference](https://beads.gascity.com/cli-reference/index) for the complete surface.
|
|
54
54
|
|
|
55
|
-
Explicit command
|
|
55
|
+
Explicit command definitions in OpenCode configuration replace plugin commands with the same name. Task-agent overrides merge field-by-field over plugin defaults, so a partial override does not discard the bounded prompt or subagent mode. Explicit fields (including `prompt`, `permission`, `tools`, `model`, `variant`, and `disable`) win; nested fields are replaced, not deep-merged. Conflicting `beads:*` commands or either named task-agent profile produce a grouped, rate-limited warning without preventing definitions from loading. An empty profile opt-in does not warn.
|
|
56
56
|
|
|
57
57
|
## Task agent
|
|
58
58
|
|
|
@@ -60,6 +60,26 @@ Explicit command and agent definitions in OpenCode configuration take precedence
|
|
|
60
60
|
|
|
61
61
|
Its runtime prompt is a fork-owned bounded OpenCode adaptation. The vendored upstream task-agent artifact supplies metadata, provenance, and compatibility validation; it is not executed as upstream's autonomous multi-task loop.
|
|
62
62
|
|
|
63
|
+
### Opt-in Luna Max profile
|
|
64
|
+
|
|
65
|
+
The default `beads-task-agent` leaves model and reasoning variant unset, preserving OpenCode's caller inheritance. To add the named Luna Max profile, merge this entry into your project `opencode.json` alongside the plugin entry:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"agent": {
|
|
70
|
+
"beads-task-agent-luna": {}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The plugin fills this profile with `model: "openai/gpt-5.6-luna"` and `variant: "max"`, using exactly the same shared prompt, subagent mode, and inherited permissions/tools as the vanilla agent. Per-agent overrides remain independent: customizations to `beads-task-agent` are not copied to Luna. Both profiles get full prime context at startup and after compaction and retain the same exact-one-Bead workflow.
|
|
76
|
+
|
|
77
|
+
Choose `beads-task-agent-luna` explicitly as the task tool's `subagent_type` (or ask the primary agent to delegate to that name). Default delegation guidance still names `beads-task-agent`; enabling Luna does not reroute existing calls or change the primary agent's model/variant. No extra orchestrator or plugin-specific config file is involved.
|
|
78
|
+
|
|
79
|
+
Omit the Luna entry from the effective configuration to keep it unregistered; set `"disable": true` inside the entry to disable it using OpenCode's normal agent configuration. To undo the opt-in, remove the entry at its source and restart OpenCode; omission in a child project does not remove an ancestor/global entry. Explicit routing or prompt overrides are honored, so changing those fields opts out of the corresponding profile defaults.
|
|
80
|
+
|
|
81
|
+
Luna requires access to `openai/gpt-5.6-luna` and its `max` variant in your OpenCode provider setup. The plugin does not install models, configure authentication, or silently fall back to another model. Native Luna Max routing was verified on OpenCode 1.18.29; the plugin's general compatibility range is not a guarantee that every provider account exposes this model.
|
|
82
|
+
|
|
63
83
|
## Vendored content
|
|
64
84
|
|
|
65
85
|
[`scripts/sync-beads.sh`](scripts/sync-beads.sh) copies the reviewed upstream command templates and task-agent artifact into `vendor/`. [`vendor/manifest.json`](vendor/manifest.json) pins the upstream tag and commit and records source paths, sorted inventory, byte lengths, and SHA-256 checksums. A deterministic adaptation layer translates reviewed host-specific instructions to OpenCode's CLI-only model.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snarkipus/opencode-beads",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A maintained OpenCode integration for the Beads issue tracker, including context injection, vendored workflows, and a task agent.",
|
|
6
6
|
"author": "Matt Jackson",
|
package/src/plugin-core.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Hooks } from "@opencode-ai/plugin";
|
|
2
|
+
import type { AgentConfig as AgentConfigV2 } from "@opencode-ai/sdk/v2";
|
|
2
3
|
import type {
|
|
3
4
|
Agent,
|
|
4
5
|
SessionMessagesResponse,
|
|
@@ -11,6 +12,12 @@ import { PrimeTimeoutError } from "./prime";
|
|
|
11
12
|
import { beadsGuidance, loadAgent, loadCommands } from "./vendor";
|
|
12
13
|
|
|
13
14
|
const BEADS_TASK_AGENT = "beads-task-agent";
|
|
15
|
+
const BEADS_TASK_AGENT_LUNA = "beads-task-agent-luna";
|
|
16
|
+
|
|
17
|
+
/** Both named profiles use the same bounded task-agent context. */
|
|
18
|
+
function isBeadsTaskAgent(name: string | undefined): boolean {
|
|
19
|
+
return name === BEADS_TASK_AGENT || name === BEADS_TASK_AGENT_LUNA;
|
|
20
|
+
}
|
|
14
21
|
|
|
15
22
|
type ChatMessageInput = Parameters<NonNullable<Hooks["chat.message"]>>[0];
|
|
16
23
|
type SessionMessageResponse = SessionMessagesResponse[number];
|
|
@@ -99,7 +106,7 @@ function beadsContextAudience(text: string): InjectionAudience | undefined {
|
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
function injectionAudience(agentName: string | undefined): InjectionAudience {
|
|
102
|
-
return agentName
|
|
109
|
+
return isBeadsTaskAgent(agentName) ? "task-agent" : "primary";
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
/** Resolve OpenCode project scope without falling back to the process directory. */
|
|
@@ -160,7 +167,7 @@ export async function createBeadsController(
|
|
|
160
167
|
agentName: string | undefined,
|
|
161
168
|
sessionID: string
|
|
162
169
|
): Promise<boolean> {
|
|
163
|
-
if (!agentName || agentName
|
|
170
|
+
if (!agentName || isBeadsTaskAgent(agentName)) return true;
|
|
164
171
|
|
|
165
172
|
const availableAgents = await runtime.getAgents(directory).catch(async () => {
|
|
166
173
|
await diagnoseSession("agents_lookup_failed", sessionID);
|
|
@@ -315,16 +322,29 @@ export async function createBeadsController(
|
|
|
315
322
|
},
|
|
316
323
|
|
|
317
324
|
async configure(config) {
|
|
325
|
+
// A named config entry opts in without another plugin option or config file.
|
|
326
|
+
const configuredAgents = { ...agents };
|
|
327
|
+
if (Object.hasOwn(config.agent ?? {}, BEADS_TASK_AGENT_LUNA)) {
|
|
328
|
+
configuredAgents[BEADS_TASK_AGENT_LUNA] = {
|
|
329
|
+
...agents[BEADS_TASK_AGENT],
|
|
330
|
+
model: "openai/gpt-5.6-luna",
|
|
331
|
+
variant: "max",
|
|
332
|
+
} satisfies AgentConfigV2;
|
|
333
|
+
}
|
|
318
334
|
const commandCollisions = Object.keys(config.command ?? {})
|
|
319
335
|
.filter(
|
|
320
336
|
(name) =>
|
|
321
337
|
Object.hasOwn(commands, name) && config.command?.[name] !== commands[name]
|
|
322
338
|
)
|
|
323
339
|
.sort();
|
|
324
|
-
const agentCollisions = Object.
|
|
325
|
-
.filter(
|
|
326
|
-
|
|
340
|
+
const agentCollisions = Object.entries(configuredAgents)
|
|
341
|
+
.filter(([name, defaults]) =>
|
|
342
|
+
defaults !== undefined &&
|
|
343
|
+
Object.entries(config.agent?.[name] ?? {}).some(
|
|
344
|
+
([key, value]) => value !== defaults[key]
|
|
345
|
+
)
|
|
327
346
|
)
|
|
347
|
+
.map(([name]) => name)
|
|
328
348
|
.sort();
|
|
329
349
|
|
|
330
350
|
await Promise.all(
|
|
@@ -341,7 +361,11 @@ export async function createBeadsController(
|
|
|
341
361
|
);
|
|
342
362
|
|
|
343
363
|
config.command = { ...commands, ...config.command };
|
|
344
|
-
|
|
364
|
+
const mergedAgents = { ...config.agent };
|
|
365
|
+
for (const [name, defaults] of Object.entries(configuredAgents)) {
|
|
366
|
+
mergedAgents[name] = { ...defaults, ...config.agent?.[name] };
|
|
367
|
+
}
|
|
368
|
+
config.agent = mergedAgents;
|
|
345
369
|
},
|
|
346
370
|
};
|
|
347
371
|
}
|