selftune 0.2.14 → 0.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/apps/local-dashboard/dist/assets/index-DOu3iLD9.js +16 -0
  2. package/apps/local-dashboard/dist/assets/vendor-ui-DIwlrGlb.js +12 -0
  3. package/apps/local-dashboard/dist/index.html +2 -2
  4. package/bin/run-hook.cjs +36 -0
  5. package/cli/selftune/analytics.ts +13 -11
  6. package/cli/selftune/badge/badge.ts +13 -9
  7. package/cli/selftune/canonical-export.ts +6 -6
  8. package/cli/selftune/contribute/contribute.ts +2 -1
  9. package/cli/selftune/cron/setup.ts +3 -1
  10. package/cli/selftune/dashboard-contract.ts +10 -0
  11. package/cli/selftune/dashboard.ts +10 -5
  12. package/cli/selftune/eval/baseline.ts +20 -30
  13. package/cli/selftune/eval/hooks-to-evals.ts +22 -12
  14. package/cli/selftune/eval/import-skillsbench.ts +21 -8
  15. package/cli/selftune/eval/unit-test-cli.ts +22 -11
  16. package/cli/selftune/evolution/description-quality.ts +224 -0
  17. package/cli/selftune/evolution/evolve-body.ts +17 -10
  18. package/cli/selftune/evolution/evolve.ts +94 -59
  19. package/cli/selftune/evolution/rollback.ts +7 -6
  20. package/cli/selftune/evolution/unblock-suggestions.ts +159 -0
  21. package/cli/selftune/grading/auto-grade.ts +24 -22
  22. package/cli/selftune/grading/grade-session.ts +21 -17
  23. package/cli/selftune/hooks/auto-activate.ts +12 -3
  24. package/cli/selftune/hooks/prompt-log.ts +7 -1
  25. package/cli/selftune/index.ts +66 -69
  26. package/cli/selftune/ingestors/claude-replay.ts +29 -14
  27. package/cli/selftune/ingestors/codex-rollout.ts +6 -1
  28. package/cli/selftune/init.ts +212 -36
  29. package/cli/selftune/monitoring/watch.ts +32 -16
  30. package/cli/selftune/orchestrate.ts +18 -17
  31. package/cli/selftune/routes/skill-report.ts +17 -0
  32. package/cli/selftune/schedule.ts +23 -9
  33. package/cli/selftune/sync.ts +7 -3
  34. package/cli/selftune/types.ts +45 -10
  35. package/cli/selftune/utils/cli-error.ts +102 -0
  36. package/cli/selftune/utils/hooks.ts +12 -2
  37. package/cli/selftune/workflows/workflows.ts +23 -17
  38. package/package.json +1 -1
  39. package/skill/SKILL.md +1 -1
  40. package/skill/Workflows/AutoActivation.md +1 -1
  41. package/skill/Workflows/Evolve.md +4 -0
  42. package/skill/Workflows/Initialize.md +8 -8
  43. package/skill/settings_snippet.json +35 -12
  44. package/apps/local-dashboard/dist/assets/index-DIrdlu2_.js +0 -16
  45. package/apps/local-dashboard/dist/assets/vendor-ui-7xD7fNEU.js +0 -12
@@ -18,6 +18,7 @@ import { dirname, join } from "node:path";
18
18
  import { parseArgs } from "node:util";
19
19
 
20
20
  import { DEFAULT_CRON_JOBS } from "./cron/setup.js";
21
+ import { CLIError, handleCLIError } from "./utils/cli-error.js";
21
22
 
22
23
  // ---------------------------------------------------------------------------
23
24
  // Binary resolution — launchd runs with minimal PATH, so we need full paths
@@ -533,10 +534,11 @@ export function cliMain(): void {
533
534
  applyCronArtifact(values["apply-cron-artifact"]);
534
535
  return;
535
536
  } catch (err) {
536
- console.error(
537
+ throw new CLIError(
537
538
  `Failed to apply selftune cron artifact: ${err instanceof Error ? err.message : String(err)}`,
539
+ "OPERATION_FAILED",
540
+ "selftune schedule --install --dry-run",
538
541
  );
539
- process.exit(1);
540
542
  }
541
543
  }
