@skillstate/opencode 2.2.2 → 3.0.1

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 (62) hide show
  1. package/README.md +194 -142
  2. package/dist/feedback.d.ts +178 -0
  3. package/dist/feedback.d.ts.map +1 -0
  4. package/dist/feedback.js +235 -0
  5. package/dist/feedback.js.map +1 -0
  6. package/dist/index.d.ts +58 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +48 -3
  9. package/dist/index.js.map +1 -1
  10. package/dist/mode.d.ts +97 -0
  11. package/dist/mode.d.ts.map +1 -0
  12. package/dist/mode.js +111 -0
  13. package/dist/mode.js.map +1 -0
  14. package/dist/opencode-adapter.d.ts +10 -46
  15. package/dist/opencode-adapter.d.ts.map +1 -1
  16. package/dist/opencode-adapter.js +1 -64
  17. package/dist/opencode-adapter.js.map +1 -1
  18. package/dist/paper-mode.d.ts +421 -0
  19. package/dist/paper-mode.d.ts.map +1 -0
  20. package/dist/paper-mode.js +445 -0
  21. package/dist/paper-mode.js.map +1 -0
  22. package/dist/plugin.d.ts +218 -76
  23. package/dist/plugin.d.ts.map +1 -1
  24. package/dist/plugin.js +867 -232
  25. package/dist/plugin.js.map +1 -1
  26. package/dist/response-sink.d.ts +208 -0
  27. package/dist/response-sink.d.ts.map +1 -0
  28. package/dist/response-sink.js +243 -0
  29. package/dist/response-sink.js.map +1 -0
  30. package/dist/runtime.d.ts +203 -0
  31. package/dist/runtime.d.ts.map +1 -0
  32. package/dist/runtime.js +332 -0
  33. package/dist/runtime.js.map +1 -0
  34. package/dist/session-registry.d.ts +148 -0
  35. package/dist/session-registry.d.ts.map +1 -0
  36. package/dist/session-registry.js +236 -0
  37. package/dist/session-registry.js.map +1 -0
  38. package/dist/spec-loader.d.ts +81 -0
  39. package/dist/spec-loader.d.ts.map +1 -0
  40. package/dist/spec-loader.js +163 -0
  41. package/dist/spec-loader.js.map +1 -0
  42. package/dist/state-store.d.ts +126 -0
  43. package/dist/state-store.d.ts.map +1 -0
  44. package/dist/state-store.js +186 -0
  45. package/dist/state-store.js.map +1 -0
  46. package/dist/step-boundary.d.ts +91 -0
  47. package/dist/step-boundary.d.ts.map +1 -0
  48. package/dist/step-boundary.js +109 -0
  49. package/dist/step-boundary.js.map +1 -0
  50. package/dist/system-hint.d.ts +129 -0
  51. package/dist/system-hint.d.ts.map +1 -0
  52. package/dist/system-hint.js +166 -0
  53. package/dist/system-hint.js.map +1 -0
  54. package/dist/tools.d.ts +148 -0
  55. package/dist/tools.d.ts.map +1 -0
  56. package/dist/tools.js +350 -0
  57. package/dist/tools.js.map +1 -0
  58. package/package.json +3 -2
  59. package/dist/plugin-types.d.ts +0 -71
  60. package/dist/plugin-types.d.ts.map +0 -1
  61. package/dist/plugin-types.js +0 -8
  62. package/dist/plugin-types.js.map +0 -1
