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.
Files changed (94) hide show
  1. package/README.md +4 -2
  2. package/apps/local-dashboard/dist/assets/index-CwOtTrUS.css +1 -0
  3. package/apps/local-dashboard/dist/assets/index-f1HQpbeH.js +59 -0
  4. package/apps/local-dashboard/dist/assets/vendor-ui-jVSaIZey.js +12 -0
  5. package/apps/local-dashboard/dist/index.html +3 -3
  6. package/cli/selftune/adapters/pi/hook.ts +273 -0
  7. package/cli/selftune/adapters/pi/install.ts +207 -0
  8. package/cli/selftune/constants.ts +10 -1
  9. package/cli/selftune/dashboard-contract.ts +14 -0
  10. package/cli/selftune/evolution/engines/judge-engine.ts +96 -0
  11. package/cli/selftune/evolution/engines/replay-engine.ts +158 -0
  12. package/cli/selftune/evolution/evidence.ts +2 -6
  13. package/cli/selftune/evolution/evolve-body.ts +73 -20
  14. package/cli/selftune/evolution/validate-body.ts +78 -42
  15. package/cli/selftune/evolution/validate-routing.ts +45 -104
  16. package/cli/selftune/hooks/skill-eval.ts +2 -1
  17. package/cli/selftune/hooks-shared/types.ts +1 -0
  18. package/cli/selftune/index.ts +23 -5
  19. package/cli/selftune/ingestors/pi-ingest.ts +726 -0
  20. package/cli/selftune/init.ts +11 -1
  21. package/cli/selftune/localdb/direct-write.ts +85 -0
  22. package/cli/selftune/localdb/materialize.ts +6 -7
  23. package/cli/selftune/localdb/queries.ts +126 -0
  24. package/cli/selftune/localdb/schema.ts +38 -0
  25. package/cli/selftune/observability.ts +8 -1
  26. package/cli/selftune/orchestrate.ts +43 -0
  27. package/cli/selftune/registry/client.ts +74 -0
  28. package/cli/selftune/registry/history.ts +54 -0
  29. package/cli/selftune/registry/index.ts +90 -0
  30. package/cli/selftune/registry/install.ts +141 -0
  31. package/cli/selftune/registry/list.ts +44 -0
  32. package/cli/selftune/registry/push.ts +171 -0
  33. package/cli/selftune/registry/rollback.ts +49 -0
  34. package/cli/selftune/registry/status.ts +62 -0
  35. package/cli/selftune/registry/sync.ts +125 -0
  36. package/cli/selftune/repair/skill-usage.ts +4 -1
  37. package/cli/selftune/status.ts +31 -0
  38. package/cli/selftune/sync.ts +127 -23
  39. package/cli/selftune/types.ts +2 -1
  40. package/cli/selftune/utils/jsonl.ts +1 -30
  41. package/cli/selftune/utils/skill-discovery.ts +22 -0
  42. package/node_modules/@selftune/telemetry-contract/fixtures/evidence-only-push.ts +1 -1
  43. package/node_modules/@selftune/telemetry-contract/fixtures/golden.test.ts +0 -1
  44. package/node_modules/@selftune/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +1 -1
  45. package/node_modules/@selftune/telemetry-contract/package.json +1 -1
  46. package/node_modules/@selftune/telemetry-contract/src/index.ts +1 -0
  47. package/node_modules/@selftune/telemetry-contract/src/schemas.ts +22 -4
  48. package/node_modules/@selftune/telemetry-contract/src/types.ts +1 -12
  49. package/node_modules/@selftune/telemetry-contract/tests/compatibility.test.ts +0 -1
  50. package/package.json +1 -1
  51. package/packages/telemetry-contract/fixtures/evidence-only-push.ts +1 -1
  52. package/packages/telemetry-contract/fixtures/golden.test.ts +0 -1
  53. package/packages/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +1 -1
  54. package/packages/telemetry-contract/package.json +1 -1
  55. package/packages/telemetry-contract/src/index.ts +1 -0
  56. package/packages/telemetry-contract/src/schemas.ts +22 -4
  57. package/packages/telemetry-contract/src/types.ts +1 -12
  58. package/packages/telemetry-contract/tests/compatibility.test.ts +0 -1
  59. package/packages/ui/AGENTS.md +16 -0
  60. package/packages/ui/README.md +1 -1
  61. package/packages/ui/package.json +1 -1
  62. package/packages/ui/src/components/ActivityTimeline.tsx +152 -168
  63. package/packages/ui/src/components/AnalyticsCharts.tsx +344 -0
  64. package/packages/ui/src/components/EvidenceViewer.tsx +153 -443
  65. package/packages/ui/src/components/EvolutionTimeline.tsx +34 -87
  66. package/packages/ui/src/components/InfoTip.tsx +1 -2
  67. package/packages/ui/src/components/InvocationsPanel.tsx +413 -0
  68. package/packages/ui/src/components/JobHistoryTimeline.tsx +156 -0
  69. package/packages/ui/src/components/OrchestrateRunsPanel.tsx +18 -36
  70. package/packages/ui/src/components/OverviewPanels.tsx +652 -0
  71. package/packages/ui/src/components/PipelineStatusBar.tsx +65 -0
  72. package/packages/ui/src/components/SkillReportGuide.tsx +215 -0
  73. package/packages/ui/src/components/SkillReportPanels.tsx +919 -0
  74. package/packages/ui/src/components/SkillsLibrary.tsx +437 -0
  75. package/packages/ui/src/components/index.ts +56 -1
  76. package/packages/ui/src/components/section-cards.tsx +18 -35
  77. package/packages/ui/src/components/skill-health-grid.tsx +47 -37
  78. package/packages/ui/src/lib/constants.tsx +0 -1
  79. package/packages/ui/src/primitives/card.tsx +1 -1
  80. package/packages/ui/src/primitives/checkbox.tsx +1 -1
  81. package/packages/ui/src/primitives/dropdown-menu.tsx +2 -2
  82. package/packages/ui/src/primitives/select.tsx +2 -2
  83. package/packages/ui/src/types.ts +172 -4
  84. package/skill/SKILL.md +18 -4
  85. package/skill/Workflows/Ingest.md +60 -2
  86. package/skill/Workflows/Initialize.md +8 -5
  87. package/skill/Workflows/PlatformHooks.md +19 -3
  88. package/skill/Workflows/Registry.md +99 -0
  89. package/skill/Workflows/Sync.md +3 -1
  90. package/apps/local-dashboard/dist/assets/index-D8O-RG1I.js +0 -60
  91. package/apps/local-dashboard/dist/assets/index-_EcLywDg.css +0 -1
  92. package/apps/local-dashboard/dist/assets/vendor-ui-CGEmUayx.js +0 -12
  93. package/cli/selftune/utils/html.ts +0 -27
  94. 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
+ }