selftune 0.2.21 → 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 (108) hide show
  1. package/README.md +15 -8
  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/cline/hook.ts +167 -0
  7. package/cli/selftune/adapters/cline/install.ts +197 -0
  8. package/cli/selftune/adapters/codex/hook.ts +296 -0
  9. package/cli/selftune/adapters/codex/install.ts +289 -0
  10. package/cli/selftune/adapters/opencode/hook.ts +222 -0
  11. package/cli/selftune/adapters/opencode/install.ts +543 -0
  12. package/cli/selftune/adapters/pi/hook.ts +273 -0
  13. package/cli/selftune/adapters/pi/install.ts +207 -0
  14. package/cli/selftune/constants.ts +10 -1
  15. package/cli/selftune/dashboard-contract.ts +14 -0
  16. package/cli/selftune/evolution/engines/judge-engine.ts +96 -0
  17. package/cli/selftune/evolution/engines/replay-engine.ts +158 -0
  18. package/cli/selftune/evolution/evidence.ts +2 -6
  19. package/cli/selftune/evolution/evolve-body.ts +73 -20
  20. package/cli/selftune/evolution/validate-body.ts +78 -42
  21. package/cli/selftune/evolution/validate-routing.ts +45 -104
  22. package/cli/selftune/hooks/auto-activate.ts +43 -37
  23. package/cli/selftune/hooks/skill-eval.ts +2 -1
  24. package/cli/selftune/hooks-shared/git-metadata.ts +149 -0
  25. package/cli/selftune/hooks-shared/hook-output.ts +105 -0
  26. package/cli/selftune/hooks-shared/normalize.ts +196 -0
  27. package/cli/selftune/hooks-shared/session-state.ts +76 -0
  28. package/cli/selftune/hooks-shared/skill-paths.ts +50 -0
  29. package/cli/selftune/hooks-shared/stdin-dispatch.ts +59 -0
  30. package/cli/selftune/hooks-shared/types.ts +91 -0
  31. package/cli/selftune/index.ts +76 -6
  32. package/cli/selftune/ingestors/pi-ingest.ts +726 -0
  33. package/cli/selftune/init.ts +11 -1
  34. package/cli/selftune/localdb/direct-write.ts +85 -0
  35. package/cli/selftune/localdb/materialize.ts +6 -7
  36. package/cli/selftune/localdb/queries.ts +126 -0
  37. package/cli/selftune/localdb/schema.ts +38 -0
  38. package/cli/selftune/observability.ts +8 -1
  39. package/cli/selftune/orchestrate.ts +43 -0
  40. package/cli/selftune/registry/client.ts +74 -0
  41. package/cli/selftune/registry/history.ts +54 -0
  42. package/cli/selftune/registry/index.ts +90 -0
  43. package/cli/selftune/registry/install.ts +141 -0
  44. package/cli/selftune/registry/list.ts +44 -0
  45. package/cli/selftune/registry/push.ts +171 -0
  46. package/cli/selftune/registry/rollback.ts +49 -0
  47. package/cli/selftune/registry/status.ts +62 -0
  48. package/cli/selftune/registry/sync.ts +125 -0
  49. package/cli/selftune/repair/skill-usage.ts +4 -1
  50. package/cli/selftune/status.ts +31 -0
  51. package/cli/selftune/sync.ts +127 -23
  52. package/cli/selftune/types.ts +2 -1
  53. package/cli/selftune/utils/jsonl.ts +1 -30
  54. package/cli/selftune/utils/llm-call.ts +99 -34
  55. package/cli/selftune/utils/skill-discovery.ts +22 -0
  56. package/node_modules/@selftune/telemetry-contract/fixtures/evidence-only-push.ts +1 -1
  57. package/node_modules/@selftune/telemetry-contract/fixtures/golden.test.ts +0 -1
  58. package/node_modules/@selftune/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +1 -1
  59. package/node_modules/@selftune/telemetry-contract/package.json +1 -1
  60. package/node_modules/@selftune/telemetry-contract/src/index.ts +1 -0
  61. package/node_modules/@selftune/telemetry-contract/src/schemas.ts +22 -4
  62. package/node_modules/@selftune/telemetry-contract/src/types.ts +1 -12
  63. package/node_modules/@selftune/telemetry-contract/tests/compatibility.test.ts +0 -1
  64. package/package.json +1 -1
  65. package/packages/telemetry-contract/fixtures/evidence-only-push.ts +1 -1
  66. package/packages/telemetry-contract/fixtures/golden.test.ts +0 -1
  67. package/packages/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +1 -1
  68. package/packages/telemetry-contract/package.json +1 -1
  69. package/packages/telemetry-contract/src/index.ts +1 -0
  70. package/packages/telemetry-contract/src/schemas.ts +22 -4
  71. package/packages/telemetry-contract/src/types.ts +1 -12
  72. package/packages/telemetry-contract/tests/compatibility.test.ts +0 -1
  73. package/packages/ui/AGENTS.md +16 -0
  74. package/packages/ui/README.md +1 -1
  75. package/packages/ui/package.json +1 -1
  76. package/packages/ui/src/components/ActivityTimeline.tsx +152 -168
  77. package/packages/ui/src/components/AnalyticsCharts.tsx +344 -0
  78. package/packages/ui/src/components/EvidenceViewer.tsx +153 -443
  79. package/packages/ui/src/components/EvolutionTimeline.tsx +34 -87
  80. package/packages/ui/src/components/InfoTip.tsx +1 -2
  81. package/packages/ui/src/components/InvocationsPanel.tsx +413 -0
  82. package/packages/ui/src/components/JobHistoryTimeline.tsx +156 -0
  83. package/packages/ui/src/components/OrchestrateRunsPanel.tsx +18 -36
  84. package/packages/ui/src/components/OverviewPanels.tsx +652 -0
  85. package/packages/ui/src/components/PipelineStatusBar.tsx +65 -0
  86. package/packages/ui/src/components/SkillReportGuide.tsx +215 -0
  87. package/packages/ui/src/components/SkillReportPanels.tsx +919 -0
  88. package/packages/ui/src/components/SkillsLibrary.tsx +437 -0
  89. package/packages/ui/src/components/index.ts +56 -1
  90. package/packages/ui/src/components/section-cards.tsx +18 -35
  91. package/packages/ui/src/components/skill-health-grid.tsx +47 -37
  92. package/packages/ui/src/lib/constants.tsx +0 -1
  93. package/packages/ui/src/primitives/card.tsx +1 -1
  94. package/packages/ui/src/primitives/checkbox.tsx +1 -1
  95. package/packages/ui/src/primitives/dropdown-menu.tsx +2 -2
  96. package/packages/ui/src/primitives/select.tsx +2 -2
  97. package/packages/ui/src/types.ts +172 -4
  98. package/skill/SKILL.md +26 -2
  99. package/skill/Workflows/Ingest.md +60 -2
  100. package/skill/Workflows/Initialize.md +54 -9
  101. package/skill/Workflows/PlatformHooks.md +109 -0
  102. package/skill/Workflows/Registry.md +99 -0
  103. package/skill/Workflows/Sync.md +3 -1
  104. package/apps/local-dashboard/dist/assets/index-D8O-RG1I.js +0 -60
  105. package/apps/local-dashboard/dist/assets/index-_EcLywDg.css +0 -1
  106. package/apps/local-dashboard/dist/assets/vendor-ui-CGEmUayx.js +0 -12
  107. package/cli/selftune/utils/html.ts +0 -27
  108. package/packages/ui/src/components/RecentActivityFeed.tsx +0 -117
@@ -1,3 +1,4 @@
1
+ import * as React from "react";
1
2
  import {
2
3
  closestCenter,
3
4
  DndContext,
@@ -33,27 +34,7 @@ import {
33
34
  type SortingState,
34
35
  type VisibilityState,
35
36
  } from "@tanstack/react-table";
36
- import {
37
- GripVerticalIcon,
38
- Columns3Icon,
39
- ChevronDownIcon,
40
- ChevronsLeftIcon,
41
- ChevronLeftIcon,
42
- ChevronRightIcon,
43
- ChevronsRightIcon,
44
- ClockIcon,
45
- LayersIcon,
46
- FilterIcon,
47
- CheckCircleIcon,
48
- AlertTriangleIcon,
49
- XCircleIcon,
50
- CircleDotIcon,
51
- HelpCircleIcon,
52
- } from "lucide-react";
53
- import * as React from "react";
54
37
 
55
- import { STATUS_CONFIG } from "../lib/constants";
56
- import { formatRate, timeAgo } from "../lib/format";
57
38
  import { Badge } from "../primitives/badge";
58
39
  import { Button } from "../primitives/button";
59
40
  import { Checkbox } from "../primitives/checkbox";
@@ -76,7 +57,26 @@ import {
76
57
  } from "../primitives/select";
77
58
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../primitives/table";
78
59
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../primitives/tabs";
60
+ import { STATUS_CONFIG } from "../lib/constants";
79
61
  import type { SkillCard, SkillHealthStatus } from "../types";
62
+ import { formatRate, timeAgo } from "../lib/format";
63
+ import {
64
+ GripVerticalIcon,
65
+ Columns3Icon,
66
+ ChevronDownIcon,
67
+ ChevronsLeftIcon,
68
+ ChevronLeftIcon,
69
+ ChevronRightIcon,
70
+ ChevronsRightIcon,
71
+ ClockIcon,
72
+ LayersIcon,
73
+ FilterIcon,
74
+ CheckCircleIcon,
75
+ AlertTriangleIcon,
76
+ XCircleIcon,
77
+ CircleDotIcon,
78
+ HelpCircleIcon,
79
+ } from "lucide-react";
80
80
 
81
81
  // ---------- Drag handle ----------
82
82
 
@@ -152,15 +152,27 @@ function createColumns(
152
152
  enableHiding: false,
153
153
  },
154
154
  {
155
- accessorKey: "scope",
156
- header: "Scope",
155
+ accessorKey: "platforms",
156
+ header: "Platforms",
157
157
  cell: ({ row }) => {
158
- const scope = row.original.scope;
159
- if (!scope) return <span className="text-xs text-muted-foreground">--</span>;
158
+ const platforms = row.original.platforms;
159
+ if (!platforms || platforms.length === 0) {
160
+ const scope = row.original.scope;
161
+ if (!scope) return <span className="text-xs text-muted-foreground">--</span>;
162
+ return (
163
+ <Badge variant="secondary" className="text-[10px]">
164
+ {scope}
165
+ </Badge>
166
+ );
167
+ }
160
168
  return (
161
- <Badge variant="secondary" className="text-[10px]">
162
- {scope}
163
- </Badge>
169
+ <div className="flex flex-wrap gap-1">
170
+ {platforms.map((p) => (
171
+ <Badge key={p} variant="secondary" className="text-[10px]">
172
+ {p}
173
+ </Badge>
174
+ ))}
175
+ </div>
164
176
  );
165
177
  },
166
178
  },
@@ -349,7 +361,7 @@ export function SkillHealthGrid({
349
361
  } else if (activeView === "recent") {
350
362
  filtered = cards
351
363
  .filter((c) => c.lastSeen !== null)
352
- .sort((a: SkillCard, b: SkillCard) => {
364
+ .sort((a, b) => {
353
365
  const aTime = a.lastSeen ? new Date(a.lastSeen).getTime() : 0;
354
366
  const bTime = b.lastSeen ? new Date(b.lastSeen).getTime() : 0;
355
367
  return bTime - aTime;
@@ -380,7 +392,7 @@ export function SkillHealthGrid({
380
392
  columnFilters,
381
393
  pagination,
382
394
  },
383
- getRowId: (row) => row.name,
395
+ getRowId: (row) => row.id ?? row.name,
384
396
  enableRowSelection: true,
385
397
  onRowSelectionChange: setRowSelection,
386
398
  onSortingChange: setSorting,
@@ -395,10 +407,8 @@ export function SkillHealthGrid({
395
407
  getFacetedUniqueValues: getFacetedUniqueValues(),
396
408
  });
397
409
 
398
- const dataIds = React.useMemo<UniqueIdentifier[]>(
399
- () => table.getRowModel().rows.map((r) => r.id),
400
- [table.getRowModel().rows],
401
- );
410
+ const rows = table.getRowModel().rows;
411
+ const dataIds = React.useMemo<UniqueIdentifier[]>(() => rows.map((r) => r.id), [rows]);
402
412
 
403
413
  const isSorted = sorting.length > 0;
404
414
 
@@ -407,7 +417,7 @@ export function SkillHealthGrid({
407
417
  const { active, over } = event;
408
418
  if (active && over && active.id !== over.id) {
409
419
  setData((prev) => {
410
- const ids = prev.map((d) => d.name);
420
+ const ids = prev.map((d) => d.id ?? d.name);
411
421
  const oldIndex = ids.indexOf(active.id as string);
412
422
  const newIndex = ids.indexOf(over.id as string);
413
423
  if (oldIndex === -1 || newIndex === -1) return prev;
@@ -537,8 +547,8 @@ export function SkillHealthGrid({
537
547
  checked={column.getIsVisible()}
538
548
  onCheckedChange={(value) => column.toggleVisibility(!!value)}
539
549
  >
540
- {column.id === "scope"
541
- ? "Scope"
550
+ {column.id === "platforms"
551
+ ? "Platforms"
542
552
  : column.id === "passRate"
543
553
  ? "Pass Rate"
544
554
  : column.id === "uniqueSessions"
@@ -559,7 +569,7 @@ export function SkillHealthGrid({
559
569
  value={activeView}
560
570
  className="relative flex flex-col gap-4 overflow-auto px-4 lg:px-6"
561
571
  >
562
- <div className="overflow-hidden rounded-lg border">
572
+ <div className="overflow-hidden rounded-lg border border-border/15">
563
573
  <DndContext
564
574
  collisionDetection={closestCenter}
565
575
  modifiers={[restrictToVerticalAxis]}
@@ -5,7 +5,6 @@ import {
5
5
  HelpCircleIcon,
6
6
  XCircleIcon,
7
7
  } from "lucide-react";
8
-
9
8
  import type { SkillHealthStatus } from "../types";
10
9
 
11
10
  export const STATUS_CONFIG: Record<
@@ -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-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",
15
+ "group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-muted py-4 text-sm text-card-foreground border border-border/15 shadow-none 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}
@@ -1,7 +1,7 @@
1
1
  import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
2
- import { CheckIcon } from "lucide-react";
3
2
 
4
3
  import { cn } from "../lib/utils";
4
+ import { CheckIcon } from "lucide-react";
5
5
 
6
6
  function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
7
7
  return (
@@ -1,8 +1,8 @@
1
- import { Menu as MenuPrimitive } from "@base-ui/react/menu";
2
- import { ChevronRightIcon, CheckIcon } from "lucide-react";
3
1
  import * as React from "react";
2
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu";
4
3
 
5
4
  import { cn } from "../lib/utils";
5
+ import { ChevronRightIcon, CheckIcon } from "lucide-react";
6
6
 
7
7
  function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
8
8
  return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
@@ -1,8 +1,8 @@
1
- import { Select as SelectPrimitive } from "@base-ui/react/select";
2
- import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react";
3
1
  import * as React from "react";
2
+ import { Select as SelectPrimitive } from "@base-ui/react/select";
4
3
 
5
4
  import { cn } from "../lib/utils";
5
+ import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react";
6
6
 
7
7
  const Select = SelectPrimitive.Root;
8
8
 
@@ -3,8 +3,10 @@
3
3
  export type SkillHealthStatus = "HEALTHY" | "WARNING" | "CRITICAL" | "UNGRADED" | "UNKNOWN";
4
4
 
5
5
  export interface SkillCard {
6
+ id?: string;
6
7
  name: string;
7
8
  scope: string | null;
9
+ platforms: string[];
8
10
  passRate: number | null;
9
11
  checks: number;
10
12
  status: SkillHealthStatus;
@@ -13,6 +15,32 @@ export interface SkillCard {
13
15
  lastSeen: string | null;
14
16
  }
15
17
 
18
+ // -- Job execution types (re-declared for package independence) ---------------
19
+
20
+ export interface JobExecution {
21
+ id: string;
22
+ jobName: string;
23
+ status: "success" | "error";
24
+ startedAt: string;
25
+ durationMs: number;
26
+ metrics: Record<string, unknown>;
27
+ error?: string;
28
+ }
29
+
30
+ export interface JobScheduleEntry {
31
+ name: string;
32
+ schedule: string;
33
+ cronExpression: string;
34
+ lastRunAt: string | null;
35
+ lastRunStatus: "success" | "error" | null;
36
+ lastRunDurationMs: number | null;
37
+ nextRunAt: string;
38
+ }
39
+
40
+ export interface JobScheduleState {
41
+ jobs: JobScheduleEntry[];
42
+ }
43
+
16
44
  // -- Dashboard contract types (re-declared for package independence) ----------
17
45
 
18
46
  export interface EvalSnapshot {
@@ -31,10 +59,6 @@ export interface EvolutionEntry {
31
59
  action: string;
32
60
  details: string;
33
61
  eval_snapshot?: EvalSnapshot | null;
34
- validation_mode?: "structural_guard" | "host_replay" | "llm_judge" | null;
35
- validation_agent?: string | null;
36
- validation_fixture_id?: string | null;
37
- validation_evidence_ref?: string | null;
38
62
  }
39
63
 
40
64
  export interface UnmatchedQuery {
@@ -90,3 +114,147 @@ export interface OrchestrateRunReport {
90
114
  skipped: number;
91
115
  skill_actions: OrchestrateRunSkillAction[];
92
116
  }
117
+
118
+ // -- Overview panel types (shared between local & cloud dashboards) ----------
119
+
120
+ export type AutonomyStatusLevel = "healthy" | "watching" | "needs_review" | "blocked";
121
+
122
+ export interface AutonomyStatus {
123
+ level: AutonomyStatusLevel;
124
+ summary: string;
125
+ skills_observed: number;
126
+ attention_required: number;
127
+ pending_reviews: number;
128
+ }
129
+
130
+ export type TrustBucket = "at_risk" | "improving" | "uncertain" | "stable";
131
+
132
+ export interface TrustWatchlistEntry {
133
+ skill_name: string;
134
+ bucket: TrustBucket;
135
+ pass_rate: number | null;
136
+ reason: string;
137
+ last_seen: string | null;
138
+ }
139
+
140
+ export type AttentionSeverity = "critical" | "warning" | "info";
141
+
142
+ export interface AttentionItem {
143
+ skill_name: string;
144
+ category: string;
145
+ severity: AttentionSeverity;
146
+ reason: string;
147
+ recommended_action: string;
148
+ timestamp: string | null;
149
+ }
150
+
151
+ export type DecisionKind =
152
+ | "proposal_created"
153
+ | "proposal_rejected"
154
+ | "validation_failed"
155
+ | "proposal_deployed"
156
+ | "rollback_triggered"
157
+ | "regression_found";
158
+
159
+ export interface AutonomousDecision {
160
+ skill_name: string;
161
+ kind: DecisionKind;
162
+ summary: string;
163
+ timestamp: string;
164
+ }
165
+
166
+ // -- Trust / skill report types (shared between local & cloud dashboards) ----
167
+
168
+ export type TrustState =
169
+ | "low_sample"
170
+ | "observed"
171
+ | "watch"
172
+ | "validated"
173
+ | "deployed"
174
+ | "rolled_back";
175
+
176
+ export type ObservationKind =
177
+ | "canonical"
178
+ | "repaired_trigger"
179
+ | "repaired_contextual_miss"
180
+ | "legacy_materialized";
181
+
182
+ export type HistoricalContext = "previously_missed";
183
+
184
+ export interface ExampleRow {
185
+ timestamp: string | null;
186
+ session_id: string;
187
+ query_text: string;
188
+ triggered: boolean;
189
+ confidence: number | null;
190
+ invocation_mode: string | null;
191
+ prompt_kind: string | null;
192
+ source: string | null;
193
+ platform: string | null;
194
+ workspace_path: string | null;
195
+ query_origin: "inline_query" | "matched_prompt" | "missing";
196
+ is_system_like: boolean;
197
+ observation_kind: ObservationKind;
198
+ historical_context?: HistoricalContext | null;
199
+ }
200
+
201
+ export interface TrustFields {
202
+ trust: {
203
+ state: TrustState;
204
+ summary: string;
205
+ };
206
+ coverage: {
207
+ checks: number;
208
+ sessions: number;
209
+ workspaces: number;
210
+ first_seen: string | null;
211
+ last_seen: string | null;
212
+ };
213
+ evidence_quality: {
214
+ prompt_link_rate: number;
215
+ inline_query_rate: number;
216
+ user_prompt_rate: number;
217
+ meta_prompt_rate: number;
218
+ internal_prompt_rate: number;
219
+ no_prompt_rate: number;
220
+ system_like_rate: number;
221
+ invocation_mode_coverage: number;
222
+ confidence_coverage: number;
223
+ source_coverage: number;
224
+ scope_coverage: number;
225
+ };
226
+ routing_quality: {
227
+ missed_triggers: number;
228
+ miss_rate: number;
229
+ avg_confidence: number | null;
230
+ confidence_coverage: number;
231
+ low_confidence_rate: number | null;
232
+ };
233
+ evolution_state: {
234
+ has_evidence: boolean;
235
+ has_pending_proposals: boolean;
236
+ latest_action: string | null;
237
+ latest_timestamp: string | null;
238
+ evidence_rows: number;
239
+ evolution_rows: number;
240
+ };
241
+ data_hygiene: {
242
+ naming_variants: string[];
243
+ source_breakdown: Array<{ source: string; count: number }>;
244
+ prompt_kind_breakdown: Array<{ kind: string; count: number }>;
245
+ observation_breakdown: Array<{ kind: ObservationKind; count: number }>;
246
+ raw_checks: number;
247
+ operational_checks: number;
248
+ internal_prompt_rows: number;
249
+ internal_prompt_rate: number;
250
+ legacy_rows: number;
251
+ legacy_rate: number;
252
+ repaired_rows: number;
253
+ repaired_rate: number;
254
+ };
255
+ examples: {
256
+ good: ExampleRow[];
257
+ missed: ExampleRow[];
258
+ noisy: ExampleRow[];
259
+ };
260
+ }
package/skill/SKILL.md CHANGED
@@ -73,6 +73,7 @@ selftune ingest claude [--since DATE] [--dry-run] [--force] [--verbose]
73
73
  selftune ingest codex # (experimental)
74
74
  selftune ingest opencode # (experimental)
75
75
  selftune ingest openclaw [--agents-dir PATH] [--since DATE] [--dry-run] [--force] [--verbose] # (experimental)
76
+ selftune ingest pi [--sessions-dir PATH] [--since DATE] [--dry-run] [--force] [--verbose] # (experimental)
76
77
  selftune ingest wrap-codex -- <codex args> # (experimental)
77
78
 
78
79
  # Grade group
@@ -109,7 +110,7 @@ selftune export [TABLE...] [--output/-o DIR] [--since DATE]
109
110
 
110
111
  # Autonomous loop
111
112
  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
+ selftune sync [--since DATE] [--dry-run] [--force] [--no-claude] [--no-codex] [--no-opencode] [--no-openclaw] [--no-pi] [--no-repair] [--json]
113
114
 
114
115
  # Discovery + badges
115
116
  selftune workflows [--skill NAME] [--skill-path PATH] [--min-occurrences N] [--window N] [--json] [save --skill NAME --skill-path PATH]
@@ -125,6 +126,25 @@ selftune uninstall [--dry-run] [--keep-logs] [--npm-uninstall]
125
126
  # Hook dispatch (for debugging/manual invocation)
126
127
  selftune hook <name> # prompt-log | session-stop | skill-eval | auto-activate | skill-change-guard | evolution-guard
127
128
 
129
+ # Platform hooks (non-Claude-Code agents)
130
+ selftune codex hook
131
+ selftune codex install [--dry-run] [--uninstall]
132
+ selftune opencode hook
133
+ selftune opencode install [--dry-run] [--uninstall]
134
+ selftune cline hook
135
+ selftune cline install [--dry-run] [--uninstall]
136
+ selftune pi hook
137
+ selftune pi install [--dry-run] [--uninstall]
138
+
139
+ # Registry (team skill distribution)
140
+ selftune registry push [name] [--version=<semver>] [--summary=<text>]
141
+ selftune registry install <name> [--global]
142
+ selftune registry sync
143
+ selftune registry status
144
+ selftune registry rollback <name> [--to=<version>] [--reason=<text>]
145
+ selftune registry history <name>
146
+ selftune registry list
147
+
128
148
  # Alpha enrollment (device-code flow — browser opens automatically)
129
149
  selftune init --alpha --alpha-email <email>
130
150
  selftune alpha upload [--dry-run]
@@ -143,7 +163,7 @@ selftune status # shows c
143
163
  | evolve rollback, undo, restore, revert evolution, go back, undo last change | Rollback | Workflows/Rollback.md |
144
164
  | watch, monitor, regression, post-deploy, keep an eye on | Watch | Workflows/Watch.md |
145
165
  | doctor, health, hooks, broken, diagnose, not working, something wrong | Doctor | Workflows/Doctor.md |
146
- | ingest, import, codex logs, opencode, openclaw, wrap codex | Ingest | Workflows/Ingest.md |
166
+ | ingest, import, codex logs, opencode, openclaw, pi, wrap codex | Ingest | Workflows/Ingest.md |
147
167
  | replay, backfill, claude transcripts, historical sessions | Replay | Workflows/Replay.md |
148
168
  | 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
169
  | 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 |
@@ -169,6 +189,8 @@ selftune status # shows c
169
189
  | repair, rebuild usage, fix skill usage, trustworthy usage, repair-skill-usage | RepairSkillUsage | Workflows/RepairSkillUsage.md |
170
190
  | export canonical, canonical export, canonical telemetry, push payload | ExportCanonical | Workflows/ExportCanonical.md |
171
191
  | hook, run hook, invoke hook, manual hook, debug hook | Hook | Workflows/Hook.md |
192
+ | codex hooks, codex install, codex setup, opencode hooks, opencode install, opencode setup, cline hooks, cline install, cline setup, pi hooks, pi install, pi setup, multi-platform, platform hooks, non-claude hooks, multiple agents, multi-agent | PlatformHooks | Workflows/PlatformHooks.md |
193
+ | registry, distribute, push skill, install skill, sync skills, team skills, version control skills, rollback skill | Registry | Workflows/Registry.md |
172
194
  | export, dump, jsonl, export sqlite, debug export | Export | _(direct command — no workflow file)_ |
173
195
  | status, health summary, skill health, how are skills, skills doing, run selftune | Status | _(direct command — no workflow file)_ |
174
196
  | last, last session, recent session, what happened, what changed | Last | _(direct command — no workflow file)_ |
@@ -357,10 +379,12 @@ accomplish a task _using_ a skill, route to that skill instead.
357
379
  | `Workflows/CreatorContributions.md` | Manage bundled `selftune.contribute.json` configs | When preparing a skill package for creator contributions |
358
380
  | `Workflows/ExportCanonical.md` | Export canonical telemetry for downstream use | When exporting data for external consumption |
359
381
  | `Workflows/Hook.md` | Manual hook invocation for debugging | When debugging or testing hooks manually |
382
+ | `Workflows/PlatformHooks.md` | Non-Claude-Code platform hook install/config | When setting up Codex, OpenCode, Cline, or Pi hooks |
360
383
  | `references/logs.md` | Log file formats (telemetry, usage, queries, audit) | When parsing or debugging log files |
361
384
  | `references/grading-methodology.md` | 3-tier grading model, evidence standards | When grading sessions or interpreting grades |
362
385
  | `references/invocation-taxonomy.md` | 4 invocation types, coverage analysis | When analyzing trigger coverage |
363
386
  | `references/interactive-config.md` | Pre-flight config pattern, model tiers | Before running mutating workflows |
364
387
  | `references/setup-patterns.md` | Platform-specific setup patterns | During complex setup scenarios |
388
+ | `Workflows/Registry.md` | Registry — team skill distribution commands | When routing to registry workflow |
365
389
  | `settings_snippet.json` | Claude Code hook configuration template | During initialization |
366
390
  | `assets/*.json` | Config templates (activation rules, settings) | During initialization |
@@ -3,8 +3,8 @@
3
3
  > **Note:** Claude Code is the fully supported platform. Codex, OpenCode, and OpenClaw adapters are experimental and may have gaps.
4
4
 
5
5
  Import sessions from agent platforms into the shared selftune log format.
6
- Covers five sub-commands: `ingest claude`, `ingest codex`, `ingest opencode`,
7
- `ingest openclaw`, and `ingest wrap-codex`.
6
+ Covers six sub-commands: `ingest claude`, `ingest codex`, `ingest opencode`,
7
+ `ingest openclaw`, `ingest pi`, and `ingest wrap-codex`.
8
8
 
9
9
  ## When to Use Each
10
10
 
@@ -14,6 +14,7 @@ Covers five sub-commands: `ingest claude`, `ingest codex`, `ingest opencode`,
14
14
  | `ingest codex` | Codex | Batch | Import existing Codex rollout logs |
15
15
  | `ingest opencode` | OpenCode | Batch | Import existing OpenCode sessions |
16
16
  | `ingest openclaw` | OpenClaw | Batch | Import existing OpenClaw agent sessions |
17
+ | `ingest pi` | Pi | Batch | Import existing Pi agent sessions |
17
18
  | `ingest wrap-codex` | Codex | Real-time | Wrap `codex exec` to capture telemetry live |
18
19
 
19
20
  ---
@@ -200,6 +201,55 @@ Writes to:
200
201
 
201
202
  ---
202
203
 
204
+ ## ingest pi
205
+
206
+ Batch ingest Pi agent session histories into the shared JSONL schema.
207
+
208
+ ### Default Command
209
+
210
+ ```bash
211
+ selftune ingest pi
212
+ ```
213
+
214
+ ### Options
215
+
216
+ | Flag | Description |
217
+ | ----------------------- | ------------------------------------------------------------------ |
218
+ | `--sessions-dir <path>` | Override default `~/.pi/agent/sessions/` directory |
219
+ | `--since <date>` | Only ingest sessions modified after this date (e.g., `2026-01-01`) |
220
+ | `--dry-run` | Show what would be ingested without writing to logs |
221
+ | `--force` | Re-ingest all sessions, ignoring the marker file |
222
+ | `--verbose` / `-v` | Show per-session progress during ingestion |
223
+
224
+ ### Source
225
+
226
+ Reads from `~/.pi/agent/sessions/`. Each session file contains Pi agent
227
+ conversation history in JSONL format.
228
+
229
+ ### Output
230
+
231
+ Writes to:
232
+
233
+ - `~/.claude/all_queries_log.jsonl` -- extracted user queries
234
+ - `~/.claude/session_telemetry_log.jsonl` -- per-session metrics with `source: "pi"`
235
+ - `~/.claude/skill_usage_log.jsonl` -- skill triggers with `source: "pi"`
236
+
237
+ ### Steps
238
+
239
+ 1. Run `selftune ingest pi --dry-run` to preview what would be ingested
240
+ 2. Run `selftune ingest pi` to ingest all sessions
241
+ 3. Run `selftune doctor` to confirm logs are healthy
242
+ 4. Run `selftune eval generate --list-skills` to see if the ingested sessions appear
243
+
244
+ ### Notes
245
+
246
+ - Idempotent: uses a marker file to track which sessions have already been ingested.
247
+ Safe to run repeatedly. Use `--force` to re-ingest everything.
248
+ - Skill detection heuristic: identifies skills by checking for `SKILL.md` file reads in
249
+ tool calls and by matching known skill names in assistant text content.
250
+
251
+ ---
252
+
203
253
  ## ingest wrap-codex
204
254
 
205
255
  Wrap `codex exec` with real-time telemetry capture. Drop-in replacement
@@ -269,6 +319,14 @@ through hooks.
269
319
 
270
320
  > Run `selftune ingest openclaw --since 2026-02-01` with an appropriate date.
271
321
 
322
+ **"Ingest Pi sessions"**
323
+
324
+ > Run `selftune ingest pi`. Reads from `~/.pi/agent/sessions/` automatically.
325
+
326
+ **"Import only recent Pi sessions"**
327
+
328
+ > Run `selftune ingest pi --since 2026-02-01` with an appropriate date.
329
+
272
330
  **"Run codex through selftune"**
273
331
 
274
332
  > Use `selftune ingest wrap-codex -- <codex args>` instead of `codex exec <args>` directly.
@@ -6,7 +6,8 @@ Bootstrap selftune for first-time use or after changing environments.
6
6
 
7
7
  - The user asks to set up selftune, configure selftune, or initialize selftune
8
8
  - The agent detects `~/.selftune/config.json` does not exist
9
- - The user has switched agent platforms (Claude Code, Codex, OpenCode)
9
+ - The user has switched agent platforms (Claude Code, Codex, OpenCode, Pi)
10
+ - The user wants to add hooks for additional platforms (multi-agent setup)
10
11
 
11
12
  ## Default Command
12
13
 
@@ -20,7 +21,7 @@ selftune init --no-alpha [--force]
20
21
 
21
22
  | Flag | Description | Default |
22
23
  | ------------------------- | ------------------------------------------------------------------------- | ------------- |
23
- | `--agent <type>` | Agent platform: `claude_code`, `codex`, `opencode`, `openclaw` | Auto-detected |
24
+ | `--agent <type>` | Agent platform: `claude_code`, `codex`, `opencode`, `openclaw`, `pi` | Auto-detected |
24
25
  | `--cli-path <path>` | Override auto-detected CLI entry-point path | Auto-detected |
25
26
  | `--force` | Reinitialize even if config already exists | Off |
26
27
  | `--no-sync` | Skip historical transcript backfill during init | Sync on |
@@ -136,15 +137,52 @@ Code subagent calls stay up to date.
136
137
  | `PostToolUse` (Bash) | `hooks/commit-track.ts` | Track git commits for session traceability | Fast-path: skips non-git Bash commands |
137
138
  | `Stop` | `hooks/session-stop.ts` | Capture session telemetry | Runs async (non-blocking), 60s timeout |
138
139
 
139
- **Codex agents:**
140
+ ### 4b. Multi-Platform Hooks
140
141
 
141
- - Use `selftune ingest wrap-codex` for real-time telemetry capture (see `Workflows/Ingest.md`)
142
- - Or batch-ingest existing sessions with `selftune ingest codex`
142
+ After Claude Code hooks are installed, check whether the user has **other** agent
143
+ CLIs available. Run these checks:
143
144
 
144
- **OpenCode agents:**
145
+ ```bash
146
+ which codex 2>/dev/null && echo "codex available"
147
+ which opencode 2>/dev/null && echo "opencode available"
148
+ ls ~/Documents/Cline/Hooks/ 2>/dev/null && echo "cline available"
149
+ ls ~/.pi/agent/ 2>/dev/null && echo "pi available"
150
+ ```
151
+
152
+ If **any** additional platforms are detected, use `AskUserQuestion` listing only
153
+ the platforms that were actually found:
154
+
155
+ > I detected these agent platforms in addition to your primary one:
156
+ > - [list only detected platforms, e.g. "Codex", "OpenCode"]
157
+ >
158
+ > Would you like to install selftune hooks for any of them? This enables
159
+ > real-time skill tracking across all your agents.
160
+
161
+ Options:
162
+ - `Yes — install hooks for all detected platforms`
163
+ - `Let me pick — show me the list` (then present only the detected platforms)
164
+ - `No — skip for now` (they can always run `selftune <platform> install` later)
165
+
166
+ For each platform the user selects, run the install command:
167
+
168
+ ```bash
169
+ selftune codex install # writes hooks.json entries
170
+ selftune opencode install # writes shell shim + config entries
171
+ selftune cline install # creates hook scripts
172
+ selftune pi install # creates extension hook scripts
173
+ ```
174
+
175
+ Use `--dry-run` first if the user wants to preview. See `Workflows/PlatformHooks.md`
176
+ for platform-specific details.
177
+
178
+ **Batch ingest** fallback for platforms without real-time hooks or to backfill history:
145
179
 
146
- - Use `selftune ingest opencode` to import sessions from the SQLite database
147
- - See `Workflows/Ingest.md` for details
180
+ ```bash
181
+ selftune ingest codex # import Codex rollout sessions
182
+ selftune ingest opencode # import OpenCode sessions from SQLite
183
+ selftune ingest openclaw # import OpenClaw sessions
184
+ selftune ingest pi # import Pi sessions
185
+ ```
148
186
 
149
187
  ### 5. Initialize Memory Directory
150
188
 
@@ -186,7 +224,7 @@ reported issues before proceeding.
186
224
 
187
225
  Init automatically runs `selftune sync` to backfill existing session
188
226
  transcripts into the SQLite database. This replays Claude Code transcripts,
189
- Codex rollouts, OpenCode sessions, and OpenClaw sessions so the eval set
227
+ Codex rollouts, OpenCode sessions, OpenClaw sessions, and Pi sessions so the eval set
190
228
  and evolution pipeline have data to work with immediately.
191
229
 
192
230
  The sync step is fail-open — if it encounters errors, init continues.
@@ -387,6 +425,13 @@ retrying with `selftune init --alpha --alpha-email <email> --force`.
387
425
  > and optional display name in chat, then run `selftune init --alpha --alpha-email ...`.
388
426
  > The browser opens automatically for approval. No manual key management needed.
389
427
 
428
+ **User uses multiple agents (Claude Code + Codex, etc.)**
429
+
430
+ > Run `selftune init` for the primary agent, then offer to install hooks for
431
+ > additional detected platforms. Run `selftune codex install`, `selftune opencode install`,
432
+ > `selftune cline install`, or `selftune pi install` as needed. All platforms
433
+ > write to the same shared log schema — no extra config required.
434
+
390
435
  **Hooks not capturing data**
391
436
 
392
437
  > Run `selftune doctor` to check hook installation. Parse the JSON output