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.
Files changed (209) hide show
  1. package/bundled/embedded/pi/loadouts-runtime.ts +19677 -0
  2. package/bundled/extensions/loadouts-runtime/index.ts +19677 -0
  3. package/bundled/extensions/loadouts-runtime/package.json +6 -0
  4. package/bundled/loadouts/opencode-runtime.yaml +5 -0
  5. package/bundled/loadouts/pi-runtime.yaml +5 -0
  6. package/bundled/opencode/plugins/loadouts/index.js +39198 -0
  7. package/bundled/opencode/plugins/loadouts/package.json +9 -0
  8. package/bundled/opencode/plugins/loadouts/tui.js +16913 -0
  9. package/dist/builtins/agent-transforms.d.ts.map +1 -1
  10. package/dist/builtins/agent-transforms.js +31 -9
  11. package/dist/builtins/agent-transforms.js.map +1 -1
  12. package/dist/builtins/index.d.ts.map +1 -1
  13. package/dist/builtins/index.js +18 -3
  14. package/dist/builtins/index.js.map +1 -1
  15. package/dist/builtins/kinds/extension-dir.d.ts +3 -0
  16. package/dist/builtins/kinds/extension-dir.d.ts.map +1 -0
  17. package/dist/builtins/kinds/extension-dir.js +8 -0
  18. package/dist/builtins/kinds/extension-dir.js.map +1 -0
  19. package/dist/builtins/kinds/opencode-cli-config.d.ts +3 -0
  20. package/dist/builtins/kinds/opencode-cli-config.d.ts.map +1 -0
  21. package/dist/builtins/kinds/opencode-cli-config.js +10 -0
  22. package/dist/builtins/kinds/opencode-cli-config.js.map +1 -0
  23. package/dist/builtins/kinds/opencode-command.d.ts +3 -0
  24. package/dist/builtins/kinds/opencode-command.d.ts.map +1 -0
  25. package/dist/builtins/kinds/opencode-command.js +8 -0
  26. package/dist/builtins/kinds/opencode-command.js.map +1 -0
  27. package/dist/builtins/kinds/opencode-plugin-dir.d.ts +3 -0
  28. package/dist/builtins/kinds/opencode-plugin-dir.d.ts.map +1 -0
  29. package/dist/builtins/kinds/opencode-plugin-dir.js +11 -0
  30. package/dist/builtins/kinds/opencode-plugin-dir.js.map +1 -0
  31. package/dist/builtins/kinds/opencode-plugin.js +1 -1
  32. package/dist/builtins/kinds/opencode-plugin.js.map +1 -1
  33. package/dist/builtins/tools/opencode.d.ts.map +1 -1
  34. package/dist/builtins/tools/opencode.js +13 -1
  35. package/dist/builtins/tools/opencode.js.map +1 -1
  36. package/dist/builtins/tools/pi.d.ts.map +1 -1
  37. package/dist/builtins/tools/pi.js +2 -1
  38. package/dist/builtins/tools/pi.js.map +1 -1
  39. package/dist/cli/commands/diff.d.ts +10 -0
  40. package/dist/cli/commands/diff.d.ts.map +1 -1
  41. package/dist/cli/commands/diff.js +71 -30
  42. package/dist/cli/commands/diff.js.map +1 -1
  43. package/dist/cli/commands/info.d.ts +10 -0
  44. package/dist/cli/commands/info.d.ts.map +1 -1
  45. package/dist/cli/commands/info.js +45 -1
  46. package/dist/cli/commands/info.js.map +1 -1
  47. package/dist/cli/commands/list.d.ts +18 -0
  48. package/dist/cli/commands/list.d.ts.map +1 -1
  49. package/dist/cli/commands/list.js +27 -0
  50. package/dist/cli/commands/list.js.map +1 -1
  51. package/dist/cli/commands/session.d.ts +3 -0
  52. package/dist/cli/commands/session.d.ts.map +1 -0
  53. package/dist/cli/commands/session.js +68 -0
  54. package/dist/cli/commands/session.js.map +1 -0
  55. package/dist/cli/commands/status.d.ts +10 -0
  56. package/dist/cli/commands/status.d.ts.map +1 -1
  57. package/dist/cli/commands/status.js +100 -2
  58. package/dist/cli/commands/status.js.map +1 -1
  59. package/dist/cli/commands/sync.d.ts.map +1 -1
  60. package/dist/cli/commands/sync.js +6 -0
  61. package/dist/cli/commands/sync.js.map +1 -1
  62. package/dist/cli/commands/tui.d.ts +3 -0
  63. package/dist/cli/commands/tui.d.ts.map +1 -0
  64. package/dist/cli/commands/tui.js +20 -0
  65. package/dist/cli/commands/tui.js.map +1 -0
  66. package/dist/cli/index.d.ts.map +1 -1
  67. package/dist/cli/index.js +4 -0
  68. package/dist/cli/index.js.map +1 -1
  69. package/dist/core/discovery.js +1 -1
  70. package/dist/core/discovery.js.map +1 -1
  71. package/dist/core/import-discovery.d.ts.map +1 -1
  72. package/dist/core/import-discovery.js +2 -0
  73. package/dist/core/import-discovery.js.map +1 -1
  74. package/dist/core/registry.d.ts +5 -0
  75. package/dist/core/registry.d.ts.map +1 -1
  76. package/dist/core/registry.js.map +1 -1
  77. package/dist/core/render.d.ts.map +1 -1
  78. package/dist/core/render.js +77 -8
  79. package/dist/core/render.js.map +1 -1
  80. package/dist/core/resolve.d.ts +1 -1
  81. package/dist/core/resolve.d.ts.map +1 -1
  82. package/dist/core/resolve.js +3 -2
  83. package/dist/core/resolve.js.map +1 -1
  84. package/dist/core/runtime.d.ts +4 -0
  85. package/dist/core/runtime.d.ts.map +1 -1
  86. package/dist/core/runtime.js +10 -3
  87. package/dist/core/runtime.js.map +1 -1
  88. package/dist/core/schema.d.ts +59 -0
  89. package/dist/core/schema.d.ts.map +1 -1
  90. package/dist/core/schema.js +5 -0
  91. package/dist/core/schema.js.map +1 -1
  92. package/dist/core/types.d.ts +13 -0
  93. package/dist/core/types.d.ts.map +1 -1
  94. package/dist/integrations/opencode/advertisement.d.ts +7 -0
  95. package/dist/integrations/opencode/advertisement.d.ts.map +1 -0
  96. package/dist/integrations/opencode/advertisement.js +17 -0
  97. package/dist/integrations/opencode/advertisement.js.map +1 -0
  98. package/dist/integrations/opencode/client.d.ts +12 -0
  99. package/dist/integrations/opencode/client.d.ts.map +1 -0
  100. package/dist/integrations/opencode/client.js +48 -0
  101. package/dist/integrations/opencode/client.js.map +1 -0
  102. package/dist/integrations/opencode/command.d.ts +11 -0
  103. package/dist/integrations/opencode/command.d.ts.map +1 -0
  104. package/dist/integrations/opencode/command.js +19 -0
  105. package/dist/integrations/opencode/command.js.map +1 -0
  106. package/dist/integrations/opencode/delta.d.ts +15 -0
  107. package/dist/integrations/opencode/delta.d.ts.map +1 -0
  108. package/dist/integrations/opencode/delta.js +75 -0
  109. package/dist/integrations/opencode/delta.js.map +1 -0
  110. package/dist/integrations/opencode/session.d.ts +207 -0
  111. package/dist/integrations/opencode/session.d.ts.map +1 -0
  112. package/dist/integrations/opencode/session.js +201 -0
  113. package/dist/integrations/opencode/session.js.map +1 -0
  114. package/dist/lib/gitignore.d.ts +7 -3
  115. package/dist/lib/gitignore.d.ts.map +1 -1
  116. package/dist/lib/gitignore.js +16 -6
  117. package/dist/lib/gitignore.js.map +1 -1
  118. package/dist/tui/core/actions.d.ts +8 -0
  119. package/dist/tui/core/actions.d.ts.map +1 -0
  120. package/dist/tui/core/actions.js +76 -0
  121. package/dist/tui/core/actions.js.map +1 -0
  122. package/dist/tui/core/controller.d.ts +28 -0
  123. package/dist/tui/core/controller.d.ts.map +1 -0
  124. package/dist/tui/core/controller.js +78 -0
  125. package/dist/tui/core/controller.js.map +1 -0
  126. package/dist/tui/core/data.d.ts +43 -0
  127. package/dist/tui/core/data.d.ts.map +1 -0
  128. package/dist/tui/core/data.js +161 -0
  129. package/dist/tui/core/data.js.map +1 -0
  130. package/dist/tui/core/host.d.ts +15 -0
  131. package/dist/tui/core/host.d.ts.map +1 -0
  132. package/dist/tui/core/host.js +2 -0
  133. package/dist/tui/core/host.js.map +1 -0
  134. package/dist/tui/core/intent.d.ts +95 -0
  135. package/dist/tui/core/intent.d.ts.map +1 -0
  136. package/dist/tui/core/intent.js +2 -0
  137. package/dist/tui/core/intent.js.map +1 -0
  138. package/dist/tui/core/keymap.d.ts +16 -0
  139. package/dist/tui/core/keymap.d.ts.map +1 -0
  140. package/dist/tui/core/keymap.js +68 -0
  141. package/dist/tui/core/keymap.js.map +1 -0
  142. package/dist/tui/core/model.d.ts +65 -0
  143. package/dist/tui/core/model.d.ts.map +1 -0
  144. package/dist/tui/core/model.js +34 -0
  145. package/dist/tui/core/model.js.map +1 -0
  146. package/dist/tui/core/selection.d.ts +12 -0
  147. package/dist/tui/core/selection.d.ts.map +1 -0
  148. package/dist/tui/core/selection.js +42 -0
  149. package/dist/tui/core/selection.js.map +1 -0
  150. package/dist/tui/core/theme.d.ts +15 -0
  151. package/dist/tui/core/theme.d.ts.map +1 -0
  152. package/dist/tui/core/theme.js +11 -0
  153. package/dist/tui/core/theme.js.map +1 -0
  154. package/dist/tui/core/update.d.ts +8 -0
  155. package/dist/tui/core/update.d.ts.map +1 -0
  156. package/dist/tui/core/update.js +206 -0
  157. package/dist/tui/core/update.js.map +1 -0
  158. package/dist/tui/core/view.d.ts +4 -0
  159. package/dist/tui/core/view.d.ts.map +1 -0
  160. package/dist/tui/core/view.js +162 -0
  161. package/dist/tui/core/view.js.map +1 -0
  162. package/dist/tui/core/viewport.d.ts +5 -0
  163. package/dist/tui/core/viewport.d.ts.map +1 -0
  164. package/dist/tui/core/viewport.js +33 -0
  165. package/dist/tui/core/viewport.js.map +1 -0
  166. package/dist/tui/core/viewspec.d.ts +49 -0
  167. package/dist/tui/core/viewspec.d.ts.map +1 -0
  168. package/dist/tui/core/viewspec.js +2 -0
  169. package/dist/tui/core/viewspec.js.map +1 -0
  170. package/dist/tui/hosts/pi/embed-entry.d.ts +3 -0
  171. package/dist/tui/hosts/pi/embed-entry.d.ts.map +1 -0
  172. package/dist/tui/hosts/pi/embed-entry.js +96 -0
  173. package/dist/tui/hosts/pi/embed-entry.js.map +1 -0
  174. package/dist/tui/hosts/pi/inject.d.ts +49 -0
  175. package/dist/tui/hosts/pi/inject.d.ts.map +1 -0
  176. package/dist/tui/hosts/pi/inject.js +31 -0
  177. package/dist/tui/hosts/pi/inject.js.map +1 -0
  178. package/dist/tui/hosts/standalone.d.ts +2 -0
  179. package/dist/tui/hosts/standalone.d.ts.map +1 -0
  180. package/dist/tui/hosts/standalone.js +109 -0
  181. package/dist/tui/hosts/standalone.js.map +1 -0
  182. package/dist/tui/skins/pi-tui/input.d.ts +5 -0
  183. package/dist/tui/skins/pi-tui/input.d.ts.map +1 -0
  184. package/dist/tui/skins/pi-tui/input.js +42 -0
  185. package/dist/tui/skins/pi-tui/input.js.map +1 -0
  186. package/dist/tui/skins/pi-tui/pi-theme-adapter.d.ts +4 -0
  187. package/dist/tui/skins/pi-tui/pi-theme-adapter.d.ts.map +1 -0
  188. package/dist/tui/skins/pi-tui/pi-theme-adapter.js +27 -0
  189. package/dist/tui/skins/pi-tui/pi-theme-adapter.js.map +1 -0
  190. package/dist/tui/skins/pi-tui/render.d.ts +38 -0
  191. package/dist/tui/skins/pi-tui/render.d.ts.map +1 -0
  192. package/dist/tui/skins/pi-tui/render.js +162 -0
  193. package/dist/tui/skins/pi-tui/render.js.map +1 -0
  194. package/dist/tui/skins/pi-tui/theme.d.ts +10 -0
  195. package/dist/tui/skins/pi-tui/theme.d.ts.map +1 -0
  196. package/dist/tui/skins/pi-tui/theme.js +58 -0
  197. package/dist/tui/skins/pi-tui/theme.js.map +1 -0
  198. package/docs/archive/opencode-v2-runtime-beta-18707.md +813 -0
  199. package/docs/authoring.md +69 -14
  200. package/docs/commands.md +24 -0
  201. package/docs/compatibility.md +8 -8
  202. package/docs/concepts.md +4 -2
  203. package/docs/opencode-v2-probes.md +253 -0
  204. package/docs/opencode-v2-runtime.md +129 -0
  205. package/docs/registry.md +1366 -0
  206. package/docs/runtime.md +115 -50
  207. package/docs/troubleshooting.md +20 -1
  208. package/docs/tui-architecture.md +503 -0
  209. 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
- permission:
30
- bash:
31
- "*": ask
32
- "git diff*": allow
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
- "plugin": ["opencode-wakatime"]
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
- import type { Plugin } from "@opencode-ai/plugin";
241
-
242
- export const Notify: Plugin = async ({ $ }) => ({
243
- "session.idle": async () => {
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 plugins belong in the managed `opencode.json(c)` `plugin` array. Local plugin source files belong in `.loadouts/opencode/plugins/`.
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
@@ -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` (inverted boolean).
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 | `.pi/rules/*.md` | `.pi/skills/` | — | `AGENTS.md` | `.pi/extensions/`, `.pi/themes/` |
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.