selftune 0.2.16 → 0.2.19

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 (91) hide show
  1. package/README.md +32 -22
  2. package/apps/local-dashboard/dist/assets/index-DnhnXQm6.js +60 -0
  3. package/apps/local-dashboard/dist/assets/index-_EcLywDg.css +1 -0
  4. package/apps/local-dashboard/dist/assets/vendor-table-BIiI3YhS.js +1 -0
  5. package/apps/local-dashboard/dist/assets/vendor-ui-CGEmUayx.js +12 -0
  6. package/apps/local-dashboard/dist/index.html +5 -5
  7. package/cli/selftune/alpha-upload/build-payloads.ts +14 -1
  8. package/cli/selftune/alpha-upload/client.ts +51 -1
  9. package/cli/selftune/alpha-upload/flush.ts +46 -5
  10. package/cli/selftune/alpha-upload/stage-canonical.ts +32 -10
  11. package/cli/selftune/alpha-upload-contract.ts +9 -0
  12. package/cli/selftune/constants.ts +92 -5
  13. package/cli/selftune/contribute/contribute.ts +30 -2
  14. package/cli/selftune/contribute/sanitize.ts +52 -5
  15. package/cli/selftune/contribution-config.ts +249 -0
  16. package/cli/selftune/contribution-relay.ts +177 -0
  17. package/cli/selftune/contribution-signals.ts +219 -0
  18. package/cli/selftune/contribution-staging.ts +147 -0
  19. package/cli/selftune/contributions.ts +532 -0
  20. package/cli/selftune/creator-contributions.ts +333 -0
  21. package/cli/selftune/dashboard-contract.ts +305 -1
  22. package/cli/selftune/dashboard-server.ts +47 -13
  23. package/cli/selftune/eval/family-overlap.ts +395 -0
  24. package/cli/selftune/eval/hooks-to-evals.ts +182 -28
  25. package/cli/selftune/eval/synthetic-evals.ts +298 -11
  26. package/cli/selftune/evolution/description-quality.ts +12 -11
  27. package/cli/selftune/evolution/evolve.ts +214 -51
  28. package/cli/selftune/evolution/validate-proposal.ts +9 -6
  29. package/cli/selftune/export.ts +2 -2
  30. package/cli/selftune/grading/grade-session.ts +20 -0
  31. package/cli/selftune/hooks/commit-track.ts +188 -0
  32. package/cli/selftune/hooks/prompt-log.ts +10 -1
  33. package/cli/selftune/hooks/session-stop.ts +2 -2
  34. package/cli/selftune/hooks/skill-eval.ts +15 -1
  35. package/cli/selftune/hooks/stdin-preview.ts +32 -0
  36. package/cli/selftune/index.ts +41 -5
  37. package/cli/selftune/ingestors/codex-rollout.ts +31 -35
  38. package/cli/selftune/ingestors/codex-wrapper.ts +32 -24
  39. package/cli/selftune/localdb/db.ts +2 -2
  40. package/cli/selftune/localdb/direct-write.ts +69 -6
  41. package/cli/selftune/localdb/queries.ts +1253 -37
  42. package/cli/selftune/localdb/schema.ts +66 -0
  43. package/cli/selftune/orchestrate.ts +32 -4
  44. package/cli/selftune/recover.ts +153 -0
  45. package/cli/selftune/repair/skill-usage.ts +363 -4
  46. package/cli/selftune/routes/actions.ts +35 -1
  47. package/cli/selftune/routes/analytics.ts +14 -0
  48. package/cli/selftune/routes/index.ts +1 -0
  49. package/cli/selftune/routes/overview.ts +150 -4
  50. package/cli/selftune/routes/skill-report.ts +648 -18
  51. package/cli/selftune/status.ts +81 -2
  52. package/cli/selftune/sync.ts +56 -2
  53. package/cli/selftune/trust-model.ts +66 -0
  54. package/cli/selftune/types.ts +80 -0
  55. package/cli/selftune/utils/skill-detection.ts +43 -0
  56. package/cli/selftune/utils/transcript.ts +210 -1
  57. package/cli/selftune/watchlist.ts +65 -0
  58. package/node_modules/@selftune/telemetry-contract/src/types.ts +11 -0
  59. package/package.json +1 -1
  60. package/packages/telemetry-contract/src/types.ts +11 -0
  61. package/packages/ui/src/components/ActivityTimeline.tsx +165 -150
  62. package/packages/ui/src/components/EvidenceViewer.tsx +335 -144
  63. package/packages/ui/src/components/EvolutionTimeline.tsx +58 -28
  64. package/packages/ui/src/components/OrchestrateRunsPanel.tsx +33 -16
  65. package/packages/ui/src/components/RecentActivityFeed.tsx +72 -41
  66. package/packages/ui/src/components/section-cards.tsx +12 -9
  67. package/packages/ui/src/primitives/card.tsx +1 -1
  68. package/skill/SKILL.md +40 -2
  69. package/skill/Workflows/AlphaUpload.md +4 -0
  70. package/skill/Workflows/Composability.md +64 -0
  71. package/skill/Workflows/Contribute.md +6 -3
  72. package/skill/Workflows/Contributions.md +97 -0
  73. package/skill/Workflows/CreatorContributions.md +74 -0
  74. package/skill/Workflows/Dashboard.md +31 -0
  75. package/skill/Workflows/Evals.md +57 -8
  76. package/skill/Workflows/Evolve.md +31 -13
  77. package/skill/Workflows/ExportCanonical.md +121 -0
  78. package/skill/Workflows/Hook.md +131 -0
  79. package/skill/Workflows/Ingest.md +7 -0
  80. package/skill/Workflows/Initialize.md +29 -9
  81. package/skill/Workflows/Orchestrate.md +27 -5
  82. package/skill/Workflows/Quickstart.md +94 -0
  83. package/skill/Workflows/Recover.md +84 -0
  84. package/skill/Workflows/RepairSkillUsage.md +95 -0
  85. package/skill/Workflows/Sync.md +18 -12
  86. package/skill/Workflows/Uninstall.md +82 -0
  87. package/skill/settings_snippet.json +11 -0
  88. package/apps/local-dashboard/dist/assets/index-BMIS6uUh.css +0 -2
  89. package/apps/local-dashboard/dist/assets/index-DOu3iLD9.js +0 -16
  90. package/apps/local-dashboard/dist/assets/vendor-table-pHbDxq36.js +0 -8
  91. package/apps/local-dashboard/dist/assets/vendor-ui-DIwlrGlb.js +0 -12
