pilothub 0.0.1

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 (272) hide show
  1. package/.env.local.example +19 -0
  2. package/.github/workflows/ci.yml +40 -0
  3. package/.oxlintrc.json +3 -0
  4. package/AGENTS.md +45 -0
  5. package/CHANGELOG.md +138 -0
  6. package/DEPRECATIONS.md +7 -0
  7. package/LICENSE +21 -0
  8. package/README.md +150 -0
  9. package/biome.json +41 -0
  10. package/convex/_generated/api.d.ts +153 -0
  11. package/convex/_generated/api.js +23 -0
  12. package/convex/_generated/dataModel.d.ts +60 -0
  13. package/convex/_generated/server.d.ts +143 -0
  14. package/convex/_generated/server.js +93 -0
  15. package/convex/auth.config.ts +8 -0
  16. package/convex/auth.ts +19 -0
  17. package/convex/comments.ts +88 -0
  18. package/convex/crons.ts +34 -0
  19. package/convex/devSeed.ts +459 -0
  20. package/convex/devSeedExtra.ts +541 -0
  21. package/convex/downloads.ts +78 -0
  22. package/convex/githubBackups.ts +170 -0
  23. package/convex/githubBackupsNode.ts +183 -0
  24. package/convex/githubImport.ts +317 -0
  25. package/convex/githubSoulBackups.ts +170 -0
  26. package/convex/githubSoulBackupsNode.ts +186 -0
  27. package/convex/http.ts +194 -0
  28. package/convex/httpApi.handlers.test.ts +488 -0
  29. package/convex/httpApi.test.ts +70 -0
  30. package/convex/httpApi.ts +305 -0
  31. package/convex/httpApiV1.handlers.test.ts +584 -0
  32. package/convex/httpApiV1.ts +1172 -0
  33. package/convex/leaderboards.ts +39 -0
  34. package/convex/lib/access.ts +36 -0
  35. package/convex/lib/apiTokenAuth.ts +36 -0
  36. package/convex/lib/badges.ts +50 -0
  37. package/convex/lib/changelog.test.ts +34 -0
  38. package/convex/lib/changelog.ts +278 -0
  39. package/convex/lib/embeddings.ts +38 -0
  40. package/convex/lib/githubBackup.ts +443 -0
  41. package/convex/lib/githubImport.test.ts +247 -0
  42. package/convex/lib/githubImport.ts +425 -0
  43. package/convex/lib/githubSoulBackup.ts +443 -0
  44. package/convex/lib/leaderboards.ts +103 -0
  45. package/convex/lib/moderation.ts +42 -0
  46. package/convex/lib/public.ts +89 -0
  47. package/convex/lib/searchText.test.ts +46 -0
  48. package/convex/lib/searchText.ts +27 -0
  49. package/convex/lib/skillBackfill.test.ts +34 -0
  50. package/convex/lib/skillBackfill.ts +67 -0
  51. package/convex/lib/skillPublish.test.ts +28 -0
  52. package/convex/lib/skillPublish.ts +284 -0
  53. package/convex/lib/skillStats.ts +80 -0
  54. package/convex/lib/skills.test.ts +197 -0
  55. package/convex/lib/skills.ts +273 -0
  56. package/convex/lib/soulChangelog.ts +273 -0
  57. package/convex/lib/soulPublish.ts +236 -0
  58. package/convex/lib/tokens.test.ts +33 -0
  59. package/convex/lib/tokens.ts +51 -0
  60. package/convex/lib/webhooks.test.ts +91 -0
  61. package/convex/lib/webhooks.ts +112 -0
  62. package/convex/maintenance.test.ts +270 -0
  63. package/convex/maintenance.ts +840 -0
  64. package/convex/rateLimits.ts +50 -0
  65. package/convex/schema.ts +472 -0
  66. package/convex/search.test.ts +12 -0
  67. package/convex/search.ts +254 -0
  68. package/convex/seed.test.ts +37 -0
  69. package/convex/seed.ts +254 -0
  70. package/convex/seedSouls.ts +111 -0
  71. package/convex/skillStatEvents.ts +568 -0
  72. package/convex/skills.ts +1606 -0
  73. package/convex/soulComments.ts +88 -0
  74. package/convex/soulDownloads.ts +14 -0
  75. package/convex/soulStars.ts +71 -0
  76. package/convex/souls.ts +570 -0
  77. package/convex/stars.ts +108 -0
  78. package/convex/statsMaintenance.ts +205 -0
  79. package/convex/telemetry.ts +434 -0
  80. package/convex/tokens.ts +88 -0
  81. package/convex/tsconfig.json +7 -0
  82. package/convex/uploads.ts +20 -0
  83. package/convex/users.ts +122 -0
  84. package/convex/webhooks.ts +50 -0
  85. package/convex.json +3 -0
  86. package/docs/README.md +32 -0
  87. package/docs/api.md +51 -0
  88. package/docs/architecture.md +61 -0
  89. package/docs/auth.md +54 -0
  90. package/docs/cli.md +117 -0
  91. package/docs/deploy.md +78 -0
  92. package/docs/diffing.md +84 -0
  93. package/docs/github-import.md +171 -0
  94. package/docs/http-api.md +187 -0
  95. package/docs/manual-testing.md +64 -0
  96. package/docs/mintlify.md +43 -0
  97. package/docs/quickstart.md +120 -0
  98. package/docs/skill-format.md +58 -0
  99. package/docs/soul-format.md +37 -0
  100. package/docs/spec.md +177 -0
  101. package/docs/telemetry.md +91 -0
  102. package/docs/troubleshooting.md +49 -0
  103. package/docs/webhook.md +51 -0
  104. package/e2e/menu-smoke.pw.test.ts +49 -0
  105. package/e2e/pilothub.e2e.test.ts +494 -0
  106. package/e2e/search-exact.pw.test.ts +97 -0
  107. package/package.json +84 -0
  108. package/packages/pilothub/LICENSE +22 -0
  109. package/packages/pilothub/README.md +57 -0
  110. package/packages/pilothub/bin/pilothub.js +2 -0
  111. package/packages/pilothub/package.json +41 -0
  112. package/packages/pilothub/src/browserAuth.test.ts +96 -0
  113. package/packages/pilothub/src/browserAuth.ts +174 -0
  114. package/packages/pilothub/src/cli/buildInfo.ts +94 -0
  115. package/packages/pilothub/src/cli/commands/auth.ts +97 -0
  116. package/packages/pilothub/src/cli/commands/delete.test.ts +73 -0
  117. package/packages/pilothub/src/cli/commands/delete.ts +83 -0
  118. package/packages/pilothub/src/cli/commands/publish.test.ts +122 -0
  119. package/packages/pilothub/src/cli/commands/publish.ts +108 -0
  120. package/packages/pilothub/src/cli/commands/skills.test.ts +191 -0
  121. package/packages/pilothub/src/cli/commands/skills.ts +380 -0
  122. package/packages/pilothub/src/cli/commands/star.ts +46 -0
  123. package/packages/pilothub/src/cli/commands/sync.test.ts +310 -0
  124. package/packages/pilothub/src/cli/commands/sync.ts +200 -0
  125. package/packages/pilothub/src/cli/commands/syncHelpers.test.ts +26 -0
  126. package/packages/pilothub/src/cli/commands/syncHelpers.ts +427 -0
  127. package/packages/pilothub/src/cli/commands/syncTypes.ts +27 -0
  128. package/packages/pilothub/src/cli/commands/unstar.ts +48 -0
  129. package/packages/pilothub/src/cli/helpStyle.ts +45 -0
  130. package/packages/pilothub/src/cli/pilotbotConfig.test.ts +159 -0
  131. package/packages/pilothub/src/cli/pilotbotConfig.ts +147 -0
  132. package/packages/pilothub/src/cli/registry.test.ts +63 -0
  133. package/packages/pilothub/src/cli/registry.ts +43 -0
  134. package/packages/pilothub/src/cli/scanSkills.test.ts +64 -0
  135. package/packages/pilothub/src/cli/scanSkills.ts +84 -0
  136. package/packages/pilothub/src/cli/slug.ts +16 -0
  137. package/packages/pilothub/src/cli/types.ts +12 -0
  138. package/packages/pilothub/src/cli/ui.ts +75 -0
  139. package/packages/pilothub/src/cli.ts +311 -0
  140. package/packages/pilothub/src/config.ts +36 -0
  141. package/packages/pilothub/src/discovery.test.ts +75 -0
  142. package/packages/pilothub/src/discovery.ts +19 -0
  143. package/packages/pilothub/src/http.test.ts +156 -0
  144. package/packages/pilothub/src/http.ts +301 -0
  145. package/packages/pilothub/src/schema/ark.ts +29 -0
  146. package/packages/pilothub/src/schema/index.ts +5 -0
  147. package/packages/pilothub/src/schema/routes.ts +22 -0
  148. package/packages/pilothub/src/schema/schemas.ts +260 -0
  149. package/packages/pilothub/src/schema/textFiles.test.ts +23 -0
  150. package/packages/pilothub/src/schema/textFiles.ts +66 -0
  151. package/packages/pilothub/src/skills.test.ts +191 -0
  152. package/packages/pilothub/src/skills.ts +172 -0
  153. package/packages/pilothub/src/types.ts +10 -0
  154. package/packages/pilothub/tsconfig.json +14 -0
  155. package/packages/schema/README.md +3 -0
  156. package/packages/schema/dist/ark.d.ts +4 -0
  157. package/packages/schema/dist/ark.js +26 -0
  158. package/packages/schema/dist/ark.js.map +1 -0
  159. package/packages/schema/dist/index.d.ts +5 -0
  160. package/packages/schema/dist/index.js +5 -0
  161. package/packages/schema/dist/index.js.map +1 -0
  162. package/packages/schema/dist/routes.d.ts +21 -0
  163. package/packages/schema/dist/routes.js +22 -0
  164. package/packages/schema/dist/routes.js.map +1 -0
  165. package/packages/schema/dist/schemas.d.ts +297 -0
  166. package/packages/schema/dist/schemas.js +243 -0
  167. package/packages/schema/dist/schemas.js.map +1 -0
  168. package/packages/schema/dist/textFiles.d.ts +5 -0
  169. package/packages/schema/dist/textFiles.js +66 -0
  170. package/packages/schema/dist/textFiles.js.map +1 -0
  171. package/packages/schema/package.json +26 -0
  172. package/packages/schema/src/ark.ts +29 -0
  173. package/packages/schema/src/index.ts +5 -0
  174. package/packages/schema/src/routes.ts +22 -0
  175. package/packages/schema/src/schemas.test.ts +123 -0
  176. package/packages/schema/src/schemas.ts +287 -0
  177. package/packages/schema/src/textFiles.test.ts +23 -0
  178. package/packages/schema/src/textFiles.ts +66 -0
  179. package/packages/schema/tsconfig.json +15 -0
  180. package/pilothub +46 -0
  181. package/playwright.config.ts +33 -0
  182. package/public/.well-known/pilothub.json +6 -0
  183. package/public/api/v1/openapi.json +379 -0
  184. package/public/favicon.ico +0 -0
  185. package/public/logo192.png +0 -0
  186. package/public/logo512.png +0 -0
  187. package/public/manifest.json +25 -0
  188. package/public/og.png +0 -0
  189. package/public/og.svg +98 -0
  190. package/public/pilot-logo.png +0 -0
  191. package/public/pilot-mark.png +0 -0
  192. package/public/robots.txt +3 -0
  193. package/public/tanstack-circle-logo.png +0 -0
  194. package/public/tanstack-word-logo-white.svg +1 -0
  195. package/scripts/check-peer-deps.ts +56 -0
  196. package/scripts/docs-list.ts +148 -0
  197. package/scripts/run-playwright-local.sh +14 -0
  198. package/server/og/fetchSkillOgMeta.ts +27 -0
  199. package/server/og/fetchSoulOgMeta.ts +27 -0
  200. package/server/og/ogAssets.ts +80 -0
  201. package/server/og/skillOgSvg.test.ts +59 -0
  202. package/server/og/skillOgSvg.ts +258 -0
  203. package/server/og/soulOgSvg.ts +209 -0
  204. package/server/routes/og/skill.png.ts +103 -0
  205. package/server/routes/og/soul.png.ts +111 -0
  206. package/src/__tests__/skill-detail-page.test.tsx +86 -0
  207. package/src/__tests__/skills-index.test.tsx +145 -0
  208. package/src/__tests__/upload.route.test.tsx +228 -0
  209. package/src/components/AppProviders.tsx +19 -0
  210. package/src/components/ClientOnly.tsx +18 -0
  211. package/src/components/Footer.tsx +29 -0
  212. package/src/components/Header.tsx +295 -0
  213. package/src/components/InstallSwitcher.tsx +53 -0
  214. package/src/components/SkillCard.tsx +36 -0
  215. package/src/components/SkillDetailPage.tsx +817 -0
  216. package/src/components/SkillDiffCard.tsx +485 -0
  217. package/src/components/SoulCard.tsx +19 -0
  218. package/src/components/SoulDetailPage.tsx +263 -0
  219. package/src/components/UserBootstrap.tsx +18 -0
  220. package/src/components/ui/dropdown-menu.tsx +67 -0
  221. package/src/components/ui/toggle-group.tsx +35 -0
  222. package/src/convex/client.ts +3 -0
  223. package/src/lib/badges.ts +29 -0
  224. package/src/lib/diffing.test.ts +163 -0
  225. package/src/lib/diffing.ts +106 -0
  226. package/src/lib/gravatar.test.ts +9 -0
  227. package/src/lib/gravatar.ts +158 -0
  228. package/src/lib/og.test.ts +142 -0
  229. package/src/lib/og.ts +156 -0
  230. package/src/lib/publicUser.ts +39 -0
  231. package/src/lib/roles.ts +19 -0
  232. package/src/lib/site.test.ts +130 -0
  233. package/src/lib/site.ts +84 -0
  234. package/src/lib/theme-transition.test.ts +134 -0
  235. package/src/lib/theme-transition.ts +134 -0
  236. package/src/lib/theme.test.tsx +88 -0
  237. package/src/lib/theme.ts +43 -0
  238. package/src/lib/uploadFiles.jsdom.test.ts +33 -0
  239. package/src/lib/uploadFiles.test.ts +123 -0
  240. package/src/lib/uploadFiles.ts +245 -0
  241. package/src/lib/uploadUtils.test.ts +78 -0
  242. package/src/lib/uploadUtils.ts +93 -0
  243. package/src/lib/useAuthStatus.ts +12 -0
  244. package/src/lib/utils.test.ts +9 -0
  245. package/src/lib/utils.ts +6 -0
  246. package/src/logo.svg +12 -0
  247. package/src/routeTree.gen.ts +345 -0
  248. package/src/router.tsx +17 -0
  249. package/src/routes/$owner/$slug.tsx +55 -0
  250. package/src/routes/__root.tsx +136 -0
  251. package/src/routes/admin.tsx +11 -0
  252. package/src/routes/cli/auth.tsx +168 -0
  253. package/src/routes/dashboard.tsx +97 -0
  254. package/src/routes/import.tsx +415 -0
  255. package/src/routes/index.tsx +252 -0
  256. package/src/routes/management.tsx +529 -0
  257. package/src/routes/settings.tsx +203 -0
  258. package/src/routes/skills/index.tsx +422 -0
  259. package/src/routes/souls/$slug.tsx +55 -0
  260. package/src/routes/souls/index.tsx +243 -0
  261. package/src/routes/stars.tsx +68 -0
  262. package/src/routes/u/$handle.tsx +307 -0
  263. package/src/routes/upload/utils.ts +81 -0
  264. package/src/routes/upload.tsx +499 -0
  265. package/src/styles.css +2718 -0
  266. package/tsconfig.json +24 -0
  267. package/tsconfig.oxlint.json +16 -0
  268. package/vercel.json +8 -0
  269. package/vite.config.ts +48 -0
  270. package/vitest.config.ts +47 -0
  271. package/vitest.e2e.config.ts +11 -0
  272. package/vitest.setup.ts +1 -0
