selftune 0.2.22 → 0.2.23
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 +4 -2
- package/apps/local-dashboard/dist/assets/index-CwOtTrUS.css +1 -0
- package/apps/local-dashboard/dist/assets/index-f1HQpbeH.js +59 -0
- package/apps/local-dashboard/dist/assets/vendor-ui-jVSaIZey.js +12 -0
- package/apps/local-dashboard/dist/index.html +3 -3
- package/cli/selftune/adapters/pi/hook.ts +273 -0
- package/cli/selftune/adapters/pi/install.ts +207 -0
- package/cli/selftune/constants.ts +10 -1
- package/cli/selftune/dashboard-contract.ts +14 -0
- package/cli/selftune/evolution/engines/judge-engine.ts +96 -0
- package/cli/selftune/evolution/engines/replay-engine.ts +158 -0
- package/cli/selftune/evolution/evidence.ts +2 -6
- package/cli/selftune/evolution/evolve-body.ts +73 -20
- package/cli/selftune/evolution/validate-body.ts +78 -42
- package/cli/selftune/evolution/validate-routing.ts +45 -104
- package/cli/selftune/hooks/skill-eval.ts +2 -1
- package/cli/selftune/hooks-shared/types.ts +1 -0
- package/cli/selftune/index.ts +23 -5
- package/cli/selftune/ingestors/pi-ingest.ts +726 -0
- package/cli/selftune/init.ts +11 -1
- package/cli/selftune/localdb/direct-write.ts +85 -0
- package/cli/selftune/localdb/materialize.ts +6 -7
- package/cli/selftune/localdb/queries.ts +126 -0
- package/cli/selftune/localdb/schema.ts +38 -0
- package/cli/selftune/observability.ts +8 -1
- package/cli/selftune/orchestrate.ts +43 -0
- package/cli/selftune/registry/client.ts +74 -0
- package/cli/selftune/registry/history.ts +54 -0
- package/cli/selftune/registry/index.ts +90 -0
- package/cli/selftune/registry/install.ts +141 -0
- package/cli/selftune/registry/list.ts +44 -0
- package/cli/selftune/registry/push.ts +171 -0
- package/cli/selftune/registry/rollback.ts +49 -0
- package/cli/selftune/registry/status.ts +62 -0
- package/cli/selftune/registry/sync.ts +125 -0
- package/cli/selftune/repair/skill-usage.ts +4 -1
- package/cli/selftune/status.ts +31 -0
- package/cli/selftune/sync.ts +127 -23
- package/cli/selftune/types.ts +2 -1
- package/cli/selftune/utils/jsonl.ts +1 -30
- package/cli/selftune/utils/skill-discovery.ts +22 -0
- package/node_modules/@selftune/telemetry-contract/fixtures/evidence-only-push.ts +1 -1
- package/node_modules/@selftune/telemetry-contract/fixtures/golden.test.ts +0 -1
- package/node_modules/@selftune/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +1 -1
- package/node_modules/@selftune/telemetry-contract/package.json +1 -1
- package/node_modules/@selftune/telemetry-contract/src/index.ts +1 -0
- package/node_modules/@selftune/telemetry-contract/src/schemas.ts +22 -4
- package/node_modules/@selftune/telemetry-contract/src/types.ts +1 -12
- package/node_modules/@selftune/telemetry-contract/tests/compatibility.test.ts +0 -1
- package/package.json +1 -1
- package/packages/telemetry-contract/fixtures/evidence-only-push.ts +1 -1
- package/packages/telemetry-contract/fixtures/golden.test.ts +0 -1
- package/packages/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +1 -1
- package/packages/telemetry-contract/package.json +1 -1
- package/packages/telemetry-contract/src/index.ts +1 -0
- package/packages/telemetry-contract/src/schemas.ts +22 -4
- package/packages/telemetry-contract/src/types.ts +1 -12
- package/packages/telemetry-contract/tests/compatibility.test.ts +0 -1
- package/packages/ui/AGENTS.md +16 -0
- package/packages/ui/README.md +1 -1
- package/packages/ui/package.json +1 -1
- package/packages/ui/src/components/ActivityTimeline.tsx +152 -168
- package/packages/ui/src/components/AnalyticsCharts.tsx +344 -0
- package/packages/ui/src/components/EvidenceViewer.tsx +153 -443
- package/packages/ui/src/components/EvolutionTimeline.tsx +34 -87
- package/packages/ui/src/components/InfoTip.tsx +1 -2
- package/packages/ui/src/components/InvocationsPanel.tsx +413 -0
- package/packages/ui/src/components/JobHistoryTimeline.tsx +156 -0
- package/packages/ui/src/components/OrchestrateRunsPanel.tsx +18 -36
- package/packages/ui/src/components/OverviewPanels.tsx +652 -0
- package/packages/ui/src/components/PipelineStatusBar.tsx +65 -0
- package/packages/ui/src/components/SkillReportGuide.tsx +215 -0
- package/packages/ui/src/components/SkillReportPanels.tsx +919 -0
- package/packages/ui/src/components/SkillsLibrary.tsx +437 -0
- package/packages/ui/src/components/index.ts +56 -1
- package/packages/ui/src/components/section-cards.tsx +18 -35
- package/packages/ui/src/components/skill-health-grid.tsx +47 -37
- package/packages/ui/src/lib/constants.tsx +0 -1
- package/packages/ui/src/primitives/card.tsx +1 -1
- package/packages/ui/src/primitives/checkbox.tsx +1 -1
- package/packages/ui/src/primitives/dropdown-menu.tsx +2 -2
- package/packages/ui/src/primitives/select.tsx +2 -2
- package/packages/ui/src/types.ts +172 -4
- package/skill/SKILL.md +18 -4
- package/skill/Workflows/Ingest.md +60 -2
- package/skill/Workflows/Initialize.md +8 -5
- package/skill/Workflows/PlatformHooks.md +19 -3
- package/skill/Workflows/Registry.md +99 -0
- package/skill/Workflows/Sync.md +3 -1
- package/apps/local-dashboard/dist/assets/index-D8O-RG1I.js +0 -60
- package/apps/local-dashboard/dist/assets/index-_EcLywDg.css +0 -1
- package/apps/local-dashboard/dist/assets/vendor-ui-CGEmUayx.js +0 -12
- package/cli/selftune/utils/html.ts +0 -27
- package/packages/ui/src/components/RecentActivityFeed.tsx +0 -117
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Card, CardContent } from "../primitives/card";
|
|
2
|
+
import { timeAgo } from "../lib/format";
|
|
3
|
+
import { ArrowRightIcon } from "lucide-react";
|
|
4
|
+
import type { JobScheduleEntry } from "../types";
|
|
5
|
+
|
|
6
|
+
function statusDot(status: "success" | "error" | null) {
|
|
7
|
+
if (status === "success") return "bg-emerald-500";
|
|
8
|
+
if (status === "error") return "bg-red-500";
|
|
9
|
+
return "bg-muted-foreground/40";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function formatNextRun(iso: string): string {
|
|
13
|
+
const ms = new Date(iso).getTime() - Date.now();
|
|
14
|
+
if (ms <= 0) return "now";
|
|
15
|
+
const mins = Math.ceil(ms / 60000);
|
|
16
|
+
if (mins < 60) return `in ${mins}m`;
|
|
17
|
+
const hours = Math.floor(mins / 60);
|
|
18
|
+
return `in ${hours}h`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function PipelineStatusBar({
|
|
22
|
+
jobs,
|
|
23
|
+
viewAllHref = "/jobs",
|
|
24
|
+
}: {
|
|
25
|
+
jobs: JobScheduleEntry[];
|
|
26
|
+
viewAllHref?: string;
|
|
27
|
+
}) {
|
|
28
|
+
if (jobs.length === 0) return null;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Card size="sm">
|
|
32
|
+
<CardContent className="flex flex-wrap items-center gap-3">
|
|
33
|
+
{jobs.map((job) => {
|
|
34
|
+
const hasError = job.lastRunStatus === "error";
|
|
35
|
+
return (
|
|
36
|
+
<div
|
|
37
|
+
key={job.name}
|
|
38
|
+
className={`flex items-center gap-2 rounded-lg border px-3 py-2 text-xs ${
|
|
39
|
+
hasError ? "border-red-500/40 bg-red-950/20" : "border-border bg-card"
|
|
40
|
+
}`}
|
|
41
|
+
>
|
|
42
|
+
<span className={`size-2 shrink-0 rounded-full ${statusDot(job.lastRunStatus)}`} />
|
|
43
|
+
<span className="font-medium text-foreground">{job.name}</span>
|
|
44
|
+
{job.lastRunAt ? (
|
|
45
|
+
<span className="text-muted-foreground font-mono">{timeAgo(job.lastRunAt)}</span>
|
|
46
|
+
) : (
|
|
47
|
+
<span className="text-muted-foreground font-mono">--</span>
|
|
48
|
+
)}
|
|
49
|
+
<span className="text-muted-foreground/60">|</span>
|
|
50
|
+
<span className="text-muted-foreground font-mono">
|
|
51
|
+
{formatNextRun(job.nextRunAt)}
|
|
52
|
+
</span>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
})}
|
|
56
|
+
<a
|
|
57
|
+
href={viewAllHref}
|
|
58
|
+
className="ml-auto flex items-center gap-1 text-xs text-primary hover:text-primary/80"
|
|
59
|
+
>
|
|
60
|
+
View all <ArrowRightIcon className="size-3" />
|
|
61
|
+
</a>
|
|
62
|
+
</CardContent>
|
|
63
|
+
</Card>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { XIcon } from "lucide-react";
|
|
3
|
+
import { Button } from "../primitives";
|
|
4
|
+
|
|
5
|
+
const SKILL_REPORT_ONBOARDING_KEY = "selftune.skill-report-onboarding-dismissed";
|
|
6
|
+
|
|
7
|
+
/* ─── Guide Sheet (slide-over panel) ──────────────────── */
|
|
8
|
+
|
|
9
|
+
export function SkillReportGuideSheet({
|
|
10
|
+
open,
|
|
11
|
+
onOpenChange,
|
|
12
|
+
}: {
|
|
13
|
+
open: boolean;
|
|
14
|
+
onOpenChange: (open: boolean) => void;
|
|
15
|
+
}) {
|
|
16
|
+
const panelRef = useRef<HTMLDivElement>(null);
|
|
17
|
+
|
|
18
|
+
// Close on Escape
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!open) return;
|
|
21
|
+
const handleKey = (e: KeyboardEvent) => {
|
|
22
|
+
if (e.key === "Escape") onOpenChange(false);
|
|
23
|
+
};
|
|
24
|
+
document.addEventListener("keydown", handleKey);
|
|
25
|
+
return () => document.removeEventListener("keydown", handleKey);
|
|
26
|
+
}, [open, onOpenChange]);
|
|
27
|
+
|
|
28
|
+
if (!open) return null;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="fixed inset-0 z-50 flex justify-end">
|
|
32
|
+
{/* Backdrop */}
|
|
33
|
+
<div
|
|
34
|
+
className="absolute inset-0 bg-black/40 backdrop-blur-[2px] transition-opacity"
|
|
35
|
+
onClick={() => onOpenChange(false)}
|
|
36
|
+
/>
|
|
37
|
+
|
|
38
|
+
{/* Panel */}
|
|
39
|
+
<div
|
|
40
|
+
ref={panelRef}
|
|
41
|
+
className="relative z-10 w-full max-w-lg overflow-y-auto bg-white shadow-xl dark:bg-slate-900 animate-in slide-in-from-right duration-200"
|
|
42
|
+
>
|
|
43
|
+
{/* Header */}
|
|
44
|
+
<div className="sticky top-0 z-10 flex items-center justify-between border-b border-slate-200 bg-white px-6 py-4 dark:border-slate-800 dark:bg-slate-900">
|
|
45
|
+
<div>
|
|
46
|
+
<h2 className="text-lg font-semibold text-slate-900 dark:text-white">
|
|
47
|
+
How to read this page
|
|
48
|
+
</h2>
|
|
49
|
+
<p className="mt-0.5 text-sm text-slate-500 dark:text-slate-400">
|
|
50
|
+
selftune earns trust by showing what it observed, what it proposed, how it tested the
|
|
51
|
+
change, and what happened next.
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
<Button
|
|
55
|
+
variant="ghost"
|
|
56
|
+
className="shrink-0 rounded-full p-1.5"
|
|
57
|
+
onClick={() => onOpenChange(false)}
|
|
58
|
+
>
|
|
59
|
+
<XIcon className="size-4" />
|
|
60
|
+
</Button>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
{/* Body */}
|
|
64
|
+
<div className="space-y-8 px-6 py-6">
|
|
65
|
+
{/* The improvement loop */}
|
|
66
|
+
<section className="space-y-3">
|
|
67
|
+
<h3 className="text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-400 dark:text-slate-500">
|
|
68
|
+
The improvement loop
|
|
69
|
+
</h3>
|
|
70
|
+
<div className="space-y-3 text-sm text-slate-600 dark:text-slate-400">
|
|
71
|
+
<p>
|
|
72
|
+
<strong className="text-slate-900 dark:text-white">1. Observe.</strong> selftune
|
|
73
|
+
watches real sessions and notes when a skill triggered, missed, or looked noisy.
|
|
74
|
+
</p>
|
|
75
|
+
<p>
|
|
76
|
+
<strong className="text-slate-900 dark:text-white">2. Propose.</strong> When the
|
|
77
|
+
signal is strong enough, it suggests a wording change to the skill.
|
|
78
|
+
</p>
|
|
79
|
+
<p>
|
|
80
|
+
<strong className="text-slate-900 dark:text-white">3. Validate.</strong> It checks
|
|
81
|
+
whether the new wording improves routing without breaking important cases.
|
|
82
|
+
</p>
|
|
83
|
+
<p>
|
|
84
|
+
<strong className="text-slate-900 dark:text-white">4. Decide.</strong> Only
|
|
85
|
+
validated winners should be deployed. Rejected or pending proposals do not change
|
|
86
|
+
the live skill.
|
|
87
|
+
</p>
|
|
88
|
+
</div>
|
|
89
|
+
</section>
|
|
90
|
+
|
|
91
|
+
{/* What each section means */}
|
|
92
|
+
<section className="space-y-3">
|
|
93
|
+
<h3 className="text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-400 dark:text-slate-500">
|
|
94
|
+
What each section means
|
|
95
|
+
</h3>
|
|
96
|
+
<div className="space-y-3 text-sm text-slate-600 dark:text-slate-400">
|
|
97
|
+
<p>
|
|
98
|
+
<strong className="text-slate-900 dark:text-white">Next Best Action</strong> tells
|
|
99
|
+
you whether you should review, deploy, or simply keep observing.
|
|
100
|
+
</p>
|
|
101
|
+
<p>
|
|
102
|
+
<strong className="text-slate-900 dark:text-white">
|
|
103
|
+
How selftune is improving this skill
|
|
104
|
+
</strong>{" "}
|
|
105
|
+
explains the current state in plain language.
|
|
106
|
+
</p>
|
|
107
|
+
<p>
|
|
108
|
+
<strong className="text-slate-900 dark:text-white">Trust Signals</strong> are the
|
|
109
|
+
condensed metrics behind that story: coverage, evidence quality, routing quality,
|
|
110
|
+
and evolution state.
|
|
111
|
+
</p>
|
|
112
|
+
<p>
|
|
113
|
+
<strong className="text-slate-900 dark:text-white">Evidence</strong> shows what
|
|
114
|
+
changed and why a proposal was accepted, rejected, or left pending.
|
|
115
|
+
</p>
|
|
116
|
+
<p>
|
|
117
|
+
<strong className="text-slate-900 dark:text-white">Invocations</strong> shows real
|
|
118
|
+
prompts where this skill triggered or likely should have triggered.
|
|
119
|
+
</p>
|
|
120
|
+
<p>
|
|
121
|
+
<strong className="text-slate-900 dark:text-white">Community</strong> surfaces
|
|
122
|
+
aggregated contributor signals and usage patterns from the broader selftune
|
|
123
|
+
community.
|
|
124
|
+
</p>
|
|
125
|
+
</div>
|
|
126
|
+
</section>
|
|
127
|
+
|
|
128
|
+
{/* FAQ */}
|
|
129
|
+
<section className="space-y-3">
|
|
130
|
+
<h3 className="text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-400 dark:text-slate-500">
|
|
131
|
+
FAQ
|
|
132
|
+
</h3>
|
|
133
|
+
<div className="space-y-4 text-sm text-slate-600 dark:text-slate-400">
|
|
134
|
+
<div>
|
|
135
|
+
<p className="font-medium text-slate-900 dark:text-white">
|
|
136
|
+
What is a missed trigger?
|
|
137
|
+
</p>
|
|
138
|
+
<p>
|
|
139
|
+
A case where selftune believes the skill should have been used, but the agent did
|
|
140
|
+
not invoke it.
|
|
141
|
+
</p>
|
|
142
|
+
</div>
|
|
143
|
+
<div>
|
|
144
|
+
<p className="font-medium text-slate-900 dark:text-white">
|
|
145
|
+
Why was a proposal rejected?
|
|
146
|
+
</p>
|
|
147
|
+
<p>
|
|
148
|
+
Usually because validation showed the new wording would regress existing behavior,
|
|
149
|
+
or because it violated a hard rule like dropping an important anchor phrase.
|
|
150
|
+
</p>
|
|
151
|
+
</div>
|
|
152
|
+
<div>
|
|
153
|
+
<p className="font-medium text-slate-900 dark:text-white">
|
|
154
|
+
When should I trust a recommendation?
|
|
155
|
+
</p>
|
|
156
|
+
<p>
|
|
157
|
+
Trust it more when the page shows broad coverage, prompt-linked evidence, and a
|
|
158
|
+
validated result. Trust it less when the sample is tiny or the data is noisy.
|
|
159
|
+
</p>
|
|
160
|
+
</div>
|
|
161
|
+
<div>
|
|
162
|
+
<p className="font-medium text-slate-900 dark:text-white">
|
|
163
|
+
Do I need to understand every metric?
|
|
164
|
+
</p>
|
|
165
|
+
<p>
|
|
166
|
+
No. Start with the plain-English summary and next best action. Use the deeper tabs
|
|
167
|
+
only when you want to inspect the evidence yourself.
|
|
168
|
+
</p>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</section>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* ─── Onboarding banner (dismissible) ─────────────────── */
|
|
179
|
+
|
|
180
|
+
export function SkillReportOnboardingBanner({ onOpenGuide }: { onOpenGuide: () => void }) {
|
|
181
|
+
const [dismissed, setDismissed] = useState(() =>
|
|
182
|
+
typeof window !== "undefined"
|
|
183
|
+
? window.localStorage.getItem(SKILL_REPORT_ONBOARDING_KEY) === "1"
|
|
184
|
+
: false,
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
if (dismissed) return null;
|
|
188
|
+
|
|
189
|
+
const handleDismiss = () => {
|
|
190
|
+
if (typeof window !== "undefined") {
|
|
191
|
+
window.localStorage.setItem(SKILL_REPORT_ONBOARDING_KEY, "1");
|
|
192
|
+
}
|
|
193
|
+
setDismissed(true);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<div className="rounded-lg border border-blue-200/40 bg-blue-50/50 px-4 py-3 dark:border-blue-900/40 dark:bg-blue-950/20">
|
|
198
|
+
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
199
|
+
<div className="text-sm text-slate-600 dark:text-slate-400">
|
|
200
|
+
<span className="font-medium text-slate-900 dark:text-white">New to selftune?</span> Start
|
|
201
|
+
with the summary below, then open the guide if you want the full improvement loop
|
|
202
|
+
explained step by step.
|
|
203
|
+
</div>
|
|
204
|
+
<div className="flex items-center gap-2">
|
|
205
|
+
<Button variant="outline" onClick={onOpenGuide}>
|
|
206
|
+
Open guide
|
|
207
|
+
</Button>
|
|
208
|
+
<Button variant="ghost" onClick={handleDismiss}>
|
|
209
|
+
Hide
|
|
210
|
+
</Button>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
}
|