package/dist/tools.js ADDED
@@ -0,0 +1,350 @@
1
+ /**
2
+ * Native OpenCode tools for skillstate.
3
+ *
4
+ * These are the NATIVE tools: the fast path when the host is opencode v2.
5
+ * They carry a real JSON Schema and structured output, where the MCP server
6
+ * (`@skillstate/mcp`, still shipped and still registered) can only offer a
7
+ * JSON-RPC round-trip and untyped text. Both read the same state file, so
8
+ * the choice between them is about speed and typing on one side and reach
9
+ * on the other -- never about which one is correct.
10
+ *
11
+ * The MCP server was once the ONLY option: an opencode v1 plugin could not
12
+ * contribute first-class tools at all.
13
+ *
14
+ * Three tools, deliberately:
15
+ *
16
+ * - `skillstate_read` - what this session has already saved.
17
+ * - `skillstate_update` - merge a patch (the only write path).
18
+ * - `skillstate_merge` - fold sub-agent notes back into the root session.
19
+ *
20
+ * A fourth "delete everything" tool is intentionally absent: `null` in a
21
+ * patch already deletes a key, and a tool whose only job is to erase state
22
+ * is a foot-gun with no upside.
23
+ *
24
+ * ── Result shape ──────────────────────────────────────────────────────────
25
+ *
26
+ * Every tool returns a DISCRIMINATED result: `{ ok: true, ... }` or
27
+ * `{ ok: false, error }`. This is not a stylistic choice. A tool that
28
+ * declares an `output` schema must return a value matching it, so a failure
29
+ * path that returned only text was rejected by the host with "tool did not
30
+ * return its declared output" — a rejected tool call is worse for the agent
31
+ * than a failed one, because it loses the reason. A validation failure is a
32
+ * normal outcome here, and it is modelled as one.
33
+ *
34
+ * SCOPING is automatic. The current session's scope comes from
35
+ * `ToolContext.sessionID` plus the {@link SessionRegistry}, so the model
36
+ * never passes a session id and can never write another agent's file by
37
+ * guessing one.
38
+ */
39
+ import { validatePatch } from '@skillstate/core';
40
+ import { stateScopeFor } from './session-registry.js';
41
+ /**
42
+ * Largest serialized patch a single `skillstate_update` accepts. The state
43
+ * file is a side channel for a human to read; a model that tries to dump a
44
+ * whole file into it should be told, not silently accommodated.
45
+ */
46
+ export const MAX_PATCH_BYTES = 64 * 1024;
47
+ /* ------------------------------------------------------------------ */
48
+ /* Result plumbing */
49
+ /* ------------------------------------------------------------------ */
50
+ /** Wrap a successful payload in the tool result envelope. */
51
+ function ok(value) {
52
+ return { ok: true, value };
53
+ }
54
+ /** Wrap a refusal in the tool result envelope. */
55
+ function refuse(error) {
56
+ return { ok: false, error };
57
+ }
58
+ /**
59
+ * Render a result as the host-facing tool result.
60
+ *
61
+ * `content` carries the same JSON so the model reads a stable shape whether
62
+ * or not the host surfaces `output` — and a refusal states its reason in
63
+ * plain text rather than burying it in a shape the model must parse.
64
+ */
65
+ function render(result) {
66
+ if (result.ok) {
67
+ return { output: result, content: JSON.stringify(result.value, null, 2) };
68
+ }
69
+ return { output: result, content: result.error };
70
+ }
71
+ /* ------------------------------------------------------------------ */
72
+ /* Schemas */
73
+ /* ------------------------------------------------------------------ */
74
+ function object(properties, required = []) {
75
+ return {
76
+ type: 'object',
77
+ properties,
78
+ required: [...required],
79
+ additionalProperties: false,
80
+ };
81
+ }
82
+ /** The state document: a free-form JSON object. */
83
+ const STATE = { type: 'object', additionalProperties: true };
84
+ const STRING_ARRAY = { type: 'array', items: { type: 'string' } };
85
+ /** The change sets a patch or merge reports. */
86
+ const CHANGES = object({ added: STRING_ARRAY, updated: STRING_ARRAY, deleted: STRING_ARRAY }, ['added', 'updated', 'deleted']);
87
+ /**
88
+ * Build an `output` schema for a result whose success payload is `value`.
89
+ *
90
+ * The `ok` discriminator is the only required key, so a refusal validates
91
+ * exactly as well as a success does — the shape the host checks is the
92
+ * shape every call actually returns.
93
+ */
94
+ function outputSchema(value) {
95
+ return object({
96
+ ok: { type: 'boolean' },
97
+ value: value,
98
+ error: { type: 'string' },
99
+ }, ['ok']);
100
+ }
101
+ const READ_OUTPUT = outputSchema(object({ state: STATE, path: { type: 'string' }, scope: { type: 'string' }, empty: { type: 'boolean' } }, [
102
+ 'state',
103
+ 'path',
104
+ 'scope',
105
+ 'empty',
106
+ ]));
107
+ const UPDATE_OUTPUT = outputSchema(object({ state: STATE, changes: CHANGES, path: { type: 'string' }, scope: { type: 'string' } }, ['state', 'changes', 'path', 'scope']));
108
+ const MERGE_OUTPUT = outputSchema(object({
109
+ state: STATE,
110
+ changes: CHANGES,
111
+ merged: STRING_ARRAY,
112
+ skipped: STRING_ARRAY,
113
+ }, ['state', 'changes', 'merged', 'skipped']));
114
+ /* ------------------------------------------------------------------ */
115
+ /* Input handling */
116
+ /* ------------------------------------------------------------------ */
117
+ /** JSON guard: a usable top-level object value. */
118
+ function isPlainObject(value) {
119
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
120
+ }
121
+ /**
122
+ * Narrow a tool's `input`.
123
+ *
124
+ * A tool declared with a JSON Schema is validated by the host before
125
+ * `execute` runs, so the parameter arrives typed `unknown` and narrowing it
126
+ * here is the honest boundary rather than a cast. Unknown keys are dropped
127
+ * instead of forwarded, so a model that invents a field cannot smuggle it
128
+ * into the patch. The host check is not sufficient on its own: it cannot
129
+ * see a byte budget, and JSON Schema does not describe "no undefined".
130
+ */
131
+ function readInput(input, guard) {
132
+ if (!isPlainObject(input))
133
+ return guard({});
134
+ return guard(input);
135
+ }
136
+ /** Reader for `skillstate_read`: an optional sub-agent scope. */
137
+ function readReadInput(input) {
138
+ return readInput(input, (raw) => {
139
+ const scope = typeof raw['scope'] === 'string' ? raw['scope'].trim() : '';
140
+ return scope.length > 0 ? { scope } : {};
141
+ });
142
+ }
143
+ /** Reader for `skillstate_update`: the required patch. */
144
+ function readUpdateInput(input) {
145
+ return readInput(input, (raw) => ({ patch: raw['patch'] }));
146
+ }
147
+ /** Reader for `skillstate_merge`: optional scope and keep policy. */
148
+ function readMergeInput(input) {
149
+ return readInput(input, (raw) => {
150
+ const scope = typeof raw['scope'] === 'string' ? raw['scope'].trim() : '';
151
+ return {
152
+ ...(scope.length > 0 ? { scope } : {}),
153
+ keep: raw['keep'] === 'source' ? 'source' : 'existing',
154
+ };
155
+ });
156
+ }
157
+ /**
158
+ * Validate and normalize an incoming patch.
159
+ *
160
+ * Returns either the patch or a human-readable reason it was rejected. The
161
+ * checks protect the file; they do not dictate the model's schema. A state
162
+ * note is free-form by design, so the only hard rules are "must be an
163
+ * object", "must be JSON-serializable", and "must fit".
164
+ */
165
+ export function normalizePatch(raw) {
166
+ if (!isPlainObject(raw)) {
167
+ return {
168
+ ok: false,
169
+ reason: '`patch` must be a JSON object, for example {"decisions": ["..."]}.',
170
+ };
171
+ }
172
+ for (const [key, value] of Object.entries(raw)) {
173
+ if (key.length === 0) {
174
+ return { ok: false, reason: '`patch` contains an empty key.' };
175
+ }
176
+ if (value === undefined) {
177
+ return {
178
+ ok: false,
179
+ reason: `\`${key}\` is undefined - omit the key instead, or set it to null to delete it.`,
180
+ };
181
+ }
182
+ }
183
+ let serialized;
184
+ try {
185
+ serialized = JSON.stringify(raw);
186
+ }
187
+ catch {
188
+ return {
189
+ ok: false,
190
+ reason: '`patch` is not JSON-serializable - use plain objects, arrays, strings, numbers, booleans and null.',
191
+ };
192
+ }
193
+ const bytes = Buffer.byteLength(serialized, 'utf-8');
194
+ if (bytes > MAX_PATCH_BYTES) {
195
+ return {
196
+ ok: false,
197
+ reason: `\`patch\` is ${bytes} bytes, over the ${MAX_PATCH_BYTES}-byte limit. Keep file contents in the repository instead and save only the path.`,
198
+ };
199
+ }
200
+ return { ok: true, patch: raw };
201
+ }
202
+ /** Best-effort error text; never leaks a stack into the model context. */
203
+ function errorMessage(error) {
204
+ return error instanceof Error ? error.message : String(error);
205
+ }
206
+ /**
207
+ * The scope a call addresses: an explicit override when the model passed
208
+ * one (only ever a sub-agent scope name it learned from the system fragment
209
+ * or from `skillstate_merge`), otherwise this session's own scope.
210
+ */
211
+ function resolveScope(scope, context, deps) {
212
+ return scope !== undefined && scope.length > 0 ? scope : deps.scopeFor(context.sessionID);
213
+ }
214
+ /* ------------------------------------------------------------------ */
215
+ /* Registration */
216
+ /* ------------------------------------------------------------------ */
217
+ /**
218
+ * Register the skillstate tools on an editor.
219
+ *
220
+ * The callback is synchronous, side-effect-free and replayable: OpenCode
221
+ * re-runs it on every registry rebuild, so it must not do I/O at
222
+ * registration time. All filesystem work happens inside `execute`.
223
+ */
224
+ export function registerTools(editor, deps) {
225
+ editor.namespace({
226
+ name: 'skillstate',
227
+ description: 'Persist project notes across context resets and compaction.',
228
+ });
229
+ /* -- skillstate_read ---------------------------------------------- */
230
+ editor.add({
231
+ name: 'skillstate_read',
232
+ description: 'Read the notes this session has saved for the current project (file: .skillstate/skillstate.json). Use after a context reset or compaction to recover earlier decisions, file paths, and remaining work. Returns the stored document, or an empty object when nothing is saved yet.',
233
+ input: object({
234
+ scope: {
235
+ type: 'string',
236
+ description: 'Optional sub-agent scope to read instead of this session. Omit to read this session own notes.',
237
+ },
238
+ }),
239
+ output: READ_OUTPUT,
240
+ async execute(input, context) {
241
+ const scope = resolveScope(readReadInput(input).scope, context, deps);
242
+ const state = deps.store.read(scope);
243
+ return render(ok({
244
+ state,
245
+ path: deps.store.pathFor(scope),
246
+ scope,
247
+ empty: Object.keys(state).length === 0,
248
+ }));
249
+ },
250
+ });
251
+ /* -- skillstate_update -------------------------------------------- */
252
+ editor.add({
253
+ name: 'skillstate_update',
254
+ description: 'Merge a patch into the notes this session has saved for the project. Keys merge shallowly, nested objects merge recursively, and a null value deletes that key. Use it to record a decision already made, a file path already located, or what is still outstanding - not to log the conversation. Returns the merged document and which keys changed.',
255
+ input: object({
256
+ patch: {
257
+ type: 'object',
258
+ description: 'Sparse patch to merge, e.g. {"decisions":["..."],"todo":["..."]}. Set a key to null to delete it.',
259
+ },
260
+ }, ['patch']),
261
+ output: UPDATE_OUTPUT,
262
+ async execute(input, context) {
263
+ const checked = normalizePatch(readUpdateInput(input).patch);
264
+ if (!checked.ok)
265
+ return render(refuse(checked.reason));
266
+ // §6.2, when there is a P to validate against. Deterministic, runtime-side,
267
+ // and it never sees the model: a refused patch leaves Σ exactly as it was.
268
+ if (deps.schema !== undefined) {
269
+ const verdict = validatePatch(deps.schema, checked.patch);
270
+ if (!verdict.valid) {
271
+ // The declared fields go in the refusal. §6.4's rollback is that a
272
+ // rejected patch has no path into Σ, which means the model gets one
273
+ // more turn with nothing changed — so the refusal has to say what the
274
+ // alternative is, or the model retries the identical patch. Naming the
275
+ // offending key alone is a dead end it cannot act on.
276
+ return render(refuse(`${verdict.error} (field: ${verdict.field}). ` +
277
+ `This project declares: ${Object.keys(deps.schema).join(', ') || 'no fields'}.`));
278
+ }
279
+ }
280
+ const scope = resolveScope(undefined, context, deps);
281
+ try {
282
+ const { state, changes } = await deps.store.patch(scope, checked.patch);
283
+ deps.onWrite?.(scope);
284
+ return render(ok({ state, changes, path: deps.store.pathFor(scope), scope }));
285
+ }
286
+ catch (error) {
287
+ return render(refuse(`Could not save the notes: ${errorMessage(error)}`));
288
+ }
289
+ },
290
+ });
291
+ /* -- skillstate_merge --------------------------------------------- */
292
+ editor.add({
293
+ name: 'skillstate_merge',
294
+ description: 'Fold a sub-agent saved notes into this session notes. Call it with no arguments from a root session to fold in every sub-agent of this session, or pass a sub-agent scope to fold just that one. Nested objects merge; conflicting values keep the existing one unless you pass keep: "source".',
295
+ input: object({
296
+ scope: {
297
+ type: 'string',
298
+ description: 'Sub-agent scope to fold in. Omit to fold in every sub-agent of this session.',
299
+ },
300
+ keep: {
301
+ type: 'string',
302
+ enum: ['existing', 'source'],
303
+ description: 'Which side wins a conflict. Defaults to "existing", this session value.',
304
+ },
305
+ }),
306
+ output: MERGE_OUTPUT,
307
+ async execute(input, context) {
308
+ if (context.signal.aborted)
309
+ return render(refuse('Cancelled.'));
310
+ if (deps.scopeFor(context.sessionID) !== '') {
311
+ return render(refuse('skillstate_merge is only available to the root session. Write your findings with skillstate_update instead - the main session folds them in.'));
312
+ }
313
+ const { scope, keep } = readMergeInput(input);
314
+ // A bulk fold enumerates SESSION ids from the registry, so each one has
315
+ // to be translated to the scope name it writes under. An explicit
316
+ // `scope` is already a scope name (the model read it from the system
317
+ // fragment) and must be passed through untouched.
318
+ const targets = scope !== undefined
319
+ ? [{ scope, label: scope }]
320
+ : deps.sessions
321
+ .descendantsOf(context.sessionID)
322
+ .map((sessionID) => ({ scope: deps.scopeFor(sessionID), label: sessionID }));
323
+ if (targets.length === 0) {
324
+ return render(ok({ state: deps.store.read(''), changes: emptyChanges(), merged: [], skipped: [] }));
325
+ }
326
+ const merged = [];
327
+ const skipped = [];
328
+ let state = {};
329
+ let changes = emptyChanges();
330
+ for (const target of targets) {
331
+ try {
332
+ const result = await deps.store.merge('', target.scope, keep);
333
+ state = result.state;
334
+ changes = result.changes;
335
+ merged.push(target.label);
336
+ }
337
+ catch {
338
+ skipped.push(target.label);
339
+ }
340
+ }
341
+ return render(ok({ state, changes, merged, skipped }));
342
+ },
343
+ });
344
+ }
345
+ /** A change set with nothing in it. */
346
+ function emptyChanges() {
347
+ return { added: [], updated: [], deleted: [] };
348
+ }
349
+ export { stateScopeFor };
350
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillstate/opencode",
3
- "version": "2.2.2",
3
+ "version": "3.0.1",
4
4
  "description": "OpenCode platform adapter for the skillstate runtime.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,6 +25,7 @@