542
544
 
@@ -569,8 +571,11 @@ For OpenClaw-specific scheduling, see: selftune cron`);
569
571
  dryRun: values["dry-run"] ?? false,
570
572
  });
571
573
  if (!result.dryRun && !result.activated) {
572
- console.error("Failed to activate installed schedule artifacts.");
573
- process.exit(1);
574
+ throw new CLIError(
575
+ "Failed to activate installed schedule artifacts.",
576
+ "OPERATION_FAILED",
577
+ "selftune schedule --install --dry-run",
578
+ );
574
579
  }
575
580
  console.log(
576
581
  JSON.stringify(
@@ -587,21 +592,30 @@ For OpenClaw-specific scheduling, see: selftune cron`);
587
592
  );
588
593
  return;
589
594
  } catch (err) {
590
- console.error(
595
+ if (err instanceof CLIError) throw err;
596
+ throw new CLIError(
591
597
  `Failed to install schedule artifacts: ${err instanceof Error ? err.message : String(err)}`,
598
+ "OPERATION_FAILED",
599
+ "selftune schedule --install --dry-run",
592
600
  );
593
- process.exit(1);
594
601
  }
595
602
  }
596
603
 
597
604
  const result = formatOutput(values.format);
598
605
  if (!result.ok) {
599
- console.error(result.error);
600
- process.exit(1);
606
+ throw new CLIError(
607
+ result.error ?? "Invalid schedule format",
608
+ "INVALID_FLAG",
609
+ "selftune schedule --format cron",
610
+ );
601
611
  }
602
612
  console.log(result.data);
603
613
  }
604
614
 
605
615
  if (import.meta.main) {
606
- cliMain();
616
+ try {
617
+ cliMain();
618
+ } catch (err) {
619
+ handleCLIError(err);
620
+ }
607
621
  }
@@ -62,6 +62,7 @@ import {
62
62
  rebuildSkillUsageFromTranscripts,
63
63
  } from "./repair/skill-usage.js";
64
64
  import type { SkillUsageRecord } from "./types.js";
65
+ import { CLIError, handleCLIError } from "./utils/cli-error.js";
65
66
  import { loadMarker, readJsonl, saveMarker } from "./utils/jsonl.js";
66
67
  import { writeRepairedSkillUsageRecords } from "./utils/skill-log.js";
67
68
 
@@ -560,8 +561,11 @@ Options:
560
561
  if (values.since) {
561
562
  since = new Date(values.since);
562
563
  if (Number.isNaN(since.getTime())) {
563
- console.error(`[ERROR] Invalid --since date: ${values.since}`);
564
- process.exit(1);
564
+ throw new CLIError(
565
+ `Invalid --since date: ${values.since}`,
566
+ "INVALID_FLAG",
567
+ "selftune sync --since 2026-01-01",
568
+ );
565
569
  }
566
570
  }
567
571
 
@@ -665,5 +669,5 @@ Options:
665
669
  }
666
670
 
667
671
  if (import.meta.main) {
668
- cliMain();
672
+ cliMain().catch(handleCLIError);
669
673
  }
