loadouts 0.2.5 → 0.4.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/bundled/embedded/pi/loadouts-runtime.ts +19677 -0
- package/bundled/extensions/loadouts-runtime/index.ts +19677 -0
- package/bundled/extensions/loadouts-runtime/package.json +6 -0
- package/bundled/loadouts/opencode-runtime.yaml +5 -0
- package/bundled/loadouts/pi-runtime.yaml +5 -0
- package/bundled/opencode/plugins/loadouts/index.js +39198 -0
- package/bundled/opencode/plugins/loadouts/package.json +9 -0
- package/bundled/opencode/plugins/loadouts/tui.js +16913 -0
- package/dist/builtins/agent-transforms.d.ts.map +1 -1
- package/dist/builtins/agent-transforms.js +31 -9
- package/dist/builtins/agent-transforms.js.map +1 -1
- package/dist/builtins/index.d.ts.map +1 -1
- package/dist/builtins/index.js +18 -3
- package/dist/builtins/index.js.map +1 -1
- package/dist/builtins/kinds/extension-dir.d.ts +3 -0
- package/dist/builtins/kinds/extension-dir.d.ts.map +1 -0
- package/dist/builtins/kinds/extension-dir.js +8 -0
- package/dist/builtins/kinds/extension-dir.js.map +1 -0
- package/dist/builtins/kinds/opencode-cli-config.d.ts +3 -0
- package/dist/builtins/kinds/opencode-cli-config.d.ts.map +1 -0
- package/dist/builtins/kinds/opencode-cli-config.js +10 -0
- package/dist/builtins/kinds/opencode-cli-config.js.map +1 -0
- package/dist/builtins/kinds/opencode-command.d.ts +3 -0
- package/dist/builtins/kinds/opencode-command.d.ts.map +1 -0
- package/dist/builtins/kinds/opencode-command.js +8 -0
- package/dist/builtins/kinds/opencode-command.js.map +1 -0
- package/dist/builtins/kinds/opencode-plugin-dir.d.ts +3 -0
- package/dist/builtins/kinds/opencode-plugin-dir.d.ts.map +1 -0
- package/dist/builtins/kinds/opencode-plugin-dir.js +11 -0
- package/dist/builtins/kinds/opencode-plugin-dir.js.map +1 -0
- package/dist/builtins/kinds/opencode-plugin.js +1 -1
- package/dist/builtins/kinds/opencode-plugin.js.map +1 -1
- package/dist/builtins/tools/opencode.d.ts.map +1 -1
- package/dist/builtins/tools/opencode.js +13 -1
- package/dist/builtins/tools/opencode.js.map +1 -1
- package/dist/builtins/tools/pi.d.ts.map +1 -1
- package/dist/builtins/tools/pi.js +2 -1
- package/dist/builtins/tools/pi.js.map +1 -1
- package/dist/cli/commands/diff.d.ts +10 -0
- package/dist/cli/commands/diff.d.ts.map +1 -1
- package/dist/cli/commands/diff.js +71 -30
- package/dist/cli/commands/diff.js.map +1 -1
- package/dist/cli/commands/info.d.ts +10 -0
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +45 -1
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/list.d.ts +18 -0
- package/dist/cli/commands/list.d.ts.map +1 -1
- package/dist/cli/commands/list.js +27 -0
- package/dist/cli/commands/list.js.map +1 -1
- package/dist/cli/commands/session.d.ts +3 -0
- package/dist/cli/commands/session.d.ts.map +1 -0
- package/dist/cli/commands/session.js +68 -0
- package/dist/cli/commands/session.js.map +1 -0
- package/dist/cli/commands/status.d.ts +10 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +100 -2
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/sync.js +6 -0
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/cli/commands/tui.d.ts +3 -0
- package/dist/cli/commands/tui.d.ts.map +1 -0
- package/dist/cli/commands/tui.js +20 -0
- package/dist/cli/commands/tui.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/discovery.js +1 -1
- package/dist/core/discovery.js.map +1 -1
- package/dist/core/import-discovery.d.ts.map +1 -1
- package/dist/core/import-discovery.js +2 -0
- package/dist/core/import-discovery.js.map +1 -1
- package/dist/core/registry.d.ts +5 -0
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js.map +1 -1
- package/dist/core/render.d.ts.map +1 -1
- package/dist/core/render.js +77 -8
- package/dist/core/render.js.map +1 -1
- package/dist/core/resolve.d.ts +1 -1
- package/dist/core/resolve.d.ts.map +1 -1
- package/dist/core/resolve.js +3 -2
- package/dist/core/resolve.js.map +1 -1
- package/dist/core/runtime.d.ts +4 -0
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +10 -3
- package/dist/core/runtime.js.map +1 -1
- package/dist/core/schema.d.ts +59 -0
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +5 -0
- package/dist/core/schema.js.map +1 -1
- package/dist/core/types.d.ts +13 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/integrations/opencode/advertisement.d.ts +7 -0
- package/dist/integrations/opencode/advertisement.d.ts.map +1 -0
- package/dist/integrations/opencode/advertisement.js +17 -0
- package/dist/integrations/opencode/advertisement.js.map +1 -0
- package/dist/integrations/opencode/client.d.ts +12 -0
- package/dist/integrations/opencode/client.d.ts.map +1 -0
- package/dist/integrations/opencode/client.js +48 -0
- package/dist/integrations/opencode/client.js.map +1 -0
- package/dist/integrations/opencode/command.d.ts +11 -0
- package/dist/integrations/opencode/command.d.ts.map +1 -0
- package/dist/integrations/opencode/command.js +19 -0
- package/dist/integrations/opencode/command.js.map +1 -0
- package/dist/integrations/opencode/delta.d.ts +15 -0
- package/dist/integrations/opencode/delta.d.ts.map +1 -0
- package/dist/integrations/opencode/delta.js +75 -0
- package/dist/integrations/opencode/delta.js.map +1 -0
- package/dist/integrations/opencode/session.d.ts +207 -0
- package/dist/integrations/opencode/session.d.ts.map +1 -0
- package/dist/integrations/opencode/session.js +201 -0
- package/dist/integrations/opencode/session.js.map +1 -0
- package/dist/lib/gitignore.d.ts +7 -3
- package/dist/lib/gitignore.d.ts.map +1 -1
- package/dist/lib/gitignore.js +16 -6
- package/dist/lib/gitignore.js.map +1 -1
- package/dist/tui/core/actions.d.ts +8 -0
- package/dist/tui/core/actions.d.ts.map +1 -0
- package/dist/tui/core/actions.js +76 -0
- package/dist/tui/core/actions.js.map +1 -0
- package/dist/tui/core/controller.d.ts +28 -0
- package/dist/tui/core/controller.d.ts.map +1 -0
- package/dist/tui/core/controller.js +78 -0
- package/dist/tui/core/controller.js.map +1 -0
- package/dist/tui/core/data.d.ts +43 -0
- package/dist/tui/core/data.d.ts.map +1 -0
- package/dist/tui/core/data.js +161 -0
- package/dist/tui/core/data.js.map +1 -0
- package/dist/tui/core/host.d.ts +15 -0
- package/dist/tui/core/host.d.ts.map +1 -0
- package/dist/tui/core/host.js +2 -0
- package/dist/tui/core/host.js.map +1 -0
- package/dist/tui/core/intent.d.ts +95 -0
- package/dist/tui/core/intent.d.ts.map +1 -0
- package/dist/tui/core/intent.js +2 -0
- package/dist/tui/core/intent.js.map +1 -0
- package/dist/tui/core/keymap.d.ts +16 -0
- package/dist/tui/core/keymap.d.ts.map +1 -0
- package/dist/tui/core/keymap.js +68 -0
- package/dist/tui/core/keymap.js.map +1 -0
- package/dist/tui/core/model.d.ts +65 -0
- package/dist/tui/core/model.d.ts.map +1 -0
- package/dist/tui/core/model.js +34 -0
- package/dist/tui/core/model.js.map +1 -0
- package/dist/tui/core/selection.d.ts +12 -0
- package/dist/tui/core/selection.d.ts.map +1 -0
- package/dist/tui/core/selection.js +42 -0
- package/dist/tui/core/selection.js.map +1 -0
- package/dist/tui/core/theme.d.ts +15 -0
- package/dist/tui/core/theme.d.ts.map +1 -0
- package/dist/tui/core/theme.js +11 -0
- package/dist/tui/core/theme.js.map +1 -0
- package/dist/tui/core/update.d.ts +8 -0
- package/dist/tui/core/update.d.ts.map +1 -0
- package/dist/tui/core/update.js +206 -0
- package/dist/tui/core/update.js.map +1 -0
- package/dist/tui/core/view.d.ts +4 -0
- package/dist/tui/core/view.d.ts.map +1 -0
- package/dist/tui/core/view.js +162 -0
- package/dist/tui/core/view.js.map +1 -0
- package/dist/tui/core/viewport.d.ts +5 -0
- package/dist/tui/core/viewport.d.ts.map +1 -0
- package/dist/tui/core/viewport.js +33 -0
- package/dist/tui/core/viewport.js.map +1 -0
- package/dist/tui/core/viewspec.d.ts +49 -0
- package/dist/tui/core/viewspec.d.ts.map +1 -0
- package/dist/tui/core/viewspec.js +2 -0
- package/dist/tui/core/viewspec.js.map +1 -0
- package/dist/tui/hosts/pi/embed-entry.d.ts +3 -0
- package/dist/tui/hosts/pi/embed-entry.d.ts.map +1 -0
- package/dist/tui/hosts/pi/embed-entry.js +96 -0
- package/dist/tui/hosts/pi/embed-entry.js.map +1 -0
- package/dist/tui/hosts/pi/inject.d.ts +49 -0
- package/dist/tui/hosts/pi/inject.d.ts.map +1 -0
- package/dist/tui/hosts/pi/inject.js +31 -0
- package/dist/tui/hosts/pi/inject.js.map +1 -0
- package/dist/tui/hosts/standalone.d.ts +2 -0
- package/dist/tui/hosts/standalone.d.ts.map +1 -0
- package/dist/tui/hosts/standalone.js +109 -0
- package/dist/tui/hosts/standalone.js.map +1 -0
- package/dist/tui/skins/pi-tui/input.d.ts +5 -0
- package/dist/tui/skins/pi-tui/input.d.ts.map +1 -0
- package/dist/tui/skins/pi-tui/input.js +42 -0
- package/dist/tui/skins/pi-tui/input.js.map +1 -0
- package/dist/tui/skins/pi-tui/pi-theme-adapter.d.ts +4 -0
- package/dist/tui/skins/pi-tui/pi-theme-adapter.d.ts.map +1 -0
- package/dist/tui/skins/pi-tui/pi-theme-adapter.js +27 -0
- package/dist/tui/skins/pi-tui/pi-theme-adapter.js.map +1 -0
- package/dist/tui/skins/pi-tui/render.d.ts +38 -0
- package/dist/tui/skins/pi-tui/render.d.ts.map +1 -0
- package/dist/tui/skins/pi-tui/render.js +162 -0
- package/dist/tui/skins/pi-tui/render.js.map +1 -0
- package/dist/tui/skins/pi-tui/theme.d.ts +10 -0
- package/dist/tui/skins/pi-tui/theme.d.ts.map +1 -0
- package/dist/tui/skins/pi-tui/theme.js +58 -0
- package/dist/tui/skins/pi-tui/theme.js.map +1 -0
- package/docs/archive/opencode-v2-runtime-beta-18707.md +813 -0
- package/docs/authoring.md +69 -14
- package/docs/commands.md +24 -0
- package/docs/compatibility.md +8 -8
- package/docs/concepts.md +4 -2
- package/docs/opencode-v2-probes.md +253 -0
- package/docs/opencode-v2-runtime.md +129 -0
- package/docs/registry.md +1366 -0
- package/docs/runtime.md +115 -50
- package/docs/troubleshooting.md +20 -1
- package/docs/tui-architecture.md +503 -0
- package/package.json +17 -3
package/docs/authoring.md
CHANGED
|
@@ -26,10 +26,13 @@ targets:
|
|
|
26
26
|
mode: subagent
|
|
27
27
|
steps: 8
|
|
28
28
|
color: accent
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"*"
|
|
32
|
-
|
|
29
|
+
permissions:
|
|
30
|
+
- action: shell
|
|
31
|
+
resource: "*"
|
|
32
|
+
effect: ask
|
|
33
|
+
- action: shell
|
|
34
|
+
resource: "git diff *"
|
|
35
|
+
effect: allow
|
|
33
36
|
|
|
34
37
|
cursor:
|
|
35
38
|
is_background: false
|
|
@@ -219,7 +222,7 @@ include:
|
|
|
219
222
|
|
|
220
223
|
---
|
|
221
224
|
|
|
222
|
-
## OpenCode Config And Plugins
|
|
225
|
+
## OpenCode Config, Commands, And Plugins
|
|
223
226
|
|
|
224
227
|
OpenCode-specific artifacts live under `.loadouts/opencode/` and only render when included in a loadout that targets `opencode`.
|
|
225
228
|
|
|
@@ -228,34 +231,86 @@ OpenCode-specific artifacts live under `.loadouts/opencode/` and only render whe
|
|
|
228
231
|
// .loadouts/opencode/opencode.jsonc
|
|
229
232
|
{
|
|
230
233
|
"$schema": "https://opencode.ai/config.json",
|
|
231
|
-
"
|
|
234
|
+
"plugins": ["opencode-wakatime"]
|
|
232
235
|
}
|
|
233
236
|
```
|
|
234
237
|
|
|
235
238
|
Project scope renders this to `opencode.jsonc`. Global scope renders it to `~/.config/opencode/opencode.jsonc`. This is whole-file ownership; if you do not include this artifact, loadouts does not touch your OpenCode config file.
|
|
236
239
|
|
|
240
|
+
**Slash commands:**
|
|
241
|
+
```md
|
|
242
|
+
<!-- .loadouts/opencode/commands/loadouts.md -->
|
|
243
|
+
# /loadouts
|
|
244
|
+
|
|
245
|
+
Arguments: `$ARGUMENTS`
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Project scope renders this to `.opencode/commands/loadouts.md`. Global scope renders it to `~/.config/opencode/commands/loadouts.md`.
|
|
249
|
+
|
|
237
250
|
**Local plugins:**
|
|
238
251
|
```ts
|
|
239
252
|
// .loadouts/opencode/plugins/notify.ts
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
await $`osascript -e 'display notification "Session completed" with title "opencode"`;
|
|
253
|
+
export default {
|
|
254
|
+
id: "notify",
|
|
255
|
+
async setup(ctx) {
|
|
256
|
+
// register hooks here
|
|
245
257
|
},
|
|
246
|
-
}
|
|
258
|
+
};
|
|
247
259
|
```
|
|
248
260
|
|
|
249
|
-
Project scope renders this to `.opencode/plugins/notify.ts`. Global scope renders it to `~/.config/opencode/plugins/notify.ts`.
|
|
261
|
+
Project scope renders this to `.opencode/plugins/notify.ts`. Global scope renders it to `~/.config/opencode/plugins/notify.ts`. A bare local server plugin file cannot resolve `@opencode/plugin` (local plugin directories do not install dependencies); reference it by path from `opencode.json(c)` `plugins`, or publish a package.
|
|
250
262
|
|
|
251
263
|
**Include in a loadout:**
|
|
252
264
|
```yaml
|
|
253
265
|
include:
|
|
254
266
|
- opencode/opencode.jsonc
|
|
267
|
+
- opencode/commands/loadouts.md
|
|
255
268
|
- opencode/plugins/notify.ts
|
|
256
269
|
```
|
|
257
270
|
|
|
258
|
-
NPM
|
|
271
|
+
NPM packages belong in the managed `opencode.json(c)` `plugins` array. Local plugin source files belong in `.loadouts/opencode/plugins/`.
|
|
272
|
+
|
|
273
|
+
**TUI plugins:**
|
|
274
|
+
|
|
275
|
+
OpenCode V2 CLI plugins are a `./tui` module of a plugin directory, loaded alongside the server entry. The TUI host supplies `@opencode/plugin/tui`, `@opentui/solid`, and `solid-js`, so local TUI sources need no install. They are useful for user-facing UI that should not be added to chat or model-visible prompt context.
|
|
276
|
+
|
|
277
|
+
**Local plugin directories** (kind `opencode-plugin-dir`) render every file in the directory:
|
|
278
|
+
|
|
279
|
+
```yaml
|
|
280
|
+
include:
|
|
281
|
+
- opencode/plugins/loadouts
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The bundled `opencode-runtime` loadout renders a working package-directory example to `plugins/loadouts/` — see `loadouts docs runtime`.
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
// .loadouts/opencode/plugins/status-popup/tui.tsx
|
|
288
|
+
/** @jsxImportSource @opentui/solid */
|
|
289
|
+
|
|
290
|
+
import { Plugin } from "@opencode/plugin/tui";
|
|
291
|
+
|
|
292
|
+
export default Plugin.define({
|
|
293
|
+
id: "status-popup.tui",
|
|
294
|
+
setup(context) {
|
|
295
|
+
context.keymap.layer(() => ({
|
|
296
|
+
commands: [
|
|
297
|
+
{
|
|
298
|
+
id: "status_popup_open",
|
|
299
|
+
title: "Open status popup",
|
|
300
|
+
palette: true,
|
|
301
|
+
slash: { name: "status-popup" },
|
|
302
|
+
run() {
|
|
303
|
+
context.ui.dialog.set({ size: "medium" });
|
|
304
|
+
context.ui.dialog.show(() => <box border paddingLeft={2}>TUI-only popup</box>);
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
}));
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
The server entry (`index.ts` with `{ id, setup }`) sits beside this file in the same directory.
|
|
259
314
|
|
|
260
315
|
---
|
|
261
316
|
|
package/docs/commands.md
CHANGED
|
@@ -52,12 +52,36 @@ Compile a runtime bundle for inspection (no filesystem activation). Defaults to
|
|
|
52
52
|
loadouts runtime # Compile default loadout
|
|
53
53
|
loadouts runtime base backend # Compile multiple loadouts
|
|
54
54
|
loadouts runtime base --tool cursor # Target a different tool
|
|
55
|
+
loadouts runtime base -l # Force local/project scope
|
|
56
|
+
loadouts runtime base -g # Force global scope
|
|
55
57
|
loadouts runtime base --json # Output RuntimeBundle JSON only
|
|
56
58
|
loadouts runtime base --system-block # Output renderRuntimeSystemBlock(bundle) only
|
|
57
59
|
```
|
|
58
60
|
|
|
59
61
|
See `docs/runtime.md` for architecture, OpenCode-first integration flow, and per-tool capability flags.
|
|
60
62
|
|
|
63
|
+
### `loadouts session <status|equip|unequip|list>`
|
|
64
|
+
Manage durable equipment on one OpenCode V2 session without filesystem activation.
|
|
65
|
+
```bash
|
|
66
|
+
loadouts session list
|
|
67
|
+
loadouts session status --session ses_...
|
|
68
|
+
loadouts session equip backend --session ses_... -p
|
|
69
|
+
loadouts session equip caveman --session ses_... -g
|
|
70
|
+
loadouts session unequip --session ses_...
|
|
71
|
+
```
|
|
72
|
+
Omit `--session` to target the most recently updated root session at the current
|
|
73
|
+
directory. `-g`/`-p` select the loadout source scope. All subcommands accept `--json`.
|
|
74
|
+
The running service is discovered automatically and is never started by this command.
|
|
75
|
+
|
|
76
|
+
### `loadouts tui`
|
|
77
|
+
Open the standalone dashboard (Node 22.19+). Use `[` / `]` for All/Global/Project
|
|
78
|
+
tabs and `h` / `l` or `Tab` to change sections. Row actions infer their filesystem
|
|
79
|
+
scope. Press `S` to target a running OpenCode conversation, then `m` to switch
|
|
80
|
+
filesystem/session mode. See [dashboard navigation](runtime.md#opencode-dashboard).
|
|
81
|
+
The embedded OpenCode dashboard
|
|
82
|
+
opens with `/loadouts` or `ctrl+l`; `/loadouts equip <names…> [-g|-p]`,
|
|
83
|
+
`/loadouts unequip`, `/loadouts status`, and `/loadouts quick` run client-side.
|
|
84
|
+
|
|
61
85
|
### `loadouts diff [name]`
|
|
62
86
|
Preview what would change if a loadout were applied.
|
|
63
87
|
```bash
|
package/docs/compatibility.md
CHANGED
|
@@ -4,13 +4,13 @@ Current built-in rendering targets for canonical artifacts.
|
|
|
4
4
|
|
|
5
5
|
## Artifact Paths (Project Scope)
|
|
6
6
|
|
|
7
|
-
| Tool | Rule | Skill | Agent | Instruction |
|
|
8
|
-
|
|
9
|
-
| Claude Code | `.claude/rules/<name>.md` | `.claude/skills/<name>/` | `.claude/agents/<name>.md` | `CLAUDE.md` (generated wrapper to `AGENTS.md`) |
|
|
10
|
-
| Cursor | `.cursor/rules/<name>.mdc` | `.cursor/skills/<name>/` | `.cursor/agents/<name>.md` | `AGENTS.md` |
|
|
11
|
-
| OpenCode | `.opencode/rules/<name>.md` | `.opencode/skills/<name>/` | `.opencode/agents/<name>.md` | `AGENTS.md` |
|
|
12
|
-
| Codex | - (no rule target) | `.agents/skills/<name>/` | `.codex/agents/<name>.toml` | `AGENTS.md` |
|
|
13
|
-
| Pi | - (no native rule target) | `.pi/skills/<name>/` | - | `AGENTS.md` |
|
|
7
|
+
| Tool | Rule | Skill | Agent | Instruction | Extra Artifacts |
|
|
8
|
+
|------|------|-------|-------|-------------|-----------------|
|
|
9
|
+
| Claude Code | `.claude/rules/<name>.md` | `.claude/skills/<name>/` | `.claude/agents/<name>.md` | `CLAUDE.md` (generated wrapper to `AGENTS.md`) | - |
|
|
10
|
+
| Cursor | `.cursor/rules/<name>.mdc` | `.cursor/skills/<name>/` | `.cursor/agents/<name>.md` | `AGENTS.md` | - |
|
|
11
|
+
| OpenCode | `.opencode/rules/<name>.md` | `.opencode/skills/<name>/` | `.opencode/agents/<name>.md` | `AGENTS.md` | `.opencode/commands/<name>.md`, `.opencode/plugins/<name>.{ts,tsx,js}`, `~/.config/opencode/cli.json(c)` (global-only), `opencode.json(c)` |
|
|
12
|
+
| Codex | - (no rule target) | `.agents/skills/<name>/` | `.codex/agents/<name>.toml` | `AGENTS.md` | - |
|
|
13
|
+
| Pi | - (no native rule target) | `.pi/skills/<name>/` | - | `AGENTS.md` | - |
|
|
14
14
|
|
|
15
15
|
Global scope uses each tool's configured global base path. Instruction files may render to home-level `AGENTS.md` or `CLAUDE.md` paths depending on the tool.
|
|
16
16
|
|
|
@@ -24,7 +24,7 @@ Rendered aliases for compatibility:
|
|
|
24
24
|
|
|
25
25
|
- Cursor/OpenCode rules: `paths` is mirrored to `globs`.
|
|
26
26
|
- Cursor/OpenCode rules: `activation` is mirrored to `alwaysApply` (`always -> true`, `scoped -> false`).
|
|
27
|
-
- OpenCode skills: `model-invocable` is mirrored to `disable-model-invocation`
|
|
27
|
+
- OpenCode skills: `model-invocable` is mirrored to `metadata["opencode/autoinvoke"]` (same boolean, not inverted — OpenCode V2 has no `disable-model-invocation` field).
|
|
28
28
|
- Agent overlays are rendered into each harness' native frontmatter. Codex agents are generated as TOML with the Markdown body mapped to `developer_instructions`.
|
|
29
29
|
|
|
30
30
|
Transforms add aliases when canonical fields are present and alias fields are not already set.
|
package/docs/concepts.md
CHANGED
|
@@ -79,7 +79,9 @@ include:
|
|
|
79
79
|
| `prompt` | file | Slash command templates |
|
|
80
80
|
| `extension` | directory | Runtime code extensions |
|
|
81
81
|
| `theme` | file | UI theme configuration |
|
|
82
|
+
| `opencode-command` | file | OpenCode slash command markdown |
|
|
82
83
|
| `opencode-config` | file | Whole-file OpenCode runtime configuration |
|
|
84
|
+
| `opencode-cli-config` | file | Whole-file OpenCode CLI configuration (global-only) |
|
|
83
85
|
| `opencode-plugin` | file | Local OpenCode plugin modules |
|
|
84
86
|
|
|
85
87
|
## Supported Tools
|
|
@@ -88,9 +90,9 @@ include:
|
|
|
88
90
|
|------|-------|--------|--------|--------------|-----------------|
|
|
89
91
|
| Claude Code | `.claude/rules/*.md` | `.claude/skills/` | `.claude/agents/*.md` | `CLAUDE.md` | — |
|
|
90
92
|
| Cursor | `.cursor/rules/*.mdc` | `.cursor/skills/` | `.cursor/agents/*.md` | `AGENTS.md` | — |
|
|
91
|
-
| OpenCode | `.opencode/rules/*.md` | `.opencode/skills/` | `.opencode/agents/*.md` | `AGENTS.md` | `opencode.json(c)`, `.opencode/plugins
|
|
93
|
+
| OpenCode | `.opencode/rules/*.md` | `.opencode/skills/` | `.opencode/agents/*.md` | `AGENTS.md` | `opencode.json(c)`, `~/.config/opencode/cli.json(c)` (global-only), `.opencode/plugins/`, `.opencode/commands/*.md` |
|
|
92
94
|
| Codex | — | `.agents/skills/` | `.codex/agents/*.toml` | `AGENTS.md` | — |
|
|
93
|
-
| Pi |
|
|
95
|
+
| Pi | — | `.pi/skills/` | — | `AGENTS.md` | `.pi/prompts/*.md`, `.pi/extensions/`, `.pi/themes/` |
|
|
94
96
|
|
|
95
97
|
## Sources (Cross-Project Config)
|
|
96
98
|
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# OpenCode V2 probes — P3/P4/P5/P6 source evidence
|
|
2
|
+
|
|
3
|
+
Status: **source-verified pass** (2026-09-14). Answers the open probes in
|
|
4
|
+
`plans/OPENCODE-V2-RUNTIME.md` §4. Every claim below was read out of exact
|
|
5
|
+
sources; nothing is inferred from docs or memory. No live TUI/service was
|
|
6
|
+
started or modified.
|
|
7
|
+
|
|
8
|
+
Version sources used (in preference order — the shared scratch under
|
|
9
|
+
`/private/var/folders/hh/y90cz08d14j4nl2l8nncn7bh0000gn/T/opencode` had several
|
|
10
|
+
ghosted/symlinked `node_modules` dirs, so tarball and git-tag sources were
|
|
11
|
+
preferred over loose scratch files):
|
|
12
|
+
|
|
13
|
+
- **opencode source, tag `v2.0.3`** — fetched raw from
|
|
14
|
+
`github.com/sst/opencode` (redirects to `anomalyco/opencode`); core files
|
|
15
|
+
also present as extracts in scratch `src/` (names prefixed
|
|
16
|
+
`core_*` = `packages/core/src/...`).
|
|
17
|
+
- **`@opencode/plugin@2.0.3`** — extracted tarball, scratch
|
|
18
|
+
`plugin-2.0.3/package/dist/` (its `package.json` pins deps:
|
|
19
|
+
`@opencode/{ai,client,protocol,schema,util}@2.0.3`, peer
|
|
20
|
+
`@opentui/{core,solid} >=0.5.10`, `solid-js >=1.9.0`).
|
|
21
|
+
- **`@opencode/util@2.0.3`**, **`@opencode/ai@2.0.3`** — extracted tarballs
|
|
22
|
+
(scratch `util203-*`, `ai-2.0.3/package`).
|
|
23
|
+
- **`@opentui/solid@0.5.11` + `@opentui/core@0.5.11`** — `npm pack`ed into
|
|
24
|
+
scratch `p3-tui/` this session.
|
|
25
|
+
- **Version correction for the plan:** opencode v2.0.3's root `package.json`
|
|
26
|
+
catalog pins `@opentui/{core,keymap,solid} = 0.5.10` and `solid-js =
|
|
27
|
+
1.9.15` — **not** `@opentui/*@0.5.11` as the plan header states. 0.5.11 is
|
|
28
|
+
what `@opencode/plugin@2.0.3`'s peer range permits, but upstream v2.0.3
|
|
29
|
+
builds and ships against 0.5.10.
|
|
30
|
+
|
|
31
|
+
Throughout: **SV** = source verified (file+line read); **LT** = live tested
|
|
32
|
+
(this pass did none); **LT\*** = live-tested in a prior session (cited, not
|
|
33
|
+
re-run).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## P4 — Global `AGENTS.md` discovery candidates
|
|
38
|
+
|
|
39
|
+
**Question:** does V2 read `~/.config/opencode/AGENTS.md` or `~/AGENTS.md` as
|
|
40
|
+
the global instruction file?
|
|
41
|
+
|
|
42
|
+
**Answer (SV):** Global scope reads **only `~/.config/opencode/AGENTS.md`**
|
|
43
|
+
(XDG config dir; `OPENCODE_CONFIG_DIR` honored). `~/AGENTS.md` is **not** a
|
|
44
|
+
global candidate — but it is *not* dead either: it is picked up by the
|
|
45
|
+
**project ancestor walk** for any session whose location sits under `$HOME`.
|
|
46
|
+
|
|
47
|
+
Evidence, `packages/core/src/config/plugin/instruction.ts` (scratch
|
|
48
|
+
`src/core_config_plugin_instruction.ts`):
|
|
49
|
+
|
|
50
|
+
- L34–37: `start = location.directory` (session cwd ref), `root =
|
|
51
|
+
location.project.directory`, `home = global.home`, and
|
|
52
|
+
`globalFile = join(global.config, "AGENTS.md")`.
|
|
53
|
+
- L36: `const stop = FSUtil.contains(home, start) ? home : root` — the
|
|
54
|
+
ancestor walk extends to `$HOME` whenever the location is under it.
|
|
55
|
+
- L43–50: candidates = `[globalFile]` (only when `discovery.global`, L44) plus
|
|
56
|
+
`ancestorDirectories(start, stop).map(dir => join(dir, "AGENTS.md"))`
|
|
57
|
+
(L46–48). `$HOME` itself is the last element of that walk, so `~/AGENTS.md`
|
|
58
|
+
is a watched candidate for under-home locations.
|
|
59
|
+
- L62–66: `globalSource` reads **only** `globalFile`.
|
|
60
|
+
- L68–75: `projectSource` = `fs.up({ targets: ["AGENTS.md"], start, stop })`.
|
|
61
|
+
- `fs.up` is inclusive of `stop` (`@opencode/util@2.0.3`
|
|
62
|
+
`dist/fs-util.js` L94–114: target checked at `current` before the
|
|
63
|
+
`stop === current` break), and `FSUtil.contains` is inclusive
|
|
64
|
+
(`dist/fs-util.js` L206–209) — so a project rooted at `~` still checks
|
|
65
|
+
`~/AGENTS.md`.
|
|
66
|
+
- Defaults: `project` and `global` discovery are both on unless configured off
|
|
67
|
+
(`src/core_instruction-discovery.ts` L99–100: `options?.project !== false`,
|
|
68
|
+
`options?.global !== false`).
|
|
69
|
+
- `global.config` = `<XDG_CONFIG_HOME || ~/.config>/opencode`
|
|
70
|
+
(`@opencode/util@2.0.3` `dist/global-roots.js` L6–9 + `roots()`), overridden
|
|
71
|
+
by `OPENCODE_CONFIG_DIR` (`src/util_global.ts` L79).
|
|
72
|
+
|
|
73
|
+
**Consequence for Loadouts:** the P4 fear is already neutralized in current
|
|
74
|
+
`src/builtins/tools/opencode.ts` — L9 sets the tool base to
|
|
75
|
+
`~/.config/opencode` and L32–36 render global instructions to
|
|
76
|
+
`{base}/AGENTS.md` with a comment citing exactly this core file. The plan §4's
|
|
77
|
+
premise ("renders global instructions to `{home}/AGENTS.md`") is stale for the
|
|
78
|
+
current source. Remaining nuance worth a comment in the tool spec: a file at
|
|
79
|
+
`~/AGENTS.md` would *also* be discovered — as **project-scope** instructions —
|
|
80
|
+
for every project under `$HOME`, so we must never render global instructions
|
|
81
|
+
there.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## P5 — Is `ui.router.current()` reactive in Solid computations?
|
|
86
|
+
|
|
87
|
+
**Question:** can the dashboard/EQUIPPED bar track session switches via
|
|
88
|
+
`ui.router.current()`?
|
|
89
|
+
|
|
90
|
+
**Answer (SV, not live-tested):** **Yes — at property granularity.**
|
|
91
|
+
`current()` returns the underlying `solid-js/store` proxy. It is reactive when
|
|
92
|
+
its *properties* (`type`, `sessionID`, `name`, `data`) are read inside a
|
|
93
|
+
computation; the call itself reads no signal, so `const r =
|
|
94
|
+
ui.router.current()` followed by property reads later still tracks, but
|
|
95
|
+
treating the returned object as an opaque "snapshot" (e.g. storing it and
|
|
96
|
+
comparing by reference) will not update.
|
|
97
|
+
|
|
98
|
+
Evidence:
|
|
99
|
+
|
|
100
|
+
- `packages/tui/src/context/route.tsx` (v2.0.3): L29–35 the route context is
|
|
101
|
+
`createStore<Route>(...)`; L37–43 `navigate` = `setStore(reconcile(route))`
|
|
102
|
+
(fine-grained per-property notifications, including key removal when
|
|
103
|
+
switching `session` → `plugin`); L38–39 `get data() { return store }`.
|
|
104
|
+
- `packages/tui/src/plugin/api.tsx` (v2.0.3, also scratch
|
|
105
|
+
`p3p5p6-src/api.tsx`): L59 `route: useRoute()` in the plugin host; L176–178
|
|
106
|
+
`current() { return host.route.data }` — a direct store hand-off, no
|
|
107
|
+
signal/memo wrapper.
|
|
108
|
+
- The host app itself relies on exactly this pattern: `packages/tui/src/app.tsx`
|
|
109
|
+
(v2.0.3) reads `route.data.type` / `route.data.sessionID` inside
|
|
110
|
+
`createEffect`s (terminal-title effect, session-environment effect), so
|
|
111
|
+
property-level tracking is the intended contract.
|
|
112
|
+
- Published d.ts (`plugin-2.0.3/package/dist/tui/context.d.ts`): L399–403
|
|
113
|
+
`router.current(): Route` carries **no** "Reactive when read in a Solid
|
|
114
|
+
computation" doc comment — unlike `panel.current()` (L411–415) and
|
|
115
|
+
`tabs.list()` (L420) which do. The doc-comment asymmetry is a trap; the
|
|
116
|
+
implementation is reactive all the same. Worth a note in our plugin code.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## P3 — TUI loader: local plugin dir import host injection
|
|
121
|
+
|
|
122
|
+
**Question:** does a local plugin *dir* without `node_modules` resolve
|
|
123
|
+
`@opencode/plugin/tui` + `@opentui/solid` in the TUI?
|
|
124
|
+
|
|
125
|
+
**Answer (SV):** **Yes, on Bun** (the TUI runtime). The TUI host installs a
|
|
126
|
+
process-wide Bun loader plugin *before* any plugin source is imported, which
|
|
127
|
+
rewrites those bare specifiers to host-provided virtual modules. **On Node,
|
|
128
|
+
there is no injection** (plugins must be precompiled). The prior empirical
|
|
129
|
+
result that the **server** loader has no such injection for local dirs
|
|
130
|
+
(LT\*, prior session) stands unchanged — server and TUI have different
|
|
131
|
+
loaders.
|
|
132
|
+
|
|
133
|
+
Evidence chain:
|
|
134
|
+
|
|
135
|
+
1. **Host installs injection at startup** — `packages/tui/src/plugin/context.tsx`
|
|
136
|
+
(v2.0.3) L24: `import "#runtime-plugin-support"`, mapped by
|
|
137
|
+
`packages/tui/package.json` `imports` to
|
|
138
|
+
`src/plugin/runtime-plugin-support.bun.ts` under the `bun` condition
|
|
139
|
+
(default/`node` maps to `.node.ts`, which is a single comment: "Node loads
|
|
140
|
+
precompiled plugins without it").
|
|
141
|
+
2. **What is injected** — `runtime-plugin-support.bun.ts` (scratch
|
|
142
|
+
`p3p5p6-src/`, L1–8) calls `ensureRuntimePluginSupport({ additional: {
|
|
143
|
+
"@opencode/plugin/tui": { Plugin, PluginContextProvider, usePlugin } } })`.
|
|
144
|
+
3. **Injection mechanism** — `@opentui/solid@0.5.11`
|
|
145
|
+
`scripts/runtime-plugin-support-configure.js`: default injected modules =
|
|
146
|
+
`@opentui/solid`, `@opentui/solid/components`, `@opentui/solid/jsx-runtime`,
|
|
147
|
+
`@opentui/solid/jsx-dev-runtime`, `solid-js`, `solid-js/store` (plus core
|
|
148
|
+
`@opentui/core` / `@opentui/core/testing`); then
|
|
149
|
+
`registerBunPlugin(createRuntimePlugin({ core, additional: modules, rewrite }))`
|
|
150
|
+
and `ensureSolidTransformPlugin({ moduleName:
|
|
151
|
+
runtimeModuleIdForSpecifier("@opentui/solid"), resolvePath })`. The Node
|
|
152
|
+
variant (`scripts/runtime-plugin-support.node.js`) throws
|
|
153
|
+
`"@opentui/solid/runtime-plugin-support is Bun-only…"`.
|
|
154
|
+
4. **Rewrite semantics** — `@opentui/core@0.5.11` `dist/runtime-plugin.js`
|
|
155
|
+
(header comment + L43/L68): matching imports are rewritten to virtual
|
|
156
|
+
`opentui:runtime-module:<encodeURIComponent(specifier)>` ids; **non-`node_modules` source files
|
|
157
|
+
(i.e. our plugin's files) get a dedicated rewrite loader immediately**;
|
|
158
|
+
`node_modules` ESM is recursively prescanned and only rewritten when it
|
|
159
|
+
transitively imports a runtime module. The configure-provided `resolvePath`
|
|
160
|
+
maps any specifier present in the module map (including the additional
|
|
161
|
+
`@opencode/plugin/tui`) to its virtual id and returns `null` for everything
|
|
162
|
+
else (normal resolution).
|
|
163
|
+
5. **`solid-js/dist/solid.js` role** — three exact facts: (a) `@opentui/solid@0.5.11`
|
|
164
|
+
`index.js` (package root) L103/205/210 etc. imports `solid-js/dist/solid.js`
|
|
165
|
+
**directly** — the host's reactive runtime is pinned to the universal
|
|
166
|
+
bundle, bypassing export conditions; (b) `scripts/solid-plugin.js`
|
|
167
|
+
onLoad-swaps `node_modules/solid-js/dist/server.js` → `solid.js` (and
|
|
168
|
+
`solid-js/store/dist/server.js` → `store.js`), so any node_modules dep in a
|
|
169
|
+
plugin graph that resolves solid's server condition gets the same runtime;
|
|
170
|
+
(c) `scripts/solid-transform.js` L20–28 exports
|
|
171
|
+
`resolveNodeSolidRuntimeImport` mapping bare `solid-js` →
|
|
172
|
+
`solid-js/dist/solid.js`, `solid-js/store` → `solid-js/store/dist/store.js`
|
|
173
|
+
— exported helper; no caller found inside the two published tarballs. For
|
|
174
|
+
**plugin source files**, bare `solid-js` does not go down any of these
|
|
175
|
+
paths: it is in the virtual-module map (`defaultRuntimeModules`), so it
|
|
176
|
+
resolves to the host's module instance, and babel `babel-preset-solid`
|
|
177
|
+
compiles JSX with `moduleName` = the virtual `@opentui/solid` id,
|
|
178
|
+
`generate: "universal"`.
|
|
179
|
+
6. **Local dir graph loading** — `packages/tui/src/plugin/discovery.ts`
|
|
180
|
+
(v2.0.3) L22–49: `discoverPluginTargets` returns **directories only** under
|
|
181
|
+
`<globalConfig>/plugins/` and `<project>/.opencode/plugins/`.
|
|
182
|
+
`packages/tui/src/plugin/context.tsx` `resolvePlugin`: local targets →
|
|
183
|
+
`sources.read(entrypoint)` → `@opencode/plugin@2.0.3` `dist/source.js`
|
|
184
|
+
(`createPluginSources`: digest-cached, file-tracked) → `dist/source.bun.js`
|
|
185
|
+
`prepareSource`: scans the local graph with `Bun.Transpiler.scan`
|
|
186
|
+
(**skips `node_modules`**, tracks files for hot reload), then `load: () =>
|
|
187
|
+
Host.load(entrypoint)`; `dist/host.js`: local dir entry resolution is
|
|
188
|
+
**file-convention** — `tui` → `path.resolve(dir, "tui")`, `server` →
|
|
189
|
+
`<dir>/server` or `<dir>/index`, `rpc` → `<dir>/rpc` (package.json
|
|
190
|
+
`exports` only matter for named package targets); `load` = `importModule`
|
|
191
|
+
→ `@opencode/util@2.0.3` `dist/runtime/import.bun.js`: plain dynamic
|
|
192
|
+
`import()` + `Bun.resolveSync` — i.e. the graph flows through Bun's native
|
|
193
|
+
loader, where the plugins from steps 3–4 are registered. Single *file*
|
|
194
|
+
local targets are skipped by the TUI reconcile and reach the TUI via the
|
|
195
|
+
server's plugin list (`source.type === "local"` → `path.dirname`), which
|
|
196
|
+
matches the plan's "single files are dropped by the loader" **V**.
|
|
197
|
+
|
|
198
|
+
**Conclusion for distribution path 1 (local dir, dotfiles):** viable for the
|
|
199
|
+
TUI entry without any `node_modules` — `@opencode/plugin/tui`,
|
|
200
|
+
`@opentui/solid`, and `solid-js` all resolve via host injection on Bun. The
|
|
201
|
+
dir needs `tui.{ts,tsx,js}` (+ optional `server.*`, `rpc.*`) at its root; a
|
|
202
|
+
`package.json` `exports` map is unnecessary for local dirs (it only affects
|
|
203
|
+
npm-package targets). Server entry of the same dir still hits the confirmed
|
|
204
|
+
missing-deps wall (LT\*) — a TUI-only dir plugin is safe; a dir whose server
|
|
205
|
+
entry imports unresolvable packages is not.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## P6 — `CacheHint` fields and semantics in `@opencode/ai`
|
|
210
|
+
|
|
211
|
+
**Question:** exact shape of `SystemPart.cache` and what it does downstream.
|
|
212
|
+
|
|
213
|
+
**Answer (SV):**
|
|
214
|
+
|
|
215
|
+
- **Shape** — `@opencode/ai@2.0.3` `dist/schema/options.d.ts` L135–140:
|
|
216
|
+
`CacheHint = { type: "ephemeral" | "persistent"; ttlSeconds?: number }`.
|
|
217
|
+
`dist/schema/messages.d.ts` L1–23: `systemPartSchema = { type: "text",
|
|
218
|
+
text: string, cache?: CacheHint, metadata?: Record<string, unknown> }` with
|
|
219
|
+
`SystemPart.make(text)` convenience. (`CachePolicy` at
|
|
220
|
+
`options.d.ts` L141–155 — request-level `"auto" | "none" | {tools, system,
|
|
221
|
+
messages, ttlSeconds}` — is a separate, per-request concept.)
|
|
222
|
+
- **Anthropic lowering** — `dist/protocols/anthropic-messages.js`:
|
|
223
|
+
L335–341 documents a hard cap of **4 `cache_control` breakpoints per
|
|
224
|
+
request** across `tools`+`system`+`messages`; beyond the cap markers are
|
|
225
|
+
**counted (`breakpoints.dropped`) and silently dropped** (L346–351).
|
|
226
|
+
L343–352: both hint types (`ephemeral` and `persistent`) lower to
|
|
227
|
+
`cache_control: { type: "ephemeral" }`; `ttlBucket(ttlSeconds) === "1h"`
|
|
228
|
+
adds `ttl: "1h"`, else the wire TTL is omitted (provider default 5m).
|
|
229
|
+
System parts each get their own `cache_control` when present (L924–930).
|
|
230
|
+
- **TTL bucket** — `dist/protocols/utils/cache.js` L5:
|
|
231
|
+
`ttlBucket = ttlSeconds !== undefined && ttlSeconds >= 3600 ? "1h" :
|
|
232
|
+
undefined`. So `ttlSeconds >= 3600` → 1h bucket; anything else (including
|
|
233
|
+
omitted) → default 5m. Same mapping used by openrouter
|
|
234
|
+
(`dist/providers/openrouter.js` L8, L57) and bedrock
|
|
235
|
+
(`dist/protocols/utils/bedrock-cache.js` L47).
|
|
236
|
+
- **OpenAI-family** — `dist/protocols/openai-chat.js` L293–294: within a
|
|
237
|
+
message only the **last** cache-bearing part gets the breakpoint
|
|
238
|
+
(`findLast`); system parts are marked individually when any system part
|
|
239
|
+
carries a cache (L344–351).
|
|
240
|
+
- **`persistent` vs `ephemeral`:** no behavioral divergence found in any
|
|
241
|
+
lowering path — every consumer gates on
|
|
242
|
+
`type !== "ephemeral" && type !== "persistent"` and then uses only
|
|
243
|
+
`ttlSeconds`. Treat `type` as an accepted-but-inert discriminator today.
|
|
244
|
+
- **Baseline:** opencode core sets **no** cache hints on its own
|
|
245
|
+
instruction/context system parts (no `cache` usage in
|
|
246
|
+
`packages/core/src/session/{context,instructions,instruction-entry}.ts` or
|
|
247
|
+
`packages/core/src/instructions/index.ts` — instructions render as plain
|
|
248
|
+
strings, `src/core_instructions_index.ts` L165–189). A `cache`-carrying part
|
|
249
|
+
injected by our phase-3 context hook would therefore consume one of the 4
|
|
250
|
+
breakpoints fresh. Practical guidance for the plan's "use `cache`" note:
|
|
251
|
+
one hint on our single injected block is safe; never hint per-artifact
|
|
252
|
+
(breakpoint cap), and `ttlSeconds: 3600` only makes sense for long sessions
|
|
253
|
+
where the 1h Anthropic cache price is worth it.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# OpenCode V2 Runtime
|
|
2
|
+
|
|
3
|
+
Implemented against **OpenCode 2.0.3**, `@opencode/client@2.0.3`,
|
|
4
|
+
`@opencode/plugin@2.0.3`, OpenTUI 0.5.11, and the host's Solid 1.9.15.
|
|
5
|
+
Verified 2026-09-14. See [runtime.md](runtime.md) for usage and
|
|
6
|
+
[the implementation plan](../plans/OPENCODE-V2-RUNTIME.md) for the original scope.
|
|
7
|
+
The [beta investigation](archive/opencode-v2-runtime-beta-18707.md) is archived.
|
|
8
|
+
|
|
9
|
+
## Three equip layers
|
|
10
|
+
|
|
11
|
+
| Layer | Storage | Applies to |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Global | `~/.config/loadouts/.state.json` and rendered tool files | Every location |
|
|
14
|
+
| Project | `.loadouts/.state.json` and rendered tool files | Sessions in the project |
|
|
15
|
+
| Session | OpenCode instruction entries and session permission rules | One session ID |
|
|
16
|
+
|
|
17
|
+
Global/project outputs use OpenCode's native hot reload. Session equips do not
|
|
18
|
+
write tool configuration or the filesystem activation manifests.
|
|
19
|
+
|
|
20
|
+
## Implementation
|
|
21
|
+
|
|
22
|
+
- `src/integrations/opencode/session.ts`: `SessionActivator`, persisted manifest,
|
|
23
|
+
entry size preflight, permission ownership, in-process serialization, rollback.
|
|
24
|
+
- `src/integrations/opencode/delta.ts`: resolver-backed compilation minus intact
|
|
25
|
+
managed OpenCode outputs. Full skill selection survives delta exclusion.
|
|
26
|
+
- `src/integrations/opencode/client.ts`: authenticated service discovery; never
|
|
27
|
+
starts a service during normal use. Reports version mismatches.
|
|
28
|
+
- `src/tui/core/`: shared `EquipLayer`, reducer, controller and `ViewSpec`.
|
|
29
|
+
- `src/tui/hosts/opencode/`: package-dir server/TUI entries and RPC contract.
|
|
30
|
+
- `src/tui/skins/opentui/`: embedded renderer. Standalone/Pi use pi-tui.
|
|
31
|
+
|
|
32
|
+
The TUI plugin calls the generated client supplied by OpenCode. `/loadouts` is
|
|
33
|
+
registered in its client-side keymap; dashboard commands never become prompts.
|
|
34
|
+
Router data captures the originating session ID. The footer uses reactive slot
|
|
35
|
+
session IDs and native instruction/permission events, with no cache-file IPC.
|
|
36
|
+
|
|
37
|
+
## Verified probes
|
|
38
|
+
|
|
39
|
+
| Probe | Result |
|
|
40
|
+
|---|---|
|
|
41
|
+
| P1: session skill deny | **Live:** blocks the permission path but does **not** remove the skill advertisement. The server context hook filters denied `<skill>` blocks. Two sessions with opposite gates produced opposite advertisements. |
|
|
42
|
+
| P2: instruction entries | **Live:** first entry appears after `AGENTS.md` in the system instruction part; a replacement produces a native “supersedes” update in messages on the next turn. The original system prefix is retained — this is future-turn supersession, not history rewriting. A control session had no entry. |
|
|
43
|
+
| P3: local package dependencies | **Live:** local server package dirs do not install/resolve `@opencode/plugin` merely because it is declared in package.json. Server libraries are bundled. TUI host aliases resolve bare `@opencode/plugin/tui`, `@opentui/solid`, and `solid-js`; rewriting Solid to `solid-js/dist/solid.js` fails. |
|
|
44
|
+
| P4: global instructions | Source: `{global.config}/AGENTS.md`. The built-in renderer now writes `{base}/AGENTS.md`, normally `~/.config/opencode/AGENTS.md`. |
|
|
45
|
+
| P5: router reactivity | Dashboard target is explicitly captured in route data; footer receives reactive slot props. See [source evidence](opencode-v2-probes.md). |
|
|
46
|
+
| P6: cache hint | See [source evidence](opencode-v2-probes.md). Entries use the server's native caching/supersession behavior. No unmeasured cache-cost warning is shown. |
|
|
47
|
+
|
|
48
|
+
Source locations and remaining distinctions between source verification and live
|
|
49
|
+
observation are recorded in [opencode-v2-probes.md](opencode-v2-probes.md).
|
|
50
|
+
|
|
51
|
+
## Persistence and ownership
|
|
52
|
+
|
|
53
|
+
One instruction entry per nonempty loadout delta uses a readable, collision-safe
|
|
54
|
+
`loadouts.bundle.<slug>-<hash>` key. `loadouts.manifest` records names, source scope,
|
|
55
|
+
fingerprint, keys, selected skills, prior/applied permission rules and selections.
|
|
56
|
+
Keys satisfy `^[a-z0-9][a-z0-9._-]*$`. Each JSON-serialized UTF-8 value is checked
|
|
57
|
+
against the server's **256 KiB per-entry** limit before any mutation.
|
|
58
|
+
|
|
59
|
+
Permission replacement appends denies for unselected catalog skills to the prior
|
|
60
|
+
session rules. It never adds an allow that could override an agent's deny. The
|
|
61
|
+
activator waits for plugin activation before reading the initial catalog.
|
|
62
|
+
Unequip restores prior permissions only when the current rules still match those
|
|
63
|
+
Loadouts applied; external edits are preserved and reported. Unrelated instruction
|
|
64
|
+
entries are preserved. Re-equips remove obsolete owned keys.
|
|
65
|
+
|
|
66
|
+
Agent/model declarations are applied per session. Existing explicit selections
|
|
67
|
+
are restored on unequip unless subsequently changed externally. V2 has no API to
|
|
68
|
+
clear an explicit selection back to an implicit default; if there was no explicit
|
|
69
|
+
prior selection, the selected value remains after unequip.
|
|
70
|
+
|
|
71
|
+
Partial HTTP failures trigger rollback of entries, permissions and known previous
|
|
72
|
+
selections. An incomplete rollback is surfaced explicitly. Operations on one
|
|
73
|
+
session are serialized within a process. **The V2 APIs provide neither an atomic
|
|
74
|
+
multi-resource transaction nor compare-and-swap:** a process crash or simultaneous
|
|
75
|
+
mutations by different clients can interrupt or race an equip. Inspect session
|
|
76
|
+
status/entries/rules before recovering such a session. A model step can observe
|
|
77
|
+
intermediate state if equip overlaps that step; the completed equip governs the
|
|
78
|
+
next step.
|
|
79
|
+
|
|
80
|
+
## Server enhancements
|
|
81
|
+
|
|
82
|
+
The package server entry filters skill advertisements by session permission
|
|
83
|
+
rules. It indexes loadout skills via `skill.transform`, preserving existing
|
|
84
|
+
catalog IDs, and advertises selected off-disk skills per session. Those additions
|
|
85
|
+
are location-wide catalog entries, with automatic invocation disabled by default.
|
|
86
|
+
The manifest remains authoritative for off-disk advertisement selection.
|
|
87
|
+
|
|
88
|
+
Headless `/loadouts equip`, auto-equip via
|
|
89
|
+
`loadouts.yaml: opencode.defaultSession`, and typed RPC share the activator.
|
|
90
|
+
In 2.0.3, `Plugin.Context.session` lacks instruction-entry methods, so these
|
|
91
|
+
optional server operations discover the managed local service. They require that
|
|
92
|
+
service; the HTTP/TUI session activation path itself uses the connected client.
|
|
93
|
+
Without managed-service discovery, ordinary instruction entries and on-disk
|
|
94
|
+
permission filtering still work, and off-disk skills remain path references.
|
|
95
|
+
|
|
96
|
+
The deny list and virtual catalog are snapshots at equip/plugin load respectively.
|
|
97
|
+
Re-equip after changing skill membership; reload the plugin to index newly added
|
|
98
|
+
off-disk loadouts. Global/project filesystem skill changes remain native hot reload.
|
|
99
|
+
|
|
100
|
+
## Build and verify
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
npm run build
|
|
104
|
+
npm run lint
|
|
105
|
+
npm test
|
|
106
|
+
node scripts/opencode-session-e2e.mjs
|
|
107
|
+
OPENCODE_E2E_MODEL=provider/model node scripts/opencode-session-e2e.mjs --prompts
|
|
108
|
+
node scripts/opencode-session-restart-e2e.mjs
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The first E2E harness creates and removes its own two sessions. `--prompts`
|
|
112
|
+
adds three model turns to verify the outgoing skill advertisements and instruction
|
|
113
|
+
supersession. The restart harness uses separate XDG config/state/data/cache roots
|
|
114
|
+
and stops only the service it created. Neither restarts the shared working service.
|
|
115
|
+
|
|
116
|
+
The OpenCode build uses Solid's **universal Babel transform**. Its host-owned
|
|
117
|
+
runtime imports stay external; the server implementation is self-contained.
|
|
118
|
+
Standalone still needs Node 22.19+ for pi-tui. OpenTUI requires Bun or Node 26.4+
|
|
119
|
+
with experimental FFI, and is only used inside OpenCode.
|
|
120
|
+
|
|
121
|
+
## V2 gotchas retained
|
|
122
|
+
|
|
123
|
+
- `instructions` in `opencode.json` is accepted but unresolved. Render instructions
|
|
124
|
+
to `AGENTS.md`; never depend on that array.
|
|
125
|
+
- `POST …/skill` works on 2.0.3; the archived beta's failure is obsolete.
|
|
126
|
+
- Documentation uses `/api/experimental/session/…` for some operations while the
|
|
127
|
+
installed 2.0.3 API uses `/api/session/…`. Always use the pinned generated client.
|
|
128
|
+
- Native instruction updates retain earlier context with supersession notices.
|
|
129
|
+
Unequip changes future turns; it does not erase information the model has seen.
|