selftune 0.2.21 → 0.2.22
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 +12 -7
- package/cli/selftune/adapters/cline/hook.ts +167 -0
- package/cli/selftune/adapters/cline/install.ts +197 -0
- package/cli/selftune/adapters/codex/hook.ts +296 -0
- package/cli/selftune/adapters/codex/install.ts +289 -0
- package/cli/selftune/adapters/opencode/hook.ts +222 -0
- package/cli/selftune/adapters/opencode/install.ts +543 -0
- package/cli/selftune/hooks/auto-activate.ts +43 -37
- package/cli/selftune/hooks-shared/git-metadata.ts +149 -0
- package/cli/selftune/hooks-shared/hook-output.ts +105 -0
- package/cli/selftune/hooks-shared/normalize.ts +196 -0
- package/cli/selftune/hooks-shared/session-state.ts +76 -0
- package/cli/selftune/hooks-shared/skill-paths.ts +50 -0
- package/cli/selftune/hooks-shared/stdin-dispatch.ts +59 -0
- package/cli/selftune/hooks-shared/types.ts +90 -0
- package/cli/selftune/index.ts +56 -4
- package/cli/selftune/utils/llm-call.ts +99 -34
- package/package.json +1 -1
- package/skill/SKILL.md +10 -0
- package/skill/Workflows/Initialize.md +48 -6
- package/skill/Workflows/PlatformHooks.md +93 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal hook types for multi-agent abstraction.
|
|
3
|
+
* All platform adapters normalize their native events to these types.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Supported agent platforms */
|
|
7
|
+
export type HookPlatform = "claude-code" | "codex" | "opencode" | "cline" | "pi";
|
|
8
|
+
|
|
9
|
+
/** Normalized event types across all platforms */
|
|
10
|
+
export type HookEventType = "pre_tool_use" | "post_tool_use" | "prompt_submit" | "session_end";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Platform-agnostic hook event. Each adapter normalizes its native payload to this shape.
|
|
14
|
+
* Fields are optional because not all platforms provide all data.
|
|
15
|
+
*/
|
|
16
|
+
export interface UnifiedHookEvent {
|
|
17
|
+
platform: HookPlatform;
|
|
18
|
+
event_type: HookEventType;
|
|
19
|
+
session_id: string;
|
|
20
|
+
cwd?: string;
|
|
21
|
+
transcript_path?: string;
|
|
22
|
+
|
|
23
|
+
// Tool-related (pre_tool_use / post_tool_use)
|
|
24
|
+
tool_name?: string;
|
|
25
|
+
tool_input?: Record<string, unknown>;
|
|
26
|
+
tool_output?: Record<string, unknown>;
|
|
27
|
+
|
|
28
|
+
// Prompt-related (prompt_submit)
|
|
29
|
+
prompt?: string;
|
|
30
|
+
|
|
31
|
+
// Session-related (session_end)
|
|
32
|
+
last_message?: string;
|
|
33
|
+
|
|
34
|
+
/** Original platform-specific payload, preserved for platform-specific logic */
|
|
35
|
+
raw_payload?: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Hook response returned to the host agent.
|
|
40
|
+
* Adapters translate this back to platform-specific format.
|
|
41
|
+
*/
|
|
42
|
+
export interface HookResponse {
|
|
43
|
+
/** Whether the hook modified the input */
|
|
44
|
+
modified: boolean;
|
|
45
|
+
/** Decision for PreToolUse guards */
|
|
46
|
+
decision?: "allow" | "block" | "skip";
|
|
47
|
+
/** Modified tool input (for pre_tool_use hooks that modify commands) */
|
|
48
|
+
updated_input?: Record<string, unknown>;
|
|
49
|
+
/** Advisory message (stderr suggestions) */
|
|
50
|
+
message?: string;
|
|
51
|
+
/** Additional context to inject (stdout JSON for Claude Code) */
|
|
52
|
+
context?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Generic session state for dedup/tracking across hook invocations */
|
|
56
|
+
export interface SessionState<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
57
|
+
session_id: string;
|
|
58
|
+
created_at: string;
|
|
59
|
+
data: T;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Platform event mapping reference */
|
|
63
|
+
export const PLATFORM_EVENT_MAP: Record<HookPlatform, Partial<Record<HookEventType, string>>> = {
|
|
64
|
+
"claude-code": {
|
|
65
|
+
pre_tool_use: "PreToolUse",
|
|
66
|
+
post_tool_use: "PostToolUse",
|
|
67
|
+
prompt_submit: "UserPromptSubmit",
|
|
68
|
+
session_end: "Stop",
|
|
69
|
+
},
|
|
70
|
+
codex: {
|
|
71
|
+
pre_tool_use: "PreToolUse",
|
|
72
|
+
post_tool_use: "PostToolUse",
|
|
73
|
+
prompt_submit: "SessionStart",
|
|
74
|
+
session_end: "Stop",
|
|
75
|
+
},
|
|
76
|
+
opencode: {
|
|
77
|
+
pre_tool_use: "tool.execute.before",
|
|
78
|
+
post_tool_use: "tool.execute.after",
|
|
79
|
+
session_end: "session.idle",
|
|
80
|
+
},
|
|
81
|
+
cline: {
|
|
82
|
+
post_tool_use: "PostToolUse",
|
|
83
|
+
session_end: "TaskComplete",
|
|
84
|
+
},
|
|
85
|
+
pi: {
|
|
86
|
+
pre_tool_use: "tool_call",
|
|
87
|
+
post_tool_use: "tool_result",
|
|
88
|
+
session_end: "session_shutdown",
|
|
89
|
+
},
|
|
90
|
+
};
|
package/cli/selftune/index.ts
CHANGED
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
* selftune telemetry — Manage anonymous usage analytics (status, enable, disable)
|
|
31
31
|
* selftune alpha <subcommand> — Alpha program management (upload)
|
|
32
32
|
* selftune hook <name> — Run a hook by name (prompt-log, session-stop, etc.)
|
|
33
|
+
* selftune codex <subcommand> — Codex platform hooks (hook, install)
|
|
34
|
+
* selftune opencode <sub> — OpenCode platform hooks (hook, install)
|
|
35
|
+
* selftune cline <subcommand> — Cline platform hooks (hook, install)
|
|
33
36
|
*/
|
|
34
37
|
|
|
35
38
|
import { CLIError, handleCLIError } from "./utils/cli-error.js";
|
|
@@ -73,20 +76,26 @@ Commands:
|
|
|
73
76
|
alpha <subcommand> Alpha program management (upload)
|
|
74
77
|
telemetry Manage anonymous usage analytics (status, enable, disable)
|
|
75
78
|
hook <name> Run a hook by name (prompt-log, session-stop, etc.)
|
|
79
|
+
codex <sub> Codex platform hooks (hook, install)
|
|
80
|
+
opencode <sub> OpenCode platform hooks (hook, install)
|
|
81
|
+
cline <sub> Cline platform hooks (hook, install)
|
|
76
82
|
|
|
77
83
|
Run 'selftune <command> --help' for command-specific options.`);
|
|
78
84
|
process.exit(0);
|
|
79
85
|
}
|
|
80
86
|
|
|
81
|
-
//
|
|
82
|
-
|
|
87
|
+
// Fast-path commands (real-time hooks) — skip analytics and auto-update to minimize latency
|
|
88
|
+
const FAST_COMMANDS: ReadonlySet<string> = new Set(["hook", "codex", "opencode", "cline"]);
|
|
89
|
+
|
|
90
|
+
// Track command usage (lazy import — skip for hooks and --help to avoid loading crypto/os)
|
|
91
|
+
if (command && !FAST_COMMANDS.has(command) && command !== "--help" && command !== "-h") {
|
|
83
92
|
import("./analytics.js")
|
|
84
93
|
.then(({ trackEvent }) => trackEvent("command_run", { command }))
|
|
85
94
|
.catch(() => {});
|
|
86
95
|
}
|
|
87
96
|
|
|
88
|
-
// Auto-update check (skip for hooks — they must be fast — and --help)
|
|
89
|
-
if (command && command
|
|
97
|
+
// Auto-update check (skip for hooks and platform hook commands — they must be fast — and --help)
|
|
98
|
+
if (command && !FAST_COMMANDS.has(command) && command !== "--help" && command !== "-h") {
|
|
90
99
|
const { autoUpdate } = await import("./auto-update.js");
|
|
91
100
|
await autoUpdate();
|
|
92
101
|
}
|
|
@@ -815,6 +824,49 @@ Output:
|
|
|
815
824
|
process.exit(result.status ?? 1);
|
|
816
825
|
break;
|
|
817
826
|
}
|
|
827
|
+
// ── Platform hook adapters ─────────────────────────────────────────
|
|
828
|
+
|
|
829
|
+
case "codex":
|
|
830
|
+
case "opencode":
|
|
831
|
+
case "cline": {
|
|
832
|
+
const platform = command;
|
|
833
|
+
const displayName = { codex: "Codex", opencode: "OpenCode", cline: "Cline" }[platform];
|
|
834
|
+
const sub = process.argv[2];
|
|
835
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
836
|
+
console.log(`selftune ${platform} — ${displayName} platform hooks
|
|
837
|
+
|
|
838
|
+
Usage:
|
|
839
|
+
selftune ${platform} <subcommand> [options]
|
|
840
|
+
|
|
841
|
+
Subcommands:
|
|
842
|
+
hook Handle a real-time hook event from ${displayName}
|
|
843
|
+
install Install or remove selftune hooks in ${displayName} config
|
|
844
|
+
|
|
845
|
+
Run 'selftune ${platform} <subcommand> --help' for subcommand-specific options.`);
|
|
846
|
+
process.exit(0);
|
|
847
|
+
}
|
|
848
|
+
process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
|
|
849
|
+
switch (sub) {
|
|
850
|
+
case "hook": {
|
|
851
|
+
const { cliMain } = await import(`./adapters/${platform}/hook.js`);
|
|
852
|
+
await cliMain();
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
case "install": {
|
|
856
|
+
const { cliMain } = await import(`./adapters/${platform}/install.js`);
|
|
857
|
+
await cliMain();
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
default:
|
|
861
|
+
throw new CLIError(
|
|
862
|
+
`Unknown ${platform} subcommand: ${sub}`,
|
|
863
|
+
"UNKNOWN_COMMAND",
|
|
864
|
+
`selftune ${platform} --help`,
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
break;
|
|
868
|
+
}
|
|
869
|
+
|
|
818
870
|
default:
|
|
819
871
|
throw new CLIError(`Unknown command: ${command}`, "UNKNOWN_COMMAND", "selftune --help");
|
|
820
872
|
}
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* modules can reuse the same calling logic.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { tmpdir } from "node:os";
|
|
11
|
-
import { join } from "node:path";
|
|
11
|
+
import { dirname, join, resolve } from "node:path";
|
|
12
12
|
|
|
13
13
|
import { AGENT_CANDIDATES } from "../constants.js";
|
|
14
14
|
import { createLogger } from "./logging.js";
|
|
@@ -33,6 +33,40 @@ function resolveModelFlag(flag: string): string {
|
|
|
33
33
|
return CLAUDE_MODEL_ALIASES[flag] ?? flag;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Map selftune model aliases to OpenCode provider/model format.
|
|
38
|
+
* OpenCode uses "provider/model" syntax (e.g. "anthropic/claude-sonnet-4-20250514").
|
|
39
|
+
*/
|
|
40
|
+
const OPENCODE_MODEL_MAP: Record<string, string> = {
|
|
41
|
+
haiku: "anthropic/claude-haiku-4-5-20251001",
|
|
42
|
+
sonnet: "anthropic/claude-sonnet-4-20250514",
|
|
43
|
+
opus: "anthropic/claude-opus-4-20250514",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Resolve a model alias to OpenCode's provider/model format. */
|
|
47
|
+
function resolveOpenCodeModel(flag: string): string {
|
|
48
|
+
return OPENCODE_MODEL_MAP[flag] ?? flag;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Bundled agent file loading (for codex inline prompt injection)
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
const BUNDLED_AGENT_DIR = resolve(dirname(import.meta.path), "..", "..", "..", "skill", "agents");
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Read the bundled agent markdown file and return its body (without frontmatter).
|
|
59
|
+
* Used by codex path to inline agent instructions into the prompt since codex
|
|
60
|
+
* has no --agent flag.
|
|
61
|
+
*/
|
|
62
|
+
function loadAgentInstructions(agentName: string): string | null {
|
|
63
|
+
const filePath = join(BUNDLED_AGENT_DIR, `${agentName}.md`);
|
|
64
|
+
if (!existsSync(filePath)) return null;
|
|
65
|
+
const content = readFileSync(filePath, "utf-8");
|
|
66
|
+
// Strip YAML frontmatter
|
|
67
|
+
return content.replace(/^---\n[\s\S]*?\n---\n*/, "").trim();
|
|
68
|
+
}
|
|
69
|
+
|
|
36
70
|
// ---------------------------------------------------------------------------
|
|
37
71
|
// Agent detection
|
|
38
72
|
// ---------------------------------------------------------------------------
|
|
@@ -155,7 +189,11 @@ export async function callViaAgent(
|
|
|
155
189
|
} else if (agent === "codex") {
|
|
156
190
|
cmd = ["codex", "exec", "--skip-git-repo-check", promptContent];
|
|
157
191
|
} else if (agent === "opencode") {
|
|
158
|
-
cmd = ["opencode", "
|
|
192
|
+
cmd = ["opencode", "run"];
|
|
193
|
+
if (modelFlag) {
|
|
194
|
+
cmd.push("--model", resolveOpenCodeModel(modelFlag));
|
|
195
|
+
}
|
|
196
|
+
cmd.push(promptContent);
|
|
159
197
|
} else {
|
|
160
198
|
throw new Error(`Unknown agent: ${agent}`);
|
|
161
199
|
}
|
|
@@ -222,9 +260,9 @@ export async function callViaAgent(
|
|
|
222
260
|
// Call LLM via named subagent (multi-turn, agentic)
|
|
223
261
|
// ---------------------------------------------------------------------------
|
|
224
262
|
|
|
225
|
-
/** Options for calling a named Claude Code
|
|
263
|
+
/** Options for calling a named subagent (Claude Code or OpenCode). */
|
|
226
264
|
export interface SubagentCallOptions {
|
|
227
|
-
/** Name of the subagent (synced into ~/.claude/agents/ by selftune init/update). */
|
|
265
|
+
/** Name of the subagent (synced into ~/.claude/agents/ or opencode.json by selftune init/update). */
|
|
228
266
|
agentName: string;
|
|
229
267
|
/** The task prompt for the subagent. */
|
|
230
268
|
prompt: string;
|
|
@@ -243,13 +281,13 @@ export interface SubagentCallOptions {
|
|
|
243
281
|
}
|
|
244
282
|
|
|
245
283
|
/**
|
|
246
|
-
* Call a named
|
|
247
|
-
*
|
|
248
|
-
*
|
|
284
|
+
* Call a named subagent in print mode. The subagent runs its multi-turn
|
|
285
|
+
* workflow (reading files, running commands, etc.) and returns the final
|
|
286
|
+
* text output.
|
|
249
287
|
*
|
|
250
|
-
*
|
|
251
|
-
* and
|
|
252
|
-
*
|
|
288
|
+
* Supports Claude Code (`claude --agent`), OpenCode (`opencode run --agent`),
|
|
289
|
+
* and Codex (`codex exec` with agent instructions inlined into the prompt).
|
|
290
|
+
* Auto-detects the available agent CLI.
|
|
253
291
|
*/
|
|
254
292
|
export async function callViaSubagent(options: SubagentCallOptions): Promise<string> {
|
|
255
293
|
const {
|
|
@@ -263,31 +301,58 @@ export async function callViaSubagent(options: SubagentCallOptions): Promise<str
|
|
|
263
301
|
allowedTools,
|
|
264
302
|
} = options;
|
|
265
303
|
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
agentName,
|
|
272
|
-
"--max-turns",
|
|
273
|
-
String(maxTurns),
|
|
274
|
-
];
|
|
275
|
-
|
|
276
|
-
if (appendSystemPrompt) {
|
|
277
|
-
cmd.push("--append-system-prompt", appendSystemPrompt);
|
|
278
|
-
}
|
|
279
|
-
if (modelFlag) {
|
|
280
|
-
const resolved = resolveModelFlag(modelFlag);
|
|
281
|
-
cmd.push("--model", resolved);
|
|
304
|
+
const agent = detectAgent();
|
|
305
|
+
if (!agent || (agent !== "claude" && agent !== "opencode" && agent !== "codex")) {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`Subagent calls require 'claude', 'opencode', or 'codex' CLI in PATH (detected: ${agent ?? "none"})`,
|
|
308
|
+
);
|
|
282
309
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (
|
|
287
|
-
|
|
310
|
+
|
|
311
|
+
let cmd: string[];
|
|
312
|
+
|
|
313
|
+
if (agent === "opencode") {
|
|
314
|
+
// OpenCode supports --agent and --model but not allowedTools, appendSystemPrompt, or maxTurns
|
|
315
|
+
if (allowedTools?.length || appendSystemPrompt) {
|
|
316
|
+
logger.warn(
|
|
317
|
+
`Subagent '${agentName}' on opencode: allowedTools and appendSystemPrompt are not supported and will be ignored`,
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
cmd = ["opencode", "run", "--agent", agentName];
|
|
321
|
+
if (modelFlag) {
|
|
322
|
+
cmd.push("--model", resolveOpenCodeModel(modelFlag));
|
|
323
|
+
}
|
|
324
|
+
cmd.push(prompt);
|
|
325
|
+
} else if (agent === "codex") {
|
|
326
|
+
// Codex has no --agent flag; inline the agent instructions into the prompt.
|
|
327
|
+
// allowedTools, appendSystemPrompt, maxTurns, and effort are not supported.
|
|
328
|
+
if (allowedTools?.length || appendSystemPrompt) {
|
|
329
|
+
logger.warn(
|
|
330
|
+
`Subagent '${agentName}' on codex: allowedTools and appendSystemPrompt are not supported and will be ignored`,
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
const agentInstructions = loadAgentInstructions(agentName);
|
|
334
|
+
const fullPrompt = agentInstructions ? `${agentInstructions}\n\n---\n\n${prompt}` : prompt;
|
|
335
|
+
cmd = ["codex", "exec", "--skip-git-repo-check", fullPrompt];
|
|
336
|
+
} else {
|
|
337
|
+
// Claude Code
|
|
338
|
+
cmd = ["claude", "-p", prompt, "--agent", agentName, "--max-turns", String(maxTurns)];
|
|
339
|
+
|
|
340
|
+
if (appendSystemPrompt) {
|
|
341
|
+
cmd.push("--append-system-prompt", appendSystemPrompt);
|
|
342
|
+
}
|
|
343
|
+
if (modelFlag) {
|
|
344
|
+
const resolved = resolveModelFlag(modelFlag);
|
|
345
|
+
cmd.push("--model", resolved);
|
|
346
|
+
}
|
|
347
|
+
if (effort) {
|
|
348
|
+
cmd.push("--effort", effort);
|
|
349
|
+
}
|
|
350
|
+
if (allowedTools && allowedTools.length > 0) {
|
|
351
|
+
cmd.push("--allowedTools", ...allowedTools);
|
|
352
|
+
}
|
|
353
|
+
// Skip permissions since this runs non-interactively in a pipeline
|
|
354
|
+
cmd.push("--dangerously-skip-permissions");
|
|
288
355
|
}
|
|
289
|
-
// Skip permissions since this runs non-interactively in a pipeline
|
|
290
|
-
cmd.push("--dangerously-skip-permissions");
|
|
291
356
|
|
|
292
357
|
const maxRetries = retryOpts?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
293
358
|
const initialBackoffMs = retryOpts?.initialBackoffMs ?? DEFAULT_INITIAL_BACKOFF_MS;
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -125,6 +125,14 @@ selftune uninstall [--dry-run] [--keep-logs] [--npm-uninstall]
|
|
|
125
125
|
# Hook dispatch (for debugging/manual invocation)
|
|
126
126
|
selftune hook <name> # prompt-log | session-stop | skill-eval | auto-activate | skill-change-guard | evolution-guard
|
|
127
127
|
|
|
128
|
+
# Platform hooks (non-Claude-Code agents)
|
|
129
|
+
selftune codex hook
|
|
130
|
+
selftune codex install [--dry-run] [--uninstall]
|
|
131
|
+
selftune opencode hook
|
|
132
|
+
selftune opencode install [--dry-run] [--uninstall]
|
|
133
|
+
selftune cline hook
|
|
134
|
+
selftune cline install [--dry-run] [--uninstall]
|
|
135
|
+
|
|
128
136
|
# Alpha enrollment (device-code flow — browser opens automatically)
|
|
129
137
|
selftune init --alpha --alpha-email <email>
|
|
130
138
|
selftune alpha upload [--dry-run]
|
|
@@ -169,6 +177,7 @@ selftune status # shows c
|
|
|
169
177
|
| repair, rebuild usage, fix skill usage, trustworthy usage, repair-skill-usage | RepairSkillUsage | Workflows/RepairSkillUsage.md |
|
|
170
178
|
| export canonical, canonical export, canonical telemetry, push payload | ExportCanonical | Workflows/ExportCanonical.md |
|
|
171
179
|
| hook, run hook, invoke hook, manual hook, debug hook | Hook | Workflows/Hook.md |
|
|
180
|
+
| codex hooks, codex install, codex setup, opencode hooks, opencode install, opencode setup, cline hooks, cline install, cline setup, multi-platform, platform hooks, non-claude hooks, multiple agents, multi-agent | PlatformHooks | Workflows/PlatformHooks.md |
|
|
172
181
|
| export, dump, jsonl, export sqlite, debug export | Export | _(direct command — no workflow file)_ |
|
|
173
182
|
| status, health summary, skill health, how are skills, skills doing, run selftune | Status | _(direct command — no workflow file)_ |
|
|
174
183
|
| last, last session, recent session, what happened, what changed | Last | _(direct command — no workflow file)_ |
|
|
@@ -357,6 +366,7 @@ accomplish a task _using_ a skill, route to that skill instead.
|
|
|
357
366
|
| `Workflows/CreatorContributions.md` | Manage bundled `selftune.contribute.json` configs | When preparing a skill package for creator contributions |
|
|
358
367
|
| `Workflows/ExportCanonical.md` | Export canonical telemetry for downstream use | When exporting data for external consumption |
|
|
359
368
|
| `Workflows/Hook.md` | Manual hook invocation for debugging | When debugging or testing hooks manually |
|
|
369
|
+
| `Workflows/PlatformHooks.md` | Non-Claude-Code platform hook install/config | When setting up Codex, OpenCode, or Cline hooks |
|
|
360
370
|
| `references/logs.md` | Log file formats (telemetry, usage, queries, audit) | When parsing or debugging log files |
|
|
361
371
|
| `references/grading-methodology.md` | 3-tier grading model, evidence standards | When grading sessions or interpreting grades |
|
|
362
372
|
| `references/invocation-taxonomy.md` | 4 invocation types, coverage analysis | When analyzing trigger coverage |
|
|
@@ -7,6 +7,7 @@ Bootstrap selftune for first-time use or after changing environments.
|
|
|
7
7
|
- The user asks to set up selftune, configure selftune, or initialize selftune
|
|
8
8
|
- The agent detects `~/.selftune/config.json` does not exist
|
|
9
9
|
- The user has switched agent platforms (Claude Code, Codex, OpenCode)
|
|
10
|
+
- The user wants to add hooks for additional platforms (multi-agent setup)
|
|
10
11
|
|
|
11
12
|
## Default Command
|
|
12
13
|
|
|
@@ -136,15 +137,49 @@ Code subagent calls stay up to date.
|
|
|
136
137
|
| `PostToolUse` (Bash) | `hooks/commit-track.ts` | Track git commits for session traceability | Fast-path: skips non-git Bash commands |
|
|
137
138
|
| `Stop` | `hooks/session-stop.ts` | Capture session telemetry | Runs async (non-blocking), 60s timeout |
|
|
138
139
|
|
|
139
|
-
|
|
140
|
+
### 4b. Multi-Platform Hooks
|
|
140
141
|
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
After Claude Code hooks are installed, check whether the user has **other** agent
|
|
143
|
+
CLIs available. Run these checks:
|
|
143
144
|
|
|
144
|
-
|
|
145
|
+
```bash
|
|
146
|
+
which codex 2>/dev/null && echo "codex available"
|
|
147
|
+
which opencode 2>/dev/null && echo "opencode available"
|
|
148
|
+
ls ~/Documents/Cline/Hooks/ 2>/dev/null && echo "cline available"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
If **any** additional platforms are detected, use `AskUserQuestion` listing only
|
|
152
|
+
the platforms that were actually found:
|
|
153
|
+
|
|
154
|
+
> I detected these agent platforms in addition to your primary one:
|
|
155
|
+
> - [list only detected platforms, e.g. "Codex", "OpenCode"]
|
|
156
|
+
>
|
|
157
|
+
> Would you like to install selftune hooks for any of them? This enables
|
|
158
|
+
> real-time skill tracking across all your agents.
|
|
159
|
+
|
|
160
|
+
Options:
|
|
161
|
+
- `Yes — install hooks for all detected platforms`
|
|
162
|
+
- `Let me pick — show me the list` (then present only the detected platforms)
|
|
163
|
+
- `No — skip for now` (they can always run `selftune <platform> install` later)
|
|
164
|
+
|
|
165
|
+
For each platform the user selects, run the install command:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
selftune codex install # writes hooks.json entries
|
|
169
|
+
selftune opencode install # writes shell shim + config entries
|
|
170
|
+
selftune cline install # creates hook scripts
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Use `--dry-run` first if the user wants to preview. See `Workflows/PlatformHooks.md`
|
|
174
|
+
for platform-specific details.
|
|
175
|
+
|
|
176
|
+
**Batch ingest** fallback for platforms without real-time hooks or to backfill history:
|
|
145
177
|
|
|
146
|
-
|
|
147
|
-
|
|
178
|
+
```bash
|
|
179
|
+
selftune ingest codex # import Codex rollout sessions
|
|
180
|
+
selftune ingest opencode # import OpenCode sessions from SQLite
|
|
181
|
+
selftune ingest openclaw # import OpenClaw sessions
|
|
182
|
+
```
|
|
148
183
|
|
|
149
184
|
### 5. Initialize Memory Directory
|
|
150
185
|
|
|
@@ -387,6 +422,13 @@ retrying with `selftune init --alpha --alpha-email <email> --force`.
|
|
|
387
422
|
> and optional display name in chat, then run `selftune init --alpha --alpha-email ...`.
|
|
388
423
|
> The browser opens automatically for approval. No manual key management needed.
|
|
389
424
|
|
|
425
|
+
**User uses multiple agents (Claude Code + Codex, etc.)**
|
|
426
|
+
|
|
427
|
+
> Run `selftune init` for the primary agent, then offer to install hooks for
|
|
428
|
+
> additional detected platforms. Run `selftune codex install`, `selftune opencode install`,
|
|
429
|
+
> or `selftune cline install` as needed. All platforms write to the same shared
|
|
430
|
+
> log schema — no extra config required.
|
|
431
|
+
|
|
390
432
|
**Hooks not capturing data**
|
|
391
433
|
|
|
392
434
|
> Run `selftune doctor` to check hook installation. Parse the JSON output
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Platform Hooks Workflow
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Install and configure selftune hooks for non-Claude-Code platforms (Codex, OpenCode, Cline).
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
- User wants selftune on Codex, OpenCode, or Cline
|
|
10
|
+
- User asks about multi-platform support
|
|
11
|
+
- User wants real-time skill tracking on a non-Claude-Code agent
|
|
12
|
+
|
|
13
|
+
## Commands
|
|
14
|
+
|
|
15
|
+
### Install hooks for a platform
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
selftune <platform> install [--dry-run] [--uninstall]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Supported platforms: `codex`, `opencode`, `cline`
|
|
22
|
+
|
|
23
|
+
| Flag | Description |
|
|
24
|
+
| ------------- | ---------------------------------------------- |
|
|
25
|
+
| `--dry-run` | Preview what would be installed without writing |
|
|
26
|
+
| `--uninstall` | Remove selftune hooks from the platform |
|
|
27
|
+
| `--help, -h` | Show usage help |
|
|
28
|
+
|
|
29
|
+
### Hook handler (called by the agent, not the user)
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
selftune <platform> hook
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This is called automatically by the agent's hook system. Users don't run this directly.
|
|
36
|
+
|
|
37
|
+
## Platform Details
|
|
38
|
+
|
|
39
|
+
### Codex
|
|
40
|
+
|
|
41
|
+
- Config: `~/.codex/hooks.json`
|
|
42
|
+
- Events: SessionStart, PreToolUse, PostToolUse, Stop
|
|
43
|
+
- Install creates hooks.json entries that prefer `$SELFTUNE_CLI_PATH codex hook`, otherwise `npx -y selftune@latest codex hook`
|
|
44
|
+
|
|
45
|
+
### OpenCode
|
|
46
|
+
|
|
47
|
+
- Config: `./opencode.json` or `~/.config/opencode/opencode.json`
|
|
48
|
+
- Plugin dir: `~/.config/opencode/plugins/` (global) or `./.opencode/plugins/` (project)
|
|
49
|
+
- Events: tool.execute.before, tool.execute.after, session.idle (via event handler)
|
|
50
|
+
- Install writes a TypeScript plugin file (`selftune-opencode-plugin.ts`) into the plugins directory (auto-discovered by OpenCode at startup)
|
|
51
|
+
- Agents are registered in the `agent` config key (identified by `[selftune]` description prefix)
|
|
52
|
+
|
|
53
|
+
### Cline
|
|
54
|
+
|
|
55
|
+
- Config: `~/Documents/Cline/Hooks/`
|
|
56
|
+
- Events: PostToolUse, TaskComplete, TaskCancel
|
|
57
|
+
- Install creates executable shell scripts in the hooks directory
|
|
58
|
+
|
|
59
|
+
## Examples
|
|
60
|
+
|
|
61
|
+
### Codex
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
selftune codex install # Install hooks into ~/.codex/hooks.json
|
|
65
|
+
selftune codex install --dry-run # Preview changes without writing
|
|
66
|
+
selftune codex install --uninstall # Remove selftune hooks
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### OpenCode
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
selftune opencode install # Install plugin (selftune-opencode-plugin.ts) + config entries
|
|
73
|
+
selftune opencode install --dry-run # Preview changes without writing
|
|
74
|
+
selftune opencode install --uninstall # Remove selftune plugin and config entries
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Cline
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
selftune cline install # Create hook scripts in ~/Documents/Cline/Hooks/
|
|
81
|
+
selftune cline install --dry-run # Preview what would be created
|
|
82
|
+
selftune cline install --uninstall # Remove selftune hook scripts
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Hook handler (agent-only, not user-facing)
|
|
86
|
+
|
|
87
|
+
The hook subcommand is called automatically by the agent. Users do not run it directly:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
printf '%s\n' "$PAYLOAD" | selftune codex hook
|
|
91
|
+
printf '%s\n' "$PAYLOAD" | selftune opencode hook
|
|
92
|
+
printf '%s\n' "$PAYLOAD" | selftune cline hook
|
|
93
|
+
```
|