25
25
  },
26
26
  "license": "MIT",
27
27
  "dependencies": {
28
- "@skillstate/core": "^2.2.2"
28
+ "@opencode/plugin": "2.0.19",
29
+ "@skillstate/core": "^3.0.0"
29
30
  }
30
31
  }
@@ -1,71 +0,0 @@
1
- /**
2
- * Minimal local types for OpenCode plugin hooks — the `@opencode-ai/plugin`
3
- * shape, verified against opencode 1.17 (PluginInput/Hooks). Declared locally
4
- * to keep `@skillstate/opencode` zero-dep; the host supplies the real types
5
- * at runtime.
6
- */
7
- /** Message roles seen by `experimental.chat.messages.transform`. */
8
- export type OpenCodeMessageRole = 'user' | 'assistant' | 'system';
9
- /**
10
- * Message envelope: the transform pipeline hands each message as
11
- * `{ info, parts }` — the role lives on `info.role`.
12
- */
13
- export interface OpenCodeMessage {
14
- info: {
15
- role: OpenCodeMessageRole | string;
16
- [key: string]: unknown;
17
- };
18
- parts: Array<{
19
- type: string;
20
- text?: string;
21
- [key: string]: unknown;
22
- }>;
23
- }
24
- /** Output payload of `experimental.chat.messages.transform`. */
25
- export interface MessagesTransformOutput {
26
- /** Mutated IN PLACE — the pipeline keeps the original array reference. */
27
- messages: OpenCodeMessage[];
28
- }
29
- /** Output payload of `experimental.session.compacting`. */
30
- export interface SessionCompactingOutput {
31
- context: string[];
32
- prompt?: string;
33
- }
34
- /** Output payload of `tool.execute.after`. */
35
- export interface ToolExecuteAfterOutput {
36
- title?: string;
37
- /** The tool's string response (state_patch extraction source). */
38
- output: unknown;
39
- metadata?: unknown;
40
- }
41
- /** OpenCode hooks used by the skillstate plugin. */
42
- export interface SkillStateHooks {
43
- /**
44
- * Host event bus (`session.created`/`session.updated` carry the full
45
- * Session, including `parentID`) — feeds the sub-agent registry.
46
- */
47
- event?: (input: {
48
- event: unknown;
49
- }) => Promise<void>;
50
- 'experimental.chat.messages.transform'?: (input: Record<string, never>, output: MessagesTransformOutput) => Promise<void>;
51
- 'experimental.session.compacting'?: (input: {
52
- sessionID: string;
53
- }, output: SessionCompactingOutput) => Promise<void>;
54
- 'tool.execute.after'?: (input: {
55
- tool: string;
56
- sessionID: string;
57
- callID: string;
58
- args: unknown;
59
- }, output: ToolExecuteAfterOutput) => Promise<void>;
60
- }
61
- /**
62
- * OpenCode plugin: an async factory receiving the host input and returning
63
- * the hooks object (`Plugin` from `@opencode-ai/plugin`).
64
- */
65
- export type SkillStatePlugin = (input: {
66
- project?: unknown;
67
- client?: unknown;
68
- $?: unknown;
69
- [key: string]: unknown;
70
- }) => Promise<SkillStateHooks>;
71
- //# sourceMappingURL=plugin-types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin-types.d.ts","sourceRoot":"","sources":["../src/plugin-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,oEAAoE;AACpE,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE;QAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACrE,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;CACvE;AAED,gEAAgE;AAChE,MAAM,WAAW,uBAAuB;IACtC,0EAA0E;IAC1E,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,8CAA8C;AAC9C,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,sCAAsC,CAAC,EAAE,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAC5B,MAAM,EAAE,uBAAuB,KAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,iCAAiC,CAAC,EAAE,CAClC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,EAC5B,MAAM,EAAE,uBAAuB,KAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,oBAAoB,CAAC,EAAE,CACrB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,EACzE,MAAM,EAAE,sBAAsB,KAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,CAAC,CAAC,EAAE,OAAO,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC"}
@@ -1,8 +0,0 @@
1
- /**
2
- * Minimal local types for OpenCode plugin hooks — the `@opencode-ai/plugin`
3
- * shape, verified against opencode 1.17 (PluginInput/Hooks). Declared locally
4
- * to keep `@skillstate/opencode` zero-dep; the host supplies the real types
5
- * at runtime.
6
- */
7
- export {};
8
- //# sourceMappingURL=plugin-types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin-types.js","sourceRoot":"","sources":["../src/plugin-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}