@skillstate/opencode 2.0.4 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -29
- package/dist/opencode-adapter.d.ts +11 -25
- package/dist/opencode-adapter.d.ts.map +1 -1
- package/dist/opencode-adapter.js +25 -163
- package/dist/opencode-adapter.js.map +1 -1
- package/dist/plugin.d.ts +28 -5
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +45 -10
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@skillstate/opencode)
|
|
8
8
|
[](https://www.npmjs.com/package/@skillstate/opencode)
|
|
9
|
-
[](https://github.com/vitkuz573/skillstate)
|
|
10
10
|
[](https://github.com/vitkuz573/skillstate/blob/main/LICENSE)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
@@ -43,19 +43,20 @@ const adapter = new OpenCodeAdapter();
|
|
|
43
43
|
// execution_context block pointing at the persisted state file:
|
|
44
44
|
const skillMd = adapter.generateSkillMd(INTERCODE_CTF_SPEC, './.skillstate.json');
|
|
45
45
|
|
|
46
|
-
// Plugin with real O(1) history trimming via experimental.chat.messages.transform
|
|
47
|
-
|
|
46
|
+
// Plugin with real O(1) history trimming via experimental.chat.messages.transform.
|
|
47
|
+
// The state path is resolved per session from the host cwd inside the plugin —
|
|
48
|
+
// no baked path:
|
|
49
|
+
const plugin = adapter.generatePluginCode();
|
|
48
50
|
|
|
49
51
|
// Default keeps the last 3 non-system messages + state injection.
|
|
50
52
|
// Configure history depth:
|
|
51
|
-
const plugin2 = adapter.generatePluginCode(
|
|
53
|
+
const plugin2 = adapter.generatePluginCode({
|
|
52
54
|
maxHistoryMessages: 5, // keep last 5 non-system messages
|
|
53
55
|
});
|
|
54
56
|
|
|
55
57
|
// Persist the plugin to disk atomically:
|
|
56
58
|
const saved = await adapter.savePluginCode(
|
|
57
59
|
'./skillstate.plugin.ts',
|
|
58
|
-
'./.skillstate.json',
|
|
59
60
|
{ maxHistoryMessages: 5 },
|
|
60
61
|
);
|
|
61
62
|
```
|
|
@@ -73,16 +74,17 @@ import { OpenCodeAdapter } from '@skillstate/opencode';
|
|
|
73
74
|
import { writeFileSync } from 'node:fs';
|
|
74
75
|
|
|
75
76
|
const adapter = new OpenCodeAdapter();
|
|
76
|
-
const
|
|
77
|
-
const code = adapter.generatePluginCode(statePath, { maxHistoryMessages: 3 });
|
|
77
|
+
const code = adapter.generatePluginCode({ maxHistoryMessages: 3 });
|
|
78
78
|
writeFileSync('/abs/path/to/skillstate.plugin.ts', code);
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
Put the generated `skillstate.plugin.ts` anywhere stable (absolute path is
|
|
82
|
-
safest), e.g. `<project>/.opencode-runtime/skillstate.plugin.ts`.
|
|
82
|
+
safest), e.g. `<project>/.opencode-runtime/skillstate.plugin.ts`. The plugin
|
|
83
|
+
must be able to resolve `@skillstate/opencode` at load time (install it in
|
|
84
|
+
the project or globally) — hook logic lives in that package.
|
|
83
85
|
|
|
84
|
-
**2. Create the initial state file** (`
|
|
85
|
-
spec schema, e.g. for `INTERCODE_CTF_SPEC`:
|
|
86
|
+
**2. Create the initial state file** (`<project>/.skillstate/skillstate.json`),
|
|
87
|
+
matching your spec schema, e.g. for `INTERCODE_CTF_SPEC`:
|
|
86
88
|
|
|
87
89
|
```json
|
|
88
90
|
{
|
|
@@ -126,39 +128,42 @@ Root path `@skillstate/opencode` exports the adapter and the static plugin:
|
|
|
126
128
|
- `new OpenCodeAdapter()` — implements `PlatformAdapter` (`name = 'opencode'`).
|
|
127
129
|
- `generateSkillMd(spec, statePath?): string` — a `SKILL.md` body with
|
|
128
130
|
frontmatter and a state-based process description.
|
|
129
|
-
- `generatePluginCode(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
hatch for environments without npm resolution of this package. Hooks:
|
|
131
|
+
- `generatePluginCode(options?): string` — a thin plugin loader
|
|
132
|
+
(`import { createSkillStatePlugin } from '@skillstate/opencode'`;
|
|
133
|
+
`options.maxHistoryMessages`, default 3). State resolution is always
|
|
134
|
+
per-project and lives inside the static plugin. Hooks:
|
|
134
135
|
`experimental.chat.messages.transform` (real history trimming),
|
|
135
136
|
`experimental.session.compacting` (inject state into compaction context),
|
|
136
137
|
`tool.execute.after` (persist `state_patch` to disk).
|
|
137
|
-
- `createSkillStatePlugin({
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
- `createSkillStatePlugin({ maxHistoryMessages? })` — the static plugin
|
|
139
|
+
factory (single source of truth for the hook logic); state is resolved from
|
|
140
|
+
the session cwd on every hook call via
|
|
141
|
+
`resolveStatePathForCwd(process.cwd(), os.homedir())` —
|
|
142
|
+
`<cwd>/.skillstate/skillstate.json`, or the global bucket
|
|
143
|
+
`<home>/.skillstate/global/skillstate.json` when the session runs from
|
|
144
|
+
`$HOME`. Re-exported from `@skillstate/opencode/plugin` as well.
|
|
145
|
+
- `resolveStatePathForCwd(cwd, home): string` — the per-project state path
|
|
146
|
+
resolution (pure path arithmetic, no filesystem access).
|
|
140
147
|
- `readSkillState` / `saveSkillState` / `mergePatch` / `extractPatch` — the
|
|
141
148
|
plugin's state helpers, shared by the static plugin.
|
|
142
|
-
- `savePluginCode(target,
|
|
143
|
-
|
|
149
|
+
- `savePluginCode(target, options?): Promise<string>` — writes the plugin
|
|
150
|
+
atomically and returns the destination. `target` accepts a raw path or a
|
|
151
|
+
`{ root, name }` ref confined by `resolveStatePath` (`..` escapes throw).
|
|
144
152
|
- `injectState(state, spec): string` / `formatPrompt(state, observation, spec): string`.
|
|
145
153
|
- `extractPatch(response): StatePatch | null` / `extractAction(response): string | null`.
|
|
146
154
|
|
|
147
|
-
Both `generatePluginCode`/`savePluginCode` accept a raw path (legacy) or a
|
|
148
|
-
`{ root, name }` ref confined by `resolveStatePath` (`..` escapes throw).
|
|
149
|
-
|
|
150
155
|
## Notes
|
|
151
156
|
|
|
152
157
|
- **Real O(1).** Unlike Claude Code and Codex, OpenCode exposes
|
|
153
158
|
`experimental.chat.messages.transform`, so the plugin drops old messages
|
|
154
159
|
instead of just hiding them — only the last N non-system messages plus an
|
|
155
160
|
injected state message reach the LLM.
|
|
156
|
-
- The
|
|
157
|
-
`createSkillStatePlugin` from `@skillstate/opencode` (one source of truth)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
161
|
+
- The generated plugin is a **thin loader**: it imports
|
|
162
|
+
`createSkillStatePlugin` from `@skillstate/opencode` (one source of truth).
|
|
163
|
+
Hook logic lives only in `src/plugin.ts` — regenerate rather than editing
|
|
164
|
+
generated files. State resolves per session:
|
|
165
|
+
`<cwd>/.skillstate/skillstate.json` (global bucket from `~` when the
|
|
166
|
+
session cwd is `$HOME`).
|
|
162
167
|
- Depends on [`@skillstate/core`](../core) for `PromptTransformer`,
|
|
163
168
|
`atomicWriteFile`, and `resolveStatePath`.
|
|
164
169
|
|
|
@@ -18,11 +18,6 @@ import type { StatePathRef } from '@skillstate/core';
|
|
|
18
18
|
export interface GeneratePluginOptions {
|
|
19
19
|
/** Non-system messages kept by the plugin (default 3). */
|
|
20
20
|
maxHistoryMessages?: number;
|
|
21
|
-
/**
|
|
22
|
-
* Inline the full plugin instead of the thin loader — escape hatch for
|
|
23
|
-
* environments without npm resolution of `@skillstate/opencode`.
|
|
24
|
-
*/
|
|
25
|
-
standalone?: boolean;
|
|
26
21
|
}
|
|
27
22
|
/**
|
|
28
23
|
* OpenCode platform adapter (@non-paper; see module doc).
|
|
@@ -63,14 +58,12 @@ export declare class OpenCodeAdapter implements PlatformAdapter {
|
|
|
63
58
|
/**
|
|
64
59
|
* Generate an OpenCode plugin file.
|
|
65
60
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* escape hatch for environments without npm resolution of
|
|
73
|
-
* `@skillstate/opencode`).
|
|
61
|
+
* The plugin is ALWAYS a thin loader in per-project resolver mode: the
|
|
62
|
+
* state path is computed from the session cwd on every hook call inside
|
|
63
|
+
* the static plugin (`resolveStatePathForCwd(process.cwd(),
|
|
64
|
+
* os.homedir())`), so each project gets its own
|
|
65
|
+
* `<cwd>/.skillstate/skillstate.json` and a session opened anywhere
|
|
66
|
+
* resolves the same state for the same project.
|
|
74
67
|
*
|
|
75
68
|
* Hooks (see `src/plugin.ts`): `experimental.chat.messages.transform`
|
|
76
69
|
* (real O(1) history trimming, `{ info, parts }` entries, in-place
|
|
@@ -78,21 +71,14 @@ export declare class OpenCodeAdapter implements PlatformAdapter {
|
|
|
78
71
|
* context), `tool.execute.after` (persist `state_patch` from
|
|
79
72
|
* `output.output`).
|
|
80
73
|
*/
|
|
81
|
-
generatePluginCode(
|
|
82
|
-
/**
|
|
83
|
-
* @non-paper additive overload: accept a `{ root, name }` ref confined
|
|
84
|
-
* via `resolveStatePath` — `..` escapes throw instead of embedding an
|
|
85
|
-
* unsafe path into the generated plugin.
|
|
86
|
-
*/
|
|
87
|
-
generatePluginCode(stateRef: StatePathRef, options?: GeneratePluginOptions): string;
|
|
74
|
+
generatePluginCode(options?: GeneratePluginOptions): string;
|
|
88
75
|
/**
|
|
89
76
|
* @non-paper additive helper: generate the plugin and persist it via
|
|
90
|
-
* `atomicWriteFile` (tmp + fsync + rename).
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* absolute destination path.
|
|
77
|
+
* `atomicWriteFile` (tmp + fsync + rename). The destination accepts a raw
|
|
78
|
+
* string or a `{ root, name }` ref confined by `resolveStatePath`.
|
|
79
|
+
* Returns the absolute destination path.
|
|
94
80
|
*/
|
|
95
|
-
savePluginCode(target: string | StatePathRef,
|
|
81
|
+
savePluginCode(target: string | StatePathRef, options?: GeneratePluginOptions): Promise<string>;
|
|
96
82
|
/**
|
|
97
83
|
* Describe the schema fields for inclusion in prompts.
|
|
98
84
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opencode-adapter.d.ts","sourceRoot":"","sources":["../src/opencode-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,WAAW,EACX,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAM1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,kBAAkB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"opencode-adapter.d.ts","sourceRoot":"","sources":["../src/opencode-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,WAAW,EACX,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAM1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,qBAAa,eAAgB,YAAW,eAAe;IACrD,QAAQ,CAAC,IAAI,cAAc;IAE3B,OAAO,CAAC,WAAW,CAAmD;IAEtE;;;;OAIG;IACH,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,GAAG,MAAM,CAsB3D;IAED;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAEhD;IAED;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE7C;IAED;;;;;OAKG;IACH,YAAY,CACV,KAAK,EAAE,UAAU,EACjB,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,cAAc,GACnB,MAAM,CAER;IAED;;;;;;;OAOG;IACH,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CA0ChE;IAED;;;;;;;;;;;;;;;OAeG;IACH,kBAAkB,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAa1D;IAED;;;;;OAKG;IACG,cAAc,CAClB,MAAM,EAAE,MAAM,GAAG,YAAY,EAC7B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAOjB;IAMD;;OAEG;IACH,OAAO,CAAC,cAAc;CASvB"}
|
package/dist/opencode-adapter.js
CHANGED
|
@@ -104,42 +104,47 @@ plugin; only the current state and the latest observation matter.
|
|
|
104
104
|
- Reasoning is discarded after execution — put anything you need to persist
|
|
105
105
|
into \`state_patch\`.`;
|
|
106
106
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Generate an OpenCode plugin file.
|
|
109
|
+
*
|
|
110
|
+
* The plugin is ALWAYS a thin loader in per-project resolver mode: the
|
|
111
|
+
* state path is computed from the session cwd on every hook call inside
|
|
112
|
+
* the static plugin (`resolveStatePathForCwd(process.cwd(),
|
|
113
|
+
* os.homedir())`), so each project gets its own
|
|
114
|
+
* `<cwd>/.skillstate/skillstate.json` and a session opened anywhere
|
|
115
|
+
* resolves the same state for the same project.
|
|
116
|
+
*
|
|
117
|
+
* Hooks (see `src/plugin.ts`): `experimental.chat.messages.transform`
|
|
118
|
+
* (real O(1) history trimming, `{ info, parts }` entries, in-place
|
|
119
|
+
* mutation), `experimental.session.compacting` (state into compaction
|
|
120
|
+
* context), `tool.execute.after` (persist `state_patch` from
|
|
121
|
+
* `output.output`).
|
|
122
|
+
*/
|
|
123
|
+
generatePluginCode(options) {
|
|
111
124
|
const maxHistory = options?.maxHistoryMessages ?? 3;
|
|
112
|
-
if (options?.standalone) {
|
|
113
|
-
return standalonePluginCode(statePath, maxHistory);
|
|
114
|
-
}
|
|
115
|
-
const sp = JSON.stringify(statePath);
|
|
116
125
|
return `// OpenCode plugin (thin loader) generated by skillstate.
|
|
117
126
|
// Hook logic lives in the static plugin — the single source of truth —
|
|
118
|
-
// shipped in the @skillstate/opencode package.
|
|
127
|
+
// shipped in the @skillstate/opencode package. PER-PROJECT state is
|
|
128
|
+
// resolved from the session cwd on every hook call —
|
|
129
|
+
// <cwd>/.skillstate/skillstate.json (global bucket when cwd === home).
|
|
119
130
|
import { createSkillStatePlugin } from '@skillstate/opencode';
|
|
120
131
|
|
|
121
132
|
export default createSkillStatePlugin({
|
|
122
|
-
statePath: ${sp},
|
|
123
133
|
maxHistoryMessages: ${maxHistory},
|
|
124
134
|
});
|
|
125
135
|
`;
|
|
126
136
|
}
|
|
127
137
|
/**
|
|
128
138
|
* @non-paper additive helper: generate the plugin and persist it via
|
|
129
|
-
* `atomicWriteFile` (tmp + fsync + rename).
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* absolute destination path.
|
|
139
|
+
* `atomicWriteFile` (tmp + fsync + rename). The destination accepts a raw
|
|
140
|
+
* string or a `{ root, name }` ref confined by `resolveStatePath`.
|
|
141
|
+
* Returns the absolute destination path.
|
|
133
142
|
*/
|
|
134
|
-
async savePluginCode(target,
|
|
143
|
+
async savePluginCode(target, options) {
|
|
135
144
|
const dest = typeof target === 'string'
|
|
136
145
|
? target
|
|
137
146
|
: resolveStatePath(target.root, target.name);
|
|
138
|
-
|
|
139
|
-
? statePath
|
|
140
|
-
: resolveStatePath(statePath.root, statePath.name);
|
|
141
|
-
const plugin = this.generatePluginCode(resolvedState, options);
|
|
142
|
-
await atomicWriteFile(dest, plugin);
|
|
147
|
+
await atomicWriteFile(dest, this.generatePluginCode(options));
|
|
143
148
|
return dest;
|
|
144
149
|
}
|
|
145
150
|
/* ------------------------------------------------------------------ */
|
|
@@ -155,147 +160,4 @@ export default createSkillStatePlugin({
|
|
|
155
160
|
return `## Schema\n${fields}`;
|
|
156
161
|
}
|
|
157
162
|
}
|
|
158
|
-
/**
|
|
159
|
-
* Standalone (escape-hatch) plugin template — a self-contained inline copy of
|
|
160
|
-
* `src/plugin.ts` for environments without npm resolution of
|
|
161
|
-
* `@skillstate/opencode`. Escape hatch only: `src/plugin.ts` remains the
|
|
162
|
-
* single source of truth; regenerate rather than editing generated files.
|
|
163
|
-
*/
|
|
164
|
-
function standalonePluginCode(statePath, maxHistory) {
|
|
165
|
-
const sp = JSON.stringify(statePath);
|
|
166
|
-
return `// OpenCode plugin (standalone escape hatch) generated by skillstate.
|
|
167
|
-
// Self-contained copy of createSkillStatePlugin from @skillstate/opencode.
|
|
168
|
-
// Real O(1) prompt economy via experimental.chat.messages.transform:
|
|
169
|
-
// trims history to last ${maxHistory} non-system messages + injected state.
|
|
170
|
-
|
|
171
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
172
|
-
import type { Plugin } from "@opencode-ai/plugin";
|
|
173
|
-
|
|
174
|
-
const STATE_PATH = ${sp};
|
|
175
|
-
const MAX_HISTORY = ${maxHistory};
|
|
176
|
-
|
|
177
|
-
function readSkillState(): Record<string, unknown> {
|
|
178
|
-
try {
|
|
179
|
-
if (existsSync(STATE_PATH)) {
|
|
180
|
-
return JSON.parse(readFileSync(STATE_PATH, "utf-8"));
|
|
181
|
-
}
|
|
182
|
-
} catch {
|
|
183
|
-
// Corrupt or unreadable state file — fall back to empty state.
|
|
184
|
-
}
|
|
185
|
-
return {};
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function saveSkillState(state: Record<string, unknown>): void {
|
|
189
|
-
try {
|
|
190
|
-
writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
|
|
191
|
-
} catch {
|
|
192
|
-
// Best-effort: read-only environments or permission issues.
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
197
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function mergePatch(base: Record<string, unknown>, patch: Record<string, unknown>): Record<string, unknown> {
|
|
201
|
-
const result = { ...base };
|
|
202
|
-
for (const key of Object.keys(patch)) {
|
|
203
|
-
const value = patch[key];
|
|
204
|
-
if (value === null) {
|
|
205
|
-
delete result[key];
|
|
206
|
-
} else if (isPlainObject(value) && isPlainObject(result[key])) {
|
|
207
|
-
result[key] = mergePatch(result[key] as Record<string, unknown>, value);
|
|
208
|
-
} else {
|
|
209
|
-
result[key] = value;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return result;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function extractPatch(response: string): Record<string, unknown> | null {
|
|
216
|
-
const match = response.match(/\`\`\`json\\s*\\n?([\\s\\S]*?)\\n?\\s*\`\`\`/);
|
|
217
|
-
if (!match) return null;
|
|
218
|
-
try {
|
|
219
|
-
const parsed: unknown = JSON.parse(match[1]);
|
|
220
|
-
if (isPlainObject(parsed) && isPlainObject(parsed["state_patch"])) {
|
|
221
|
-
return parsed["state_patch"];
|
|
222
|
-
}
|
|
223
|
-
} catch {
|
|
224
|
-
// Malformed JSON — ignore.
|
|
225
|
-
}
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const SkillStatePlugin: Plugin = async () => {
|
|
230
|
-
return {
|
|
231
|
-
// ── O(1) history trimming ──────────────────────────────────────────
|
|
232
|
-
// Filters messages BEFORE each LLM call: keeps all system messages
|
|
233
|
-
// plus the last ${maxHistory} non-system messages, then injects a
|
|
234
|
-
// synthetic state element. Old messages are DROPPED from the prompt,
|
|
235
|
-
// not just hidden.
|
|
236
|
-
"experimental.chat.messages.transform": async (_input, output) => {
|
|
237
|
-
const state = readSkillState();
|
|
238
|
-
const messages = output.messages;
|
|
239
|
-
const systemMessages = messages.filter((m) => m.info.role === "system");
|
|
240
|
-
const trimmed = messages
|
|
241
|
-
.filter((m) => m.info.role !== "system")
|
|
242
|
-
.slice(-MAX_HISTORY);
|
|
243
|
-
|
|
244
|
-
const stateMessage = {
|
|
245
|
-
info: {
|
|
246
|
-
id: "skillstate-state-inject",
|
|
247
|
-
sessionID: "skillstate",
|
|
248
|
-
role: "user",
|
|
249
|
-
time: { created: 0 },
|
|
250
|
-
agent: "skillstate",
|
|
251
|
-
model: { providerID: "skillstate", modelID: "skillstate" },
|
|
252
|
-
},
|
|
253
|
-
parts: [
|
|
254
|
-
{
|
|
255
|
-
id: "skillstate-state-inject-text",
|
|
256
|
-
sessionID: "skillstate",
|
|
257
|
-
messageID: "skillstate-state-inject",
|
|
258
|
-
type: "text",
|
|
259
|
-
synthetic: true,
|
|
260
|
-
text: "Current skill state (JSON): " + JSON.stringify(state),
|
|
261
|
-
},
|
|
262
|
-
],
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
// The pipeline holds the original array reference — mutate in place.
|
|
266
|
-
const kept = [...systemMessages, ...trimmed, stateMessage];
|
|
267
|
-
messages.length = 0;
|
|
268
|
-
messages.push(...kept);
|
|
269
|
-
},
|
|
270
|
-
|
|
271
|
-
// ── Compaction context injection ───────────────────────────────────
|
|
272
|
-
// Before compaction, inject the current state into the context so the
|
|
273
|
-
// compaction summary preserves state even after history is compressed.
|
|
274
|
-
"experimental.session.compacting": async (_input, output) => {
|
|
275
|
-
const state = readSkillState();
|
|
276
|
-
const stateContext = "Skillstate: " + JSON.stringify(state);
|
|
277
|
-
if (!Array.isArray(output.context)) {
|
|
278
|
-
output.context = [];
|
|
279
|
-
}
|
|
280
|
-
output.context.push(stateContext);
|
|
281
|
-
},
|
|
282
|
-
|
|
283
|
-
// ── State persistence from LLM responses ───────────────────────────
|
|
284
|
-
// After tool execution, extract state_patch from the tool response
|
|
285
|
-
// (output.output), merge it, and save to disk.
|
|
286
|
-
"tool.execute.after": async (_input, output) => {
|
|
287
|
-
const response = output.output ?? "";
|
|
288
|
-
if (typeof response === "string") {
|
|
289
|
-
const patch = extractPatch(response);
|
|
290
|
-
if (patch) {
|
|
291
|
-
saveSkillState(STATE_PATH, mergePatch(readSkillState(), patch));
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
};
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
export default SkillStatePlugin;
|
|
299
|
-
`;
|
|
300
|
-
}
|
|
301
163
|
//# sourceMappingURL=opencode-adapter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opencode-adapter.js","sourceRoot":"","sources":["../src/opencode-adapter.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EACL,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"opencode-adapter.js","sourceRoot":"","sources":["../src/opencode-adapter.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EACL,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAS1B;;GAEG;AACH,MAAM,OAAO,eAAe;IACjB,IAAI,GAAG,UAAU,CAAC;IAEnB,WAAW,GAAG,IAAI,iBAAiB,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IAEtE;;;;OAIG;IACH,WAAW,CAAC,KAAiB,EAAE,IAAoB;QACjD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEpD,OAAO,gBAAgB,IAAI,CAAC,EAAE;gBAClB,IAAI,CAAC,YAAY;EAC/B,UAAU;;EAEV,SAAS;;;;;;;;;;;;;kIAauH,CAAC;IACjI,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,QAAgB;QAC3B,OAAO,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,QAAgB;QAC5B,OAAO,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED;;;;;OAKG;IACH,YAAY,CACV,KAAiB,EACjB,WAAwB,EACxB,IAAoB;QAEpB,OAAO,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;IACtE,CAAC;IAED;;;;;;;OAOG;IACH,eAAe,CAAC,IAAoB,EAAE,SAAkB;QACtD,MAAM,iBAAiB,GAAG,SAAS,IAAI,oBAAoB,CAAC;QAE5D,OAAO;QACH,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;eAClB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC;WACrC,IAAI,CAAC,OAAO;;gBAEP,iBAAiB;;;;IAI7B,IAAI,CAAC,IAAI;;EAEX,IAAI,CAAC,YAAY;;;;yCAIsB,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;wBAuBlC,CAAC;IACvB,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,kBAAkB,CAAC,OAA+B;QAChD,MAAM,UAAU,GAAG,OAAO,EAAE,kBAAkB,IAAI,CAAC,CAAC;QACpD,OAAO;;;;;;;;wBAQa,UAAU;;CAEjC,CAAC;IACA,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,cAAc,CAClB,MAA6B,EAC7B,OAA+B;QAE/B,MAAM,IAAI,GACR,OAAO,MAAM,KAAK,QAAQ;YACxB,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wEAAwE;IACxE,yEAAyE;IACzE,wEAAwE;IAExE;;OAEG;IACK,cAAc,CAAC,MAAgC;QACrD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;aAClC,GAAG,CACF,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAChB,KAAK,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,WAAW,IAAI,gBAAgB,EAAE,CACxE;aACA,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,OAAO,cAAc,MAAM,EAAE,CAAC;IAChC,CAAC;CACF"}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -2,11 +2,27 @@ import type { SkillStatePlugin } from './plugin-types.js';
|
|
|
2
2
|
export * from './plugin-types.js';
|
|
3
3
|
/** Options for {@link createSkillStatePlugin}. */
|
|
4
4
|
export interface SkillStatePluginOptions {
|
|
5
|
-
/** Absolute path of the persisted state file. */
|
|
6
|
-
statePath: string;
|
|
7
5
|
/** Non-system messages kept in the prompt (default 3). */
|
|
8
6
|
maxHistoryMessages?: number;
|
|
9
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the per-project state file for a session working directory
|
|
10
|
+
* (`cwd` of the current opencode session):
|
|
11
|
+
*
|
|
12
|
+
* - `cwd === home` — a session launched straight from `$HOME` has no single
|
|
13
|
+
* project, so state goes to the global bucket
|
|
14
|
+
* `<home>/.skillstate/global/skillstate.json`;
|
|
15
|
+
* - any other cwd (including subdirectories of `$HOME`) — the state lives in
|
|
16
|
+
* the project: `<cwd>/.skillstate/skillstate.json`.
|
|
17
|
+
*
|
|
18
|
+
* Pure path arithmetic: both arguments are normalized via `path.resolve`
|
|
19
|
+
* before comparison, and there is NO filesystem access. The same project
|
|
20
|
+
* directory therefore always maps to the same state file no matter where
|
|
21
|
+
* the host was launched from, while different projects never share state.
|
|
22
|
+
* Zero-dep by design (node builtins only) so generated plugins and the MCP
|
|
23
|
+
* server can inline the same semantics. Keep any copy in sync.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveStatePathForCwd(cwd: string, home: string): string;
|
|
10
26
|
/**
|
|
11
27
|
* Read the state file. Missing or corrupt files yield `{}` (best-effort).
|
|
12
28
|
* The on-disk envelope is `{ version: 1, state }` (migrations-compatible);
|
|
@@ -15,8 +31,9 @@ export interface SkillStatePluginOptions {
|
|
|
15
31
|
export declare function readSkillState(statePath: string): Record<string, unknown>;
|
|
16
32
|
/**
|
|
17
33
|
* Persist the state file (best-effort: read-only environments are ignored).
|
|
18
|
-
*
|
|
19
|
-
*
|
|
34
|
+
* Creates the parent directory when missing (the per-project resolver may
|
|
35
|
+
* target a fresh `<cwd>/.skillstate/`). Writes the `{ version: 1, state }`
|
|
36
|
+
* envelope so `migrate()`/runtime resume read the same file.
|
|
20
37
|
*/
|
|
21
38
|
export declare function saveSkillState(statePath: string, state: Record<string, unknown>): void;
|
|
22
39
|
/**
|
|
@@ -33,6 +50,12 @@ export declare function extractPatch(response: string): Record<string, unknown>
|
|
|
33
50
|
/**
|
|
34
51
|
* Build the OpenCode plugin function with the same behavior for every host
|
|
35
52
|
* entry point (thin generated loaders, direct imports).
|
|
53
|
+
*
|
|
54
|
+
* State resolution is ALWAYS per-project: the state file path is computed
|
|
55
|
+
* from the session cwd on EVERY hook call via
|
|
56
|
+
* `resolveStatePathForCwd(process.cwd(), os.homedir())` — each project gets
|
|
57
|
+
* its own `<cwd>/.skillstate/skillstate.json`, and a session launched from
|
|
58
|
+
* `$HOME` uses the global bucket.
|
|
36
59
|
*/
|
|
37
|
-
export declare function createSkillStatePlugin(options
|
|
60
|
+
export declare function createSkillStatePlugin(options?: SkillStatePluginOptions): SkillStatePlugin;
|
|
38
61
|
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAGV,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B,cAAc,mBAAmB,CAAC;AAElC,kDAAkD;AAClD,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxE;AAOD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqBzE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAOtF;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAY7E;AAKD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB,CAgF9F"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Static OpenCode plugin — the SINGLE SOURCE OF TRUTH for the skillstate
|
|
3
|
-
* host integration. `OpenCodeAdapter.generatePluginCode`
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* without npm resolution of `@skillstate/opencode`.
|
|
3
|
+
* host integration. `OpenCodeAdapter.generatePluginCode` emits a thin loader
|
|
4
|
+
* that imports `createSkillStatePlugin` from this module; the per-project
|
|
5
|
+
* state resolution lives inside the plugin itself.
|
|
7
6
|
*
|
|
8
7
|
* Hooks (opencode 1.17 contract, verified on host):
|
|
9
8
|
* - `experimental.chat.messages.transform` — entries are `{ info, parts }`
|
|
@@ -16,7 +15,34 @@
|
|
|
16
15
|
* ```json `state_patch` block is merged (paper ⊕: null deletes) and saved.
|
|
17
16
|
*/
|
|
18
17
|
import * as fs from 'node:fs';
|
|
18
|
+
import * as os from 'node:os';
|
|
19
|
+
import * as path from 'node:path';
|
|
19
20
|
export * from './plugin-types.js';
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the per-project state file for a session working directory
|
|
23
|
+
* (`cwd` of the current opencode session):
|
|
24
|
+
*
|
|
25
|
+
* - `cwd === home` — a session launched straight from `$HOME` has no single
|
|
26
|
+
* project, so state goes to the global bucket
|
|
27
|
+
* `<home>/.skillstate/global/skillstate.json`;
|
|
28
|
+
* - any other cwd (including subdirectories of `$HOME`) — the state lives in
|
|
29
|
+
* the project: `<cwd>/.skillstate/skillstate.json`.
|
|
30
|
+
*
|
|
31
|
+
* Pure path arithmetic: both arguments are normalized via `path.resolve`
|
|
32
|
+
* before comparison, and there is NO filesystem access. The same project
|
|
33
|
+
* directory therefore always maps to the same state file no matter where
|
|
34
|
+
* the host was launched from, while different projects never share state.
|
|
35
|
+
* Zero-dep by design (node builtins only) so generated plugins and the MCP
|
|
36
|
+
* server can inline the same semantics. Keep any copy in sync.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveStatePathForCwd(cwd, home) {
|
|
39
|
+
const resolvedCwd = path.resolve(cwd);
|
|
40
|
+
const resolvedHome = path.resolve(home);
|
|
41
|
+
if (resolvedCwd === resolvedHome) {
|
|
42
|
+
return path.join(resolvedHome, '.skillstate', 'global', 'skillstate.json');
|
|
43
|
+
}
|
|
44
|
+
return path.join(resolvedCwd, '.skillstate', 'skillstate.json');
|
|
45
|
+
}
|
|
20
46
|
/** True for plain (non-null, non-array) objects. */
|
|
21
47
|
function isPlainObject(value) {
|
|
22
48
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -49,11 +75,13 @@ export function readSkillState(statePath) {
|
|
|
49
75
|
}
|
|
50
76
|
/**
|
|
51
77
|
* Persist the state file (best-effort: read-only environments are ignored).
|
|
52
|
-
*
|
|
53
|
-
*
|
|
78
|
+
* Creates the parent directory when missing (the per-project resolver may
|
|
79
|
+
* target a fresh `<cwd>/.skillstate/`). Writes the `{ version: 1, state }`
|
|
80
|
+
* envelope so `migrate()`/runtime resume read the same file.
|
|
54
81
|
*/
|
|
55
82
|
export function saveSkillState(statePath, state) {
|
|
56
83
|
try {
|
|
84
|
+
fs.mkdirSync(path.dirname(statePath), { recursive: true });
|
|
57
85
|
fs.writeFileSync(statePath, `${JSON.stringify({ version: 1, state }, null, 2)}\n`);
|
|
58
86
|
}
|
|
59
87
|
catch {
|
|
@@ -105,9 +133,15 @@ const STATE_MESSAGE_ID = 'skillstate-state-inject';
|
|
|
105
133
|
/**
|
|
106
134
|
* Build the OpenCode plugin function with the same behavior for every host
|
|
107
135
|
* entry point (thin generated loaders, direct imports).
|
|
136
|
+
*
|
|
137
|
+
* State resolution is ALWAYS per-project: the state file path is computed
|
|
138
|
+
* from the session cwd on EVERY hook call via
|
|
139
|
+
* `resolveStatePathForCwd(process.cwd(), os.homedir())` — each project gets
|
|
140
|
+
* its own `<cwd>/.skillstate/skillstate.json`, and a session launched from
|
|
141
|
+
* `$HOME` uses the global bucket.
|
|
108
142
|
*/
|
|
109
|
-
export function createSkillStatePlugin(options) {
|
|
110
|
-
const
|
|
143
|
+
export function createSkillStatePlugin(options = {}) {
|
|
144
|
+
const resolvePath = () => resolveStatePathForCwd(process.cwd(), os.homedir());
|
|
111
145
|
const maxHistory = options.maxHistoryMessages ?? 3;
|
|
112
146
|
return async () => {
|
|
113
147
|
return {
|
|
@@ -117,7 +151,7 @@ export function createSkillStatePlugin(options) {
|
|
|
117
151
|
// synthetic state element. Old messages are DROPPED from the prompt,
|
|
118
152
|
// not just hidden.
|
|
119
153
|
'experimental.chat.messages.transform': async (_input, output) => {
|
|
120
|
-
const state = readSkillState(
|
|
154
|
+
const state = readSkillState(resolvePath());
|
|
121
155
|
const messages = output.messages;
|
|
122
156
|
const systemMessages = messages.filter((m) => m.info.role === 'system');
|
|
123
157
|
const trimmed = messages
|
|
@@ -155,7 +189,7 @@ export function createSkillStatePlugin(options) {
|
|
|
155
189
|
// Before compaction, inject the current state into the context so the
|
|
156
190
|
// compaction summary preserves state even after history is compressed.
|
|
157
191
|
'experimental.session.compacting': async (_input, output) => {
|
|
158
|
-
const state = readSkillState(
|
|
192
|
+
const state = readSkillState(resolvePath());
|
|
159
193
|
if (!Array.isArray(output.context)) {
|
|
160
194
|
output.context = [];
|
|
161
195
|
}
|
|
@@ -168,6 +202,7 @@ export function createSkillStatePlugin(options) {
|
|
|
168
202
|
const response = output.output ?? '';
|
|
169
203
|
if (typeof response !== 'string')
|
|
170
204
|
return;
|
|
205
|
+
const statePath = resolvePath();
|
|
171
206
|
const patch = extractPatch(response);
|
|
172
207
|
if (patch) {
|
|
173
208
|
saveSkillState(statePath, mergePatch(readSkillState(statePath), patch));
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|