@@ -0,0 +1,459 @@
1
+ import { v } from 'convex/values'
2
+ import { internal } from './_generated/api'
3
+ import type { ActionCtx } from './_generated/server'
4
+ import { internalAction, internalMutation } from './_generated/server'
5
+ import { EMBEDDING_DIMENSIONS } from './lib/embeddings'
6
+ import { parseFrontmatter, parsePilotbotMetadata } from './lib/skills'
7
+
8
+ type SeedSkillSpec = {
9
+ slug: string
10
+ displayName: string
11
+ summary: string
12
+ version: string
13
+ metadata: Record<string, unknown>
14
+ rawSkillMd: string
15
+ }
16
+
17
+ type SeedActionArgs = {
18
+ reset?: boolean
19
+ }
20
+
21
+ type SeedActionResult = {
22
+ ok: true
23
+ results: Array<Record<string, unknown> & { slug: string }>
24
+ }
25
+
26
+ type SeedMutationResult = Record<string, unknown>
27
+
28
+ const SEED_SKILLS: SeedSkillSpec[] = [
29
+ {
30
+ slug: 'padel',
31
+ displayName: 'Padel',
32
+ summary: 'Check padel court availability and manage bookings via Playtomic.',
33
+ version: '0.1.0',
34
+ metadata: {
35
+ pilotbot: {
36
+ nix: {
37
+ plugin: 'github:joshp123/padel-cli',
38
+ systems: ['aarch64-darwin', 'x86_64-linux'],
39
+ },
40
+ config: {
41
+ requiredEnv: ['PADEL_AUTH_FILE'],
42
+ stateDirs: ['.config/padel'],
43
+ example:
44
+ 'config = { env = { PADEL_AUTH_FILE = "/run/agenix/padel-auth"; }; stateDirs = [ ".config/padel" ]; };',
45
+ },
46
+ cliHelp: `Padel CLI for availability
47
+
48
+ Usage:
49
+ padel [command]
50
+
51
+ Available Commands:
52
+ auth Manage authentication
53
+ availability Show availability for a club on a date
54
+ book Book a court
55
+ bookings Manage bookings history
56
+ search Search for available courts
57
+ venues Manage saved venues
58
+
59
+ Flags:
60
+ -h, --help help for padel
61
+ --json Output JSON
62
+
63
+ Use "padel [command] --help" for more information about a command.
64
+ `,
65
+ },
66
+ },
67
+ rawSkillMd: `---
68
+ name: padel
69
+ description: Check padel court availability and manage bookings via the padel CLI.
70
+ ---
71
+
72
+ # Padel Booking Skill
73
+
74
+ ## CLI
75
+
76
+ \`\`\`bash
77
+ padel # On PATH (pilotbot plugin bundle)
78
+ \`\`\`
79
+
80
+ ## Venues
81
+
82
+ Use the configured venue list in order of preference. If no venues are configured, ask for a venue name or location.
83
+
84
+ ## Commands
85
+
86
+ ### Check next booking
87
+ \`\`\`bash
88
+ padel bookings list 2>&1 | head -3
89
+ \`\`\`
90
+
91
+ ### Search availability
92
+ \`\`\`bash
93
+ padel search --venues VENUE1,VENUE2 --date YYYY-MM-DD --time 09:00-12:00
94
+ \`\`\`
95
+
96
+ ## Response guidelines
97
+
98
+ - Keep responses concise.
99
+ - Use 🎾 emoji.
100
+ - End with a call to action.
101
+
102
+ ## Authorization
103
+
104
+ Only the authorized booker can confirm bookings. If the requester is not authorized, ask the authorized user to confirm.
105
+ `,
106
+ },
107
+ {
108
+ slug: 'gohome',
109
+ displayName: 'GoHome',
110
+ summary: 'Operate GoHome via gRPC discovery, metrics, and Grafana dashboards.',
111
+ version: '0.1.0',
112
+ metadata: {
113
+ pilotbot: {
114
+ nix: {
115
+ plugin: 'github:joshp123/gohome',
116
+ systems: ['x86_64-linux', 'aarch64-linux'],
117
+ },
118
+ config: {
119
+ requiredEnv: ['GOHOME_GRPC_ADDR', 'GOHOME_HTTP_BASE'],
120
+ example:
121
+ 'config = { env = { GOHOME_GRPC_ADDR = "gohome:9000"; GOHOME_HTTP_BASE = "http://gohome:8080"; }; };',
122
+ },
123
+ cliHelp: `GoHome CLI
124
+
125
+ Usage:
126
+ gohome-cli [command]
127
+
128
+ Available Commands:
129
+ services List registered services
130
+ plugins Inspect loaded plugins
131
+ methods List RPC methods
132
+ call Call an RPC method
133
+ roborock Manage roborock devices
134
+ tado Manage tado zones
135
+
136
+ Flags:
137
+ --grpc-addr string gRPC endpoint (host:port)
138
+ -h, --help help for gohome-cli
139
+ `,
140
+ },
141
+ },
142
+ rawSkillMd: `---
143
+ name: gohome
144
+ description: Use when Pilotbot needs to test or operate GoHome via gRPC discovery, metrics, and Grafana.
145
+ ---
146
+
147
+ # GoHome Skill
148
+
149
+ ## Quick start
150
+
151
+ \`\`\`bash
152
+ export GOHOME_HTTP_BASE="http://gohome:8080"
153
+ export GOHOME_GRPC_ADDR="gohome:9000"
154
+ \`\`\`
155
+
156
+ ## CLI
157
+
158
+ \`\`\`bash
159
+ gohome-cli services
160
+ \`\`\`
161
+
162
+ ## Discovery flow (read-only)
163
+
164
+ 1) List plugins.
165
+ 2) Describe a plugin.
166
+ 3) List RPC methods.
167
+ 4) Call a read-only RPC.
168
+
169
+ ## Metrics validation
170
+
171
+ \`\`\`bash
172
+ curl -s "\${GOHOME_HTTP_BASE}/gohome/metrics" | rg -n "gohome_"
173
+ \`\`\`
174
+
175
+ ## Stateful actions
176
+
177
+ Only call write RPCs after explicit user approval.
178
+ `,
179
+ },
180
+ {
181
+ slug: 'xuezh',
182
+ displayName: 'Xuezh',
183
+ summary: 'Teach Mandarin with the xuezh engine for review, speaking, and audits.',
184
+ version: '0.1.0',
185
+ metadata: {
186
+ pilotbot: {
187
+ nix: {
188
+ plugin: 'github:joshp123/xuezh',
189
+ systems: ['aarch64-darwin', 'x86_64-linux'],
190
+ },
191
+ config: {
192
+ requiredEnv: ['XUEZH_AZURE_SPEECH_KEY_FILE', 'XUEZH_AZURE_SPEECH_REGION'],
193
+ stateDirs: ['.config/xuezh'],
194
+ example:
195
+ 'config = { env = { XUEZH_AZURE_SPEECH_KEY_FILE = "/run/agenix/xuezh-azure-speech-key"; XUEZH_AZURE_SPEECH_REGION = "westeurope"; }; stateDirs = [ ".config/xuezh" ]; };',
196
+ },
197
+ cliHelp: `xuezh - Chinese learning engine
198
+
199
+ Usage:
200
+ xuezh [command]
201
+
202
+ Available Commands:
203
+ snapshot Fetch learner state snapshot
204
+ review Review due items
205
+ audio Process speech audio
206
+ items Manage learning items
207
+ events Log learning events
208
+
209
+ Flags:
210
+ -h, --help help for xuezh
211
+ --json Output JSON
212
+ `,
213
+ },
214
+ },
215
+ rawSkillMd: `---
216
+ name: xuezh
217
+ description: Teach Mandarin using the xuezh engine for review, speaking, and audits.
218
+ ---
219
+
220
+ # Xuezh Skill
221
+
222
+ ## Contract
223
+
224
+ Use the xuezh CLI exactly as specified. If a command is missing, ask for implementation instead of guessing.
225
+
226
+ ## Default loop
227
+
228
+ 1) Call \`xuezh snapshot\`.
229
+ 2) Pick a tiny plan (1-2 bullets).
230
+ 3) Run a short activity.
231
+ 4) Log outcomes.
232
+
233
+ ## CLI examples
234
+
235
+ \`\`\`bash
236
+ xuezh snapshot --profile default
237
+ xuezh review next --limit 10
238
+ xuezh audio process-voice --file ./utterance.wav
239
+ \`\`\`
240
+ `,
241
+ },
242
+ ]
243
+
244
+ function injectMetadata(rawSkillMd: string, metadata: Record<string, unknown>) {
245
+ const frontmatterEnd = rawSkillMd.indexOf('\n---', 3)
246
+ if (frontmatterEnd === -1) return rawSkillMd
247
+ return `${rawSkillMd.slice(0, frontmatterEnd)}\nmetadata: ${JSON.stringify(
248
+ metadata,
249
+ )}${rawSkillMd.slice(frontmatterEnd)}`
250
+ }
251
+
252
+ async function seedNixSkillsHandler(
253
+ ctx: ActionCtx,
254
+ args: SeedActionArgs,
255
+ ): Promise<SeedActionResult> {
256
+ const results: Array<Record<string, unknown> & { slug: string }> = []
257
+
258
+ for (const spec of SEED_SKILLS) {
259
+ const skillMd = injectMetadata(spec.rawSkillMd, spec.metadata)
260
+ const frontmatter = parseFrontmatter(skillMd)
261
+ const pilotbot = parsePilotbotMetadata(frontmatter)
262
+ const storageId = await ctx.storage.store(new Blob([skillMd], { type: 'text/markdown' }))
263
+
264
+ const result: SeedMutationResult = await ctx.runMutation(internal.devSeed.seedSkillMutation, {
265
+ reset: args.reset,
266
+ storageId,
267
+ metadata: spec.metadata,
268
+ frontmatter,
269
+ pilotbot,
270
+ skillMd,
271
+ slug: spec.slug,
272
+ displayName: spec.displayName,
273
+ summary: spec.summary,
274
+ version: spec.version,
275
+ })
276
+
277
+ results.push({ slug: spec.slug, ...result })
278
+ }
279
+
280
+ return { ok: true, results }
281
+ }
282
+
283
+ export const seedNixSkills: ReturnType<typeof internalAction> = internalAction({
284
+ args: {
285
+ reset: v.optional(v.boolean()),
286
+ },
287
+ handler: seedNixSkillsHandler,
288
+ })
289
+
290
+ async function seedPadelSkillHandler(
291
+ ctx: ActionCtx,
292
+ args: SeedActionArgs,
293
+ ): Promise<SeedMutationResult> {
294
+ const spec = SEED_SKILLS.find((entry) => entry.slug === 'padel')
295
+ if (!spec) throw new Error('padel seed spec missing')
296
+
297
+ const skillMd = injectMetadata(spec.rawSkillMd, spec.metadata)
298
+ const frontmatter = parseFrontmatter(skillMd)
299
+ const pilotbot = parsePilotbotMetadata(frontmatter)
300
+ const storageId = await ctx.storage.store(new Blob([skillMd], { type: 'text/markdown' }))
301
+
302
+ return (await ctx.runMutation(internal.devSeed.seedSkillMutation, {
303
+ reset: args.reset,
304
+ storageId,
305
+ metadata: spec.metadata,
306
+ frontmatter,
307
+ pilotbot,
308
+ skillMd,
309
+ slug: spec.slug,
310
+ displayName: spec.displayName,
311
+ summary: spec.summary,
312
+ version: spec.version,
313
+ })) as SeedMutationResult
314
+ }
315
+
316
+ export const seedPadelSkill: ReturnType<typeof internalAction> = internalAction({
317
+ args: {
318
+ reset: v.optional(v.boolean()),
319
+ },
320
+ handler: seedPadelSkillHandler,
321
+ })
322
+
323
+ export const seedSkillMutation = internalMutation({
324
+ args: {
325
+ reset: v.optional(v.boolean()),
326
+ storageId: v.id('_storage'),
327
+ metadata: v.any(),
328
+ frontmatter: v.any(),
329
+ pilotbot: v.any(),
330
+ skillMd: v.string(),
331
+ slug: v.string(),
332
+ displayName: v.string(),
333
+ summary: v.optional(v.string()),
334
+ version: v.string(),
335
+ },
336
+ handler: async (ctx, args) => {
337
+ const existing = await ctx.db
338
+ .query('skills')
339
+ .withIndex('by_slug', (q) => q.eq('slug', args.slug))
340
+ .unique()
341
+
342
+ if (existing && !args.reset) {
343
+ return { ok: true, skipped: true, skillId: existing._id }
344
+ }
345
+
346
+ if (existing && args.reset) {
347
+ const versions = await ctx.db
348
+ .query('skillVersions')
349
+ .withIndex('by_skill', (q) => q.eq('skillId', existing._id))
350
+ .collect()
351
+ for (const version of versions) {
352
+ await ctx.db.delete(version._id)
353
+ }
354
+ const embeddings = await ctx.db
355
+ .query('skillEmbeddings')
356
+ .withIndex('by_skill', (q) => q.eq('skillId', existing._id))
357
+ .collect()
358
+ for (const embedding of embeddings) {
359
+ await ctx.db.delete(embedding._id)
360
+ }
361
+ await ctx.db.delete(existing._id)
362
+ }
363
+
364
+ const now = Date.now()
365
+ const existingUsers = await ctx.db
366
+ .query('users')
367
+ .withIndex('handle', (q) => q.eq('handle', 'local'))
368
+ .collect()
369
+
370
+ const userId =
371
+ existingUsers[0]?._id ??
372
+ (await ctx.db.insert('users', {
373
+ handle: 'local',
374
+ displayName: 'Local Dev',
375
+ role: 'admin',
376
+ createdAt: now,
377
+ updatedAt: now,
378
+ }))
379
+
380
+ const skillId = await ctx.db.insert('skills', {
381
+ slug: args.slug,
382
+ displayName: args.displayName,
383
+ summary: args.summary,
384
+ ownerUserId: userId,
385
+ latestVersionId: undefined,
386
+ tags: {},
387
+ softDeletedAt: undefined,
388
+ badges: { redactionApproved: undefined },
389
+ statsDownloads: 0,
390
+ statsStars: 0,
391
+ statsInstallsCurrent: 0,
392
+ statsInstallsAllTime: 0,
393
+ stats: {
394
+ downloads: 0,
395
+ installsCurrent: 0,
396
+ installsAllTime: 0,
397
+ stars: 0,
398
+ versions: 0,
399
+ comments: 0,
400
+ },
401
+ createdAt: now,
402
+ updatedAt: now,
403
+ })
404
+
405
+ const versionId = await ctx.db.insert('skillVersions', {
406
+ skillId,
407
+ version: args.version,
408
+ changelog: 'Seeded local version for screenshots.',
409
+ files: [
410
+ {
411
+ path: 'SKILL.md',
412
+ size: args.skillMd.length,
413
+ storageId: args.storageId,
414
+ sha256: 'seeded',
415
+ contentType: 'text/markdown',
416
+ },
417
+ ],
418
+ parsed: {
419
+ frontmatter: args.frontmatter,
420
+ metadata: args.metadata,
421
+ pilotbot: args.pilotbot,
422
+ },
423
+ createdBy: userId,
424
+ createdAt: now,
425
+ softDeletedAt: undefined,
426
+ })
427
+
428
+ const embeddingId = await ctx.db.insert('skillEmbeddings', {
429
+ skillId,
430
+ versionId,
431
+ ownerId: userId,
432
+ embedding: Array.from({ length: EMBEDDING_DIMENSIONS }, () => 0),
433
+ isLatest: true,
434
+ isApproved: true,
435
+ visibility: 'latest-approved',
436
+ updatedAt: now,
437
+ })
438
+
439
+ await ctx.db.patch(skillId, {
440
+ latestVersionId: versionId,
441
+ tags: { latest: versionId },
442
+ statsDownloads: 0,
443
+ statsStars: 0,
444
+ statsInstallsCurrent: 0,
445
+ statsInstallsAllTime: 0,
446
+ stats: {
447
+ downloads: 0,
448
+ installsCurrent: 0,
449
+ installsAllTime: 0,
450
+ stars: 0,
451
+ versions: 1,
452
+ comments: 0,
453
+ },
454
+ updatedAt: now,
455
+ })
456
+
457
+ return { ok: true, skillId, versionId, embeddingId }
458
+ },
459
+ })