selftune 0.2.18 → 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 (65) hide show
  1. package/README.md +9 -4
  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/stage-canonical.ts +7 -6
  8. package/cli/selftune/constants.ts +10 -0
  9. package/cli/selftune/contribute/contribute.ts +30 -2
  10. package/cli/selftune/contribution-config.ts +249 -0
  11. package/cli/selftune/contribution-relay.ts +177 -0
  12. package/cli/selftune/contribution-signals.ts +219 -0
  13. package/cli/selftune/contribution-staging.ts +147 -0
  14. package/cli/selftune/contributions.ts +532 -0
  15. package/cli/selftune/creator-contributions.ts +333 -0
  16. package/cli/selftune/dashboard-contract.ts +205 -1
  17. package/cli/selftune/dashboard-server.ts +45 -11
  18. package/cli/selftune/eval/family-overlap.ts +395 -0
  19. package/cli/selftune/eval/hooks-to-evals.ts +182 -28
  20. package/cli/selftune/eval/synthetic-evals.ts +298 -11
  21. package/cli/selftune/export.ts +2 -2
  22. package/cli/selftune/index.ts +41 -5
  23. package/cli/selftune/ingestors/codex-rollout.ts +31 -35
  24. package/cli/selftune/ingestors/codex-wrapper.ts +32 -24
  25. package/cli/selftune/localdb/db.ts +2 -2
  26. package/cli/selftune/localdb/queries.ts +701 -30
  27. package/cli/selftune/localdb/schema.ts +20 -0
  28. package/cli/selftune/recover.ts +153 -0
  29. package/cli/selftune/repair/skill-usage.ts +363 -4
  30. package/cli/selftune/routes/actions.ts +35 -1
  31. package/cli/selftune/routes/analytics.ts +14 -0
  32. package/cli/selftune/routes/index.ts +1 -0
  33. package/cli/selftune/routes/overview.ts +112 -4
  34. package/cli/selftune/routes/skill-report.ts +569 -10
  35. package/cli/selftune/status.ts +81 -2
  36. package/cli/selftune/sync.ts +56 -2
  37. package/cli/selftune/trust-model.ts +66 -0
  38. package/cli/selftune/types.ts +49 -0
  39. package/cli/selftune/utils/skill-detection.ts +43 -0
  40. package/cli/selftune/watchlist.ts +65 -0
  41. package/package.json +1 -1
  42. package/packages/ui/src/components/ActivityTimeline.tsx +165 -150
  43. package/packages/ui/src/components/EvidenceViewer.tsx +335 -144
  44. package/packages/ui/src/components/EvolutionTimeline.tsx +58 -28
  45. package/packages/ui/src/components/OrchestrateRunsPanel.tsx +33 -16
  46. package/packages/ui/src/components/RecentActivityFeed.tsx +72 -41
  47. package/packages/ui/src/components/section-cards.tsx +12 -9
  48. package/packages/ui/src/primitives/card.tsx +1 -1
  49. package/skill/SKILL.md +11 -1
  50. package/skill/Workflows/AlphaUpload.md +4 -0
  51. package/skill/Workflows/Composability.md +64 -0
  52. package/skill/Workflows/Contribute.md +6 -3
  53. package/skill/Workflows/Contributions.md +97 -0
  54. package/skill/Workflows/CreatorContributions.md +74 -0
  55. package/skill/Workflows/Dashboard.md +31 -0
  56. package/skill/Workflows/Evals.md +57 -8
  57. package/skill/Workflows/Ingest.md +7 -0
  58. package/skill/Workflows/Initialize.md +20 -1
  59. package/skill/Workflows/Recover.md +84 -0
  60. package/skill/Workflows/RepairSkillUsage.md +12 -4
  61. package/skill/Workflows/Sync.md +18 -12
  62. package/apps/local-dashboard/dist/assets/index-BMIS6uUh.css +0 -2
  63. package/apps/local-dashboard/dist/assets/index-DOu3iLD9.js +0 -16
  64. package/apps/local-dashboard/dist/assets/vendor-table-pHbDxq36.js +0 -8
  65. package/apps/local-dashboard/dist/assets/vendor-ui-DIwlrGlb.js +0 -12
