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.
- package/apps/local-dashboard/dist/assets/index-DOu3iLD9.js +16 -0
- package/apps/local-dashboard/dist/assets/vendor-ui-DIwlrGlb.js +12 -0
- package/apps/local-dashboard/dist/index.html +2 -2
- package/bin/run-hook.cjs +36 -0
- package/cli/selftune/analytics.ts +13 -11
- package/cli/selftune/badge/badge.ts +13 -9
- package/cli/selftune/canonical-export.ts +6 -6
- package/cli/selftune/contribute/contribute.ts +2 -1
- package/cli/selftune/cron/setup.ts +3 -1
- package/cli/selftune/dashboard-contract.ts +10 -0
- package/cli/selftune/dashboard.ts +10 -5
- package/cli/selftune/eval/baseline.ts +20 -30
- package/cli/selftune/eval/hooks-to-evals.ts +22 -12
- package/cli/selftune/eval/import-skillsbench.ts +21 -8
- package/cli/selftune/eval/unit-test-cli.ts +22 -11
- package/cli/selftune/evolution/description-quality.ts +224 -0
- package/cli/selftune/evolution/evolve-body.ts +17 -10
- package/cli/selftune/evolution/evolve.ts +94 -59
- package/cli/selftune/evolution/rollback.ts +7 -6
- package/cli/selftune/evolution/unblock-suggestions.ts +159 -0
- package/cli/selftune/grading/auto-grade.ts +24 -22
- package/cli/selftune/grading/grade-session.ts +21 -17
- package/cli/selftune/hooks/auto-activate.ts +12 -3
- package/cli/selftune/hooks/prompt-log.ts +7 -1
- package/cli/selftune/index.ts +66 -69
- package/cli/selftune/ingestors/claude-replay.ts +29 -14
- package/cli/selftune/ingestors/codex-rollout.ts +6 -1
- package/cli/selftune/init.ts +212 -36
- package/cli/selftune/monitoring/watch.ts +32 -16
- package/cli/selftune/orchestrate.ts +18 -17
- package/cli/selftune/routes/skill-report.ts +17 -0
- package/cli/selftune/schedule.ts +23 -9
- package/cli/selftune/sync.ts +7 -3
- package/cli/selftune/types.ts +45 -10
- package/cli/selftune/utils/cli-error.ts +102 -0
- package/cli/selftune/utils/hooks.ts +12 -2
- package/cli/selftune/workflows/workflows.ts +23 -17
- package/package.json +1 -1
- package/skill/SKILL.md +1 -1
- package/skill/Workflows/AutoActivation.md +1 -1
- package/skill/Workflows/Evolve.md +4 -0
- package/skill/Workflows/Initialize.md +8 -8
- package/skill/settings_snippet.json +35 -12
- package/apps/local-dashboard/dist/assets/index-DIrdlu2_.js +0 -16
- package/apps/local-dashboard/dist/assets/vendor-ui-7xD7fNEU.js +0 -12
package/cli/selftune/schedule.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
573
|
-
|
|
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
|
-
|
|
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
|
-
|
|
600
|
-
|
|
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
|
-
|
|
616
|
+
try {
|
|
617
|
+
cliMain();
|
|
618
|
+
} catch (err) {
|
|
619
|
+
handleCLIError(err);
|
|
620
|
+
}
|
|
607
621
|
}
|
package/cli/selftune/sync.ts
CHANGED
|
@@ -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
|
-
|
|
564
|
-
|
|
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
|
}
|
package/cli/selftune/types.ts
CHANGED
|
@@ -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
|
-
|
|
189
|
+
tool_use_id?: string;
|
|
174
190
|
}
|
|
175
191
|
|
|
176
|
-
export interface PromptSubmitPayload {
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
200
|
+
/** Tool execution result, schema depends on the tool. */
|
|
201
|
+
tool_response?: Record<string, unknown>;
|
|
183
202
|
}
|
|
184
203
|
|
|
185
|
-
export interface StopPayload {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
154
|
-
|
|
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
package/skill/SKILL.md
CHANGED
|
@@ -110,7 +110,7 @@ The hook is registered under `UserPromptSubmit`:
|
|
|
110
110
|
"hooks": {
|
|
111
111
|
"UserPromptSubmit": [
|
|
112
112
|
{
|
|
113
|
-
"command": "
|
|
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": "
|
|
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": "
|
|
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
|
-
"
|
|
30
|
-
"
|
|
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
|
-
"
|
|
35
|
-
"
|
|
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": "
|
|
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": "
|
|
58
|
-
"timeout":
|
|
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
|
}
|