selftune 0.2.9 → 0.2.12
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 +35 -35
- package/apps/local-dashboard/dist/assets/index-4_dAY17K.js +16 -0
- package/apps/local-dashboard/dist/assets/index-BxV5WZHc.css +2 -0
- package/apps/local-dashboard/dist/assets/rolldown-runtime-Dw2cE7zH.js +1 -0
- package/apps/local-dashboard/dist/assets/vendor-react-CKkiCskZ.js +11 -0
- package/apps/local-dashboard/dist/assets/vendor-table-pHbDxq36.js +8 -0
- package/apps/local-dashboard/dist/assets/vendor-ui-7xD7fNEU.js +12 -0
- package/apps/local-dashboard/dist/index.html +16 -15
- package/bin/selftune.cjs +1 -1
- package/cli/selftune/activation-rules.ts +1 -0
- package/cli/selftune/alpha-upload/build-payloads.ts +18 -2
- package/cli/selftune/alpha-upload/stage-canonical.ts +94 -0
- package/cli/selftune/auth/device-code.ts +32 -0
- package/cli/selftune/auto-update.ts +12 -0
- package/cli/selftune/badge/badge.ts +1 -0
- package/cli/selftune/canonical-export.ts +5 -0
- package/cli/selftune/claude-agents.ts +154 -0
- package/cli/selftune/contribute/bundle.ts +1 -0
- package/cli/selftune/contribute/contribute.ts +1 -0
- package/cli/selftune/cron/setup.ts +2 -2
- package/cli/selftune/dashboard-server.ts +1 -0
- package/cli/selftune/eval/hooks-to-evals.ts +1 -0
- package/cli/selftune/eval/import-skillsbench.ts +1 -0
- package/cli/selftune/eval/synthetic-evals.ts +2 -3
- package/cli/selftune/eval/unit-test.ts +1 -0
- package/cli/selftune/evolution/deploy-proposal.ts +9 -238
- package/cli/selftune/evolution/evolve-body.ts +93 -6
- package/cli/selftune/evolution/evolve.ts +3 -7
- package/cli/selftune/evolution/propose-body.ts +3 -2
- package/cli/selftune/evolution/propose-routing.ts +3 -2
- package/cli/selftune/evolution/refine-body.ts +3 -2
- package/cli/selftune/evolution/rollback.ts +1 -1
- package/cli/selftune/export.ts +1 -0
- package/cli/selftune/grading/grade-session.ts +8 -0
- package/cli/selftune/hooks/auto-activate.ts +1 -0
- package/cli/selftune/hooks/evolution-guard.ts +1 -1
- package/cli/selftune/hooks/prompt-log.ts +1 -0
- package/cli/selftune/hooks/session-stop.ts +34 -40
- package/cli/selftune/hooks/skill-change-guard.ts +1 -0
- package/cli/selftune/hooks/skill-eval.ts +1 -1
- package/cli/selftune/index.ts +23 -14
- package/cli/selftune/ingestors/claude-replay.ts +1 -0
- package/cli/selftune/ingestors/codex-rollout.ts +1 -0
- package/cli/selftune/ingestors/codex-wrapper.ts +1 -0
- package/cli/selftune/ingestors/openclaw-ingest.ts +1 -0
- package/cli/selftune/ingestors/opencode-ingest.ts +1 -0
- package/cli/selftune/init.ts +121 -29
- package/cli/selftune/localdb/db.ts +1 -0
- package/cli/selftune/localdb/direct-write.ts +39 -0
- package/cli/selftune/localdb/materialize.ts +2 -0
- package/cli/selftune/localdb/queries.ts +53 -0
- package/cli/selftune/localdb/schema.ts +28 -0
- package/cli/selftune/normalization.ts +1 -0
- package/cli/selftune/observability.ts +1 -0
- package/cli/selftune/repair/skill-usage.ts +1 -0
- package/cli/selftune/routes/orchestrate-runs.ts +1 -0
- package/cli/selftune/routes/overview.ts +1 -0
- package/cli/selftune/routes/report.ts +1 -1
- package/cli/selftune/routes/skill-report.ts +2 -1
- package/cli/selftune/status.ts +1 -1
- package/cli/selftune/sync.ts +30 -1
- package/cli/selftune/uninstall.ts +412 -0
- package/cli/selftune/utils/canonical-log.ts +2 -0
- package/cli/selftune/utils/frontmatter.ts +50 -7
- package/cli/selftune/utils/jsonl.ts +1 -0
- package/cli/selftune/utils/llm-call.ts +131 -3
- package/cli/selftune/utils/skill-log.ts +1 -0
- package/cli/selftune/utils/transcript.ts +1 -0
- package/cli/selftune/utils/trigger-check.ts +1 -1
- package/cli/selftune/workflows/skill-md-writer.ts +5 -5
- package/cli/selftune/workflows/workflows.ts +1 -0
- package/package.json +37 -33
- package/packages/telemetry-contract/fixtures/golden.test.ts +1 -0
- package/packages/telemetry-contract/package.json +1 -1
- package/packages/telemetry-contract/src/schemas.ts +1 -0
- package/packages/telemetry-contract/tests/compatibility.test.ts +1 -0
- package/packages/ui/README.md +35 -34
- package/packages/ui/package.json +3 -3
- package/packages/ui/src/components/ActivityTimeline.tsx +50 -43
- package/packages/ui/src/components/EvidenceViewer.tsx +306 -182
- package/packages/ui/src/components/EvolutionTimeline.tsx +83 -72
- package/packages/ui/src/components/InfoTip.tsx +4 -3
- package/packages/ui/src/components/OrchestrateRunsPanel.tsx +60 -53
- package/packages/ui/src/components/section-cards.tsx +20 -25
- package/packages/ui/src/components/skill-health-grid.tsx +213 -193
- package/packages/ui/src/lib/constants.tsx +1 -0
- package/packages/ui/src/primitives/badge.tsx +12 -15
- package/packages/ui/src/primitives/button.tsx +7 -7
- package/packages/ui/src/primitives/card.tsx +15 -26
- package/packages/ui/src/primitives/checkbox.tsx +7 -8
- package/packages/ui/src/primitives/collapsible.tsx +5 -5
- package/packages/ui/src/primitives/dropdown-menu.tsx +45 -55
- package/packages/ui/src/primitives/label.tsx +6 -6
- package/packages/ui/src/primitives/select.tsx +28 -37
- package/packages/ui/src/primitives/table.tsx +17 -44
- package/packages/ui/src/primitives/tabs.tsx +14 -21
- package/packages/ui/src/primitives/tooltip.tsx +10 -22
- package/skill/SKILL.md +70 -57
- package/skill/Workflows/AlphaUpload.md +4 -4
- package/skill/Workflows/AutoActivation.md +11 -6
- package/skill/Workflows/Badge.md +22 -16
- package/skill/Workflows/Baseline.md +34 -36
- package/skill/Workflows/Composability.md +16 -11
- package/skill/Workflows/Contribute.md +26 -21
- package/skill/Workflows/Cron.md +23 -22
- package/skill/Workflows/Dashboard.md +32 -27
- package/skill/Workflows/Doctor.md +33 -27
- package/skill/Workflows/Evals.md +48 -47
- package/skill/Workflows/EvolutionMemory.md +31 -21
- package/skill/Workflows/Evolve.md +84 -82
- package/skill/Workflows/EvolveBody.md +58 -47
- package/skill/Workflows/Grade.md +16 -13
- package/skill/Workflows/ImportSkillsBench.md +9 -6
- package/skill/Workflows/Ingest.md +36 -21
- package/skill/Workflows/Initialize.md +108 -40
- package/skill/Workflows/Orchestrate.md +22 -16
- package/skill/Workflows/Replay.md +12 -7
- package/skill/Workflows/Rollback.md +13 -6
- package/skill/Workflows/Schedule.md +6 -6
- package/skill/Workflows/Sync.md +18 -11
- package/skill/Workflows/UnitTest.md +28 -17
- package/skill/Workflows/Watch.md +28 -21
- package/skill/agents/diagnosis-analyst.md +11 -0
- package/skill/agents/evolution-reviewer.md +15 -1
- package/skill/agents/integration-guide.md +10 -0
- package/skill/agents/pattern-analyst.md +12 -1
- package/skill/references/grading-methodology.md +23 -24
- package/skill/references/interactive-config.md +7 -7
- package/skill/references/invocation-taxonomy.md +22 -20
- package/skill/references/logs.md +14 -6
- package/skill/references/setup-patterns.md +4 -2
- package/.claude/agents/diagnosis-analyst.md +0 -156
- package/.claude/agents/evolution-reviewer.md +0 -180
- package/.claude/agents/integration-guide.md +0 -212
- package/.claude/agents/pattern-analyst.md +0 -160
- package/apps/local-dashboard/dist/assets/index-Bs3Y4ixf.css +0 -1
- package/apps/local-dashboard/dist/assets/index-C4UYGWKr.js +0 -15
- package/apps/local-dashboard/dist/assets/vendor-react-BQH_6WrG.js +0 -60
- package/apps/local-dashboard/dist/assets/vendor-table-dK1QMLq9.js +0 -26
- package/apps/local-dashboard/dist/assets/vendor-ui-CO2mrx6e.js +0 -341
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
<!
|
|
1
|
+
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<
|
|
16
|
-
|
|
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-4_dAY17K.js"></script>
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Dw2cE7zH.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-react-CKkiCskZ.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-ui-7xD7fNEU.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-table-pHbDxq36.js">
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BxV5WZHc.css">
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<div id="root"></div>
|
|
17
|
+
</body>
|
|
17
18
|
</html>
|
package/bin/selftune.cjs
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
14
14
|
import { dirname, join } from "node:path";
|
|
15
|
+
|
|
15
16
|
import { EVOLUTION_AUDIT_LOG, QUERY_LOG } from "./constants.js";
|
|
16
17
|
import { getDb } from "./localdb/db.js";
|
|
17
18
|
import { queryEvolutionAudit, queryQueryLog, querySkillUsageRecords } from "./localdb/queries.js";
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { Database } from "bun:sqlite";
|
|
13
|
+
|
|
13
14
|
import type { CanonicalRecord } from "@selftune/telemetry-contract";
|
|
15
|
+
|
|
14
16
|
import { buildPushPayloadV2 } from "../canonical-export.js";
|
|
15
17
|
import type { EvolutionEvidenceEntry } from "../types.js";
|
|
16
18
|
|
|
@@ -74,6 +76,8 @@ export function buildV2PushPayload(
|
|
|
74
76
|
const canonicalRecords: CanonicalRecord[] = [];
|
|
75
77
|
const evidenceEntries: EvolutionEvidenceEntry[] = [];
|
|
76
78
|
const orchestrateRuns: Record<string, unknown>[] = [];
|
|
79
|
+
const gradingResults: Record<string, unknown>[] = [];
|
|
80
|
+
const improvementSignals: Record<string, unknown>[] = [];
|
|
77
81
|
let lastParsedSeq: number | null = null;
|
|
78
82
|
let hitMalformedRow = false;
|
|
79
83
|
|
|
@@ -118,6 +122,10 @@ export function buildV2PushPayload(
|
|
|
118
122
|
} else if (row.record_kind === "orchestrate_run") {
|
|
119
123
|
// Orchestrate run records -- pass through as-is
|
|
120
124
|
orchestrateRuns.push(parsed);
|
|
125
|
+
} else if (row.record_kind === "grading_result") {
|
|
126
|
+
gradingResults.push(parsed);
|
|
127
|
+
} else if (row.record_kind === "improvement_signal") {
|
|
128
|
+
improvementSignals.push(parsed);
|
|
121
129
|
} else {
|
|
122
130
|
// Canonical telemetry records -- pass through as-is
|
|
123
131
|
canonicalRecords.push(parsed as unknown as CanonicalRecord);
|
|
@@ -130,12 +138,20 @@ export function buildV2PushPayload(
|
|
|
130
138
|
if (
|
|
131
139
|
canonicalRecords.length === 0 &&
|
|
132
140
|
evidenceEntries.length === 0 &&
|
|
133
|
-
orchestrateRuns.length === 0
|
|
141
|
+
orchestrateRuns.length === 0 &&
|
|
142
|
+
gradingResults.length === 0 &&
|
|
143
|
+
improvementSignals.length === 0
|
|
134
144
|
) {
|
|
135
145
|
return null;
|
|
136
146
|
}
|
|
137
147
|
|
|
138
|
-
const payload = buildPushPayloadV2(
|
|
148
|
+
const payload = buildPushPayloadV2(
|
|
149
|
+
canonicalRecords,
|
|
150
|
+
evidenceEntries,
|
|
151
|
+
orchestrateRuns,
|
|
152
|
+
gradingResults,
|
|
153
|
+
improvementSignals,
|
|
154
|
+
);
|
|
139
155
|
if (lastParsedSeq === null) {
|
|
140
156
|
return null;
|
|
141
157
|
}
|
|
@@ -11,13 +11,17 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Database } from "bun:sqlite";
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
|
+
|
|
14
15
|
import type { CanonicalRecord } from "@selftune/telemetry-contract";
|
|
15
16
|
import { isCanonicalRecord } from "@selftune/telemetry-contract";
|
|
17
|
+
|
|
16
18
|
import { CANONICAL_LOG } from "../constants.js";
|
|
17
19
|
import {
|
|
18
20
|
getOrchestrateRuns,
|
|
19
21
|
queryCanonicalRecordsForStaging,
|
|
20
22
|
queryEvolutionEvidence,
|
|
23
|
+
queryGradingResults,
|
|
24
|
+
queryImprovementSignals,
|
|
21
25
|
} from "../localdb/queries.js";
|
|
22
26
|
import { readJsonl } from "../utils/jsonl.js";
|
|
23
27
|
|
|
@@ -47,6 +51,22 @@ export function generateEvidenceId(record: Record<string, unknown>): string {
|
|
|
47
51
|
return `ev_${createHash("sha256").update(key).digest("hex").slice(0, 16)}`;
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Generate a deterministic grading_id from the result's natural key.
|
|
56
|
+
*/
|
|
57
|
+
export function generateGradingId(record: Record<string, unknown>): string {
|
|
58
|
+
const key = `${record.session_id}:${record.skill_name}:${record.graded_at}`;
|
|
59
|
+
return `gr_${createHash("sha256").update(key).digest("hex").slice(0, 16)}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate a deterministic signal_id from an improvement signal's natural key.
|
|
64
|
+
*/
|
|
65
|
+
export function generateSignalId(record: Record<string, unknown>): string {
|
|
66
|
+
const key = `${record.session_id}:${record.query}:${record.signal_type}:${record.timestamp}`;
|
|
67
|
+
return `sig_${createHash("sha256").update(key).digest("hex").slice(0, 16)}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
50
70
|
/**
|
|
51
71
|
* Enrich a raw parsed record: if it is an execution_fact missing
|
|
52
72
|
* execution_fact_id, inject a deterministic one.
|
|
@@ -247,5 +267,79 @@ export function stageCanonicalRecords(db: Database, logPath: string = CANONICAL_
|
|
|
247
267
|
}
|
|
248
268
|
}
|
|
249
269
|
|
|
270
|
+
// 4. Stage grading results from SQLite
|
|
271
|
+
try {
|
|
272
|
+
const gradingResults = queryGradingResults(db);
|
|
273
|
+
for (const gr of gradingResults) {
|
|
274
|
+
const recordJson = JSON.stringify({
|
|
275
|
+
grading_id: gr.grading_id,
|
|
276
|
+
session_id: gr.session_id,
|
|
277
|
+
skill_name: gr.skill_name,
|
|
278
|
+
transcript_path: gr.transcript_path,
|
|
279
|
+
graded_at: gr.graded_at,
|
|
280
|
+
pass_rate: gr.pass_rate,
|
|
281
|
+
mean_score: gr.mean_score,
|
|
282
|
+
score_std_dev: gr.score_std_dev,
|
|
283
|
+
passed_count: gr.passed_count,
|
|
284
|
+
failed_count: gr.failed_count,
|
|
285
|
+
total_count: gr.total_count,
|
|
286
|
+
expectations_json: gr.expectations_json,
|
|
287
|
+
claims_json: gr.claims_json,
|
|
288
|
+
eval_feedback_json: gr.eval_feedback_json,
|
|
289
|
+
failure_feedback_json: gr.failure_feedback_json,
|
|
290
|
+
execution_metrics_json: gr.execution_metrics_json,
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const result = stmt.run(
|
|
294
|
+
"grading_result",
|
|
295
|
+
gr.grading_id,
|
|
296
|
+
recordJson,
|
|
297
|
+
gr.session_id,
|
|
298
|
+
null, // no prompt_id
|
|
299
|
+
gr.graded_at,
|
|
300
|
+
now,
|
|
301
|
+
);
|
|
302
|
+
if (result.changes > 0) staged++;
|
|
303
|
+
}
|
|
304
|
+
} catch (err) {
|
|
305
|
+
if (process.env.DEBUG || process.env.NODE_ENV === "development") {
|
|
306
|
+
console.error("[stage-canonical] failed to stage grading results:", err);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// 5. Stage improvement signals from SQLite
|
|
311
|
+
try {
|
|
312
|
+
const signals = queryImprovementSignals(db);
|
|
313
|
+
for (const sig of signals) {
|
|
314
|
+
const signalId = generateSignalId(sig);
|
|
315
|
+
const recordJson = JSON.stringify({
|
|
316
|
+
signal_id: signalId,
|
|
317
|
+
timestamp: sig.timestamp,
|
|
318
|
+
session_id: sig.session_id,
|
|
319
|
+
query: sig.query,
|
|
320
|
+
signal_type: sig.signal_type,
|
|
321
|
+
mentioned_skill: sig.mentioned_skill,
|
|
322
|
+
consumed: sig.consumed,
|
|
323
|
+
consumed_at: sig.consumed_at,
|
|
324
|
+
consumed_by_run: sig.consumed_by_run,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const result = stmt.run(
|
|
328
|
+
"improvement_signal",
|
|
329
|
+
signalId,
|
|
330
|
+
recordJson,
|
|
331
|
+
sig.session_id,
|
|
332
|
+
null, // no prompt_id
|
|
333
|
+
sig.timestamp,
|
|
334
|
+
now,
|
|
335
|
+
);
|
|
336
|
+
if (result.changes > 0) staged++;
|
|
337
|
+
}
|
|
338
|
+
} catch (err) {
|
|
339
|
+
if (process.env.DEBUG || process.env.NODE_ENV === "development") {
|
|
340
|
+
console.error("[stage-canonical] failed to stage improvement signals:", err);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
250
344
|
return staged;
|
|
251
345
|
}
|
|
@@ -22,6 +22,38 @@ export interface DeviceCodeResult {
|
|
|
22
22
|
org_id: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export function tryOpenUrl(url: string): boolean {
|
|
26
|
+
const command =
|
|
27
|
+
process.platform === "darwin"
|
|
28
|
+
? ["open", url]
|
|
29
|
+
: process.platform === "linux"
|
|
30
|
+
? ["xdg-open", url]
|
|
31
|
+
: process.platform === "win32"
|
|
32
|
+
? ["cmd", "/c", "start", "", url]
|
|
33
|
+
: null;
|
|
34
|
+
|
|
35
|
+
if (!command) return false;
|
|
36
|
+
if (process.platform !== "win32" && !Bun.which(command[0])) return false;
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
Bun.spawn(command, { stdout: "ignore", stderr: "ignore" });
|
|
40
|
+
return true;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function buildVerificationUrl(verificationUrl: string, userCode: string): string {
|
|
47
|
+
try {
|
|
48
|
+
const url = new URL(verificationUrl);
|
|
49
|
+
url.searchParams.set("code", userCode);
|
|
50
|
+
return url.toString();
|
|
51
|
+
} catch {
|
|
52
|
+
const separator = verificationUrl.includes("?") ? "&" : "?";
|
|
53
|
+
return `${verificationUrl}${separator}code=${encodeURIComponent(userCode)}`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
25
57
|
/**
|
|
26
58
|
* Derive the cloud API base URL from SELFTUNE_ALPHA_ENDPOINT.
|
|
27
59
|
* The endpoint is the push URL (e.g., https://api.selftune.dev/api/v1/push).
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
import { spawnSync } from "node:child_process";
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { homedir } from "node:os";
|
|
11
12
|
import { join } from "node:path";
|
|
13
|
+
|
|
12
14
|
import { SELFTUNE_CONFIG_DIR } from "./constants.js";
|
|
13
15
|
|
|
14
16
|
const UPDATE_CHECK_PATH = join(SELFTUNE_CONFIG_DIR, "update-check.json");
|
|
@@ -118,6 +120,16 @@ async function performUpdate(currentVersion: string, latestVersion: string): Pro
|
|
|
118
120
|
console.error(`[selftune] Updated to v${latestVersion}.`);
|
|
119
121
|
// Update cache to reflect new version
|
|
120
122
|
writeCache({ lastCheck: Date.now(), currentVersion: latestVersion, latestVersion });
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const claudeDir = join(homedir(), ".claude");
|
|
126
|
+
if (existsSync(claudeDir)) {
|
|
127
|
+
const { installAgentFiles } = await import("./claude-agents.js");
|
|
128
|
+
installAgentFiles({ force: true });
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
// Non-critical — updated CLI is usable even if agent sync fails
|
|
132
|
+
}
|
|
121
133
|
} else {
|
|
122
134
|
const stderr = result.stderr?.toString().trim();
|
|
123
135
|
console.error(
|
|
@@ -4,6 +4,7 @@ import { randomUUID } from "node:crypto";
|
|
|
4
4
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { parseArgs } from "node:util";
|
|
7
|
+
|
|
7
8
|
import { CANONICAL_LOG, CLAUDE_CODE_PROJECTS_DIR } from "./constants.js";
|
|
8
9
|
import {
|
|
9
10
|
buildCanonicalRecordsFromReplay,
|
|
@@ -84,6 +85,8 @@ export function buildPushPayloadV2(
|
|
|
84
85
|
records: CanonicalRecord[],
|
|
85
86
|
evidenceEntries: EvolutionEvidenceEntry[] = [],
|
|
86
87
|
orchestrateRuns: Record<string, unknown>[] = [],
|
|
88
|
+
gradingResults: Record<string, unknown>[] = [],
|
|
89
|
+
improvementSignals: Record<string, unknown>[] = [],
|
|
87
90
|
): Record<string, unknown> {
|
|
88
91
|
const sessions = records.filter((record) => record.record_kind === "session");
|
|
89
92
|
const prompts = records.filter((record) => record.record_kind === "prompt");
|
|
@@ -120,6 +123,8 @@ export function buildPushPayloadV2(
|
|
|
120
123
|
validation_json: entry.validation,
|
|
121
124
|
})),
|
|
122
125
|
orchestrate_runs: orchestrateRuns,
|
|
126
|
+
grading_results: gradingResults,
|
|
127
|
+
improvement_signals: improvementSignals,
|
|
123
128
|
},
|
|
124
129
|
};
|
|
125
130
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
|
|
5
|
+
const MANIFEST_FILENAME = ".selftune-manifest.json";
|
|
6
|
+
|
|
7
|
+
const LEGACY_SELFTUNE_AGENT_FILES = [
|
|
8
|
+
"diagnosis-analyst.md",
|
|
9
|
+
"evolution-reviewer.md",
|
|
10
|
+
"integration-guide.md",
|
|
11
|
+
"pattern-analyst.md",
|
|
12
|
+
] as const;
|
|
13
|
+
|
|
14
|
+
const BUNDLED_AGENT_DIR = resolve(dirname(import.meta.path), "..", "..", "skill", "agents");
|
|
15
|
+
|
|
16
|
+
interface AgentManifest {
|
|
17
|
+
version: 1;
|
|
18
|
+
files: string[];
|
|
19
|
+
synced_at: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readManifest(path: string): AgentManifest | null {
|
|
23
|
+
try {
|
|
24
|
+
if (!existsSync(path)) return null;
|
|
25
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8")) as Partial<AgentManifest>;
|
|
26
|
+
if (!Array.isArray(parsed.files)) return null;
|
|
27
|
+
return {
|
|
28
|
+
version: 1,
|
|
29
|
+
files: parsed.files.filter((name): name is string => typeof name === "string"),
|
|
30
|
+
synced_at: typeof parsed.synced_at === "string" ? parsed.synced_at : "",
|
|
31
|
+
};
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function writeManifest(path: string, files: string[]): void {
|
|
38
|
+
const manifest: AgentManifest = {
|
|
39
|
+
version: 1,
|
|
40
|
+
files: [...files].sort(),
|
|
41
|
+
synced_at: new Date().toISOString(),
|
|
42
|
+
};
|
|
43
|
+
writeFileSync(path, JSON.stringify(manifest, null, 2), "utf-8");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readTextIfExists(path: string): string | null {
|
|
47
|
+
try {
|
|
48
|
+
if (!existsSync(path)) return null;
|
|
49
|
+
return readFileSync(path, "utf-8");
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getClaudeAgentsDir(homeDir = homedir()): string {
|
|
56
|
+
return join(homeDir, ".claude", "agents");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getClaudeAgentManifestPath(homeDir = homedir()): string {
|
|
60
|
+
return join(getClaudeAgentsDir(homeDir), MANIFEST_FILENAME);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function listBundledAgentFiles(sourceDir = BUNDLED_AGENT_DIR): string[] {
|
|
64
|
+
try {
|
|
65
|
+
if (!existsSync(sourceDir)) return [];
|
|
66
|
+
return readdirSync(sourceDir)
|
|
67
|
+
.filter((name) => name.endsWith(".md"))
|
|
68
|
+
.sort();
|
|
69
|
+
} catch {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function installAgentFiles(options?: {
|
|
75
|
+
homeDir?: string;
|
|
76
|
+
force?: boolean;
|
|
77
|
+
sourceDir?: string;
|
|
78
|
+
}): string[] {
|
|
79
|
+
const homeDir = options?.homeDir ?? homedir();
|
|
80
|
+
const targetDir = getClaudeAgentsDir(homeDir);
|
|
81
|
+
const manifestPath = getClaudeAgentManifestPath(homeDir);
|
|
82
|
+
const sourceDir = options?.sourceDir ?? BUNDLED_AGENT_DIR;
|
|
83
|
+
const sourceFiles = listBundledAgentFiles(sourceDir);
|
|
84
|
+
if (sourceFiles.length === 0) return [];
|
|
85
|
+
|
|
86
|
+
mkdirSync(targetDir, { recursive: true });
|
|
87
|
+
|
|
88
|
+
const manifest = readManifest(manifestPath);
|
|
89
|
+
const managedFiles = new Set<string>([
|
|
90
|
+
...LEGACY_SELFTUNE_AGENT_FILES,
|
|
91
|
+
...(manifest?.files ?? []),
|
|
92
|
+
]);
|
|
93
|
+
const sourceSet = new Set(sourceFiles);
|
|
94
|
+
const changed = new Set<string>();
|
|
95
|
+
|
|
96
|
+
for (const staleFile of managedFiles) {
|
|
97
|
+
if (sourceSet.has(staleFile)) continue;
|
|
98
|
+
const stalePath = join(targetDir, staleFile);
|
|
99
|
+
if (existsSync(stalePath)) {
|
|
100
|
+
rmSync(stalePath, { force: true });
|
|
101
|
+
changed.add(staleFile);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const fileName of sourceFiles) {
|
|
106
|
+
const sourcePath = join(sourceDir, fileName);
|
|
107
|
+
const targetPath = join(targetDir, fileName);
|
|
108
|
+
const sourceContent = readTextIfExists(sourcePath);
|
|
109
|
+
if (sourceContent === null) continue;
|
|
110
|
+
const existingContent = readTextIfExists(targetPath);
|
|
111
|
+
|
|
112
|
+
if (options?.force || existingContent !== sourceContent) {
|
|
113
|
+
writeFileSync(targetPath, sourceContent, "utf-8");
|
|
114
|
+
changed.add(fileName);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
writeManifest(manifestPath, sourceFiles);
|
|
119
|
+
return [...changed].sort();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function removeInstalledAgentFiles(options?: { homeDir?: string; dryRun?: boolean }): {
|
|
123
|
+
removed: number;
|
|
124
|
+
files: string[];
|
|
125
|
+
} {
|
|
126
|
+
const homeDir = options?.homeDir ?? homedir();
|
|
127
|
+
const targetDir = getClaudeAgentsDir(homeDir);
|
|
128
|
+
const manifestPath = getClaudeAgentManifestPath(homeDir);
|
|
129
|
+
const manifest = readManifest(manifestPath);
|
|
130
|
+
const managedFiles = new Set<string>([
|
|
131
|
+
...LEGACY_SELFTUNE_AGENT_FILES,
|
|
132
|
+
...listBundledAgentFiles(),
|
|
133
|
+
...(manifest?.files ?? []),
|
|
134
|
+
]);
|
|
135
|
+
const removed: string[] = [];
|
|
136
|
+
|
|
137
|
+
for (const fileName of managedFiles) {
|
|
138
|
+
const targetPath = join(targetDir, fileName);
|
|
139
|
+
if (!existsSync(targetPath)) continue;
|
|
140
|
+
if (!options?.dryRun) {
|
|
141
|
+
rmSync(targetPath, { force: true });
|
|
142
|
+
}
|
|
143
|
+
removed.push(targetPath);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (existsSync(manifestPath)) {
|
|
147
|
+
if (!options?.dryRun) {
|
|
148
|
+
rmSync(manifestPath, { force: true });
|
|
149
|
+
}
|
|
150
|
+
removed.push(manifestPath);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return { removed: removed.length, files: removed };
|
|
154
|
+
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { spawnSync } from "node:child_process";
|
|
11
11
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { parseArgs } from "node:util";
|
|
13
|
+
|
|
13
14
|
import { CONTRIBUTIONS_DIR } from "../constants.js";
|
|
14
15
|
import { assembleBundle } from "./bundle.js";
|
|
15
16
|
import { sanitizeBundle } from "./sanitize.js";
|
|
@@ -46,10 +46,10 @@ export const DEFAULT_CRON_JOBS: CronJobConfig[] = [
|
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
name: "selftune-orchestrate",
|
|
49
|
-
cron: "0 */
|
|
49
|
+
cron: "0 */2 * * *",
|
|
50
50
|
message:
|
|
51
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
|
|
52
|
+
description: "Autonomous improvement loop every 2 hours",
|
|
53
53
|
},
|
|
54
54
|
];
|
|
55
55
|
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import type { Database } from "bun:sqlite";
|
|
20
20
|
import { existsSync, readFileSync, unwatchFile, watchFile } from "node:fs";
|
|
21
21
|
import { dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
22
|
+
|
|
22
23
|
import type { BadgeFormat } from "./badge/badge-svg.js";
|
|
23
24
|
import { LOG_DIR, SELFTUNE_CONFIG_DIR } from "./constants.js";
|
|
24
25
|
import type {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
import { parseArgs } from "node:util";
|
|
16
|
+
|
|
16
17
|
import type { EvalEntry, SkillsBenchTask } from "../types.js";
|
|
17
18
|
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
@@ -181,9 +181,8 @@ export async function generateSyntheticEvals(
|
|
|
181
181
|
try {
|
|
182
182
|
const { getDb } = await import("../localdb/db.js");
|
|
183
183
|
const { querySkillUsageRecords, queryQueryLog } = await import("../localdb/queries.js");
|
|
184
|
-
const { isHighConfidencePositiveSkillRecord } =
|
|
185
|
-
"../utils/skill-usage-confidence.js"
|
|
186
|
-
);
|
|
184
|
+
const { isHighConfidencePositiveSkillRecord } =
|
|
185
|
+
await import("../utils/skill-usage-confidence.js");
|
|
187
186
|
|
|
188
187
|
const db = getDb();
|
|
189
188
|
|