@@ -166,26 +166,46 @@ export interface TranscriptMetrics {
166
166
  // Hook payloads (received via stdin from Claude Code)
167
167
  // ---------------------------------------------------------------------------
168
168
 
169
+ /**
170
+ * Common fields present on ALL hook event payloads per Claude Code docs.
171
+ * Individual payloads extend this with event-specific fields.
172
+ */
173
+ export interface CommonHookPayload {
174
+ session_id?: string;
175
+ transcript_path?: string;
176
+ cwd?: string;
177
+ permission_mode?: string;
178
+ hook_event_name?: string;
179
+ /** Present when hook fires inside a subagent. */
180
+ agent_id?: string;
181
+ /** Agent name (e.g. "Explore", "Plan", or custom agent name). */
182
+ agent_type?: string;
183
+ }
184
+
169
185
  // Shared base for pre/post tool-use hook payloads
170
- export interface BaseToolUsePayload {
186
+ export interface BaseToolUsePayload extends CommonHookPayload {
171
187
  tool_name: string;
172
188
  tool_input: Record<string, unknown>;
173
- session_id?: string;
189
+ tool_use_id?: string;
174
190
  }
175
191
 
176
- export interface PromptSubmitPayload {
177
- user_prompt: string;
178
- session_id?: string;
192
+ export interface PromptSubmitPayload extends CommonHookPayload {
193
+ /** Current field name per Claude Code docs (2025+). */
194
+ prompt?: string;
195
+ /** Legacy field name — kept for backwards compatibility. */
196
+ user_prompt?: string;
179
197
  }
180
198
 
181
199
  export interface PostToolUsePayload extends BaseToolUsePayload {
182
- transcript_path?: string;
200
+ /** Tool execution result, schema depends on the tool. */
201
+ tool_response?: Record<string, unknown>;
183
202
  }
184
203
 
185
- export interface StopPayload {
186
- session_id?: string;
187
- transcript_path?: string;
188
- cwd?: string;
204
+ export interface StopPayload extends CommonHookPayload {
205
+ /** True when Claude Code is continuing as a result of a stop hook. */
206
+ stop_hook_active?: boolean;
207
+ /** Text content of Claude's final response. */
208
+ last_assistant_message?: string;
189
209
  }
190
210
 
191
211
  // ---------------------------------------------------------------------------
@@ -394,6 +414,18 @@ export interface EvolutionConfig {
394
414
  // Validation result base (self-contained for Pareto types)
395
415
  // ---------------------------------------------------------------------------
396
416
 
417
+ /** Heuristic quality score for a skill description (no LLM, pure function). */
418
+ export interface DescriptionQualityScore {
419
+ composite: number; // 0.0-1.0 weighted aggregate
420
+ criteria: {
421
+ length: number; // description length in optimal range
422
+ trigger_context: number; // includes when/if/before/after context
423
+ vagueness: number; // absence of vague words
424
+ specificity: number; // concrete action verbs present
425
+ not_just_name: number; // not just restating the skill name
426
+ };
427
+ }
428
+
397
429
  /** Compact summary of an evolve run, used for CLI JSON output. */
398
430
  export interface EvolveResultSummary {
399
431
  skill: string;
@@ -412,6 +444,9 @@ export interface EvolveResultSummary {
412
444
  rationale: string;
413
445
  version?: string;
414
446
  dashboard_url: string;
447
+ description_quality_before?: number;
448
+ description_quality_after?: number;
449
+ suggestions?: string[];
415
450
  }
416
451
 
417
452
  export interface ValidationResultBase {
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Typed CLI error with machine-readable code, agent-actionable suggestion, and exit code.
3
+ *
4
+ * Replaces ad-hoc `console.error() + process.exit(1)` patterns across the CLI.
5
+ * When `--json` mode is active, errors serialize to structured JSON on stderr.
6
+ * When text mode is active, errors print human-readable messages with suggestions.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * throw new CLIError(
11
+ * "No selftune config found",
12
+ * "CONFIG_MISSING",
13
+ * "Run: selftune init",
14
+ * 4, // exit code for config-missing per agent-cli-contract
15
+ * );
16
+ * ```
17
+ */
18
+
19
+ export type CLIErrorCode =
20
+ | "INVALID_FLAG"
21
+ | "MISSING_FLAG"
22
+ | "CONFIG_MISSING"
23
+ | "FILE_NOT_FOUND"
24
+ | "AGENT_NOT_FOUND"
25
+ | "UNKNOWN_COMMAND"
26
+ | "GUARD_BLOCKED"
27
+ | "OPERATION_FAILED"
28
+ | "MISSING_DATA"
29
+ | "INTERNAL_ERROR";
30
+
31
+ export class CLIError extends Error {
32
+ constructor(
33
+ message: string,
34
+ /** Machine-readable error code (SCREAMING_SNAKE_CASE). */
35
+ public readonly code: CLIErrorCode,
36
+ /** Agent-actionable next command or remediation step. */
37
+ public readonly suggestion?: string,
38
+ /** Process exit code. Default 1 (general error). */
39
+ public readonly exitCode: number = 1,
40
+ /** Whether the agent should retry the same command. */
41
+ public readonly retryable: boolean = false,
42
+ ) {
43
+ super(message);
44
+ this.name = "CLIError";
45
+ }
46
+
47
+ /** Structured JSON representation for `--json` mode. */
48
+ toJSON(): {
49
+ error: {
50
+ code: CLIErrorCode;
51
+ message: string;
52
+ suggestion?: string;
53
+ retryable: boolean;
54
+ };
55
+ } {
56
+ return {
57
+ error: {
58
+ code: this.code,
59
+ message: this.message,
60
+ ...(this.suggestion ? { suggestion: this.suggestion } : {}),
61
+ retryable: this.retryable,
62
+ },
63
+ };
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Top-level error handler for CLI entry points.
69
+ *
70
+ * Install at the bottom of any CLI entry point:
71
+ * ```ts
72
+ * cliMain().catch(handleCLIError);
73
+ * ```
74
+ */
75
+ /** Detect JSON output mode: explicit --json flag or non-TTY stdout (automation). */
76
+ export function isJsonOutputMode(): boolean {
77
+ return process.argv.includes("--json") || process.stdout?.isTTY === false;
78
+ }
79
+
80
+ export function handleCLIError(error: unknown): never {
81
+ const jsonMode = isJsonOutputMode();
82
+
83
+ if (error instanceof CLIError) {
84
+ if (jsonMode) {
85
+ console.error(JSON.stringify(error.toJSON()));
86
+ process.exit(error.exitCode);
87
+ }
88
+ console.error(`[ERROR] ${error.message}`);
89
+ if (error.suggestion) {
90
+ console.error(` → ${error.suggestion}`);
91
+ }
92
+ process.exit(error.exitCode);
93
+ }
94
+
95
+ const message = error instanceof Error ? error.message : String(error);
96
+ if (jsonMode) {
97
+ console.error(JSON.stringify({ error: { code: "INTERNAL_ERROR", message, retryable: false } }));
98
+ process.exit(1);
99
+ }
100
+ console.error(`[FATAL] ${message}`);
101
+ process.exit(1);
102
+ }
@@ -13,14 +13,24 @@ function isHookEntry(value: unknown): value is ClaudeCodeHookEntry {
13
13
  return typeof value === "object" && value !== null;
14
14
  }
15
15
 
16
+ /** Check if a command string references a selftune-managed hook. */
17
+ export function isSelftuneCommand(command: string): boolean {
18
+ const normalized = command.replace(/\\/g, "/");
19
+ return (
20
+ normalized.includes("/cli/selftune/hooks/") ||
21
+ normalized.includes("/bin/run-hook.cjs") ||
22
+ normalized.startsWith("npx selftune hook ")
23
+ );
24
+ }
25
+
16
26
  export function entryReferencesSelftune(entry: ClaudeCodeHookEntry): boolean {
17
- if (typeof entry.command === "string" && entry.command.includes("selftune")) {
27
+ if (typeof entry.command === "string" && isSelftuneCommand(entry.command)) {
18
28
  return true;
19
29
  }
20
30
 
21
31
  if (Array.isArray(entry.hooks)) {
22
32
  return entry.hooks.some(
23
- (hook) => typeof hook.command === "string" && hook.command.includes("selftune"),
33
+ (hook) => typeof hook.command === "string" && isSelftuneCommand(hook.command),
24
34
  );
25
35
  }
26
36
 
@@ -19,6 +19,7 @@ import type {
19
19
  SkillUsageRecord,
20
20
  WorkflowDiscoveryReport,
21
21
  } from "../types.js";
22
+ import { CLIError } from "../utils/cli-error.js";
22
23
  import { discoverWorkflows } from "./discover.js";
23
24
  import { appendWorkflow } from "./skill-md-writer.js";
24
25
 
@@ -79,13 +80,11 @@ export async function cliMain(): Promise<void> {
79
80
  ? Number.parseInt(values["min-occurrences"], 10)
80
81
  : undefined;
81
82
  if (minOccurrences !== undefined && (Number.isNaN(minOccurrences) || minOccurrences < 0)) {
82
- console.error("[ERROR] --min-occurrences must be a non-negative integer.");
83
- process.exit(1);
83
+ throw new CLIError("--min-occurrences must be a non-negative integer.", "INVALID_FLAG");
84
84
  }
85
85
  const window = values.window ? Number.parseInt(values.window, 10) : undefined;
86
86
  if (window !== undefined && (Number.isNaN(window) || window < 0)) {
87
- console.error("[ERROR] --window must be a non-negative integer.");
88
- process.exit(1);
87
+ throw new CLIError("--window must be a non-negative integer.", "INVALID_FLAG");
89
88
  }
90
89
 
91
90
  // Read telemetry and skill usage logs from SQLite
@@ -104,8 +103,11 @@ export async function cliMain(): Promise<void> {
104
103
  // Save subcommand: find workflow, append to SKILL.md
105
104
  const nameArg = positionals[1];
106
105
  if (!nameArg) {
107
- console.error("[ERROR] Usage: selftune workflows save <name-or-index>");
108
- process.exit(1);
106
+ throw new CLIError(
107
+ "Usage: selftune workflows save <name-or-index>",
108
+ "MISSING_FLAG",
109
+ "Provide a workflow name or index (e.g., selftune workflows save 1).",
110
+ );
109
111
  }
110
112
 
111
113
  // Match by numeric index (1-based) or workflow_id
@@ -118,9 +120,11 @@ export async function cliMain(): Promise<void> {
118
120
  }
119
121
 
120
122
  if (!workflow) {
121
- console.error(`[ERROR] No workflow found matching "${nameArg}".`);
122
- console.error("Run 'selftune workflows' to see discovered workflows and their indices.");
123
- process.exit(1);
123
+ throw new CLIError(
124
+ `No workflow found matching "${nameArg}".`,
125
+ "INVALID_FLAG",
126
+ "Run 'selftune workflows' to see discovered workflows and their indices.",
127
+ );
124
128
  }
125
129
 
126
130
  // Determine SKILL.md path
@@ -140,18 +144,20 @@ export async function cliMain(): Promise<void> {
140
144
  skillPath = uniquePaths[0];
141
145
  } else if (uniquePaths.length > 1) {
142
146
  // Ambiguous: multiple SKILL.md paths found across contributing sessions
143
- console.error(`[ERROR] Multiple SKILL.md paths found for "${firstSkill}":`);
144
- for (const p of uniquePaths) {
145
- console.error(` - ${p}`);
146
- }
147
- console.error("Use --skill-path to specify which one to update.");
148
- process.exit(1);
147
+ throw new CLIError(
148
+ `Multiple SKILL.md paths found for "${firstSkill}": ${uniquePaths.join(", ")}`,
149
+ "INVALID_FLAG",
150
+ "Use --skill-path to specify which one to update.",
151
+ );
149
152
  }
150
153
  }
151
154
 
152
155
  if (!skillPath || !existsSync(skillPath)) {
153
- console.error(`[ERROR] Could not determine SKILL.md path. Use --skill-path to specify.`);
154
- process.exit(1);
156
+ throw new CLIError(
157
+ "Could not determine SKILL.md path.",
158
+ "FILE_NOT_FOUND",
159
+ "Use --skill-path to specify the SKILL.md file to update.",
160
+ );
155
161
  }
156
162
 
157
163
  // Build CodifiedWorkflow
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selftune",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "description": "Self-improving skills CLI for AI agents",
5
5
  "keywords": [
6
6
  "agent",
package/skill/SKILL.md CHANGED
@@ -12,7 +12,7 @@ description: >
12
12
  even if they don't say "selftune" explicitly.
13
13
  metadata:
14
14
  author: selftune-dev
15
- version: 0.2.14
15
+ version: 0.2.16
16
16
  category: developer-tools
17
17
  ---
18
18
 
@@ -110,7 +110,7 @@ The hook is registered under `UserPromptSubmit`:
110
110
  "hooks": {
111
111
  "UserPromptSubmit": [
112
112
  {
113
- "command": "bun run /path/to/cli/selftune/hooks/auto-activate.ts"
113
+ "command": "node /path/to/bin/run-hook.cjs /path/to/cli/selftune/hooks/auto-activate.ts"
114
114
  }
115
115
  ]
116
116
  }
@@ -278,6 +278,10 @@ After evolution completes (deploy or dry-run), the memory writer updates:
278
278
  This ensures the next evolve, watch, or rollback workflow has full context
279
279
  even after a context window reset.
280
280
 
281
+ ### Description Quality Scoring
282
+
283
+ Proposals are scored on heuristic quality criteria (no LLM required). The composite score (0.0–1.0) uses five weighted criteria: trigger context (0.30), vagueness absence (0.20), specificity (0.20), length (0.15), and not-just-name (0.15). Proposals that regress in quality score are rejected. See `docs/design-docs/evolution-pipeline.md` for full criteria details.
284
+
281
285
  ### Stopping Criteria
282
286
 
283
287
  The evolution loop stops when any of these conditions is met (priority order):
@@ -126,14 +126,14 @@ Code subagent calls stay up to date.
126
126
 
127
127
  **Hook reference** (for troubleshooting):
128
128
 
129
- | Hook | Script | Purpose |
130
- | -------------------------- | ----------------------------- | ----------------------------------------------- |
131
- | `UserPromptSubmit` | `hooks/prompt-log.ts` | Log every user query |
132
- | `UserPromptSubmit` | `hooks/auto-activate.ts` | Suggest skills before prompt processing |
133
- | `PreToolUse` (Write/Edit) | `hooks/skill-change-guard.ts` | Detect uncontrolled skill edits |
134
- | `PreToolUse` (Write/Edit) | `hooks/evolution-guard.ts` | Block SKILL.md edits on monitored skills |
135
- | `PostToolUse` (Read/Skill) | `hooks/skill-eval.ts` | Track skill triggers and Skill tool invocations |
136
- | `Stop` | `hooks/session-stop.ts` | Capture session telemetry |
129
+ | Hook | Script | Purpose | Notes |
130
+ | -------------------------- | ----------------------------- | ----------------------------------------------- | ---------------------------------------------- |
131
+ | `UserPromptSubmit` | `hooks/prompt-log.ts` | Log every user query | Accepts both `prompt` and legacy `user_prompt` |
132
+ | `UserPromptSubmit` | `hooks/auto-activate.ts` | Suggest skills before prompt processing | Uses `additionalContext` JSON for suggestions |
133
+ | `PreToolUse` (Write/Edit) | `hooks/skill-change-guard.ts` | Detect uncontrolled skill edits | `if` filter: only fires on `*SKILL.md` paths |
134
+ | `PreToolUse` (Write/Edit) | `hooks/evolution-guard.ts` | Block SKILL.md edits on monitored skills | `if` filter: only fires on `*SKILL.md` paths |
135
+ | `PostToolUse` (Read/Skill) | `hooks/skill-eval.ts` | Track skill triggers and Skill tool invocations | |
136
+ | `Stop` | `hooks/session-stop.ts` | Capture session telemetry | Runs async (non-blocking), 60s timeout |
137
137
 
138
138
  **Codex agents:**
139
139
 
@@ -9,13 +9,15 @@
9
9
  "hooks": [
10
10
  {
11
11
  "type": "command",
12
- "command": "bun run /PATH/TO/cli/selftune/hooks/prompt-log.ts",
13
- "timeout": 5
12
+ "command": "node /PATH/TO/bin/run-hook.cjs /PATH/TO/cli/selftune/hooks/prompt-log.ts",
13
+ "timeout": 5,
14
+ "statusMessage": "selftune: logging prompt"
14
15
  },
15
16
  {
16
17
  "type": "command",
17
- "command": "bun run /PATH/TO/cli/selftune/hooks/auto-activate.ts",
18
- "timeout": 5
18
+ "command": "node /PATH/TO/bin/run-hook.cjs /PATH/TO/cli/selftune/hooks/auto-activate.ts",
19
+ "timeout": 5,
20
+ "statusMessage": "selftune: checking activation rules"
19
21
  }
20
22
  ]
21
23
  }
@@ -26,13 +28,31 @@
26
28
  "hooks": [
27
29
  {
28
30
  "type": "command",
29
- "command": "bun run /PATH/TO/cli/selftune/hooks/skill-change-guard.ts",
30
- "timeout": 5
31
+ "if": "Write(*SKILL.md)",
32
+ "command": "node /PATH/TO/bin/run-hook.cjs /PATH/TO/cli/selftune/hooks/skill-change-guard.ts",
33
+ "timeout": 5,
34
+ "statusMessage": "selftune: checking skill change guard"
31
35
  },
32
36
  {
33
37
  "type": "command",
34
- "command": "bun run /PATH/TO/cli/selftune/hooks/evolution-guard.ts",
35
- "timeout": 5
38
+ "if": "Edit(*SKILL.md)",
39
+ "command": "node /PATH/TO/bin/run-hook.cjs /PATH/TO/cli/selftune/hooks/skill-change-guard.ts",
40
+ "timeout": 5,
41
+ "statusMessage": "selftune: checking skill change guard"
42
+ },
43
+ {
44
+ "type": "command",
45
+ "if": "Write(*SKILL.md)",
46
+ "command": "node /PATH/TO/bin/run-hook.cjs /PATH/TO/cli/selftune/hooks/evolution-guard.ts",
47
+ "timeout": 5,
48
+ "statusMessage": "selftune: checking evolution guard"
49
+ },
50
+ {
51
+ "type": "command",
52
+ "if": "Edit(*SKILL.md)",
53
+ "command": "node /PATH/TO/bin/run-hook.cjs /PATH/TO/cli/selftune/hooks/evolution-guard.ts",
54
+ "timeout": 5,
55
+ "statusMessage": "selftune: checking evolution guard"
36
56
  }
37
57
  ]
38
58
  }
@@ -43,8 +63,9 @@
43
63
  "hooks": [
44
64
  {
45
65
  "type": "command",
46
- "command": "bun run /PATH/TO/cli/selftune/hooks/skill-eval.ts",
47
- "timeout": 5
66
+ "command": "node /PATH/TO/bin/run-hook.cjs /PATH/TO/cli/selftune/hooks/skill-eval.ts",
67
+ "timeout": 5,
68
+ "statusMessage": "selftune: evaluating skill usage"
48
69
  }
49
70
  ]
50
71
  }
@@ -54,8 +75,10 @@
54
75
  "hooks": [
55
76
  {
56
77
  "type": "command",
57
- "command": "bun run /PATH/TO/cli/selftune/hooks/session-stop.ts",
58
- "timeout": 15
78
+ "command": "node /PATH/TO/bin/run-hook.cjs /PATH/TO/cli/selftune/hooks/session-stop.ts",
79
+ "timeout": 60,
80
+ "async": true,
81
+ "statusMessage": "selftune: capturing session telemetry"
59
82
  }
60
83
  ]
61
84
  }