@@ -25,27 +25,43 @@ const ACTION_ICON: Record<string, React.ReactNode> = {
25
25
  };
26
26
 
27
27
  const ACTION_COLOR: Record<string, string> = {
28
- created: "bg-blue-500",
29
- validated: "bg-amber-500",
30
- deployed: "bg-emerald-500",
31
- rejected: "bg-red-500",
32
- rolled_back: "bg-red-400",
28
+ created: "bg-primary/15",
29
+ validated: "bg-primary/25",
30
+ deployed: "bg-primary/30",
31
+ rejected: "bg-destructive/20",
32
+ rolled_back: "bg-destructive/15",
33
+ };
34
+
35
+ const ACTION_ICON_COLOR: Record<string, string> = {
36
+ created: "text-primary/70",
37
+ validated: "text-primary/85",
38
+ deployed: "text-primary",
39
+ rejected: "text-destructive",
40
+ rolled_back: "text-destructive/70",
33
41
  };
34
42
 
35
43
  const ACTION_RING: Record<string, string> = {
36
- created: "ring-blue-500/30",
37
- validated: "ring-amber-500/30",
38
- deployed: "ring-emerald-500/30",
39
- rejected: "ring-red-500/30",
40
- rolled_back: "ring-red-400/30",
44
+ created: "ring-primary/15",
45
+ validated: "ring-primary/25",
46
+ deployed: "ring-primary/30",
47
+ rejected: "ring-destructive/25",
48
+ rolled_back: "ring-destructive/15",
49
+ };
50
+
51
+ const ACTION_DOT: Record<string, string> = {
52
+ created: "bg-primary/40 ring-primary/30",
53
+ validated: "bg-primary/60 ring-primary/40",
54
+ deployed: "bg-primary ring-primary/50",
55
+ rejected: "bg-destructive/60 ring-destructive/40",
56
+ rolled_back: "bg-destructive/40 ring-destructive/30",
41
57
  };
42
58
 
43
59
  const ACTION_LINE: Record<string, string> = {
44
- created: "bg-blue-500/30",
45
- validated: "bg-amber-500/30",
46
- deployed: "bg-emerald-500/30",
47
- rejected: "bg-red-500/30",
48
- rolled_back: "bg-red-400/30",
60
+ created: "bg-primary/15",
61
+ validated: "bg-primary/20",
62
+ deployed: "bg-primary/25",
63
+ rejected: "bg-destructive/20",
64
+ rolled_back: "bg-destructive/15",
49
65
  };
50
66
 
