selftune 0.2.0 → 0.2.2
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/.claude/agents/diagnosis-analyst.md +20 -10
- package/.claude/agents/evolution-reviewer.md +14 -1
- package/.claude/agents/integration-guide.md +18 -6
- package/.claude/agents/pattern-analyst.md +18 -5
- package/CHANGELOG.md +12 -4
- package/README.md +43 -35
- package/apps/local-dashboard/dist/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
- package/apps/local-dashboard/dist/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
- package/apps/local-dashboard/dist/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
- package/apps/local-dashboard/dist/assets/index-C4EOTFZ2.js +15 -0
- package/apps/local-dashboard/dist/assets/index-bl-Webyd.css +1 -0
- package/apps/local-dashboard/dist/assets/vendor-react-U7zYD9Rg.js +60 -0
- package/apps/local-dashboard/dist/assets/vendor-table-B7VF2Ipl.js +26 -0
- package/apps/local-dashboard/dist/assets/vendor-ui-D7_zX_qy.js +346 -0
- package/apps/local-dashboard/dist/favicon.png +0 -0
- package/apps/local-dashboard/dist/index.html +17 -0
- package/apps/local-dashboard/dist/logo.png +0 -0
- package/apps/local-dashboard/dist/logo.svg +9 -0
- package/cli/selftune/badge/badge-data.ts +1 -1
- package/cli/selftune/badge/badge.ts +4 -8
- package/cli/selftune/canonical-export.ts +183 -0
- package/cli/selftune/constants.ts +28 -0
- package/cli/selftune/contribute/contribute.ts +1 -1
- package/cli/selftune/cron/setup.ts +17 -17
- package/cli/selftune/dashboard-contract.ts +202 -0
- package/cli/selftune/dashboard-server.ts +653 -186
- package/cli/selftune/dashboard.ts +41 -176
- package/cli/selftune/eval/baseline.ts +5 -4
- package/cli/selftune/eval/composability-v2.ts +273 -0
- package/cli/selftune/eval/hooks-to-evals.ts +34 -15
- package/cli/selftune/eval/unit-test-cli.ts +1 -1
- package/cli/selftune/evolution/evidence.ts +26 -0
- package/cli/selftune/evolution/evolve-body.ts +105 -11
- package/cli/selftune/evolution/evolve.ts +371 -25
- package/cli/selftune/evolution/extract-patterns.ts +87 -29
- package/cli/selftune/evolution/rollback.ts +2 -2
- package/cli/selftune/grading/auto-grade.ts +200 -0
- package/cli/selftune/grading/grade-session.ts +448 -97
- package/cli/selftune/grading/results.ts +42 -0
- package/cli/selftune/hooks/prompt-log.ts +172 -2
- package/cli/selftune/hooks/session-stop.ts +123 -3
- package/cli/selftune/hooks/skill-eval.ts +119 -3
- package/cli/selftune/index.ts +395 -116
- package/cli/selftune/ingestors/claude-replay.ts +140 -114
- package/cli/selftune/ingestors/codex-rollout.ts +345 -46
- package/cli/selftune/ingestors/codex-wrapper.ts +207 -39
- package/cli/selftune/ingestors/openclaw-ingest.ts +141 -8
- package/cli/selftune/ingestors/opencode-ingest.ts +193 -17
- package/cli/selftune/init.ts +227 -14
- package/cli/selftune/last.ts +14 -5
- package/cli/selftune/localdb/db.ts +63 -0
- package/cli/selftune/localdb/materialize.ts +428 -0
- package/cli/selftune/localdb/queries.ts +376 -0
- package/cli/selftune/localdb/schema.ts +204 -0
- package/cli/selftune/monitoring/watch.ts +66 -15
- package/cli/selftune/normalization.ts +682 -0
- package/cli/selftune/observability.ts +19 -44
- package/cli/selftune/orchestrate.ts +1073 -0
- package/cli/selftune/quickstart.ts +203 -0
- package/cli/selftune/repair/skill-usage.ts +576 -0
- package/cli/selftune/schedule.ts +561 -0
- package/cli/selftune/status.ts +48 -26
- package/cli/selftune/sync.ts +627 -0
- package/cli/selftune/types.ts +148 -0
- package/cli/selftune/utils/canonical-log.ts +45 -0
- package/cli/selftune/utils/hooks.ts +41 -0
- package/cli/selftune/utils/html.ts +27 -0
- package/cli/selftune/utils/llm-call.ts +78 -20
- package/cli/selftune/utils/math.ts +10 -0
- package/cli/selftune/utils/query-filter.ts +139 -0
- package/cli/selftune/utils/skill-discovery.ts +340 -0
- package/cli/selftune/utils/skill-log.ts +68 -0
- package/cli/selftune/utils/skill-usage-confidence.ts +18 -0
- package/cli/selftune/utils/transcript.ts +272 -26
- package/cli/selftune/workflows/discover.ts +254 -0
- package/cli/selftune/workflows/skill-md-writer.ts +288 -0
- package/cli/selftune/workflows/workflows.ts +188 -0
- package/package.json +21 -8
- package/packages/telemetry-contract/README.md +11 -0
- package/packages/telemetry-contract/fixtures/golden.json +87 -0
- package/packages/telemetry-contract/fixtures/golden.test.ts +42 -0
- package/packages/telemetry-contract/index.ts +1 -0
- package/packages/telemetry-contract/package.json +19 -0
- package/packages/telemetry-contract/src/index.ts +2 -0
- package/packages/telemetry-contract/src/types.ts +163 -0
- package/packages/telemetry-contract/src/validators.ts +109 -0
- package/skill/SKILL.md +84 -53
- package/skill/Workflows/AutoActivation.md +17 -16
- package/skill/Workflows/Badge.md +6 -0
- package/skill/Workflows/Baseline.md +46 -23
- package/skill/Workflows/Composability.md +12 -5
- package/skill/Workflows/Contribute.md +17 -14
- package/skill/Workflows/Cron.md +56 -79
- package/skill/Workflows/Dashboard.md +45 -34
- package/skill/Workflows/Doctor.md +30 -17
- package/skill/Workflows/Evals.md +64 -40
- package/skill/Workflows/EvolutionMemory.md +2 -0
- package/skill/Workflows/Evolve.md +102 -47
- package/skill/Workflows/EvolveBody.md +6 -6
- package/skill/Workflows/Grade.md +36 -31
- package/skill/Workflows/ImportSkillsBench.md +11 -5
- package/skill/Workflows/Ingest.md +43 -36
- package/skill/Workflows/Initialize.md +44 -30
- package/skill/Workflows/Orchestrate.md +139 -0
- package/skill/Workflows/Replay.md +39 -18
- package/skill/Workflows/Rollback.md +3 -3
- package/skill/Workflows/Schedule.md +61 -0
- package/skill/Workflows/Sync.md +88 -0
- package/skill/Workflows/UnitTest.md +34 -22
- package/skill/Workflows/Watch.md +14 -4
- package/skill/Workflows/Workflows.md +129 -0
- package/skill/assets/activation-rules-default.json +26 -0
- package/skill/assets/multi-skill-settings.json +63 -0
- package/skill/assets/single-skill-settings.json +57 -0
- package/skill/references/invocation-taxonomy.md +2 -2
- package/skill/references/logs.md +164 -2
- package/skill/references/setup-patterns.md +65 -0
- package/skill/references/version-history.md +40 -0
- package/skill/settings_snippet.json +1 -1
- package/templates/multi-skill-settings.json +7 -7
- package/templates/single-skill-settings.json +6 -6
- package/dashboard/index.html +0 -1680
|
Binary file
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>selftune — Dashboard</title>
|
|
7
|
+
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-C4EOTFZ2.js"></script>
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-react-U7zYD9Rg.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-ui-D7_zX_qy.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-table-B7VF2Ipl.js">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-bl-Webyd.css">
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="root"></div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
Binary file
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="250" height="250" viewBox="0 0 250 250" fill="none">
|
|
2
|
+
<path d="M 190.16,31.49 C 187.91,29.88 184.51,32.19 185.88,35.16 C 186.31,36.11 187.08,36.54 187.71,37.01 C 218.75,59.86 237.63,92.71 237.63,128.82 C 237.63,175.99 205.12,218.56 153.82,234.69 C 149.89,235.93 150.91,241.71 154.91,240.66 C 205.98,226.96 243.01,181.94 243,128.45 C 242.99,90.87 223.47,56.18 190.16,31.49 Z" fill="currentColor"/>
|
|
3
|
+
<path d="M 125.19,243.91 C 138.08,243.91 147.18,236.44 151.21,225.01 C 193.72,217.79 226.98,184.02 226.98,140.81 C 226.98,121.17 219.82,103.78 209.93,87.04 C 191.42,55.45 165.15,34.72 117.71,28.65 C 112.91,28.04 113.77,34.35 117.19,34.82 C 161.67,39.33 185.84,56.71 203.76,86.42 C 213.87,103.68 220.68,119.61 220.68,140.81 C 220.68,179.96 190.81,211.95 148.71,219.16 C 147.11,219.47 146.27,220.32 145.92,221.8 C 142.95,231.11 135.72,238.02 125.19,237.66 C 64.48,237.66 11.67,191.61 11.67,127.51 C 11.67,79.61 44.82,36.38 93.89,27.77 L 94.11,27.73 L 94.38,26.64 C 97.04,16.61 104.57,11.82 114.19,11.82 C 134.12,13.36 152.91,18.15 170.48,26.08 C 171.92,26.78 173.81,27.09 174.76,25.59 C 176.05,23.72 175.31,21.07 173.01,20.34 C 154.78,11.96 137.21,7.17 114.47,6 H 113.52 C 101.91,6 93.46,12.16 89.49,21.78 C 42.36,31.26 6.17,74.76 6.17,128.08 C 6.17,190.05 57.92,243.91 125.19,243.91 Z" fill="currentColor"/>
|
|
4
|
+
<path d="M 93.67,40.64 C 100.51,52.07 109.54,51.33 114.05,52.17 C 128.72,53.91 141.48,55.78 157.38,62.16 C 162.72,64.47 162.29,58.19 159.18,57.01 C 145.11,51.33 132.48,49.79 111.31,47.48 C 101.83,46.29 95.45,41.18 93.75,32.81 C 55.21,39.46 22.06,72.17 22.06,112.48 C 22.06,131.98 30.36,149.82 43.26,164.49 C 46.23,167.59 50.19,164.13 48.32,161.02 C 36.21,145.54 28.42,129.78 28.42,112.4 C 28.42,79.11 54.91,48.36 89.91,40.36 C 90.76,40.15 91.04,39.87 91.62,40.01 C 92.62,40.01 93.04,39.65 93.67,40.64 Z" fill="currentColor"/>
|
|
5
|
+
<path d="M 152.72,82.77 C 126.61,82.77 113.07,99.44 103.01,119.33 C 100.56,123.36 103.74,125.03 105.61,123.92 C 107.15,123.22 107.89,121.05 108.73,119.61 C 118.22,102.16 130.33,88.56 152.72,88.56 C 181.62,88.56 201.91,116.01 201.91,147.31 C 201.91,175.12 183.47,199.96 152.51,205.75 C 151.84,205.96 151.63,206.03 151.56,205.54 C 147.74,195.37 139.36,188.15 128.07,186.48 C 113.2,184.24 101.23,182.36 83.8,176.81 C 79.3,175.48 77.91,182.36 82.41,183.09 C 97.21,187.46 108.09,189.47 126.25,192.65 C 136.78,194.31 145.41,201.71 147.11,210.95 C 147.74,213.05 149.13,213.41 150.15,213.26 C 183.75,208.61 208.26,180.93 208.26,147.24 C 208.26,115.06 186.94,82.77 152.72,82.77 Z" fill="currentColor"/>
|
|
6
|
+
<path d="M 129.77,105.21 C 122.93,112.05 118.97,122.73 113.77,130.41 C 111.31,133.45 114.56,136.63 117.46,134.46 C 123.75,126.23 127.43,115.62 135.15,108.71 C 138.22,105.81 134.73,101.09 129.77,105.21 Z" fill="currentColor"/>
|
|
7
|
+
<path d="M 136.78,120.31 C 127.71,136.71 120.12,154.91 93.74,154.91 C 66.07,154.91 47.76,128.53 47.76,104.78 C 47.76,84.47 58.57,66.08 77.66,56.25 C 82.23,54.21 79.85,47.76 75.34,49.93 C 54.77,59.72 42.01,80.11 42.01,104.71 C 42.01,131.77 61.86,161.31 93.67,161.31 C 114.77,161.31 128.91,147.24 139.86,124.06 C 142.76,120.45 139.15,117.73 136.78,120.31 Z" fill="currentColor"/>
|
|
8
|
+
<path d="M 30.73,154.7 C 27.76,152.97 23.87,155.93 25.41,158.76 C 41.73,188.36 68.94,199.79 105.75,206.41 C 112.25,207.66 122.07,208.75 123.46,209.03 C 128.07,209.95 128.07,220.18 121.78,220.18 C 107.64,218.94 92.06,215.98 76.23,211.33 C 72.13,210.24 71.04,216.69 75.27,217.64 C 90.41,222.22 103.95,224.74 120.47,226.54 C 133.73,226.54 136.56,209.03 126.03,203.38 C 123.75,202.13 122.73,202.56 112.04,200.76 C 78.09,195.04 54.06,188.98 32.12,155.65 C 31.77,155.23 31.28,154.91 30.73,154.7 Z" fill="currentColor"/>
|
|
9
|
+
</svg>
|
|
@@ -15,7 +15,7 @@ export interface BadgeData {
|
|
|
15
15
|
label: string;
|
|
16
16
|
passRate: number | null;
|
|
17
17
|
trend: "up" | "down" | "stable" | "unknown";
|
|
18
|
-
status: "HEALTHY" | "WARNING" | "CRITICAL" | "UNKNOWN";
|
|
18
|
+
status: "HEALTHY" | "WARNING" | "CRITICAL" | "UNGRADED" | "UNKNOWN";
|
|
19
19
|
color: string;
|
|
20
20
|
message: string;
|
|
21
21
|
}
|
|
@@ -8,16 +8,12 @@
|
|
|
8
8
|
|
|
9
9
|
import { writeFileSync } from "node:fs";
|
|
10
10
|
import { parseArgs } from "node:util";
|
|
11
|
-
import { EVOLUTION_AUDIT_LOG, QUERY_LOG,
|
|
11
|
+
import { EVOLUTION_AUDIT_LOG, QUERY_LOG, TELEMETRY_LOG } from "../constants.js";
|
|
12
12
|
import { doctor } from "../observability.js";
|
|
13
13
|
import { computeStatus } from "../status.js";
|
|
14
|
-
import type {
|
|
15
|
-
EvolutionAuditEntry,
|
|
16
|
-
QueryLogRecord,
|
|
17
|
-
SessionTelemetryRecord,
|
|
18
|
-
SkillUsageRecord,
|
|
19
|
-
} from "../types.js";
|
|
14
|
+
import type { EvolutionAuditEntry, QueryLogRecord, SessionTelemetryRecord } from "../types.js";
|
|
20
15
|
import { readJsonl } from "../utils/jsonl.js";
|
|
16
|
+
import { readEffectiveSkillUsageRecords } from "../utils/skill-log.js";
|
|
21
17
|
import type { BadgeFormat } from "./badge-data.js";
|
|
22
18
|
import { findSkillBadgeData } from "./badge-data.js";
|
|
23
19
|
import { formatBadgeOutput } from "./badge-svg.js";
|
|
@@ -70,7 +66,7 @@ export function cliMain(): void {
|
|
|
70
66
|
|
|
71
67
|
// Read log files
|
|
72
68
|
const telemetry = readJsonl<SessionTelemetryRecord>(TELEMETRY_LOG);
|
|
73
|
-
const skillRecords =
|
|
69
|
+
const skillRecords = readEffectiveSkillUsageRecords();
|
|
74
70
|
const queryRecords = readJsonl<QueryLogRecord>(QUERY_LOG);
|
|
75
71
|
const auditEntries = readJsonl<EvolutionAuditEntry>(EVOLUTION_AUDIT_LOG);
|
|
76
72
|
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { parseArgs } from "node:util";
|
|
7
|
+
import { CANONICAL_LOG, CLAUDE_CODE_PROJECTS_DIR, EVOLUTION_EVIDENCE_LOG } from "./constants.js";
|
|
8
|
+
import {
|
|
9
|
+
buildCanonicalRecordsFromReplay,
|
|
10
|
+
findTranscriptFiles,
|
|
11
|
+
parseSession,
|
|
12
|
+
} from "./ingestors/claude-replay.js";
|
|
13
|
+
import {
|
|
14
|
+
CANONICAL_PLATFORMS,
|
|
15
|
+
CANONICAL_RECORD_KINDS,
|
|
16
|
+
type CanonicalPlatform,
|
|
17
|
+
type CanonicalRecord,
|
|
18
|
+
type CanonicalRecordKind,
|
|
19
|
+
type EvolutionEvidenceEntry,
|
|
20
|
+
} from "./types.js";
|
|
21
|
+
import {
|
|
22
|
+
filterCanonicalRecords,
|
|
23
|
+
readCanonicalRecords,
|
|
24
|
+
serializeCanonicalRecords,
|
|
25
|
+
} from "./utils/canonical-log.js";
|
|
26
|
+
import { readJsonl } from "./utils/jsonl.js";
|
|
27
|
+
|
|
28
|
+
function exitWithUsage(message?: string): never {
|
|
29
|
+
if (message) console.error(`[ERROR] ${message}`);
|
|
30
|
+
console.error(
|
|
31
|
+
`Usage: selftune export-canonical [--out FILE] [--platform NAME] [--record-kind KIND] [--pretty] [--log FILE] [--projects-dir PATH] [--push-payload]`,
|
|
32
|
+
);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function validatePlatform(value: string | undefined): CanonicalPlatform | undefined {
|
|
37
|
+
if (!value) return undefined;
|
|
38
|
+
if (!CANONICAL_PLATFORMS.includes(value as CanonicalPlatform)) {
|
|
39
|
+
exitWithUsage(`Unknown platform: ${value}`);
|
|
40
|
+
}
|
|
41
|
+
return value as CanonicalPlatform;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function validateRecordKind(value: string | undefined): CanonicalRecordKind | undefined {
|
|
45
|
+
if (!value) return undefined;
|
|
46
|
+
if (!CANONICAL_RECORD_KINDS.includes(value as CanonicalRecordKind)) {
|
|
47
|
+
exitWithUsage(`Unknown record kind: ${value}`);
|
|
48
|
+
}
|
|
49
|
+
return value as CanonicalRecordKind;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getClientVersion(): string {
|
|
53
|
+
try {
|
|
54
|
+
const pkg = JSON.parse(readFileSync(join(import.meta.dir, "../../package.json"), "utf-8"));
|
|
55
|
+
return pkg.version ?? "unknown";
|
|
56
|
+
} catch {
|
|
57
|
+
return "unknown";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function loadCanonicalRecordsForExport(
|
|
62
|
+
logPath: string = CANONICAL_LOG,
|
|
63
|
+
projectsDir: string = CLAUDE_CODE_PROJECTS_DIR,
|
|
64
|
+
platform?: CanonicalPlatform,
|
|
65
|
+
): CanonicalRecord[] {
|
|
66
|
+
const canonical = readCanonicalRecords(logPath);
|
|
67
|
+
if (canonical.length > 0) return canonical;
|
|
68
|
+
|
|
69
|
+
// Existing installs may have rich Claude Code transcripts but no canonical log yet.
|
|
70
|
+
// Fall back to synthesizing exportable records directly from transcripts.
|
|
71
|
+
if (platform && platform !== "claude_code") return [];
|
|
72
|
+
|
|
73
|
+
const records: CanonicalRecord[] = [];
|
|
74
|
+
for (const transcriptPath of findTranscriptFiles(projectsDir)) {
|
|
75
|
+
const session = parseSession(transcriptPath);
|
|
76
|
+
if (!session) continue;
|
|
77
|
+
records.push(...buildCanonicalRecordsFromReplay(session));
|
|
78
|
+
}
|
|
79
|
+
return records;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function buildPushPayloadV2(
|
|
83
|
+
records: CanonicalRecord[],
|
|
84
|
+
evidenceEntries: EvolutionEvidenceEntry[] = [],
|
|
85
|
+
): Record<string, unknown> {
|
|
86
|
+
const sessions = records.filter((record) => record.record_kind === "session");
|
|
87
|
+
const prompts = records.filter((record) => record.record_kind === "prompt");
|
|
88
|
+
const skillInvocations = records.filter((record) => record.record_kind === "skill_invocation");
|
|
89
|
+
const executionFacts = records.filter((record) => record.record_kind === "execution_fact");
|
|
90
|
+
const normalizationRuns = records.filter((record) => record.record_kind === "normalization_run");
|
|
91
|
+
const normalizerVersion = records[0]?.normalizer_version ?? "1.0.0";
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
schema_version: "2.0",
|
|
95
|
+
client_version: getClientVersion(),
|
|
96
|
+
push_id: randomUUID(),
|
|
97
|
+
normalizer_version: normalizerVersion,
|
|
98
|
+
canonical: {
|
|
99
|
+
sessions,
|
|
100
|
+
prompts,
|
|
101
|
+
skill_invocations: skillInvocations,
|
|
102
|
+
execution_facts: executionFacts,
|
|
103
|
+
normalization_runs: normalizationRuns,
|
|
104
|
+
evolution_evidence: evidenceEntries.map((entry) => ({
|
|
105
|
+
skill_name: entry.skill_name,
|
|
106
|
+
proposal_id: entry.proposal_id,
|
|
107
|
+
target: entry.target,
|
|
108
|
+
stage: entry.stage,
|
|
109
|
+
rationale: entry.rationale,
|
|
110
|
+
confidence: entry.confidence,
|
|
111
|
+
original_text: entry.original_text,
|
|
112
|
+
proposed_text: entry.proposed_text,
|
|
113
|
+
eval_set_json: entry.eval_set,
|
|
114
|
+
validation_json: entry.validation,
|
|
115
|
+
})),
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function cliMain(): void {
|
|
121
|
+
const { values } = parseArgs({
|
|
122
|
+
args: process.argv.slice(2),
|
|
123
|
+
options: {
|
|
124
|
+
out: { type: "string" },
|
|
125
|
+
platform: { type: "string" },
|
|
126
|
+
"record-kind": { type: "string" },
|
|
127
|
+
pretty: { type: "boolean", default: false },
|
|
128
|
+
log: { type: "string", default: CANONICAL_LOG },
|
|
129
|
+
"projects-dir": { type: "string", default: CLAUDE_CODE_PROJECTS_DIR },
|
|
130
|
+
"push-payload": { type: "boolean", default: false },
|
|
131
|
+
},
|
|
132
|
+
strict: true,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const platform = validatePlatform(values.platform);
|
|
136
|
+
const recordKind = validateRecordKind(values["record-kind"]);
|
|
137
|
+
const records = filterCanonicalRecords(
|
|
138
|
+
loadCanonicalRecordsForExport(values.log, values["projects-dir"], platform),
|
|
139
|
+
{
|
|
140
|
+
platform,
|
|
141
|
+
record_kind: recordKind,
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const output = values["push-payload"]
|
|
146
|
+
? `${JSON.stringify(
|
|
147
|
+
buildPushPayloadV2(records, readJsonl<EvolutionEvidenceEntry>(EVOLUTION_EVIDENCE_LOG)),
|
|
148
|
+
null,
|
|
149
|
+
values.pretty ? 2 : undefined,
|
|
150
|
+
)}\n`
|
|
151
|
+
: serializeCanonicalRecords(records, values.pretty);
|
|
152
|
+
|
|
153
|
+
if (values.out) {
|
|
154
|
+
writeFileSync(values.out, output, "utf-8");
|
|
155
|
+
console.log(
|
|
156
|
+
JSON.stringify(
|
|
157
|
+
{
|
|
158
|
+
ok: true,
|
|
159
|
+
out: values.out,
|
|
160
|
+
count: records.length,
|
|
161
|
+
format: values["push-payload"] ? "push-payload-v2" : "jsonl",
|
|
162
|
+
pretty: values.pretty,
|
|
163
|
+
platform: platform ?? null,
|
|
164
|
+
record_kind: recordKind ?? null,
|
|
165
|
+
},
|
|
166
|
+
null,
|
|
167
|
+
values.pretty ? 2 : undefined,
|
|
168
|
+
),
|
|
169
|
+
);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
process.stdout.write(output);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (import.meta.main) {
|
|
177
|
+
try {
|
|
178
|
+
cliMain();
|
|
179
|
+
} catch (error) {
|
|
180
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
181
|
+
exitWithUsage(message);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -12,8 +12,15 @@ export const LOG_DIR = join(homedir(), ".claude");
|
|
|
12
12
|
|
|
13
13
|
export const TELEMETRY_LOG = join(LOG_DIR, "session_telemetry_log.jsonl");
|
|
14
14
|
export const SKILL_LOG = join(LOG_DIR, "skill_usage_log.jsonl");
|
|
15
|
+
export const REPAIRED_SKILL_LOG = join(LOG_DIR, "skill_usage_repaired.jsonl");
|
|
16
|
+
export const CANONICAL_LOG = join(LOG_DIR, "canonical_telemetry_log.jsonl");
|
|
17
|
+
export const REPAIRED_SKILL_SESSIONS_MARKER = join(LOG_DIR, "skill_usage_repaired_sessions.json");
|
|
15
18
|
export const QUERY_LOG = join(LOG_DIR, "all_queries_log.jsonl");
|
|
16
19
|
export const EVOLUTION_AUDIT_LOG = join(LOG_DIR, "evolution_audit_log.jsonl");
|
|
20
|
+
export const EVOLUTION_EVIDENCE_LOG = join(LOG_DIR, "evolution_evidence_log.jsonl");
|
|
21
|
+
export const ORCHESTRATE_RUN_LOG = join(LOG_DIR, "orchestrate_runs.jsonl");
|
|
22
|
+
export const SIGNAL_LOG = join(LOG_DIR, "improvement_signals.jsonl");
|
|
23
|
+
export const ORCHESTRATE_LOCK = join(LOG_DIR, ".orchestrate.lock");
|
|
17
24
|
|
|
18
25
|
/** Evolution memory directory — human-readable session context that survives resets. */
|
|
19
26
|
export const MEMORY_DIR = join(SELFTUNE_CONFIG_DIR, "memory");
|
|
@@ -65,11 +72,20 @@ export const REQUIRED_FIELDS: Record<string, Set<string>> = {
|
|
|
65
72
|
skill_usage: new Set(["timestamp", "session_id", "skill_name"]),
|
|
66
73
|
all_queries: new Set(["timestamp", "session_id", "query"]),
|
|
67
74
|
evolution_audit: new Set(["timestamp", "proposal_id", "action"]),
|
|
75
|
+
evolution_evidence: new Set(["timestamp", "proposal_id", "skill_name", "stage"]),
|
|
68
76
|
};
|
|
69
77
|
|
|
70
78
|
/** Agent CLI candidates in detection order. */
|
|
71
79
|
export const AGENT_CANDIDATES = ["claude", "codex", "opencode", "openclaw"] as const;
|
|
72
80
|
|
|
81
|
+
/** Required Claude Code hook keys in settings.json. */
|
|
82
|
+
export const CLAUDE_CODE_HOOK_KEYS = [
|
|
83
|
+
"UserPromptSubmit",
|
|
84
|
+
"PreToolUse",
|
|
85
|
+
"PostToolUse",
|
|
86
|
+
"Stop",
|
|
87
|
+
] as const;
|
|
88
|
+
|
|
73
89
|
/** Path for user-defined activation rule overrides. */
|
|
74
90
|
export const ACTIVATION_RULES_PATH = join(SELFTUNE_CONFIG_DIR, "activation-rules.json");
|
|
75
91
|
|
|
@@ -83,6 +99,12 @@ export function sessionStatePath(sessionId: string): string {
|
|
|
83
99
|
return join(SESSION_STATE_DIR, `session-state-${safe}.json`);
|
|
84
100
|
}
|
|
85
101
|
|
|
102
|
+
/** Build a canonical prompt state file path from a session ID. */
|
|
103
|
+
export function canonicalSessionStatePath(sessionId: string): string {
|
|
104
|
+
const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
105
|
+
return join(SESSION_STATE_DIR, `canonical-session-state-${safe}.json`);
|
|
106
|
+
}
|
|
107
|
+
|
|
86
108
|
/** Claude Code settings file path. */
|
|
87
109
|
export const CLAUDE_SETTINGS_PATH = join(homedir(), ".claude", "settings.json");
|
|
88
110
|
|
|
@@ -92,6 +114,12 @@ export const CLAUDE_CODE_PROJECTS_DIR = join(homedir(), ".claude", "projects");
|
|
|
92
114
|
/** Marker file tracking which Claude Code sessions have been ingested. */
|
|
93
115
|
export const CLAUDE_CODE_MARKER = join(homedir(), ".claude", "claude_code_ingested_sessions.json");
|
|
94
116
|
|
|
117
|
+
/** Marker file tracking which Codex rollout files have been ingested. */
|
|
118
|
+
export const CODEX_INGEST_MARKER = join(homedir(), ".claude", "codex_ingested_rollouts.json");
|
|
119
|
+
|
|
120
|
+
/** Marker file tracking which OpenCode sessions have been ingested. */
|
|
121
|
+
export const OPENCODE_INGEST_MARKER = join(homedir(), ".claude", "opencode_ingested_sessions.json");
|
|
122
|
+
|
|
95
123
|
/** OpenClaw agents directory containing session data. */
|
|
96
124
|
export const OPENCLAW_AGENTS_DIR = join(homedir(), ".openclaw", "agents");
|
|
97
125
|
|
|
@@ -152,7 +152,7 @@ async function submitToService(
|
|
|
152
152
|
// ---------------------------------------------------------------------------
|
|
153
153
|
|
|
154
154
|
function submitToGitHub(json: string, outputPath: string): boolean {
|
|
155
|
-
const repo = "
|
|
155
|
+
const repo = "selftune-dev/selftune";
|
|
156
156
|
const sizeKB = Buffer.byteLength(json, "utf-8") / 1024;
|
|
157
157
|
|
|
158
158
|
let body: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
* selftune cron —
|
|
3
|
+
* selftune cron — OpenClaw cron integration for selftune automation.
|
|
4
4
|
*
|
|
5
5
|
* Subcommands:
|
|
6
6
|
* setup Register default selftune cron jobs with OpenClaw
|
|
@@ -31,29 +31,25 @@ export interface CronJobConfig {
|
|
|
31
31
|
|
|
32
32
|
export const DEFAULT_CRON_JOBS: CronJobConfig[] = [
|
|
33
33
|
{
|
|
34
|
-
name: "selftune-
|
|
34
|
+
name: "selftune-sync",
|
|
35
35
|
cron: "*/30 * * * *",
|
|
36
|
-
message:
|
|
37
|
-
|
|
36
|
+
message:
|
|
37
|
+
"Run selftune sync to replay and ingest new Claude Code, Codex, OpenCode, and OpenClaw source data, then rebuild the repaired skill-usage overlay.",
|
|
38
|
+
description: "Sync source-truth telemetry every 30 minutes",
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
41
|
name: "selftune-status",
|
|
41
42
|
cron: "0 8 * * *",
|
|
42
|
-
message: "Run selftune status --json and report any skills with pass rate below 80%.",
|
|
43
|
-
description: "Daily health check at 8am",
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: "selftune-evolve",
|
|
47
|
-
cron: "0 3 * * 0",
|
|
48
43
|
message:
|
|
49
|
-
"Run
|
|
50
|
-
description: "
|
|
44
|
+
"Run selftune sync first, then run selftune status --json and report any skills with pass rate below 80% or still ungraded due to sparse recent checks.",
|
|
45
|
+
description: "Daily health check after source sync",
|
|
51
46
|
},
|
|
52
47
|
{
|
|
53
|
-
name: "selftune-
|
|
48
|
+
name: "selftune-orchestrate",
|
|
54
49
|
cron: "0 */6 * * *",
|
|
55
|
-
message:
|
|
56
|
-
|
|
50
|
+
message:
|
|
51
|
+
"Run selftune orchestrate --max-skills 3. This performs source-truth sync, selects candidate skills, evolves validated low-risk descriptions autonomously, and watches recent deployments for regressions.",
|
|
52
|
+
description: "Autonomous improvement loop every 6 hours",
|
|
57
53
|
},
|
|
58
54
|
];
|
|
59
55
|
|
|
@@ -120,7 +116,7 @@ export function loadCronJobs(jobsPath: string): CronJobConfig[] {
|
|
|
120
116
|
/** Register default cron jobs with OpenClaw. */
|
|
121
117
|
export async function setupCronJobs(tz: string, dryRun: boolean): Promise<void> {
|
|
122
118
|
const openclawPath = Bun.which("openclaw");
|
|
123
|
-
if (!openclawPath) {
|
|
119
|
+
if (!dryRun && !openclawPath) {
|
|
124
120
|
console.error("Error: openclaw is not installed or not in PATH.");
|
|
125
121
|
console.error("");
|
|
126
122
|
console.error("Install OpenClaw:");
|
|
@@ -246,7 +242,11 @@ export async function cliMain(): Promise<void> {
|
|
|
246
242
|
await removeCronJobs(values["dry-run"] ?? false);
|
|
247
243
|
break;
|
|
248
244
|
default:
|
|
249
|
-
console.log(`selftune cron —
|
|
245
|
+
console.log(`selftune cron — OpenClaw cron integration
|
|
246
|
+
|
|
247
|
+
Registers selftune automation jobs with OpenClaw's Gateway Scheduler.
|
|
248
|
+
This is an optional convenience for OpenClaw users. For generic scheduling
|
|
249
|
+
with system cron, launchd, or systemd, see: selftune schedule
|
|
250
250
|
|
|
251
251
|
Usage:
|
|
252
252
|
selftune cron setup [--dry-run] [--tz <timezone>]
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
export interface TelemetryRecord {
|
|
2
|
+
timestamp: string;
|
|
3
|
+
session_id: string;
|
|
4
|
+
skills_triggered: string[];
|
|
5
|
+
errors_encountered: number;
|
|
6
|
+
total_tool_calls: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface SkillUsageRecord {
|
|
10
|
+
timestamp: string;
|
|
11
|
+
session_id: string;
|
|
12
|
+
skill_name: string;
|
|
13
|
+
skill_path: string;
|
|
14
|
+
query: string;
|
|
15
|
+
triggered: boolean;
|
|
16
|
+
source: string | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface EvalSnapshot {
|
|
20
|
+
before_pass_rate?: number;
|
|
21
|
+
after_pass_rate?: number;
|
|
22
|
+
net_change?: number;
|
|
23
|
+
improved?: boolean;
|
|
24
|
+
regressions?: Array<Record<string, unknown>>;
|
|
25
|
+
new_passes?: Array<Record<string, unknown>>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface EvolutionEntry {
|
|
29
|
+
timestamp: string;
|
|
30
|
+
proposal_id: string;
|
|
31
|
+
action: string;
|
|
32
|
+
details: string;
|
|
33
|
+
eval_snapshot?: EvalSnapshot | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface UnmatchedQuery {
|
|
37
|
+
timestamp: string;
|
|
38
|
+
session_id: string;
|
|
39
|
+
query: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface PendingProposal {
|
|
43
|
+
proposal_id: string;
|
|
44
|
+
action: string;
|
|
45
|
+
timestamp: string;
|
|
46
|
+
details: string;
|
|
47
|
+
skill_name?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SkillSummary {
|
|
51
|
+
skill_name: string;
|
|
52
|
+
skill_scope: string | null;
|
|
53
|
+
total_checks: number;
|
|
54
|
+
triggered_count: number;
|
|
55
|
+
pass_rate: number;
|
|
56
|
+
unique_sessions: number;
|
|
57
|
+
last_seen: string | null;
|
|
58
|
+
has_evidence: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface OverviewPayload {
|
|
62
|
+
telemetry: TelemetryRecord[];
|
|
63
|
+
skills: SkillUsageRecord[];
|
|
64
|
+
evolution: EvolutionEntry[];
|
|
65
|
+
counts: {
|
|
66
|
+
telemetry: number;
|
|
67
|
+
skills: number;
|
|
68
|
+
evolution: number;
|
|
69
|
+
evidence: number;
|
|
70
|
+
sessions: number;
|
|
71
|
+
prompts: number;
|
|
72
|
+
};
|
|
73
|
+
unmatched_queries: UnmatchedQuery[];
|
|
74
|
+
pending_proposals: PendingProposal[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface OverviewResponse {
|
|
78
|
+
overview: OverviewPayload;
|
|
79
|
+
skills: SkillSummary[];
|
|
80
|
+
version?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface EvidenceEntry {
|
|
84
|
+
proposal_id: string;
|
|
85
|
+
target: string;
|
|
86
|
+
stage: string;
|
|
87
|
+
timestamp: string;
|
|
88
|
+
rationale: string | null;
|
|
89
|
+
confidence: number | null;
|
|
90
|
+
original_text: string | null;
|
|
91
|
+
proposed_text: string | null;
|
|
92
|
+
validation: Record<string, unknown> | null;
|
|
93
|
+
details: string | null;
|
|
94
|
+
eval_set: Array<Record<string, unknown>>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface CanonicalInvocation {
|
|
98
|
+
timestamp: string;
|
|
99
|
+
session_id: string;
|
|
100
|
+
skill_name: string;
|
|
101
|
+
invocation_mode: string | null;
|
|
102
|
+
triggered: boolean;
|
|
103
|
+
confidence: number | null;
|
|
104
|
+
tool_name: string | null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface PromptSample {
|
|
108
|
+
prompt_text: string;
|
|
109
|
+
prompt_kind: string | null;
|
|
110
|
+
is_actionable: boolean;
|
|
111
|
+
occurred_at: string;
|
|
112
|
+
session_id: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface SessionMeta {
|
|
116
|
+
session_id: string;
|
|
117
|
+
platform: string | null;
|
|
118
|
+
model: string | null;
|
|
119
|
+
agent_cli: string | null;
|
|
120
|
+
branch: string | null;
|
|
121
|
+
workspace_path: string | null;
|
|
122
|
+
started_at: string | null;
|
|
123
|
+
ended_at: string | null;
|
|
124
|
+
completion_status: string | null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface SkillReportPayload {
|
|
128
|
+
skill_name: string;
|
|
129
|
+
usage: {
|
|
130
|
+
total_checks: number;
|
|
131
|
+
triggered_count: number;
|
|
132
|
+
pass_rate: number;
|
|
133
|
+
};
|
|
134
|
+
recent_invocations: Array<{
|
|
135
|
+
timestamp: string;
|
|
136
|
+
session_id: string;
|
|
137
|
+
query: string;
|
|
138
|
+
triggered: boolean;
|
|
139
|
+
source: string | null;
|
|
140
|
+
}>;
|
|
141
|
+
evidence: EvidenceEntry[];
|
|
142
|
+
sessions_with_skill: number;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// -- Orchestrate run report types --------------------------------------------
|
|
146
|
+
|
|
147
|
+
export interface OrchestrateRunSkillAction {
|
|
148
|
+
skill: string;
|
|
149
|
+
action: "evolve" | "watch" | "skip";
|
|
150
|
+
reason: string;
|
|
151
|
+
deployed?: boolean;
|
|
152
|
+
rolledBack?: boolean;
|
|
153
|
+
alert?: string | null;
|
|
154
|
+
elapsed_ms?: number;
|
|
155
|
+
llm_calls?: number;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface OrchestrateRunReport {
|
|
159
|
+
run_id: string;
|
|
160
|
+
timestamp: string;
|
|
161
|
+
elapsed_ms: number;
|
|
162
|
+
dry_run: boolean;
|
|
163
|
+
approval_mode: "auto" | "review";
|
|
164
|
+
total_skills: number;
|
|
165
|
+
evaluated: number;
|
|
166
|
+
evolved: number;
|
|
167
|
+
deployed: number;
|
|
168
|
+
watched: number;
|
|
169
|
+
skipped: number;
|
|
170
|
+
skill_actions: OrchestrateRunSkillAction[];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface OrchestrateRunsResponse {
|
|
174
|
+
runs: OrchestrateRunReport[];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// -- Doctor / health check types ----------------------------------------------
|
|
178
|
+
export type { DoctorResult, HealthCheck, HealthStatus } from "./types.js";
|
|
179
|
+
|
|
180
|
+
export interface SkillReportResponse extends SkillReportPayload {
|
|
181
|
+
evolution: EvolutionEntry[];
|
|
182
|
+
pending_proposals: PendingProposal[];
|
|
183
|
+
token_usage: {
|
|
184
|
+
total_input_tokens: number;
|
|
185
|
+
total_output_tokens: number;
|
|
186
|
+
};
|
|
187
|
+
canonical_invocations: CanonicalInvocation[];
|
|
188
|
+
duration_stats: {
|
|
189
|
+
avg_duration_ms: number;
|
|
190
|
+
total_duration_ms: number;
|
|
191
|
+
execution_count: number;
|
|
192
|
+
total_errors: number;
|
|
193
|
+
};
|
|
194
|
+
selftune_stats: {
|
|
195
|
+
total_llm_calls: number;
|
|
196
|
+
total_elapsed_ms: number;
|
|
197
|
+
avg_elapsed_ms: number;
|
|
198
|
+
run_count: number;
|
|
199
|
+
};
|
|
200
|
+
prompt_samples: PromptSample[];
|
|
201
|
+
session_metadata: SessionMeta[];
|
|
202
|
+
}
|