@@ -21,15 +21,179 @@ export function ActivityPanel({
21
21
  pendingProposals,
22
22
  unmatchedQueries,
23
23
  onSelectProposal,
24
+ embedded = false,
24
25
  }: {
25
26
  evolution: EvolutionEntry[];
26
27
  pendingProposals: PendingProposal[];
27
28
  unmatchedQueries: UnmatchedQuery[];
28
29
  onSelectProposal?: (skillName: string, proposalId: string) => void;
30
+ embedded?: boolean;
29
31
  }) {
30
32
  const hasActivity =
31
33
  evolution.length > 0 || pendingProposals.length > 0 || unmatchedQueries.length > 0;
32
34
 
35
+ const content = hasActivity ? (
36
+ <Tabs
37
+ defaultValue={
38
+ pendingProposals.length > 0 ? "pending" : evolution.length > 0 ? "timeline" : "unmatched"
39
+ }
40
+ >
41
+ <TooltipProvider>
42
+ <TabsList className="w-full">
43
+ {pendingProposals.length > 0 && (
44
+ <Tooltip>
45
+ <TooltipTrigger
46
+ render={
47
+ <TabsTrigger
48
+ value="pending"
49
+ className="flex-1 gap-1.5"
50
+ aria-label={`Pending proposals (${pendingProposals.length})`}
51
+ />
52
+ }
53
+ >
54
+ <GitPullRequestArrowIcon className="size-3.5" />
55
+ <Badge variant="secondary" className="h-4 px-1 text-[10px]">
56
+ {pendingProposals.length}
57
+ </Badge>
58
+ </TooltipTrigger>
59
+ <TooltipContent>Undeployed proposals</TooltipContent>
60
+ </Tooltip>
61
+ )}
62
+ <Tooltip>
63
+ <TooltipTrigger
64
+ render={<TabsTrigger value="timeline" className="flex-1" aria-label="Timeline" />}
65
+ >
66
+ <ClockIcon className="size-3.5" />
67
+ </TooltipTrigger>
68
+ <TooltipContent>Timeline</TooltipContent>
69
+ </Tooltip>
70
+ {unmatchedQueries.length > 0 && (
71
+ <Tooltip>
72
+ <TooltipTrigger
73
+ render={
74
+ <TabsTrigger
75
+ value="unmatched"
76
+ className="flex-1 gap-1.5"
77
+ aria-label={`Unmatched queries (${unmatchedQueries.length})`}
78
+ />
79
+ }
80
+ >
81
+ <SearchXIcon className="size-3.5" />
82
+ <Badge variant="destructive" className="h-4 px-1 text-[10px]">
83
+ {unmatchedQueries.length}
84
+ </Badge>
85
+ </TooltipTrigger>
86
+ <TooltipContent>Unmatched queries</TooltipContent>
87
+ </Tooltip>
88
+ )}
89
+ </TabsList>
90
+ </TooltipProvider>
91
+
92
+ {pendingProposals.length > 0 && (
93
+ <TabsContent value="pending" className="mt-4 space-y-3">
94
+ {pendingProposals.slice(0, 10).map((p) => (
95
+ <button
96
+ key={p.proposal_id}
97
+ type="button"
98
+ onClick={() => {
99
+ if (p.skill_name && onSelectProposal) onSelectProposal(p.skill_name, p.proposal_id);
100
+ }}
101
+ disabled={!p.skill_name || !onSelectProposal}
102
+ className="flex w-full gap-3 rounded-md p-1.5 text-left transition-colors enabled:hover:bg-accent/40 disabled:cursor-default"
103
+ >
104
+ <div className="mt-1 size-2 shrink-0 rounded-full bg-primary-accent" />
105
+ <div className="flex-1 min-w-0 space-y-1">
106
+ <div className="flex items-center gap-2">
107
+ <Badge variant={ACTION_VARIANT[p.action] ?? "secondary"} className="text-[10px]">
108
+ {p.action}
109
+ </Badge>
110
+ <span className="text-[10px] text-slate-500 font-mono">
111
+ {timeAgo(p.timestamp)}
112
+ </span>
113
+ </div>
114
+ <p className="text-xs text-muted-foreground line-clamp-2">{p.details}</p>
115
+ {p.skill_name && (
116
+ <span className="text-[10px] text-muted-foreground/60 font-mono">
117
+ {p.skill_name} · #{p.proposal_id.slice(0, 8)}
118
+ </span>
119
+ )}
120
+ </div>
121
+ </button>
122
+ ))}
123
+ </TabsContent>
124
+ )}
125
+
126
+ <TabsContent value="timeline" className="mt-4 space-y-3">
127
+ {evolution.slice(0, 30).map((entry, i) => (
128
+ <button
129
+ key={`${entry.proposal_id}-${i}`}
130
+ type="button"
131
+ onClick={() => {
132
+ if (entry.skill_name && onSelectProposal)
133
+ onSelectProposal(entry.skill_name, entry.proposal_id);
134
+ }}
135
+ disabled={!entry.skill_name || !onSelectProposal}
136
+ className="flex w-full gap-3 rounded-md p-1.5 text-left transition-colors enabled:hover:bg-accent/40 disabled:cursor-default"
137
+ >
138
+ <div
139
+ className={`mt-1 size-2 shrink-0 rounded-full ${
140
+ entry.action === "deployed"
141
+ ? "bg-primary"
142
+ : entry.action === "rejected" || entry.action === "rolled_back"
143
+ ? "bg-destructive"
144
+ : entry.action === "validated"
145
+ ? "bg-primary-accent"
146
+ : "bg-primary-accent"
147
+ }`}
148
+ />
149
+ <div className="flex-1 min-w-0 space-y-1">
150
+ <div className="flex items-center gap-2">
151
+ <Badge
152
+ variant={ACTION_VARIANT[entry.action] ?? "secondary"}
153
+ className="text-[10px]"
154
+ >
155
+ {entry.action}
156
+ </Badge>
157
+ <span className="text-xs text-muted-foreground font-mono">
158
+ {timeAgo(entry.timestamp)}
159
+ </span>
160
+ </div>
161
+ <p className="text-xs text-muted-foreground line-clamp-2">{entry.details}</p>
162
+ <span className="text-[10px] text-muted-foreground/60 font-mono">
163
+ {entry.skill_name ? `${entry.skill_name} · ` : ""}#{entry.proposal_id.slice(0, 8)}
164
+ </span>
165
+ </div>
166
+ </button>
167
+ ))}
168
+ {evolution.length === 0 && (
169
+ <p className="text-sm text-muted-foreground py-4 text-center">No timeline events</p>
170
+ )}
171
+ </TabsContent>
172
+
173
+ {unmatchedQueries.length > 0 && (
174
+ <TabsContent value="unmatched" className="mt-4 space-y-2">
175
+ {unmatchedQueries.slice(0, 15).map((q, i) => (
176
+ <div key={`${q.session_id}-${i}`} className="flex gap-3">
177
+ <div className="mt-1 size-2 shrink-0 rounded-full bg-muted-foreground/40" />
178
+ <div className="flex-1 min-w-0 space-y-0.5">
179
+ <span className="font-mono text-xs text-muted-foreground">
180
+ {timeAgo(q.timestamp)}
181
+ </span>
182
+ <p className="line-clamp-2 font-mono text-xs text-foreground/80">{q.query}</p>
183
+ </div>
184
+ </div>
185
+ ))}
186
+ </TabsContent>
187
+ )}
188
+ </Tabs>
189
+ ) : (
190
+ <p className="py-6 text-center text-sm text-muted-foreground">No recent activity</p>
191
+ );
192
+
193
+ if (embedded) {
194
+ return <div>{content}</div>;
195
+ }
196
+
33
197
  if (!hasActivity) {
34
198
  return (
35
199
  <Card>
@@ -55,156 +219,7 @@ export function ActivityPanel({
55
219
  </CardTitle>
56
220
  <CardDescription>Recent evolution events and queries</CardDescription>
57
221
  </CardHeader>
58
- <CardContent>
59
- <Tabs
60
- defaultValue={
61
- pendingProposals.length > 0
62
- ? "pending"
63
- : evolution.length > 0
64
- ? "timeline"
65
- : "unmatched"
66
- }
67
- >
68
- <TooltipProvider>
69
- <TabsList className="w-full">
70
- {pendingProposals.length > 0 && (
71
- <Tooltip>
72
- <TooltipTrigger
73
- render={<TabsTrigger value="pending" className="flex-1 gap-1.5" />}
74
- >
75
- <GitPullRequestArrowIcon className="size-3.5" />
76
- <Badge variant="secondary" className="h-4 px-1 text-[10px]">
77
- {pendingProposals.length}
78
- </Badge>
79
- </TooltipTrigger>
80
- <TooltipContent>Undeployed proposals</TooltipContent>
81
- </Tooltip>
82
- )}
83
- <Tooltip>
84
- <TooltipTrigger render={<TabsTrigger value="timeline" className="flex-1" />}>
85
- <ClockIcon className="size-3.5" />
86
- </TooltipTrigger>
87
- <TooltipContent>Timeline</TooltipContent>
88
- </Tooltip>
89
- {unmatchedQueries.length > 0 && (
90
- <Tooltip>
91
- <TooltipTrigger
92
- render={<TabsTrigger value="unmatched" className="flex-1 gap-1.5" />}
93
- >
94
- <SearchXIcon className="size-3.5" />
95
- <Badge variant="destructive" className="h-4 px-1 text-[10px]">
96
- {unmatchedQueries.length}
97
- </Badge>
98
- </TooltipTrigger>
99
- <TooltipContent>Unmatched queries</TooltipContent>
100
- </Tooltip>
101
- )}
102
- </TabsList>
103
- </TooltipProvider>
104
-
105
- {pendingProposals.length > 0 && (
106
- <TabsContent value="pending" className="mt-4 space-y-3">
107
- {pendingProposals.slice(0, 10).map((p) => (
108
- <button
109
- key={p.proposal_id}
110
- type="button"
111
- onClick={() => {
112
- if (p.skill_name && onSelectProposal)
113
- onSelectProposal(p.skill_name, p.proposal_id);
114
- }}
115
- disabled={!p.skill_name || !onSelectProposal}
116
- className="flex w-full gap-3 rounded-md p-1.5 text-left transition-colors enabled:hover:bg-accent/40 disabled:cursor-default"
117
- >
118
- <div className="mt-1 size-2 shrink-0 rounded-full bg-amber-400" />
119
- <div className="flex-1 min-w-0 space-y-1">
120
- <div className="flex items-center gap-2">
121
- <Badge
122
- variant={ACTION_VARIANT[p.action] ?? "secondary"}
123
- className="text-[10px]"
124
- >
125
- {p.action}
126
- </Badge>
127
- <span className="text-xs text-muted-foreground font-mono">
128
- {timeAgo(p.timestamp)}
129
- </span>
130
- </div>
131
- <p className="text-xs text-muted-foreground line-clamp-2">{p.details}</p>
132
- {p.skill_name && (
133
- <span className="text-[10px] text-muted-foreground/60 font-mono">
134
- {p.skill_name} · #{p.proposal_id.slice(0, 8)}
135
- </span>
136
- )}
137
- </div>
138
- </button>
139
- ))}
140
- </TabsContent>
141
- )}
142
-
143
- <TabsContent value="timeline" className="mt-4 space-y-3">
144
- {evolution.slice(0, 30).map((entry, i) => (
145
- <button
146
- key={`${entry.proposal_id}-${i}`}
147
- type="button"
148
- onClick={() => {
149
- if (entry.skill_name && onSelectProposal)
150
- onSelectProposal(entry.skill_name, entry.proposal_id);
151
- }}
152
- disabled={!entry.skill_name || !onSelectProposal}
153
- className="flex w-full gap-3 rounded-md p-1.5 text-left transition-colors enabled:hover:bg-accent/40 disabled:cursor-default"
154
- >
155
- <div
156
- className={`mt-1 size-2 shrink-0 rounded-full ${
157
- entry.action === "deployed"
158
- ? "bg-emerald-500"
159
- : entry.action === "rejected" || entry.action === "rolled_back"
160
- ? "bg-red-500"
161
- : entry.action === "validated"
162
- ? "bg-amber-400"
163
- : "bg-primary-accent"
164
- }`}
165
- />
166
- <div className="flex-1 min-w-0 space-y-1">
167
- <div className="flex items-center gap-2">
168
- <Badge
169
- variant={ACTION_VARIANT[entry.action] ?? "secondary"}
170
- className="text-[10px]"
171
- >
172
- {entry.action}
173
- </Badge>
174
- <span className="text-xs text-muted-foreground font-mono">
175
- {timeAgo(entry.timestamp)}
176
- </span>
177
- </div>
178
- <p className="text-xs text-muted-foreground line-clamp-2">{entry.details}</p>
179
- <span className="text-[10px] text-muted-foreground/60 font-mono">
180
- {entry.skill_name ? `${entry.skill_name} · ` : ""}#
181
- {entry.proposal_id.slice(0, 8)}
182
- </span>
183
- </div>
184
- </button>
185
- ))}
186
- {evolution.length === 0 && (
187
- <p className="text-sm text-muted-foreground text-center py-4">No timeline events</p>
188
- )}
189
- </TabsContent>
190
-
191
- {unmatchedQueries.length > 0 && (
192
- <TabsContent value="unmatched" className="mt-4 space-y-2">
193
- {unmatchedQueries.slice(0, 15).map((q, i) => (
194
- <div key={`${q.session_id}-${i}`} className="flex gap-3">
195
- <div className="mt-1 size-2 shrink-0 rounded-full bg-muted-foreground/40" />
196
- <div className="flex-1 min-w-0 space-y-0.5">
197
- <span className="text-xs text-muted-foreground font-mono">
198
- {timeAgo(q.timestamp)}
199
- </span>
200
- <p className="text-xs font-mono text-foreground/80 line-clamp-2">{q.query}</p>
201
- </div>
202
- </div>
203
- ))}
204
- </TabsContent>
205
- )}
206
- </Tabs>
207
- </CardContent>
222
+ <CardContent>{content}</CardContent>
208
223
  </Card>
209
224
  );
210
225
  }