@skillstate/codex 2.0.6 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codex-adapter.d.ts +39 -15
- package/dist/codex-adapter.d.ts.map +1 -1
- package/dist/codex-adapter.js +70 -266
- package/dist/codex-adapter.js.map +1 -1
- package/package.json +1 -1
package/dist/codex-adapter.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveHostStateForCwd } from '@skillstate/core';
|
|
1
2
|
import type { ProceduralSpec, StatePathRef } from '@skillstate/core';
|
|
2
3
|
/** Codex hook events this adapter generates scripts for (script/CLI names). */
|
|
3
4
|
export type CodexHookEvent = 'user-prompt-submit' | 'session-start-compact' | 'post-tool-use';
|
|
@@ -32,10 +33,20 @@ export interface CodexHooksConfigOptions {
|
|
|
32
33
|
* Resolve the per-project state file for a working directory — the SAME
|
|
33
34
|
* semantics as the OpenCode plugin (`<cwd>/.skillstate/skillstate.json`;
|
|
34
35
|
* the global bucket `<home>/.skillstate/global/skillstate.json` when cwd
|
|
35
|
-
* equals home).
|
|
36
|
-
* (generated scripts
|
|
36
|
+
* equals home). Single source of truth: {@link resolveHostStateForCwd} in
|
|
37
|
+
* `@skillstate/core` (the generated hook scripts keep a byte-equivalent
|
|
38
|
+
* embedded copy because they cannot import `@skillstate/*`).
|
|
39
|
+
*/
|
|
40
|
+
export declare const resolveStateForCwd: typeof resolveHostStateForCwd;
|
|
41
|
+
/**
|
|
42
|
+
* OpenAI Codex platform adapter (@non-paper; see module doc).
|
|
43
|
+
*
|
|
44
|
+
* Every generated hook script is a SELF-CONTAINED CommonJS file (Node
|
|
45
|
+
* builtins only, no `@skillstate/*` import): Codex executes them directly
|
|
46
|
+
* via `node <script> <event>` with the hook JSON on stdin, and each script
|
|
47
|
+
* resolves the per-project state from `input.cwd` at runtime — so one
|
|
48
|
+
* global `hooks.json` + one script directory serve every project.
|
|
37
49
|
*/
|
|
38
|
-
export declare function resolveStateForCwd(cwd: string, home?: string): string;
|
|
39
50
|
export declare class CodexAdapter {
|
|
40
51
|
readonly name = "codex";
|
|
41
52
|
/**
|
|
@@ -79,42 +90,55 @@ export declare class CodexAdapter {
|
|
|
79
90
|
* cwd-resolving and never bakes an absolute state path in.
|
|
80
91
|
*/
|
|
81
92
|
generateHookScript(event: CodexHookEvent, statePath?: string | StatePathRef): string;
|
|
93
|
+
/**
|
|
94
|
+
* Inject-script template (`UserPromptSubmit` / `SessionStart`): brand
|
|
95
|
+
* header + the shared hook-runtime snippet ({@link hookRuntimeSnippet}
|
|
96
|
+
* inlined via `fn.toString()`) + a stdin loop that emits
|
|
97
|
+
* `{ hookSpecificOutput: { hookEventName, additionalContext } }` with the
|
|
98
|
+
* current state JSON read from the SESSION cwd. Brand specifics stay in
|
|
99
|
+
* the adapter (comment lines only); the `contextSuffix` is the shared
|
|
100
|
+
* {@link HISTORY_UNRELIABLE_NOTE} from `@skillstate/core`.
|
|
101
|
+
*/
|
|
102
|
+
private buildInjectScriptTemplate;
|
|
82
103
|
/**
|
|
83
104
|
* Generate a SKILL.md for Codex's skill directory
|
|
84
105
|
* (`~/.codex/skills/<name>/SKILL.md`). The body instructs the agent to
|
|
85
106
|
* treat the hook-injected state as authoritative (history is not
|
|
86
|
-
* reliable), to
|
|
87
|
-
* to persist via `state.patch` — the PostToolUse hook also merges any
|
|
107
|
+
* reliable), to orient via `state.summary` (full dump via `state.get`),
|
|
108
|
+
* and to persist via `state.patch` — the PostToolUse hook also merges any
|
|
88
109
|
* fenced ```json `state_patch` block printed by a Bash tool call.
|
|
89
110
|
*/
|
|
90
111
|
generateSkillMd(spec: ProceduralSpec, statePath?: string): string;
|
|
91
112
|
/**
|
|
92
|
-
* Generate the hooks.json document and persist it via
|
|
93
|
-
* Both the destination and the embedded state path
|
|
94
|
-
* `{ root, name }` refs confined by
|
|
95
|
-
* absolute destination path.
|
|
113
|
+
* Generate the hooks.json document and persist it via the shared
|
|
114
|
+
* `saveGenerated`. Both the destination and the embedded state path
|
|
115
|
+
* accept raw strings or `{ root, name }` refs confined by
|
|
116
|
+
* `resolveStatePath`. Returns the absolute destination path.
|
|
96
117
|
*/
|
|
97
118
|
saveHooksConfig(target: string | StatePathRef, statePath: string | StatePathRef, options?: CodexHooksConfigOptions): Promise<string>;
|
|
98
119
|
/**
|
|
99
120
|
* Merge the skillstate hook groups into an existing `hooks.json` text.
|
|
100
121
|
* Idempotent: if any skillstate command is already wired, the document is
|
|
101
122
|
* returned unchanged. Existing (non-skillstate) hooks are preserved.
|
|
123
|
+
*
|
|
124
|
+
* The mechanics live in the shared {@link mergeHookGroups}; this method
|
|
125
|
+
* supplies only the format specifics (generated hooks.json groups —
|
|
126
|
+
* with the codex `description`/`statusMessage`/`additionalContextLimit`
|
|
127
|
+
* fields — and the `node <script> <event>` skillstate commands).
|
|
102
128
|
*/
|
|
103
129
|
mergeHooksConfig(existingJson: string, options?: CodexHooksConfigOptions): string;
|
|
104
130
|
/**
|
|
105
|
-
* Generate a hook script and persist it via
|
|
106
|
-
* is the script destination (usually
|
|
131
|
+
* Generate a hook script and persist it via the shared `saveGenerated`
|
|
132
|
+
* (atomic temp-rename write). `target` is the script destination (usually
|
|
107
133
|
* {@link CodexAdapter.codexHookScriptPath}); `statePath` is forwarded to
|
|
108
134
|
* {@link generateHookScript}. Returns the absolute destination path.
|
|
109
135
|
*/
|
|
110
136
|
saveHookScript(event: CodexHookEvent, target: string | StatePathRef, statePath?: string | StatePathRef): Promise<string>;
|
|
111
137
|
/**
|
|
112
|
-
* Generate a SKILL.md and persist it via `
|
|
113
|
-
* absolute destination path.
|
|
138
|
+
* Generate a SKILL.md and persist it via the shared `saveGenerated`.
|
|
139
|
+
* Returns the absolute destination path.
|
|
114
140
|
*/
|
|
115
141
|
saveSkillMd(target: string | StatePathRef, spec: ProceduralSpec, statePath?: string): Promise<string>;
|
|
116
|
-
/** Resolve a `string | StatePathRef` via `resolveStatePath` (throws on `..`). */
|
|
117
|
-
private resolve;
|
|
118
142
|
/** PostToolUse script: extract state_patch, ⊕ merge, persist. */
|
|
119
143
|
private buildPostToolUseScript;
|
|
120
144
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-adapter.d.ts","sourceRoot":"","sources":["../src/codex-adapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codex-adapter.d.ts","sourceRoot":"","sources":["../src/codex-adapter.ts"],"names":[],"mappings":"AA4BA,OAAO,EAIL,sBAAsB,EAIvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErE,+EAA+E;AAC/E,MAAM,MAAM,cAAc,GACtB,oBAAoB,GACpB,uBAAuB,GACvB,eAAe,CAAC;AAEpB,6DAA6D;AAC7D,eAAO,MAAM,iBAAiB,2EAIgB,CAAC;AAE/C,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,cAAc,CAAC;AAEvD,2DAA2D;AAC3D,eAAO,MAAM,2BAA2B,WAAW,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAEnD,gFAAgF;AAChF,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C,4DAA4D;AAC5D,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,+BAAyB,CAAC;AAEzD;;;;;;;;GAQG;AAEH,qBAAa,YAAY;IACvB,QAAQ,CAAC,IAAI,WAAW;IAExB;;;;;OAKG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAEpE;IAED;;;;;;;;;;;OAWG;IACH,mBAAmB,CACjB,SAAS,EAAE,MAAM,GAAG,YAAY,EAChC,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CA6CR;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,kBAAkB,CAChB,KAAK,EAAE,cAAc,EACrB,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,GAChC,MAAM,CAiBR;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,yBAAyB;IAiDjC;;;;;;;OAOG;IACH,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAOhE;IAED;;;;;OAKG;IACG,eAAe,CACnB,MAAM,EAAE,MAAM,GAAG,YAAY,EAC7B,SAAS,EAAE,MAAM,GAAG,YAAY,EAChC,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,MAAM,CAAC,CAEjB;IAED;;;;;;;;;OASG;IACH,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,MAAM,CAahF;IAED;;;;;OAKG;IACG,cAAc,CAClB,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,MAAM,GAAG,YAAY,EAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,GAChC,OAAO,CAAC,MAAM,CAAC,CAEjB;IAED;;;OAGG;IACG,WAAW,CACf,MAAM,EAAE,MAAM,GAAG,YAAY,EAC7B,IAAI,EAAE,cAAc,EACpB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAEjB;IAMD,iEAAiE;IACjE,OAAO,CAAC,sBAAsB;CAkE/B"}
|
package/dist/codex-adapter.js
CHANGED
|
@@ -25,9 +25,8 @@
|
|
|
25
25
|
*
|
|
26
26
|
* @non-paper — no adapters exist in arXiv 2608.26263v3.
|
|
27
27
|
*/
|
|
28
|
-
import * as os from 'node:os';
|
|
29
28
|
import * as path from 'node:path';
|
|
30
|
-
import {
|
|
29
|
+
import { HISTORY_UNRELIABLE_NOTE, hookRuntimeSnippet, mergeHookGroups, resolveHostStateForCwd, resolveTarget, saveGenerated, skillMdBody, } from '@skillstate/core';
|
|
31
30
|
/** All {@link CodexHookEvent} values in generation order. */
|
|
32
31
|
export const CODEX_HOOK_EVENTS = [
|
|
33
32
|
'user-prompt-submit',
|
|
@@ -49,17 +48,11 @@ export const CODEX_HOOK_TIMEOUT_SECONDS = 30;
|
|
|
49
48
|
* Resolve the per-project state file for a working directory — the SAME
|
|
50
49
|
* semantics as the OpenCode plugin (`<cwd>/.skillstate/skillstate.json`;
|
|
51
50
|
* the global bucket `<home>/.skillstate/global/skillstate.json` when cwd
|
|
52
|
-
* equals home).
|
|
53
|
-
* (generated scripts
|
|
51
|
+
* equals home). Single source of truth: {@link resolveHostStateForCwd} in
|
|
52
|
+
* `@skillstate/core` (the generated hook scripts keep a byte-equivalent
|
|
53
|
+
* embedded copy because they cannot import `@skillstate/*`).
|
|
54
54
|
*/
|
|
55
|
-
export
|
|
56
|
-
const resolvedCwd = path.resolve(cwd);
|
|
57
|
-
const resolvedHome = path.resolve(home ?? os.homedir());
|
|
58
|
-
if (resolvedCwd === resolvedHome) {
|
|
59
|
-
return path.join(resolvedHome, '.skillstate', 'global', 'skillstate.json');
|
|
60
|
-
}
|
|
61
|
-
return path.join(resolvedCwd, '.skillstate', 'skillstate.json');
|
|
62
|
-
}
|
|
55
|
+
export const resolveStateForCwd = resolveHostStateForCwd;
|
|
63
56
|
/**
|
|
64
57
|
* OpenAI Codex platform adapter (@non-paper; see module doc).
|
|
65
58
|
*
|
|
@@ -69,10 +62,6 @@ export function resolveStateForCwd(cwd, home) {
|
|
|
69
62
|
* resolves the per-project state from `input.cwd` at runtime — so one
|
|
70
63
|
* global `hooks.json` + one script directory serve every project.
|
|
71
64
|
*/
|
|
72
|
-
/** Narrow record check for hooks.json documents (module scope). */
|
|
73
|
-
function isPlainObjectDoc(value) {
|
|
74
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
75
|
-
}
|
|
76
65
|
export class CodexAdapter {
|
|
77
66
|
name = 'codex';
|
|
78
67
|
/**
|
|
@@ -97,7 +86,7 @@ export class CodexAdapter {
|
|
|
97
86
|
* file's directory).
|
|
98
87
|
*/
|
|
99
88
|
generateHooksConfig(statePath, options) {
|
|
100
|
-
const resolved =
|
|
89
|
+
const resolved = resolveTarget(statePath);
|
|
101
90
|
const scriptDir = options?.scriptDir ?? path.dirname(resolved);
|
|
102
91
|
const command = (event) => options?.command ?? `node ${JSON.stringify(this.codexHookScriptPath(scriptDir, event))} ${event}`;
|
|
103
92
|
const timeout = options?.timeoutSeconds ?? CODEX_HOOK_TIMEOUT_SECONDS;
|
|
@@ -155,49 +144,45 @@ export class CodexAdapter {
|
|
|
155
144
|
* cwd-resolving and never bakes an absolute state path in.
|
|
156
145
|
*/
|
|
157
146
|
generateHookScript(event, statePath) {
|
|
158
|
-
const resolved = statePath === undefined ? undefined :
|
|
147
|
+
const resolved = statePath === undefined ? undefined : resolveTarget(statePath);
|
|
159
148
|
const header = `// State file (per-project resolver): ${resolved ?? '<cwd>/.skillstate/skillstate.json (global bucket when cwd === home)'}`;
|
|
160
149
|
if (event === 'post-tool-use') {
|
|
161
150
|
return this.buildPostToolUseScript(header);
|
|
162
151
|
}
|
|
163
152
|
const hookEventName = event === 'session-start-compact' ? 'SessionStart' : 'UserPromptSubmit';
|
|
153
|
+
// A1 text is shared: both hosts append the SAME HISTORY_UNRELIABLE_NOTE
|
|
154
|
+
// (MCP tools + fenced state_patch channel) after the state JSON.
|
|
155
|
+
return this.buildInjectScriptTemplate({
|
|
156
|
+
header,
|
|
157
|
+
hookEventName,
|
|
158
|
+
contextSuffix: HISTORY_UNRELIABLE_NOTE,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Inject-script template (`UserPromptSubmit` / `SessionStart`): brand
|
|
163
|
+
* header + the shared hook-runtime snippet ({@link hookRuntimeSnippet}
|
|
164
|
+
* inlined via `fn.toString()`) + a stdin loop that emits
|
|
165
|
+
* `{ hookSpecificOutput: { hookEventName, additionalContext } }` with the
|
|
166
|
+
* current state JSON read from the SESSION cwd. Brand specifics stay in
|
|
167
|
+
* the adapter (comment lines only); the `contextSuffix` is the shared
|
|
168
|
+
* {@link HISTORY_UNRELIABLE_NOTE} from `@skillstate/core`.
|
|
169
|
+
*/
|
|
170
|
+
buildInjectScriptTemplate(options) {
|
|
164
171
|
return [
|
|
165
172
|
'#!/usr/bin/env node',
|
|
166
|
-
`// skillstate Codex hook — ${hookEventName} (generated by @skillstate/codex).`,
|
|
173
|
+
`// skillstate Codex hook — ${options.hookEventName} (generated by @skillstate/codex).`,
|
|
167
174
|
'// Self-contained CommonJS: reads one hook JSON document on stdin,',
|
|
168
175
|
'// resolves the state from the SESSION cwd, and emits',
|
|
169
176
|
'// { hookSpecificOutput: { hookEventName, additionalContext } }.',
|
|
170
|
-
header,
|
|
177
|
+
options.header,
|
|
171
178
|
"'use strict';",
|
|
172
179
|
'const fs = require("fs");',
|
|
173
180
|
'const os = require("os");',
|
|
174
181
|
'const path = require("path");',
|
|
175
182
|
'',
|
|
176
|
-
`const HOOK_EVENT_NAME = ${JSON.stringify(hookEventName)};`,
|
|
183
|
+
`const HOOK_EVENT_NAME = ${JSON.stringify(options.hookEventName)};`,
|
|
177
184
|
'',
|
|
178
|
-
|
|
179
|
-
' const resolvedCwd = path.resolve(cwd);',
|
|
180
|
-
' const resolvedHome = path.resolve(os.homedir());',
|
|
181
|
-
' if (resolvedCwd === resolvedHome) {',
|
|
182
|
-
' return path.join(resolvedHome, ".skillstate", "global", "skillstate.json");',
|
|
183
|
-
' }',
|
|
184
|
-
' return path.join(resolvedCwd, ".skillstate", "skillstate.json");',
|
|
185
|
-
'}',
|
|
186
|
-
'',
|
|
187
|
-
'function readState(statePath) {',
|
|
188
|
-
' try {',
|
|
189
|
-
' if (fs.existsSync(statePath)) {',
|
|
190
|
-
' const parsed = JSON.parse(fs.readFileSync(statePath, "utf-8"));',
|
|
191
|
-
' if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {',
|
|
192
|
-
' if (parsed.state !== null && typeof parsed.state === "object" && !Array.isArray(parsed.state)) {',
|
|
193
|
-
' return parsed.state;',
|
|
194
|
-
' }',
|
|
195
|
-
' return parsed;',
|
|
196
|
-
' }',
|
|
197
|
-
' }',
|
|
198
|
-
' } catch (error) {}',
|
|
199
|
-
' return {};',
|
|
200
|
-
'}',
|
|
185
|
+
hookRuntimeSnippet(),
|
|
201
186
|
'',
|
|
202
187
|
'let raw = "";',
|
|
203
188
|
'process.stdin.setEncoding("utf-8");',
|
|
@@ -210,12 +195,15 @@ export class CodexAdapter {
|
|
|
210
195
|
' cwd = input.cwd;',
|
|
211
196
|
' }',
|
|
212
197
|
' } catch (error) {}',
|
|
213
|
-
' const state =
|
|
198
|
+
' const state = readStateEnvelope(',
|
|
199
|
+
' resolveStatePathForCwd(path.resolve(cwd), path.resolve(os.homedir())),',
|
|
200
|
+
' (p) => fs.readFileSync(p, "utf-8"),',
|
|
201
|
+
' );',
|
|
214
202
|
' const output = {',
|
|
215
203
|
' hookSpecificOutput: {',
|
|
216
204
|
' hookEventName: HOOK_EVENT_NAME,',
|
|
217
205
|
' additionalContext: "Current skill state (JSON): " + JSON.stringify(state)',
|
|
218
|
-
|
|
206
|
+
` + ${JSON.stringify(options.contextSuffix)},`,
|
|
219
207
|
' },',
|
|
220
208
|
' };',
|
|
221
209
|
' process.stdout.write(JSON.stringify(output));',
|
|
@@ -227,154 +215,67 @@ export class CodexAdapter {
|
|
|
227
215
|
* Generate a SKILL.md for Codex's skill directory
|
|
228
216
|
* (`~/.codex/skills/<name>/SKILL.md`). The body instructs the agent to
|
|
229
217
|
* treat the hook-injected state as authoritative (history is not
|
|
230
|
-
* reliable), to
|
|
231
|
-
* to persist via `state.patch` — the PostToolUse hook also merges any
|
|
218
|
+
* reliable), to orient via `state.summary` (full dump via `state.get`),
|
|
219
|
+
* and to persist via `state.patch` — the PostToolUse hook also merges any
|
|
232
220
|
* fenced ```json `state_patch` block printed by a Bash tool call.
|
|
233
221
|
*/
|
|
234
222
|
generateSkillMd(spec, statePath) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
`version: ${spec.version}`,
|
|
242
|
-
'execution_context:',
|
|
243
|
-
` state_path: ${resolvedStatePath}`,
|
|
244
|
-
' format: json',
|
|
245
|
-
'---',
|
|
246
|
-
'',
|
|
247
|
-
`# ${spec.name}`,
|
|
248
|
-
'',
|
|
249
|
-
spec.instructions,
|
|
250
|
-
'',
|
|
251
|
-
'## Execution Context',
|
|
252
|
-
'',
|
|
253
|
-
`Your execution state lives at \`${resolvedStatePath}\` (per project; a`,
|
|
254
|
-
'session started in $HOME uses `~/.skillstate/global/skillstate.json`).',
|
|
255
|
-
'The skillstate Codex hooks:',
|
|
256
|
-
'',
|
|
257
|
-
'- inject the CURRENT state as developer context on every prompt submit',
|
|
258
|
-
' (`UserPromptSubmit`) and re-inject it after compaction',
|
|
259
|
-
' (`SessionStart` matcher `^compact$`);',
|
|
260
|
-
'- watch every Bash tool result and merge a fenced ```json block carrying',
|
|
261
|
-
' a `state_patch` into the state file (`PostToolUse`).',
|
|
262
|
-
'',
|
|
263
|
-
'The injected state is authoritative — history is not reliable. Never',
|
|
264
|
-
'reconstruct execution context from the conversation.',
|
|
265
|
-
'',
|
|
266
|
-
'## Process',
|
|
267
|
-
'',
|
|
268
|
-
'1. Read the current state from the injected context, or fetch it with',
|
|
269
|
-
' the skillstate MCP tool `state.get`.',
|
|
270
|
-
'2. Observe the result of your last action.',
|
|
271
|
-
'3. Reason about what to do next, given the state and the observation.',
|
|
272
|
-
'4. Persist progress with the skillstate MCP tool `state.patch` (sparse',
|
|
273
|
-
' patch; set a key to `null` to delete it), and/or emit a fenced JSON',
|
|
274
|
-
' block with exactly two keys inside a Bash tool call so the',
|
|
275
|
-
' `PostToolUse` hook merges it:',
|
|
276
|
-
'',
|
|
277
|
-
`${fence}json`,
|
|
278
|
-
'{',
|
|
279
|
-
' "state_patch": { "key": "new_value", "obsolete_key": null },',
|
|
280
|
-
' "action": "next_action_name"',
|
|
281
|
-
'}',
|
|
282
|
-
fence,
|
|
283
|
-
'',
|
|
284
|
-
'- In `state_patch`, set keys to `null` to delete them. Only include',
|
|
285
|
-
' fields you want to change. Omit fields to leave them unchanged.',
|
|
286
|
-
'- Put anything you need to survive into `state_patch`; never rely on',
|
|
287
|
-
' the conversation remembering it.',
|
|
288
|
-
'- `action` names what you will do next (e.g. "continue", "done").',
|
|
289
|
-
'',
|
|
290
|
-
].join('\n');
|
|
223
|
+
return skillMdBody({
|
|
224
|
+
hostLabel: 'Codex',
|
|
225
|
+
injectionPhrase: 'provided as developer context',
|
|
226
|
+
spec,
|
|
227
|
+
statePath,
|
|
228
|
+
});
|
|
291
229
|
}
|
|
292
230
|
/**
|
|
293
|
-
* Generate the hooks.json document and persist it via
|
|
294
|
-
* Both the destination and the embedded state path
|
|
295
|
-
* `{ root, name }` refs confined by
|
|
296
|
-
* absolute destination path.
|
|
231
|
+
* Generate the hooks.json document and persist it via the shared
|
|
232
|
+
* `saveGenerated`. Both the destination and the embedded state path
|
|
233
|
+
* accept raw strings or `{ root, name }` refs confined by
|
|
234
|
+
* `resolveStatePath`. Returns the absolute destination path.
|
|
297
235
|
*/
|
|
298
236
|
async saveHooksConfig(target, statePath, options) {
|
|
299
|
-
|
|
300
|
-
await atomicWriteFile(dest, this.generateHooksConfig(statePath, options));
|
|
301
|
-
return dest;
|
|
237
|
+
return saveGenerated(target, this.generateHooksConfig(statePath, options));
|
|
302
238
|
}
|
|
303
239
|
/**
|
|
304
240
|
* Merge the skillstate hook groups into an existing `hooks.json` text.
|
|
305
241
|
* Idempotent: if any skillstate command is already wired, the document is
|
|
306
242
|
* returned unchanged. Existing (non-skillstate) hooks are preserved.
|
|
243
|
+
*
|
|
244
|
+
* The mechanics live in the shared {@link mergeHookGroups}; this method
|
|
245
|
+
* supplies only the format specifics (generated hooks.json groups —
|
|
246
|
+
* with the codex `description`/`statusMessage`/`additionalContextLimit`
|
|
247
|
+
* fields — and the `node <script> <event>` skillstate commands).
|
|
307
248
|
*/
|
|
308
249
|
mergeHooksConfig(existingJson, options) {
|
|
309
|
-
let doc = {};
|
|
310
|
-
try {
|
|
311
|
-
doc = JSON.parse(existingJson);
|
|
312
|
-
}
|
|
313
|
-
catch {
|
|
314
|
-
// Missing or malformed user file: start from a fresh document.
|
|
315
|
-
}
|
|
316
|
-
if (!isPlainObjectDoc(doc.hooks)) {
|
|
317
|
-
doc.hooks = {};
|
|
318
|
-
}
|
|
319
250
|
const scriptDir = options?.scriptDir ?? '<stateDir>/hooks';
|
|
320
251
|
const command = (event) => `node ${JSON.stringify(this.codexHookScriptPath(scriptDir, event))} ${event}`;
|
|
321
|
-
const skillstateCommands = new Set(CODEX_HOOK_EVENTS.map((event) => JSON.stringify(command(event))));
|
|
322
|
-
let alreadyWired = false;
|
|
323
|
-
for (const groups of Object.values(doc.hooks)) {
|
|
324
|
-
if (!Array.isArray(groups))
|
|
325
|
-
continue;
|
|
326
|
-
for (const group of groups) {
|
|
327
|
-
if (!isPlainObjectDoc(group) || !Array.isArray(group['hooks']))
|
|
328
|
-
continue;
|
|
329
|
-
for (const handler of group['hooks']) {
|
|
330
|
-
if (isPlainObjectDoc(handler) && skillstateCommands.has(JSON.stringify(handler['command']))) {
|
|
331
|
-
alreadyWired = true;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
if (alreadyWired) {
|
|
337
|
-
return `${JSON.stringify(doc, null, 2)}\n`;
|
|
338
|
-
}
|
|
339
252
|
const statePathRef = { root: path.dirname(options?.scriptDir ?? '.'), name: 'skillstate.json' };
|
|
340
253
|
const generated = JSON.parse(this.generateHooksConfig(statePathRef, { ...options, scriptDir }));
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
return `${JSON.stringify(doc, null, 2)}\n`;
|
|
254
|
+
return mergeHookGroups({
|
|
255
|
+
existingJson,
|
|
256
|
+
generatedGroups: generated.hooks,
|
|
257
|
+
commandsOf: new Set(CODEX_HOOK_EVENTS.map((event) => JSON.stringify(command(event)))),
|
|
258
|
+
});
|
|
348
259
|
}
|
|
349
260
|
/**
|
|
350
|
-
* Generate a hook script and persist it via
|
|
351
|
-
* is the script destination (usually
|
|
261
|
+
* Generate a hook script and persist it via the shared `saveGenerated`
|
|
262
|
+
* (atomic temp-rename write). `target` is the script destination (usually
|
|
352
263
|
* {@link CodexAdapter.codexHookScriptPath}); `statePath` is forwarded to
|
|
353
264
|
* {@link generateHookScript}. Returns the absolute destination path.
|
|
354
265
|
*/
|
|
355
266
|
async saveHookScript(event, target, statePath) {
|
|
356
|
-
|
|
357
|
-
await atomicWriteFile(dest, this.generateHookScript(event, statePath));
|
|
358
|
-
return dest;
|
|
267
|
+
return saveGenerated(target, this.generateHookScript(event, statePath));
|
|
359
268
|
}
|
|
360
269
|
/**
|
|
361
|
-
* Generate a SKILL.md and persist it via `
|
|
362
|
-
* absolute destination path.
|
|
270
|
+
* Generate a SKILL.md and persist it via the shared `saveGenerated`.
|
|
271
|
+
* Returns the absolute destination path.
|
|
363
272
|
*/
|
|
364
273
|
async saveSkillMd(target, spec, statePath) {
|
|
365
|
-
|
|
366
|
-
await atomicWriteFile(dest, this.generateSkillMd(spec, statePath));
|
|
367
|
-
return dest;
|
|
274
|
+
return saveGenerated(target, this.generateSkillMd(spec, statePath));
|
|
368
275
|
}
|
|
369
276
|
/* ------------------------------------------------------------------ */
|
|
370
277
|
/* Internal helpers */
|
|
371
278
|
/* ------------------------------------------------------------------ */
|
|
372
|
-
/** Resolve a `string | StatePathRef` via `resolveStatePath` (throws on `..`). */
|
|
373
|
-
resolve(target) {
|
|
374
|
-
return typeof target === 'string'
|
|
375
|
-
? target
|
|
376
|
-
: resolveStatePath(target.root, target.name);
|
|
377
|
-
}
|
|
378
279
|
/** PostToolUse script: extract state_patch, ⊕ merge, persist. */
|
|
379
280
|
buildPostToolUseScript(header) {
|
|
380
281
|
const fence = '```';
|
|
@@ -391,109 +292,9 @@ export class CodexAdapter {
|
|
|
391
292
|
'const os = require("os");',
|
|
392
293
|
'const path = require("path");',
|
|
393
294
|
'',
|
|
394
|
-
'function isPlainObject(value) {',
|
|
395
|
-
' return typeof value === "object" && value !== null && !Array.isArray(value);',
|
|
396
|
-
'}',
|
|
397
|
-
'',
|
|
398
|
-
'function resolveStatePathForCwd(cwd) {',
|
|
399
|
-
' const resolvedCwd = path.resolve(cwd);',
|
|
400
|
-
' const resolvedHome = path.resolve(os.homedir());',
|
|
401
|
-
' if (resolvedCwd === resolvedHome) {',
|
|
402
|
-
' return path.join(resolvedHome, ".skillstate", "global", "skillstate.json");',
|
|
403
|
-
' }',
|
|
404
|
-
' return path.join(resolvedCwd, ".skillstate", "skillstate.json");',
|
|
405
|
-
'}',
|
|
406
|
-
'',
|
|
407
|
-
'function readState(statePath) {',
|
|
408
|
-
' try {',
|
|
409
|
-
' if (fs.existsSync(statePath)) {',
|
|
410
|
-
' const parsed = JSON.parse(fs.readFileSync(statePath, "utf-8"));',
|
|
411
|
-
' if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {',
|
|
412
|
-
' if (parsed.state !== null && typeof parsed.state === "object" && !Array.isArray(parsed.state)) {',
|
|
413
|
-
' return parsed.state;',
|
|
414
|
-
' }',
|
|
415
|
-
' return parsed;',
|
|
416
|
-
' }',
|
|
417
|
-
' }',
|
|
418
|
-
' } catch (error) {}',
|
|
419
|
-
' return {};',
|
|
420
|
-
'}',
|
|
421
|
-
'',
|
|
422
|
-
'// Paper ⊕ merge: null deletes a key, nested plain objects merge',
|
|
423
|
-
'// recursively, everything else replaces.',
|
|
424
|
-
'function mergePatch(base, patch) {',
|
|
425
|
-
' const result = { ...base };',
|
|
426
|
-
' for (const key of Object.keys(patch)) {',
|
|
427
|
-
' const value = patch[key];',
|
|
428
|
-
' if (value === null) {',
|
|
429
|
-
' delete result[key];',
|
|
430
|
-
' } else if (isPlainObject(value) && isPlainObject(result[key])) {',
|
|
431
|
-
' result[key] = mergePatch(result[key], value);',
|
|
432
|
-
' } else {',
|
|
433
|
-
' result[key] = value;',
|
|
434
|
-
' }',
|
|
435
|
-
' }',
|
|
436
|
-
' return result;',
|
|
437
|
-
'}',
|
|
438
|
-
'',
|
|
439
|
-
'function readResponseText(response) {',
|
|
440
|
-
' if (typeof response === "string") return response;',
|
|
441
|
-
' if (isPlainObject(response)) {',
|
|
442
|
-
' if (typeof response.content === "string") return response.content;',
|
|
443
|
-
' if (typeof response.text === "string") return response.text;',
|
|
444
|
-
' return JSON.stringify(response);',
|
|
445
|
-
' }',
|
|
446
|
-
' return response === null || response === undefined ? "" : String(response);',
|
|
447
|
-
'}',
|
|
448
|
-
'',
|
|
449
295
|
`const FENCE = ${JSON.stringify(fence)};`,
|
|
450
|
-
'const FENCE_RE = /' + '```' + 'json\\s*\\n?([\\s\\S]*?)\\n?\\s*' + '```' + '/;',
|
|
451
|
-
'// An open fence that never closes (truncated output) is still a patch',
|
|
452
|
-
'// attempt: match from ```json to end-of-text so it classifies as invalid.',
|
|
453
|
-
'const OPEN_FENCE_RE = /' + '```' + 'json\\s*\\n?([\\s\\S]+)$/',
|
|
454
|
-
'',
|
|
455
|
-
'// Look for a fenced ```json block: { patch } when it parses and carries',
|
|
456
|
-
'// an object state_patch, { invalid: true } when a block exists but is',
|
|
457
|
-
'// malformed, { absent: true } when there is no block at all.',
|
|
458
|
-
'function findFencedPatch(text) {',
|
|
459
|
-
' const match = text.match(FENCE_RE) || text.match(OPEN_FENCE_RE);',
|
|
460
|
-
' if (!match) return { absent: true };',
|
|
461
|
-
' try {',
|
|
462
|
-
' const parsed = JSON.parse(match[1]);',
|
|
463
|
-
' if (isPlainObject(parsed) && isPlainObject(parsed.state_patch)) {',
|
|
464
|
-
' return { patch: parsed.state_patch };',
|
|
465
|
-
' }',
|
|
466
|
-
' } catch (error) {}',
|
|
467
|
-
' return { invalid: true };',
|
|
468
|
-
'}',
|
|
469
296
|
'',
|
|
470
|
-
|
|
471
|
-
'// Ordinary JSON output without a state_patch key is simply not a patch.',
|
|
472
|
-
'function findRawPatch(text) {',
|
|
473
|
-
' const trimmed = text.trim();',
|
|
474
|
-
' const candidates = [];',
|
|
475
|
-
' try {',
|
|
476
|
-
' candidates.push(JSON.parse(trimmed));',
|
|
477
|
-
' } catch (error) {}',
|
|
478
|
-
' const first = trimmed.indexOf("{");',
|
|
479
|
-
' const last = trimmed.lastIndexOf("}");',
|
|
480
|
-
' if (first !== -1 && last > first) {',
|
|
481
|
-
' try {',
|
|
482
|
-
' candidates.push(JSON.parse(trimmed.slice(first, last + 1)));',
|
|
483
|
-
' } catch (error) {}',
|
|
484
|
-
' }',
|
|
485
|
-
' for (const candidate of candidates) {',
|
|
486
|
-
' if (isPlainObject(candidate)) {',
|
|
487
|
-
' if (isPlainObject(candidate.state_patch)) {',
|
|
488
|
-
' return { patch: candidate.state_patch };',
|
|
489
|
-
' }',
|
|
490
|
-
' if (Object.prototype.hasOwnProperty.call(candidate, "state_patch")) {',
|
|
491
|
-
' return { invalid: true };',
|
|
492
|
-
' }',
|
|
493
|
-
' }',
|
|
494
|
-
' }',
|
|
495
|
-
' return { absent: true };',
|
|
496
|
-
'}',
|
|
297
|
+
hookRuntimeSnippet(),
|
|
497
298
|
'',
|
|
498
299
|
'let raw = "";',
|
|
499
300
|
'process.stdin.setEncoding("utf-8");',
|
|
@@ -506,7 +307,7 @@ export class CodexAdapter {
|
|
|
506
307
|
' if (typeof input.cwd === "string" && input.cwd.length > 0) {',
|
|
507
308
|
' cwd = input.cwd;',
|
|
508
309
|
' }',
|
|
509
|
-
' const statePath = resolveStatePathForCwd(cwd);',
|
|
310
|
+
' const statePath = resolveStatePathForCwd(path.resolve(cwd), path.resolve(os.homedir()));',
|
|
510
311
|
' const response = input.tool_response;',
|
|
511
312
|
' let result;',
|
|
512
313
|
' if (isPlainObject(response) && isPlainObject(response.state_patch)) {',
|
|
@@ -517,10 +318,13 @@ export class CodexAdapter {
|
|
|
517
318
|
' if (result.absent) result = findRawPatch(text);',
|
|
518
319
|
' }',
|
|
519
320
|
' if (result.patch !== undefined) {',
|
|
520
|
-
' const merged = mergePatch(
|
|
321
|
+
' const merged = mergePatch(',
|
|
322
|
+
' readStateEnvelope(statePath, (p) => fs.readFileSync(p, "utf-8")),',
|
|
323
|
+
' result.patch,',
|
|
324
|
+
' );',
|
|
521
325
|
' try {',
|
|
522
326
|
' fs.mkdirSync(path.dirname(statePath), { recursive: true });',
|
|
523
|
-
'
|
|
327
|
+
' saveStateEnvelope(statePath, merged, (p, data) => fs.writeFileSync(p, data));',
|
|
524
328
|
' } catch (writeError) {',
|
|
525
329
|
" output.systemMessage = 'skillstate: failed to persist state (' + writeError.message + ')';",
|
|
526
330
|
' }',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-adapter.js","sourceRoot":"","sources":["../src/codex-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"codex-adapter.js","sourceRoot":"","sources":["../src/codex-adapter.ts"],"names":[],"mappings":"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"}
|