51
67
  interface Props {
@@ -93,7 +109,7 @@ function PassRateDelta({ snapshot }: { snapshot: EvalSnapshot }) {
93
109
  <span
94
110
  className={cn(
95
111
  "inline-flex items-center gap-0.5 text-[10px] font-mono font-medium",
96
- isPositive ? "text-emerald-600 dark:text-emerald-400" : "text-red-500",
112
+ isPositive ? "text-primary" : "text-destructive",
97
113
  )}
98
114
  >
99
115
  {isPositive ? (
@@ -119,23 +135,35 @@ function LifecycleLegend() {
119
135
  const [open, setOpen] = useState(false);
120
136
 
121
137
  return (
122
- <div className="px-2 pb-2">
138
+ <div className="px-2 pb-3">
123
139
  <button
124
140
  type="button"
125
141
  onClick={() => setOpen(!open)}
126
- className="flex items-center gap-1 text-[10px] text-muted-foreground/70 hover:text-muted-foreground transition-colors w-full"
142
+ aria-expanded={open}
143
+ aria-controls="evolution-lifecycle-stages"
144
+ className="flex w-full items-center gap-1 text-[10px] text-muted-foreground/70 transition-colors hover:text-muted-foreground"
127
145
  >
128
146
  {open ? <ChevronDownIcon className="size-3" /> : <ChevronRightIcon className="size-3" />}
129
147
  Lifecycle stages
130
148
  </button>
131
149
  {open && (
132
- <div className="mt-1.5 space-y-1.5 rounded-md border bg-muted/30 p-2">
150
+ <div
151
+ id="evolution-lifecycle-stages"
152
+ className="mt-1.5 space-y-2.5 rounded-md border bg-muted/30 p-2"
153
+ >
133
154
  {LIFECYCLE_STEPS.map((step) => (
134
155
  <div key={step.action} className="flex items-start gap-2">
135
- <div className={cn("size-2 rounded-full mt-1 shrink-0", ACTION_COLOR[step.action])} />
136
- <div className="min-w-0">
137
- <span className="text-[10px] font-medium">{step.label}</span>
138
- <p className="text-[10px] text-muted-foreground/70 leading-tight">{step.desc}</p>
156
+ <div
157
+ className={cn(
158
+ "size-2 rounded-full shrink-0 ring-1 mt-[3px]",
159
+ ACTION_DOT[step.action],
160
+ )}
161
+ />
162
+ <div className="min-w-0 flex flex-col gap-0.5">
163
+ <span className="text-[10px] font-medium leading-none">{step.label}</span>
164
+ <span className="text-[10px] text-muted-foreground/70 leading-tight">
165
+ {step.desc}
166
+ </span>
139
167
  </div>
140
168
  </div>
141
169
  ))}
@@ -158,7 +186,7 @@ export function EvolutionTimeline({ entries, selectedProposalId, onSelect }: Pro
158
186
 
159
187
  return (
160
188
  <div className="flex flex-col gap-0">
161
- <h2 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider px-2 pb-2 sticky top-0 z-10 bg-background">
189
+ <h2 className="px-2 pb-2 text-[10px] font-semibold uppercase tracking-[0.18em] text-muted-foreground/80">
162
190
  Evolution
163
191
  </h2>
164
192
  <LifecycleLegend />
@@ -167,7 +195,8 @@ export function EvolutionTimeline({ entries, selectedProposalId, onSelect }: Pro
167
195
  const terminal = terminalAction(steps);
168
196
  const isSelected = selectedProposalId === proposalId;
169
197
  const lastStep = steps[steps.length - 1];
170
- const dotColor = ACTION_COLOR[terminal] ?? "bg-muted-foreground";
198
+ const dotColor = ACTION_COLOR[terminal] ?? "bg-muted-foreground/20";
199
+ const iconColor = ACTION_ICON_COLOR[terminal] ?? "text-muted-foreground";
171
200
  const ringColor = ACTION_RING[terminal] ?? "ring-muted-foreground/30";
172
201
  const lineColor = ACTION_LINE[terminal] ?? "bg-border";
173
202
  const isLast = groupIdx === groups.length - 1;
@@ -179,14 +208,15 @@ export function EvolutionTimeline({ entries, selectedProposalId, onSelect }: Pro
179
208
  <div className="flex flex-col items-center">
180
209
  <div
181
210
  className={cn(
182
- "flex items-center justify-center size-7 rounded-full ring-2 text-white shrink-0 z-10",
211
+ "flex items-center justify-center size-7 rounded-full ring-2 shrink-0 z-10",
183
212
  dotColor,
184
213
  ringColor,
214
+ iconColor,
185
215
  )}
186
216
  >
187
217
  {ACTION_ICON[terminal] ?? <CircleDotIcon className="size-3.5" />}
188
218
  </div>
189
- {!isLast && <div className={cn("w-0.5 flex-1 min-h-[16px]", lineColor)} />}
219
+ {!isLast && <div className={cn("w-0.5 flex-1 min-h-[8px] my-1", lineColor)} />}
190
220
  </div>
191
221
 
192
222
  {/* Content */}
@@ -241,7 +271,7 @@ export function EvolutionTimeline({ entries, selectedProposalId, onSelect }: Pro
241
271
  key={`${s.action}-${i}`}
242
272
  className={cn(
243
273
  "size-1.5 rounded-full",
244
- ACTION_COLOR[s.action] ?? "bg-muted-foreground",
274
+ ACTION_DOT[s.action] ?? "bg-muted-foreground/40",
245
275
  )}
246
276
  />
247
277
  ))}
@@ -8,7 +8,7 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../primitiv
8
8
  import type { OrchestrateRunReport, OrchestrateRunSkillAction } from "../types";
9
9
 
10
10
  const ACTION_ICON: Record<string, React.ReactNode> = {
11
- evolve: <ZapIcon className="size-3 text-amber-500" />,
11
+ evolve: <ZapIcon className="size-3 text-primary-accent" />,
12
12
  watch: <EyeIcon className="size-3 text-blue-500" />,
13
13
  skip: <SkipForwardIcon className="size-3 text-muted-foreground" />,
14
14
  };
@@ -66,17 +66,15 @@ function RunCard({ run }: { run: OrchestrateRunReport }) {
66
66
  <div
67
67
  className={`mt-1.5 size-2 shrink-0 rounded-full ${
68
68
  run.deployed > 0
69
- ? "bg-emerald-500"
69
+ ? "bg-primary"
70
70
  : run.evolved > 0
71
- ? "bg-amber-400"
71
+ ? "bg-primary-accent"
72
72
  : "bg-muted-foreground/40"
73
73
  }`}
74
74
  />
75
75
  <div className="flex-1 min-w-0">
76
76
  <div className="flex items-center gap-2">
77
- <span className="text-xs font-mono text-muted-foreground">
78
- {timeAgo(run.timestamp)}
79
- </span>
77
+ <span className="text-[10px] font-mono text-slate-500">{timeAgo(run.timestamp)}</span>
80
78
  {run.dry_run && (
81
79
  <Badge variant="outline" className="text-[10px] h-4 px-1.5">
82
80
  dry-run
@@ -90,7 +88,7 @@ function RunCard({ run }: { run: OrchestrateRunReport }) {
90
88
  </div>
91
89
  <div className="flex items-center gap-3 mt-1 text-xs text-muted-foreground">
92
90
  {run.deployed > 0 && (
93
- <span className="text-emerald-600 font-medium">{run.deployed} deployed</span>
91
+ <span className="text-primary font-medium">{run.deployed} deployed</span>
94
92
  )}
95
93
  {run.evolved > 0 && <span>{run.evolved} evolved</span>}
96
94
  {run.watched > 0 && <span>{run.watched} watched</span>}
@@ -104,7 +102,7 @@ function RunCard({ run }: { run: OrchestrateRunReport }) {
104
102
  </div>
105
103
  </CollapsibleTrigger>
106
104
  <CollapsibleContent>
107
- <div className="ml-5 pl-3 border-l border-border space-y-0.5 pb-2">
105
+ <div className="ml-5 pl-3 border-l border-border/15 space-y-0.5 pb-2">
108
106
  {nonSkipActions.map((action, i) => (
109
107
  <SkillActionRow key={`${action.skill}-${i}`} action={action} />
110
108
  ))}
@@ -126,7 +124,32 @@ function RunCard({ run }: { run: OrchestrateRunReport }) {
126
124
  );
127
125
  }
128
126
 
129
- export function OrchestrateRunsPanel({ runs }: { runs: OrchestrateRunReport[] }) {
127
+ export function OrchestrateRunsPanel({
128
+ runs,
129
+ embedded = false,
130
+ }: {
131
+ runs: OrchestrateRunReport[];
132
+ embedded?: boolean;
133
+ }) {
134
+ const totalDeployed = runs.reduce((sum, r) => sum + r.deployed, 0);
135
+ const content =
136
+ runs.length === 0 ? (
137
+ <p className="py-4 text-center text-sm text-muted-foreground">
138
+ No orchestrate runs yet. Run{" "}
139
+ <code className="rounded bg-muted px-1 py-0.5 text-xs">selftune orchestrate</code> to start.
140
+ </p>
141
+ ) : (
142
+ <div className="space-y-0">
143
+ {runs.slice(0, 10).map((run) => (
144
+ <RunCard key={run.run_id} run={run} />
145
+ ))}
146
+ </div>
147
+ );
148
+
149
+ if (embedded) {
150
+ return <div>{content}</div>;
151
+ }
152
+
130
153
  if (runs.length === 0) {
131
154
  return (
132
155
  <Card>
@@ -147,8 +170,6 @@ export function OrchestrateRunsPanel({ runs }: { runs: OrchestrateRunReport[] })
147
170
  );
148
171
  }
149
172
 
150
- const totalDeployed = runs.reduce((sum, r) => sum + r.deployed, 0);
151
-
152
173
  return (
153
174
  <Card>
154
175
  <CardHeader>
@@ -161,11 +182,7 @@ export function OrchestrateRunsPanel({ runs }: { runs: OrchestrateRunReport[] })
161
182
  {totalDeployed > 0 && <> &middot; {totalDeployed} total deployments</>}
162
183
  </CardDescription>
163
184
  </CardHeader>
164
- <CardContent className="space-y-0">
165
- {runs.slice(0, 10).map((run) => (
166
- <RunCard key={run.run_id} run={run} />
167
- ))}
168
- </CardContent>
185
+ <CardContent>{content}</CardContent>
169
186
  </Card>
170
187
  );
171
188
  }
@@ -13,7 +13,77 @@ export interface RecentActivityItem {
13
13
  is_live: boolean;
14
14
  }
15
15
 
16
- export function RecentActivityFeed({ items }: { items: RecentActivityItem[] }) {
16
+ export function RecentActivityFeed({
17
+ items,
18
+ embedded = false,
19
+ }: {
20
+ items: RecentActivityItem[];
21
+ embedded?: boolean;
22
+ }) {
23
+ const content =
24
+ items.length === 0 ? (
25
+ <p className="py-6 text-center text-sm text-muted-foreground">No recent skill invocations</p>
26
+ ) : (
27
+ <div className="space-y-2.5">
28
+ {items.slice(0, 20).map((item, i) => (
29
+ <div
30
+ key={`${item.session_id}-${item.skill_name}-${i}`}
31
+ className="flex gap-3 rounded-md p-1.5"
32
+ >
33
+ <div
34
+ className={`mt-0.5 w-10 h-10 shrink-0 rounded-xl bg-input flex items-center justify-center`}
35
+ >
36
+ <div
37
+ className={`size-2 rounded-full ${
38
+ item.triggered
39
+ ? "bg-primary shadow-[0_0_8px_rgba(79,242,255,0.6)]"
40
+ : "bg-muted-foreground/40"
41
+ }`}
42
+ />
43
+ </div>
44
+ <div className="flex-1 min-w-0 space-y-0.5">
45
+ <div className="flex flex-wrap items-center gap-2">
46
+ <span className="truncate font-bold text-sm">{item.skill_name}</span>
47
+ {item.is_live && (
48
+ <Badge variant="outline" className="h-4 gap-1 px-1 text-[10px]">
49
+ <CircleDotIcon className="size-2.5 text-primary" />
50
+ live
51
+ </Badge>
52
+ )}
53
+ {item.triggered ? (
54
+ <Badge
55
+ variant="default"
56
+ className="h-4 px-1 text-[10px] font-bold uppercase tracking-tighter bg-primary/10 text-primary"
57
+ >
58
+ triggered
59
+ </Badge>
60
+ ) : (
61
+ <Badge
62
+ variant="secondary"
63
+ className="h-4 px-1 text-[10px] font-bold uppercase tracking-tighter"
64
+ >
65
+ checked
66
+ </Badge>
67
+ )}
68
+ <span className="ml-auto shrink-0 font-mono text-[10px] text-slate-500">
69
+ {timeAgo(item.timestamp)}
70
+ </span>
71
+ </div>
72
+ {item.query && (
73
+ <p className="line-clamp-1 text-sm text-card-foreground leading-relaxed">
74
+ {item.query}
75
+ </p>
76
+ )}
77
+ </div>
78
+ </div>
79
+ ))}
80
+ </div>
81
+ );
82
+
83
+ if (embedded) {
84
+ return <div>{content}</div>;
85
+ }
86
+
17
87
  if (items.length === 0) {
18
88
  return (
19
89
  <Card>
@@ -41,46 +111,7 @@ export function RecentActivityFeed({ items }: { items: RecentActivityItem[] }) {
41
111
  </CardTitle>
42
112
  <CardDescription>Latest skill invocations across sessions</CardDescription>
43
113
  </CardHeader>
44
- <CardContent className="space-y-2.5">
45
- {items.slice(0, 20).map((item, i) => (
46
- <div
47
- key={`${item.session_id}-${item.skill_name}-${i}`}
48
- className="flex gap-3 rounded-md p-1.5"
49
- >
50
- <div
51
- className={`mt-1 size-2 shrink-0 rounded-full ${
52
- item.triggered ? "bg-emerald-500" : "bg-muted-foreground/40"
53
- }`}
54
- />
55
- <div className="flex-1 min-w-0 space-y-0.5">
56
- <div className="flex items-center gap-2 flex-wrap">
57
- <span className="text-xs font-medium truncate">{item.skill_name}</span>
58
- {item.is_live && (
59
- <Badge variant="outline" className="h-4 px-1 text-[10px] gap-1">
60
- <CircleDotIcon className="size-2.5 text-emerald-500" />
61
- live
62
- </Badge>
63
- )}
64
- {item.triggered ? (
65
- <Badge variant="default" className="h-4 px-1 text-[10px]">
66
- triggered
67
- </Badge>
68
- ) : (
69
- <Badge variant="secondary" className="h-4 px-1 text-[10px]">
70
- checked
71
- </Badge>
72
- )}
73
- <span className="text-[10px] text-muted-foreground font-mono ml-auto shrink-0">
74
- {timeAgo(item.timestamp)}
75
- </span>
76
- </div>
77
- {item.query && (
78
- <p className="text-xs text-muted-foreground line-clamp-1 font-mono">{item.query}</p>
79
- )}
80
- </div>
81
- </div>
82
- ))}
83
- </CardContent>
114
+ <CardContent>{content}</CardContent>
84
115
  </Card>
85
116
  );
86
117
  }
@@ -24,6 +24,9 @@ interface SectionCardsProps {
24
24
  activeSessionsCount?: number;
25
25
  }
26
26
 
27
+ const CARD_DESCRIPTION_CLASS =
28
+ "flex items-center gap-1.5 text-[10px] uppercase tracking-widest text-slate-500";
29
+
27
30
  export function SectionCards({
28
31
  skillsCount,
29
32
  avgPassRate,
@@ -38,10 +41,10 @@ export function SectionCards({
38
41
  const passRateGood = avgPassRate !== null && avgPassRate >= 0.7;
39
42
 
40
43
  return (
41
- <div className="grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-3">
44
+ <div className="grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:shadow-none lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-3">
42
45
  <Card className="@container/card">
43
46
  <CardHeader>
44
- <CardDescription className="flex items-center gap-1.5">
47
+ <CardDescription className={CARD_DESCRIPTION_CLASS}>
45
48
  <LayersIcon className="size-3.5" />
46
49
  Skills Monitored
47
50
  <InfoTip text="Total number of skills detected and being tracked by selftune" />
@@ -60,7 +63,7 @@ export function SectionCards({
60
63
 
61
64
  <Card className="@container/card">
62
65
  <CardHeader>
63
- <CardDescription className="flex items-center gap-1.5">
66
+ <CardDescription className={CARD_DESCRIPTION_CLASS}>
64
67
  <FlaskConicalIcon className="size-3.5" />
65
68
  Avg Trigger Rate
66
69
  <InfoTip text="Average percentage of skill checks that resulted in a trigger across all graded skills (5+ checks). Run selftune evolve to improve this." />
@@ -91,7 +94,7 @@ export function SectionCards({
91
94
 
92
95
  <Card className="@container/card">
93
96
  <CardHeader>
94
- <CardDescription className="flex items-center gap-1.5">
97
+ <CardDescription className={CARD_DESCRIPTION_CLASS}>
95
98
  <SearchXIcon className="size-3.5" />
96
99
  Unmatched Queries
97
100
  <InfoTip text="User prompts that didn't match any skill's trigger criteria — potential gaps in coverage" />
@@ -112,7 +115,7 @@ export function SectionCards({
112
115
 
113
116
  <Card className="@container/card">
114
117
  <CardHeader>
115
- <CardDescription className="flex items-center gap-1.5">
118
+ <CardDescription className={CARD_DESCRIPTION_CLASS}>
116
119
  <ActivityIcon className="size-3.5" />
117
120
  Sessions
118
121
  <InfoTip text="Total agent sessions that have been recorded and analyzed" />
@@ -124,8 +127,8 @@ export function SectionCards({
124
127
  <CardAction>
125
128
  <Badge variant="outline" className="gap-1.5">
126
129
  <span className="relative flex size-2">
127
- <span className="absolute inline-flex size-full animate-ping rounded-full bg-emerald-400 opacity-75" />
128
- <span className="relative inline-flex size-2 rounded-full bg-emerald-500" />
130
+ <span className="absolute inline-flex size-full animate-ping rounded-full bg-primary opacity-75" />
131
+ <span className="relative inline-flex size-2 rounded-full bg-primary shadow-[0_0_8px_color-mix(in_srgb,var(--primary)_60%,transparent)]" />
129
132
  </span>
130
133
  {activeSessionsCount} in progress
131
134
  </Badge>
@@ -136,7 +139,7 @@ export function SectionCards({
136
139
 
137
140
  <Card className="@container/card">
138
141
  <CardHeader>
139
- <CardDescription className="flex items-center gap-1.5">
142
+ <CardDescription className={CARD_DESCRIPTION_CLASS}>
140
143
  <AlertTriangleIcon className="size-3.5" />
141
144
  Undeployed Proposals
142
145
  <InfoTip text="Evolution proposals that have been generated but not yet validated or deployed. Requires running selftune evolve." />
@@ -158,7 +161,7 @@ export function SectionCards({
158
161
 
159
162
  <Card className="@container/card">
160
163
  <CardHeader>
161
- <CardDescription className="flex items-center gap-1.5">
164
+ <CardDescription className={CARD_DESCRIPTION_CLASS}>
162
165
  <EyeIcon className="size-3.5" />
163
166
  Total Evidence
164
167
  <InfoTip text="Number of evidence entries documenting skill changes with before/after validation results. Requires running selftune evolve." />
@@ -12,7 +12,7 @@ function Card({
12
12
  data-slot="card"
13
13
  data-size={size}
14
14
  className={cn(
15
- "group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
15
+ "group/card flex flex-col gap-4 overflow-hidden rounded-2xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/5 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
16
16
  className,
17
17
  )}
18
18
  {...props}
package/skill/SKILL.md CHANGED
@@ -12,7 +12,7 @@ description: >
12
12
  even if they don't say "selftune" explicitly.
13
13
  metadata:
14
14
  author: selftune-dev
15
- version: 0.2.16
15
+ version: 0.2.10
16
16
  category: developer-tools
17
17
  ---
18
18
 
@@ -89,6 +89,7 @@ selftune eval generate --skill <name> [--list-skills] [--stats] [--max N] [
89
89
  selftune eval unit-test --skill <name> --tests <path> [--run-agent] [--generate]
90
90
  selftune eval import --dir <path> --skill <name> --output <path> [--match-strategy exact|fuzzy]
91
91
  selftune eval composability --skill <name> [--window N] [--telemetry-log <path>]
92
+ selftune eval family-overlap --prefix <family-> | --skills <a,b,c> [--parent-skill <name>] [--min-overlap 0.3] [--min-shared 2]
92
93
 
93
94
  # Other commands
94
95
  selftune watch --skill <name> --skill-path <path> [--auto-rollback]
@@ -96,6 +97,8 @@ selftune status
96
97
  selftune last
97
98
  selftune doctor
98
99
  selftune dashboard [--port <port>] [--no-open]
100
+ selftune contributions [status|preview <skill>|upload [--dry-run]|approve <skill>|revoke <skill>|default <ask|always|never>|reset]
101
+ selftune creator-contributions [status|enable --skill <name>|enable --all [--prefix <value>]|disable --skill <name>]
99
102
  selftune contribute [--skill NAME] [--preview] [--sanitize LEVEL] [--submit]
100
103
  selftune cron setup [--dry-run] # auto-detect platform (cron/launchd/systemd)
101
104
  selftune cron setup --platform openclaw [--dry-run] [--tz <timezone>] # OpenClaw-specific
@@ -104,9 +107,28 @@ selftune cron remove [--dry-run]
104
107
  selftune telemetry [status|enable|disable]
105
108
  selftune export [TABLE...] [--output/-o DIR] [--since DATE]
106
109
 
110
+ # Autonomous loop
111
+ selftune orchestrate [--dry-run] [--review-required] [--auto-approve] [--skill NAME] [--max-skills N] [--recent-window HOURS] [--sync-force] [--max-auto-grade N] [--loop] [--loop-interval SECS]
112
+ selftune sync [--since DATE] [--dry-run] [--force] [--no-claude] [--no-codex] [--no-opencode] [--no-openclaw] [--no-repair] [--json]
113
+
114
+ # Discovery + badges
115
+ selftune workflows [--skill NAME] [--skill-path PATH] [--min-occurrences N] [--window N] [--json] [save --skill NAME --skill-path PATH]
116
+ selftune badge --skill <name> [--format svg|markdown|url] [--output PATH]
117
+
118
+ # Maintenance
119
+ selftune quickstart
120
+ selftune repair-skill-usage [--since DATE] [--dry-run]
121
+ selftune recover [--full] [--force] [--since DATE]
122
+ selftune export-canonical [--out FILE] [--platform NAME] [--record-kind KIND] [--pretty] [--push-payload]
123
+ selftune uninstall [--dry-run] [--keep-logs] [--npm-uninstall]
124
+
125
+ # Hook dispatch (for debugging/manual invocation)
126
+ selftune hook <name> # prompt-log | session-stop | skill-eval | auto-activate | skill-change-guard | evolution-guard
127
+
107
128
  # Alpha enrollment (device-code flow — browser opens automatically)
108
129
  selftune init --alpha --alpha-email <email>
109
130
  selftune alpha upload [--dry-run]
131
+ selftune alpha relink
110
132
  selftune status # shows cloud link state + upload readiness
111
133
  ```
112
134
 
@@ -123,6 +145,8 @@ selftune status # shows c
123
145
  | doctor, health, hooks, broken, diagnose, not working, something wrong | Doctor | Workflows/Doctor.md |
124
146
  | ingest, import, codex logs, opencode, openclaw, wrap codex | Ingest | Workflows/Ingest.md |
125
147
  | replay, backfill, claude transcripts, historical sessions | Replay | Workflows/Replay.md |
148
+ | contributions, sharing preferences, opt in creator sharing, opt out creator sharing, approve contributions, revoke contributions, preview contributions, upload contributions, relay queue, contribution upload, contribution preview | Contributions | Workflows/Contributions.md |
149
+ | creator contributions, bundle contribution config, selftune.contribute.json, enable creator contribution, disable creator contribution, bulk enable creator contribution, enable all creator contributions, creator prefix config, --all, --prefix | CreatorContributions | Workflows/CreatorContributions.md |
126
150
  | contribute, share, community, export data, anonymized, give back | Contribute | Workflows/Contribute.md |
127
151
  | init, setup, set up, bootstrap, first time, install, configure selftune, alpha, enroll, alpha enrollment, cloud link, upload credential | Initialize | Workflows/Initialize.md |
128
152
  | cron, schedule, automate evolution, run automatically | Cron | Workflows/Cron.md |
@@ -131,7 +155,7 @@ selftune status # shows c
131
155
  | evolution memory, session continuity, what happened last | EvolutionMemory | Workflows/EvolutionMemory.md |
132
156
  | grade baseline, baseline lift, adds value, skill value, no-skill comparison | Baseline | Workflows/Baseline.md |
133
157
  | eval unit-test, skill test, test skill, generate tests, run tests | UnitTest | Workflows/UnitTest.md |
134
- | eval composability, co-occurrence, skill conflicts, skills together | Composability | Workflows/Composability.md |
158
+ | eval composability, co-occurrence, skill conflicts, skills together, family overlap, sibling confusion, consolidate skill family | Composability | Workflows/Composability.md |
135
159
  | eval import, skillsbench, external evals, benchmark tasks | ImportSkillsBench | Workflows/ImportSkillsBench.md |
136
160
  | telemetry, analytics, disable analytics, opt out, tracking, privacy | Telemetry | Workflows/Telemetry.md |
137
161
  | orchestrate, autonomous, full loop, improve all skills, run selftune loop | Orchestrate | Workflows/Orchestrate.md |
@@ -139,6 +163,12 @@ selftune status # shows c
139
163
  | badge, readme badge, skill badge, health badge | Badge | Workflows/Badge.md |
140
164
  | workflows, discover workflows, list workflows, multi-skill workflows | Workflows | Workflows/Workflows.md |
141
165
  | alpha upload, upload data, send alpha data, manual upload, dry run upload | AlphaUpload | Workflows/AlphaUpload.md |
166
+ | recover, rebuild sqlite, recover db, legacy backfill, restore from export snapshot | Recover | Workflows/Recover.md |
167
+ | quickstart, getting started, onboard, first time setup, new user | Quickstart | Workflows/Quickstart.md |
168
+ | uninstall, remove selftune, clean up, teardown | Uninstall | Workflows/Uninstall.md |
169
+ | repair, rebuild usage, fix skill usage, trustworthy usage, repair-skill-usage | RepairSkillUsage | Workflows/RepairSkillUsage.md |
170
+ | export canonical, canonical export, canonical telemetry, push payload | ExportCanonical | Workflows/ExportCanonical.md |
171
+ | hook, run hook, invoke hook, manual hook, debug hook | Hook | Workflows/Hook.md |
142
172
  | export, dump, jsonl, export sqlite, debug export | Export | _(direct command — no workflow file)_ |
143
173
  | status, health summary, skill health, how are skills, skills doing, run selftune | Status | _(direct command — no workflow file)_ |
144
174
  | last, last session, recent session, what happened, what changed | Last | _(direct command — no workflow file)_ |
@@ -319,6 +349,14 @@ accomplish a task _using_ a skill, route to that skill instead.
319
349
  | `agents/pattern-analyst.md` | Cross-skill conflict detection | Spawn when composability flags conflicts |
320
350
  | `agents/evolution-reviewer.md` | Safety gate for evolution proposals | Spawn before deploying high-stakes evolutions |
321
351
  | `agents/integration-guide.md` | Guided setup for complex projects | Spawn for monorepos, multi-skill setups |
352
+ | `Workflows/Quickstart.md` | Guided onboarding: init, ingest, status | First-time setup for new users |
353
+ | `Workflows/Uninstall.md` | Clean removal of selftune data and config | When removing selftune completely |
354
+ | `Workflows/RepairSkillUsage.md` | Rebuild skill usage from source transcripts | When skill usage data seems inaccurate |
355
+ | `Workflows/Recover.md` | Recover SQLite from legacy/exported JSONL | When rebuilding or backfilling SQLite |
356
+ | `Workflows/Contributions.md` | Manage creator-directed sharing preferences | When approving or revoking creator contribution |
357
+ | `Workflows/CreatorContributions.md` | Manage bundled `selftune.contribute.json` configs | When preparing a skill package for creator contributions |
358
+ | `Workflows/ExportCanonical.md` | Export canonical telemetry for downstream use | When exporting data for external consumption |
359
+ | `Workflows/Hook.md` | Manual hook invocation for debugging | When debugging or testing hooks manually |
322
360
  | `references/logs.md` | Log file formats (telemetry, usage, queries, audit) | When parsing or debugging log files |
323
361
  | `references/grading-methodology.md` | 3-tier grading model, evidence standards | When grading sessions or interpreting grades |
324
362
  | `references/invocation-taxonomy.md` | 4 invocation types, coverage analysis | When analyzing trigger coverage |
@@ -24,6 +24,10 @@ selftune alpha upload [--dry-run]
24
24
  4. Build V2 push envelopes and flush them to the cloud API
25
25
  5. Print a JSON summary with `enrolled`, `prepared`, `sent`, `failed`, `skipped`, and optional `guidance`
26
26
 
27
+ `selftune sync` already triggers an upload cycle automatically when alpha is
28
+ enrolled. Use this workflow when you want a manual upload now or want to see a
29
+ dry-run summary of what SQLite-backed staging would send.
30
+
27
31
  ## Examples
28
32
 
29
33
  Preview the upload: