selftune 0.2.23 → 0.2.25

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 (219) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +93 -15
  3. package/apps/local-dashboard/dist/assets/index-DgY2KGP-.css +1 -0
  4. package/apps/local-dashboard/dist/assets/index-Dhgv5BQO.js +15 -0
  5. package/apps/local-dashboard/dist/assets/vendor-react-C5oyHiV1.js +11 -0
  6. package/apps/local-dashboard/dist/assets/{vendor-table-BIiI3YhS.js → vendor-table-Bc_bbKd8.js} +1 -1
  7. package/apps/local-dashboard/dist/assets/vendor-ui-B3BPIYy7.js +1 -0
  8. package/apps/local-dashboard/dist/index.html +5 -5
  9. package/cli/selftune/adapters/codex/install.ts +310 -78
  10. package/cli/selftune/adapters/opencode/install.ts +3 -4
  11. package/cli/selftune/alpha-upload/build-payloads.ts +3 -3
  12. package/cli/selftune/alpha-upload/stage-canonical.ts +17 -11
  13. package/cli/selftune/auto-update.ts +200 -8
  14. package/cli/selftune/canonical-export.ts +55 -25
  15. package/cli/selftune/command-surface.ts +397 -0
  16. package/cli/selftune/contribute/contribute.ts +64 -13
  17. package/cli/selftune/contribution-config.ts +57 -3
  18. package/cli/selftune/contribution-preferences.ts +117 -0
  19. package/cli/selftune/contribution-signals.ts +8 -4
  20. package/cli/selftune/contribution-staging.ts +13 -2
  21. package/cli/selftune/contributions.ts +55 -121
  22. package/cli/selftune/creator-contributions.ts +29 -10
  23. package/cli/selftune/cron/setup.ts +7 -3
  24. package/cli/selftune/dashboard-contract.ts +73 -0
  25. package/cli/selftune/dashboard-server.ts +168 -17
  26. package/cli/selftune/dashboard.ts +350 -17
  27. package/cli/selftune/eval/baseline.ts +21 -5
  28. package/cli/selftune/eval/execution-eval.ts +170 -0
  29. package/cli/selftune/eval/family-overlap.ts +2 -2
  30. package/cli/selftune/eval/hooks-to-evals.ts +228 -82
  31. package/cli/selftune/eval/import-skillsbench.ts +2 -2
  32. package/cli/selftune/eval/invocation-classifier.ts +56 -0
  33. package/cli/selftune/eval/synthetic-evals.ts +5 -3
  34. package/cli/selftune/eval/unit-test-cli.ts +7 -4
  35. package/cli/selftune/evolution/apply-proposal.ts +295 -0
  36. package/cli/selftune/evolution/engines/replay-engine.ts +79 -57
  37. package/cli/selftune/evolution/evolve-body.ts +100 -39
  38. package/cli/selftune/evolution/evolve.ts +244 -52
  39. package/cli/selftune/evolution/rollback.ts +0 -1
  40. package/cli/selftune/evolution/validate-body.ts +68 -42
  41. package/cli/selftune/evolution/validate-host-replay.ts +510 -60
  42. package/cli/selftune/evolution/validate-proposal.ts +11 -150
  43. package/cli/selftune/evolution/validate-routing.ts +43 -41
  44. package/cli/selftune/evolution/validation-contract.ts +91 -0
  45. package/cli/selftune/grading/auto-grade.ts +11 -7
  46. package/cli/selftune/grading/grade-session.ts +10 -16
  47. package/cli/selftune/index.ts +35 -10
  48. package/cli/selftune/ingestors/claude-replay.ts +15 -10
  49. package/cli/selftune/ingestors/codex-wrapper.ts +3 -3
  50. package/cli/selftune/ingestors/opencode-ingest.ts +2 -2
  51. package/cli/selftune/ingestors/pi-ingest.ts +3 -2
  52. package/cli/selftune/init.ts +27 -3
  53. package/cli/selftune/localdb/direct-write.ts +35 -1
  54. package/cli/selftune/localdb/queries/cron.ts +34 -0
  55. package/cli/selftune/localdb/queries/dashboard.ts +834 -0
  56. package/cli/selftune/localdb/queries/evolution.ts +158 -0
  57. package/cli/selftune/localdb/queries/execution.ts +133 -0
  58. package/cli/selftune/localdb/queries/json.ts +18 -0
  59. package/cli/selftune/localdb/queries/monitoring.ts +263 -0
  60. package/cli/selftune/localdb/queries/raw.ts +95 -0
  61. package/cli/selftune/localdb/queries/staging.ts +270 -0
  62. package/cli/selftune/localdb/queries/trust.ts +392 -0
  63. package/cli/selftune/localdb/queries.ts +60 -2288
  64. package/cli/selftune/localdb/schema.ts +21 -0
  65. package/cli/selftune/monitoring/watch.ts +96 -29
  66. package/cli/selftune/normalization.ts +3 -0
  67. package/cli/selftune/observability.ts +4 -2
  68. package/cli/selftune/orchestrate/cli.ts +161 -0
  69. package/cli/selftune/orchestrate/execute.ts +295 -0
  70. package/cli/selftune/orchestrate/finalize.ts +157 -0
  71. package/cli/selftune/orchestrate/locks.ts +40 -0
  72. package/cli/selftune/orchestrate/plan.ts +131 -0
  73. package/cli/selftune/orchestrate/post-run.ts +59 -0
  74. package/cli/selftune/orchestrate/prepare.ts +334 -0
  75. package/cli/selftune/orchestrate/report.ts +182 -0
  76. package/cli/selftune/orchestrate/runtime.ts +120 -0
  77. package/cli/selftune/orchestrate/signals.ts +48 -0
  78. package/cli/selftune/orchestrate.ts +150 -1173
  79. package/cli/selftune/repair/skill-usage.ts +5 -2
  80. package/cli/selftune/routes/overview.ts +5 -2
  81. package/cli/selftune/routes/skill-report.ts +15 -2
  82. package/cli/selftune/schedule.ts +5 -5
  83. package/cli/selftune/status.ts +39 -2
  84. package/cli/selftune/testing-readiness.ts +597 -0
  85. package/cli/selftune/types.ts +44 -4
  86. package/cli/selftune/uninstall.ts +2 -1
  87. package/cli/selftune/utils/canonical-log.ts +1 -9
  88. package/cli/selftune/utils/cli-error.ts +9 -0
  89. package/cli/selftune/utils/llm-call.ts +126 -6
  90. package/cli/selftune/utils/skill-discovery.ts +2 -0
  91. package/cli/selftune/workflows/proposals.ts +184 -0
  92. package/cli/selftune/workflows/skill-scaffold.ts +241 -0
  93. package/cli/selftune/workflows/workflows.ts +100 -26
  94. package/node_modules/@selftune/telemetry-contract/fixtures/complete-push.ts +1 -1
  95. package/node_modules/@selftune/telemetry-contract/fixtures/evidence-only-push.ts +1 -1
  96. package/node_modules/@selftune/telemetry-contract/fixtures/partial-push-no-sessions.ts +1 -1
  97. package/node_modules/@selftune/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +1 -1
  98. package/node_modules/@selftune/telemetry-contract/src/schemas.ts +41 -1
  99. package/node_modules/@selftune/telemetry-contract/src/types.ts +103 -2
  100. package/package.json +25 -9
  101. package/packages/dashboard-core/AGENTS.md +18 -0
  102. package/packages/dashboard-core/README.md +30 -0
  103. package/packages/dashboard-core/index.ts +3 -0
  104. package/packages/dashboard-core/package.json +39 -0
  105. package/packages/dashboard-core/src/chrome/DashboardChrome.tsx +74 -0
  106. package/packages/dashboard-core/src/chrome/DashboardHeader.tsx +200 -0
  107. package/packages/dashboard-core/src/chrome/DashboardSidebar.tsx +219 -0
  108. package/packages/dashboard-core/src/chrome/RuntimeBadge.tsx +46 -0
  109. package/packages/dashboard-core/src/chrome/index.ts +14 -0
  110. package/packages/dashboard-core/src/chrome/types.ts +81 -0
  111. package/packages/dashboard-core/src/chrome/utils.ts +23 -0
  112. package/packages/dashboard-core/src/gates/FeatureGate.tsx +11 -0
  113. package/packages/dashboard-core/src/gates/LockedRoute.tsx +29 -0
  114. package/packages/dashboard-core/src/gates/UpgradeCard.tsx +89 -0
  115. package/packages/dashboard-core/src/gates/index.ts +3 -0
  116. package/packages/dashboard-core/src/host/DashboardHostProvider.tsx +62 -0
  117. package/packages/dashboard-core/src/host/adapter.ts +47 -0
  118. package/packages/dashboard-core/src/host/capabilities.ts +55 -0
  119. package/packages/dashboard-core/src/host/index.ts +3 -0
  120. package/packages/dashboard-core/src/models/analytics.ts +39 -0
  121. package/packages/dashboard-core/src/models/index.ts +4 -0
  122. package/packages/dashboard-core/src/models/overview.ts +98 -0
  123. package/packages/dashboard-core/src/models/runtime.ts +7 -0
  124. package/packages/dashboard-core/src/models/skills.ts +34 -0
  125. package/packages/dashboard-core/src/routes/index.ts +2 -0
  126. package/packages/dashboard-core/src/routes/manifest.test.ts +70 -0
  127. package/packages/dashboard-core/src/routes/manifest.ts +451 -0
  128. package/packages/dashboard-core/src/routes/types.ts +39 -0
  129. package/packages/dashboard-core/src/screens/analytics/AnalyticsScreen.tsx +278 -0
  130. package/packages/dashboard-core/src/screens/analytics/index.ts +1 -0
  131. package/packages/dashboard-core/src/screens/index.ts +37 -0
  132. package/packages/dashboard-core/src/screens/overview/OverviewComparisonSurface.test.ts +101 -0
  133. package/packages/dashboard-core/src/screens/overview/OverviewComparisonSurface.tsx +393 -0
  134. package/packages/dashboard-core/src/screens/overview/OverviewCompositionSurface.test.tsx +113 -0
  135. package/packages/dashboard-core/src/screens/overview/OverviewCompositionSurface.tsx +72 -0
  136. package/packages/dashboard-core/src/screens/overview/OverviewCoreSurface.tsx +71 -0
  137. package/packages/dashboard-core/src/screens/overview/OverviewOnboardingBanner.tsx +90 -0
  138. package/packages/dashboard-core/src/screens/overview/OverviewRunSummary.tsx +40 -0
  139. package/packages/dashboard-core/src/screens/overview/index.ts +16 -0
  140. package/packages/dashboard-core/src/screens/overview/types.ts +13 -0
  141. package/packages/dashboard-core/src/screens/skill-report/SkillReportDailyBreakdownSection.tsx +99 -0
  142. package/packages/dashboard-core/src/screens/skill-report/SkillReportDataQualityTabContent.tsx +35 -0
  143. package/packages/dashboard-core/src/screens/skill-report/SkillReportEvidenceRail.tsx +71 -0
  144. package/packages/dashboard-core/src/screens/skill-report/SkillReportEvidenceSection.tsx +63 -0
  145. package/packages/dashboard-core/src/screens/skill-report/SkillReportEvidenceTabContent.tsx +25 -0
  146. package/packages/dashboard-core/src/screens/skill-report/SkillReportInvocationsSection.tsx +24 -0
  147. package/packages/dashboard-core/src/screens/skill-report/SkillReportMissedQueriesSection.tsx +79 -0
  148. package/packages/dashboard-core/src/screens/skill-report/SkillReportScaffold.tsx +150 -0
  149. package/packages/dashboard-core/src/screens/skill-report/SkillReportSections.test.tsx +224 -0
  150. package/packages/dashboard-core/src/screens/skill-report/SkillReportTabs.test.tsx +76 -0
  151. package/packages/dashboard-core/src/screens/skill-report/SkillReportTabs.tsx +88 -0
  152. package/packages/dashboard-core/src/screens/skill-report/SkillReportTrendSection.tsx +33 -0
  153. package/packages/dashboard-core/src/screens/skill-report/SkillReportTrustBadge.tsx +67 -0
  154. package/packages/dashboard-core/src/screens/skill-report/index.ts +45 -0
  155. package/packages/dashboard-core/src/screens/skills/SkillsLibraryScreen.tsx +162 -0
  156. package/packages/dashboard-core/src/screens/skills/index.ts +6 -0
  157. package/packages/telemetry-contract/fixtures/complete-push.ts +1 -1
  158. package/packages/telemetry-contract/fixtures/evidence-only-push.ts +1 -1
  159. package/packages/telemetry-contract/fixtures/partial-push-no-sessions.ts +1 -1
  160. package/packages/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +1 -1
  161. package/packages/telemetry-contract/src/schemas.ts +41 -1
  162. package/packages/telemetry-contract/src/types.ts +103 -2
  163. package/packages/ui/src/components/EvidenceViewer.tsx +80 -25
  164. package/packages/ui/src/components/OverviewPanels.tsx +67 -26
  165. package/packages/ui/src/primitives/tabs.tsx +7 -6
  166. package/packages/ui/src/types.ts +10 -0
  167. package/skill/SKILL.md +130 -332
  168. package/skill/agents/diagnosis-analyst.md +3 -3
  169. package/skill/agents/evolution-reviewer.md +3 -3
  170. package/skill/agents/integration-guide.md +3 -3
  171. package/skill/agents/pattern-analyst.md +2 -2
  172. package/skill/references/cli-quick-reference.md +89 -0
  173. package/skill/references/creator-playbook.md +131 -0
  174. package/skill/references/examples.md +48 -0
  175. package/skill/references/troubleshooting.md +47 -0
  176. package/skill/references/version-history.md +1 -1
  177. package/skill/selftune.contribute.json +11 -0
  178. package/skill/{Workflows → workflows}/Baseline.md +20 -1
  179. package/skill/{Workflows → workflows}/Contribute.md +23 -10
  180. package/skill/{Workflows → workflows}/Contributions.md +13 -5
  181. package/skill/workflows/CreateTestDeploy.md +170 -0
  182. package/skill/{Workflows → workflows}/CreatorContributions.md +18 -6
  183. package/skill/{Workflows → workflows}/Cron.md +1 -1
  184. package/skill/{Workflows → workflows}/Dashboard.md +20 -0
  185. package/skill/{Workflows → workflows}/Doctor.md +1 -1
  186. package/skill/{Workflows → workflows}/Evals.md +67 -2
  187. package/skill/{Workflows → workflows}/Evolve.md +119 -30
  188. package/skill/{Workflows → workflows}/EvolveBody.md +41 -1
  189. package/skill/{Workflows → workflows}/Grade.md +1 -1
  190. package/skill/{Workflows → workflows}/Initialize.md +8 -4
  191. package/skill/{Workflows → workflows}/Orchestrate.md +13 -3
  192. package/skill/{Workflows → workflows}/Schedule.md +3 -3
  193. package/skill/workflows/SignalsDashboard.md +87 -0
  194. package/skill/{Workflows → workflows}/UnitTest.md +19 -0
  195. package/skill/{Workflows → workflows}/Watch.md +42 -2
  196. package/skill/{Workflows → workflows}/Workflows.md +39 -2
  197. package/apps/local-dashboard/dist/assets/index-CwOtTrUS.css +0 -1
  198. package/apps/local-dashboard/dist/assets/index-f1HQpbeH.js +0 -59
  199. package/apps/local-dashboard/dist/assets/vendor-react-CKkiCskZ.js +0 -11
  200. package/apps/local-dashboard/dist/assets/vendor-ui-jVSaIZey.js +0 -12
  201. /package/skill/{Workflows → workflows}/AlphaUpload.md +0 -0
  202. /package/skill/{Workflows → workflows}/AutoActivation.md +0 -0
  203. /package/skill/{Workflows → workflows}/Badge.md +0 -0
  204. /package/skill/{Workflows → workflows}/Composability.md +0 -0
  205. /package/skill/{Workflows → workflows}/EvolutionMemory.md +0 -0
  206. /package/skill/{Workflows → workflows}/ExportCanonical.md +0 -0
  207. /package/skill/{Workflows → workflows}/Hook.md +0 -0
  208. /package/skill/{Workflows → workflows}/ImportSkillsBench.md +0 -0
  209. /package/skill/{Workflows → workflows}/Ingest.md +0 -0
  210. /package/skill/{Workflows → workflows}/PlatformHooks.md +0 -0
  211. /package/skill/{Workflows → workflows}/Quickstart.md +0 -0
  212. /package/skill/{Workflows → workflows}/Recover.md +0 -0
  213. /package/skill/{Workflows → workflows}/Registry.md +0 -0
  214. /package/skill/{Workflows → workflows}/RepairSkillUsage.md +0 -0
  215. /package/skill/{Workflows → workflows}/Replay.md +0 -0
  216. /package/skill/{Workflows → workflows}/Rollback.md +0 -0
  217. /package/skill/{Workflows → workflows}/Sync.md +0 -0
  218. /package/skill/{Workflows → workflows}/Telemetry.md +0 -0
  219. /package/skill/{Workflows → workflows}/Uninstall.md +0 -0
@@ -0,0 +1,451 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+ import {
3
+ BarChart3Icon,
4
+ BrainCircuitIcon,
5
+ GitPullRequestIcon,
6
+ HeartPulseIcon,
7
+ HelpCircleIcon,
8
+ LayoutDashboardIcon,
9
+ PackageIcon,
10
+ SettingsIcon,
11
+ UsersIcon,
12
+ } from "lucide-react";
13
+
14
+ import {
15
+ canDiscoverFeature,
16
+ canUseFeature,
17
+ type Capabilities,
18
+ type DashboardDiscoverableFeatureKey,
19
+ type DashboardFeatureKey,
20
+ type DashboardHostKind,
21
+ } from "../host/capabilities";
22
+ import type { DashboardRouteAccess } from "./types";
23
+
24
+ export type DashboardRouteId =
25
+ | "overview"
26
+ | "skills"
27
+ | "analytics"
28
+ | "status"
29
+ | "registry"
30
+ | "signals"
31
+ | "proposals"
32
+ | "unmatched"
33
+ | "settings";
34
+
35
+ export type DashboardRouteMatchMode = "exact" | "prefix";
36
+
37
+ export interface DashboardPathMatcher {
38
+ mode: DashboardRouteMatchMode;
39
+ value: string;
40
+ }
41
+
42
+ export interface DashboardHostRouteConfig {
43
+ path: string;
44
+ title?: string;
45
+ badge?: string;
46
+ backHref?: string | null;
47
+ backLabel?: string | null;
48
+ activePatterns?: DashboardPathMatcher[];
49
+ detailPrefixes?: string[];
50
+ detailBadge?: string;
51
+ detailBackHref?: string | null;
52
+ detailBackLabel?: string | null;
53
+ }
54
+
55
+ export interface DashboardRouteManifestEntry {
56
+ id: DashboardRouteId;
57
+ label: string;
58
+ tooltip: string;
59
+ icon: LucideIcon;
60
+ feature?: DashboardFeatureKey;
61
+ discoverableFeature?: DashboardDiscoverableFeatureKey;
62
+ lockedTitle?: string;
63
+ lockedBody?: string;
64
+ lockedHighlights?: readonly string[];
65
+ lockedPrimaryCtaLabel?: string;
66
+ lockedPrimaryCtaHref?: string;
67
+ lockedSecondaryCtaLabel?: string;
68
+ lockedSecondaryCtaHref?: string;
69
+ hosts: Partial<Record<DashboardHostKind, DashboardHostRouteConfig>>;
70
+ }
71
+
72
+ export interface ResolvedDashboardRoute {
73
+ id: DashboardRouteId;
74
+ label: string;
75
+ tooltip: string;
76
+ icon: LucideIcon;
77
+ host: DashboardHostKind;
78
+ path: string;
79
+ title: string;
80
+ badge: string;
81
+ backHref: string | null;
82
+ backLabel: string | null;
83
+ activePatterns: DashboardPathMatcher[];
84
+ detailPrefixes: string[];
85
+ detailBadge: string;
86
+ detailBackHref: string | null;
87
+ detailBackLabel: string | null;
88
+ access: DashboardRouteAccess;
89
+ lockedTitle?: string;
90
+ lockedBody?: string;
91
+ lockedHighlights?: readonly string[];
92
+ lockedPrimaryCtaLabel?: string;
93
+ lockedPrimaryCtaHref?: string;
94
+ lockedSecondaryCtaLabel?: string;
95
+ lockedSecondaryCtaHref?: string;
96
+ }
97
+
98
+ export interface MatchedDashboardRoute extends ResolvedDashboardRoute {
99
+ matchKind: "route" | "detail";
100
+ }
101
+
102
+ function matchesPattern(pathname: string, pattern: DashboardPathMatcher): boolean {
103
+ if (pattern.mode === "exact") {
104
+ return pathname === pattern.value;
105
+ }
106
+
107
+ return pathname.startsWith(pattern.value);
108
+ }
109
+
110
+ function getManifestRouteAccess(
111
+ route: DashboardRouteManifestEntry,
112
+ capabilities: Capabilities,
113
+ ): DashboardRouteAccess {
114
+ if (!route.feature) {
115
+ return "enabled";
116
+ }
117
+
118
+ if (canUseFeature(capabilities, route.feature)) {
119
+ return "enabled";
120
+ }
121
+
122
+ if (route.discoverableFeature && canDiscoverFeature(capabilities, route.discoverableFeature)) {
123
+ return "locked";
124
+ }
125
+
126
+ return "hidden";
127
+ }
128
+
129
+ export const DASHBOARD_ROUTE_MANIFEST: readonly DashboardRouteManifestEntry[] = [
130
+ {
131
+ id: "overview",
132
+ label: "Overview",
133
+ tooltip: "Dashboard overview",
134
+ icon: LayoutDashboardIcon,
135
+ hosts: {
136
+ cloud: {
137
+ path: "/",
138
+ title: "Dashboard",
139
+ badge: "Overview",
140
+ activePatterns: [{ mode: "exact", value: "/" }],
141
+ },
142
+ local: {
143
+ path: "/",
144
+ title: "Dashboard",
145
+ badge: "Overview",
146
+ activePatterns: [{ mode: "exact", value: "/" }],
147
+ },
148
+ },
149
+ },
150
+ {
151
+ id: "skills",
152
+ label: "Skills",
153
+ tooltip: "Skills Library",
154
+ icon: BrainCircuitIcon,
155
+ hosts: {
156
+ cloud: {
157
+ path: "/skills",
158
+ title: "Skills",
159
+ badge: "Library",
160
+ backHref: "/",
161
+ backLabel: "Dashboard",
162
+ activePatterns: [
163
+ { mode: "exact", value: "/skills" },
164
+ { mode: "prefix", value: "/skills/" },
165
+ ],
166
+ detailPrefixes: ["/skills/"],
167
+ detailBadge: "Skill Report",
168
+ detailBackHref: "/skills",
169
+ detailBackLabel: "Skills",
170
+ },
171
+ local: {
172
+ path: "/skills",
173
+ title: "Skills",
174
+ badge: "Library",
175
+ backHref: "/",
176
+ backLabel: "Dashboard",
177
+ activePatterns: [
178
+ { mode: "exact", value: "/skills" },
179
+ { mode: "prefix", value: "/skills/" },
180
+ ],
181
+ detailPrefixes: ["/skills/"],
182
+ detailBadge: "Skill Report",
183
+ detailBackHref: "/skills",
184
+ detailBackLabel: "Skills",
185
+ },
186
+ },
187
+ },
188
+ {
189
+ id: "analytics",
190
+ label: "Analytics",
191
+ tooltip: "Performance analytics",
192
+ icon: BarChart3Icon,
193
+ feature: "analytics",
194
+ hosts: {
195
+ cloud: {
196
+ path: "/analytics",
197
+ title: "Analytics",
198
+ badge: "Performance",
199
+ backHref: "/",
200
+ backLabel: "Dashboard",
201
+ activePatterns: [{ mode: "exact", value: "/analytics" }],
202
+ },
203
+ local: {
204
+ path: "/analytics",
205
+ title: "Analytics",
206
+ badge: "Performance",
207
+ backHref: "/",
208
+ backLabel: "Dashboard",
209
+ activePatterns: [{ mode: "exact", value: "/analytics" }],
210
+ },
211
+ },
212
+ },
213
+ {
214
+ id: "registry",
215
+ label: "Registry",
216
+ tooltip: "Team skill registry",
217
+ icon: PackageIcon,
218
+ feature: "registry",
219
+ discoverableFeature: "registry",
220
+ lockedTitle: "Team Registry lives in Selftune Cloud",
221
+ lockedBody:
222
+ "Publish versioned skills, watch installations across projects, and roll back bad versions from a single cloud workspace.",
223
+ lockedHighlights: [
224
+ "Version timeline with rollback controls",
225
+ "Installation map across your team",
226
+ "Managed publish flow for shared skills",
227
+ ],
228
+ lockedPrimaryCtaLabel: "Upgrade to Team",
229
+ lockedPrimaryCtaHref: "https://selftune.dev/pricing",
230
+ lockedSecondaryCtaLabel: "Read registry docs",
231
+ lockedSecondaryCtaHref: "https://docs.selftune.dev/cloud/registry",
232
+ hosts: {
233
+ cloud: {
234
+ path: "/registry",
235
+ title: "Registry",
236
+ badge: "Team",
237
+ backHref: "/",
238
+ backLabel: "Dashboard",
239
+ activePatterns: [{ mode: "exact", value: "/registry" }],
240
+ },
241
+ local: {
242
+ path: "/registry",
243
+ title: "Registry",
244
+ badge: "Locked",
245
+ backHref: "/",
246
+ backLabel: "Dashboard",
247
+ activePatterns: [{ mode: "exact", value: "/registry" }],
248
+ },
249
+ },
250
+ },
251
+ {
252
+ id: "signals",
253
+ label: "Signals",
254
+ tooltip: "Contributor signals",
255
+ icon: UsersIcon,
256
+ feature: "signals",
257
+ discoverableFeature: "signals",
258
+ lockedTitle: "Contributor signals run through Selftune Cloud",
259
+ lockedBody:
260
+ "See anonymized contributor signals, compare bundle submissions, and turn real-world usage into proposals without leaving the shared dashboard.",
261
+ lockedHighlights: [
262
+ "Cross-skill contributor signal overview",
263
+ "Bundle submission trends and cohorts",
264
+ "Proposal generation from contributor evidence",
265
+ ],
266
+ lockedPrimaryCtaLabel: "View cloud plans",
267
+ lockedPrimaryCtaHref: "https://selftune.dev/pricing",
268
+ lockedSecondaryCtaLabel: "Read signals docs",
269
+ lockedSecondaryCtaHref: "https://docs.selftune.dev/cloud/signals",
270
+ hosts: {
271
+ cloud: {
272
+ path: "/signals",
273
+ title: "Signals",
274
+ badge: "Signals",
275
+ backHref: "/",
276
+ backLabel: "Dashboard",
277
+ activePatterns: [{ mode: "exact", value: "/signals" }],
278
+ },
279
+ local: {
280
+ path: "/signals",
281
+ title: "Signals",
282
+ badge: "Locked",
283
+ backHref: "/",
284
+ backLabel: "Dashboard",
285
+ activePatterns: [{ mode: "exact", value: "/signals" }],
286
+ },
287
+ },
288
+ },
289
+ {
290
+ id: "proposals",
291
+ label: "Proposals",
292
+ tooltip: "Evolution proposals",
293
+ icon: GitPullRequestIcon,
294
+ feature: "proposals",
295
+ discoverableFeature: "proposals",
296
+ lockedTitle: "Proposal review is unlocked in Cloud",
297
+ lockedBody:
298
+ "Keep a shared review queue for contributor-driven improvements, approve the right changes, and coordinate deployment across your team.",
299
+ lockedHighlights: [
300
+ "Shared approval and rejection queue",
301
+ "Proposal detail with rationale and evidence",
302
+ "Tighter loop from contributor signals to deployment",
303
+ ],
304
+ lockedPrimaryCtaLabel: "Upgrade for review workflows",
305
+ lockedPrimaryCtaHref: "https://selftune.dev/pricing",
306
+ lockedSecondaryCtaLabel: "See dashboard docs",
307
+ lockedSecondaryCtaHref: "https://docs.selftune.dev/cloud/dashboard",
308
+ hosts: {
309
+ cloud: {
310
+ path: "/proposals",
311
+ title: "Proposals",
312
+ badge: "Review Queue",
313
+ backHref: "/",
314
+ backLabel: "Dashboard",
315
+ activePatterns: [{ mode: "exact", value: "/proposals" }],
316
+ },
317
+ local: {
318
+ path: "/proposals",
319
+ title: "Proposals",
320
+ badge: "Locked",
321
+ backHref: "/",
322
+ backLabel: "Dashboard",
323
+ activePatterns: [{ mode: "exact", value: "/proposals" }],
324
+ },
325
+ },
326
+ },
327
+ {
328
+ id: "unmatched",
329
+ label: "Unmatched",
330
+ tooltip: "Unmatched queries",
331
+ icon: HelpCircleIcon,
332
+ hosts: {
333
+ cloud: {
334
+ path: "/unmatched",
335
+ title: "Unmatched",
336
+ badge: "Attention",
337
+ backHref: "/",
338
+ backLabel: "Dashboard",
339
+ activePatterns: [{ mode: "exact", value: "/unmatched" }],
340
+ },
341
+ },
342
+ },
343
+ {
344
+ id: "settings",
345
+ label: "Settings",
346
+ tooltip: "Settings and API keys",
347
+ icon: SettingsIcon,
348
+ hosts: {
349
+ cloud: {
350
+ path: "/settings",
351
+ title: "Settings",
352
+ badge: "Workspace",
353
+ backHref: "/",
354
+ backLabel: "Dashboard",
355
+ activePatterns: [{ mode: "exact", value: "/settings" }],
356
+ },
357
+ },
358
+ },
359
+ {
360
+ id: "status",
361
+ label: "System Status",
362
+ tooltip: "System health diagnostics",
363
+ icon: HeartPulseIcon,
364
+ feature: "runtimeStatus",
365
+ hosts: {
366
+ local: {
367
+ path: "/status",
368
+ title: "System Status",
369
+ badge: "Diagnostics",
370
+ backHref: "/",
371
+ backLabel: "Dashboard",
372
+ activePatterns: [{ mode: "exact", value: "/status" }],
373
+ },
374
+ },
375
+ },
376
+ ] as const;
377
+
378
+ export function resolveDashboardRoutes(
379
+ host: DashboardHostKind,
380
+ capabilities: Capabilities,
381
+ ): ResolvedDashboardRoute[] {
382
+ return DASHBOARD_ROUTE_MANIFEST.flatMap((route) => {
383
+ const hostConfig = route.hosts[host];
384
+ if (!hostConfig) {
385
+ return [];
386
+ }
387
+
388
+ const access = getManifestRouteAccess(route, capabilities);
389
+ if (access === "hidden") {
390
+ return [];
391
+ }
392
+
393
+ return [
394
+ {
395
+ id: route.id,
396
+ label: route.label,
397
+ tooltip: route.tooltip,
398
+ icon: route.icon,
399
+ host,
400
+ path: hostConfig.path,
401
+ title: hostConfig.title ?? route.label,
402
+ badge: hostConfig.badge ?? route.label,
403
+ backHref: hostConfig.backHref ?? null,
404
+ backLabel: hostConfig.backLabel ?? null,
405
+ activePatterns: hostConfig.activePatterns ?? [{ mode: "exact", value: hostConfig.path }],
406
+ detailPrefixes: hostConfig.detailPrefixes ?? [],
407
+ detailBadge: hostConfig.detailBadge ?? hostConfig.badge ?? route.label,
408
+ detailBackHref: hostConfig.detailBackHref ?? hostConfig.backHref ?? null,
409
+ detailBackLabel: hostConfig.detailBackLabel ?? hostConfig.backLabel ?? null,
410
+ access,
411
+ lockedTitle: route.lockedTitle,
412
+ lockedBody: route.lockedBody,
413
+ lockedHighlights: route.lockedHighlights,
414
+ lockedPrimaryCtaLabel: route.lockedPrimaryCtaLabel,
415
+ lockedPrimaryCtaHref: route.lockedPrimaryCtaHref,
416
+ lockedSecondaryCtaLabel: route.lockedSecondaryCtaLabel,
417
+ lockedSecondaryCtaHref: route.lockedSecondaryCtaHref,
418
+ },
419
+ ];
420
+ });
421
+ }
422
+
423
+ export function isDashboardRouteActive(pathname: string, route: ResolvedDashboardRoute): boolean {
424
+ return Boolean(matchDashboardRoute(pathname, [route]));
425
+ }
426
+
427
+ export function matchDashboardRoute(
428
+ pathname: string,
429
+ routes: readonly ResolvedDashboardRoute[],
430
+ ): MatchedDashboardRoute | null {
431
+ for (const route of routes) {
432
+ if (route.detailPrefixes.some((prefix) => pathname.startsWith(prefix))) {
433
+ return {
434
+ ...route,
435
+ matchKind: "detail",
436
+ badge: route.detailBadge,
437
+ backHref: route.detailBackHref,
438
+ backLabel: route.detailBackLabel,
439
+ };
440
+ }
441
+
442
+ if (route.activePatterns.some((pattern) => matchesPattern(pathname, pattern))) {
443
+ return {
444
+ ...route,
445
+ matchKind: "route",
446
+ };
447
+ }
448
+ }
449
+
450
+ return null;
451
+ }
@@ -0,0 +1,39 @@
1
+ import type { ComponentType } from "react";
2
+
3
+ import type { Capabilities } from "../host/capabilities";
4
+
5
+ export type DashboardRoutePredicate = boolean | ((capabilities: Capabilities) => boolean);
6
+ export type DashboardRouteAccess = "enabled" | "locked" | "hidden";
7
+
8
+ export interface DashboardRouteDefinition {
9
+ id: string;
10
+ path: string;
11
+ label: string;
12
+ component: ComponentType;
13
+ visible?: DashboardRoutePredicate;
14
+ enabled?: DashboardRoutePredicate;
15
+ lockedTitle?: string;
16
+ lockedBody?: string;
17
+ ctaLabel?: string;
18
+ }
19
+
20
+ export function resolveRoutePredicate(
21
+ predicate: DashboardRoutePredicate | undefined,
22
+ capabilities: Capabilities,
23
+ fallback: boolean,
24
+ ): boolean {
25
+ if (typeof predicate === "undefined") return fallback;
26
+ if (typeof predicate === "boolean") return predicate;
27
+ return predicate(capabilities);
28
+ }
29
+
30
+ export function getRouteAccess(
31
+ route: DashboardRouteDefinition,
32
+ capabilities: Capabilities,
33
+ ): DashboardRouteAccess {
34
+ const visible = resolveRoutePredicate(route.visible, capabilities, true);
35
+ if (!visible) return "hidden";
36
+
37
+ const enabled = resolveRoutePredicate(route.enabled, capabilities, true);
38
+ return enabled ? "enabled" : "locked";
39
+ }