pilothub 0.0.1 → 0.0.2

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 (388) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +36 -129
  3. package/dist/browserAuth.d.ts +20 -0
  4. package/dist/browserAuth.js +156 -0
  5. package/dist/browserAuth.js.map +1 -0
  6. package/dist/browserAuth.test.d.ts +1 -0
  7. package/dist/browserAuth.test.js +83 -0
  8. package/dist/browserAuth.test.js.map +1 -0
  9. package/dist/cli/buildInfo.d.ts +3 -0
  10. package/dist/cli/buildInfo.js +103 -0
  11. package/dist/cli/buildInfo.js.map +1 -0
  12. package/dist/cli/commands/auth.d.ts +9 -0
  13. package/dist/cli/commands/auth.js +75 -0
  14. package/dist/cli/commands/auth.js.map +1 -0
  15. package/dist/cli/commands/delete.d.ts +11 -0
  16. package/dist/cli/commands/delete.js +67 -0
  17. package/dist/cli/commands/delete.js.map +1 -0
  18. package/dist/cli/commands/delete.test.d.ts +1 -0
  19. package/dist/cli/commands/delete.test.js +52 -0
  20. package/dist/cli/commands/delete.test.js.map +1 -0
  21. package/dist/cli/commands/publish.d.ts +9 -0
  22. package/dist/cli/commands/publish.js +87 -0
  23. package/dist/cli/commands/publish.js.map +1 -0
  24. package/dist/cli/commands/publish.test.d.ts +1 -0
  25. package/dist/cli/commands/publish.test.js +104 -0
  26. package/dist/cli/commands/publish.test.js.map +1 -0
  27. package/dist/cli/commands/skills.d.ts +23 -0
  28. package/dist/cli/commands/skills.js +298 -0
  29. package/dist/cli/commands/skills.js.map +1 -0
  30. package/dist/cli/commands/skills.test.d.ts +1 -0
  31. package/dist/cli/commands/skills.test.js +156 -0
  32. package/dist/cli/commands/skills.test.js.map +1 -0
  33. package/dist/cli/commands/star.d.ts +8 -0
  34. package/dist/cli/commands/star.js +38 -0
  35. package/dist/cli/commands/star.js.map +1 -0
  36. package/dist/cli/commands/sync.d.ts +3 -0
  37. package/dist/cli/commands/sync.js +160 -0
  38. package/dist/cli/commands/sync.js.map +1 -0
  39. package/dist/cli/commands/sync.test.d.ts +1 -0
  40. package/dist/cli/commands/sync.test.js +277 -0
  41. package/dist/cli/commands/sync.test.js.map +1 -0
  42. package/dist/cli/commands/syncHelpers.d.ts +76 -0
  43. package/dist/cli/commands/syncHelpers.js +349 -0
  44. package/dist/cli/commands/syncHelpers.js.map +1 -0
  45. package/dist/cli/commands/syncHelpers.test.d.ts +1 -0
  46. package/dist/cli/commands/syncHelpers.test.js +22 -0
  47. package/dist/cli/commands/syncHelpers.test.js.map +1 -0
  48. package/dist/cli/commands/syncTypes.d.ts +24 -0
  49. package/dist/cli/commands/syncTypes.js +2 -0
  50. package/dist/cli/commands/syncTypes.js.map +1 -0
  51. package/dist/cli/commands/unstar.d.ts +8 -0
  52. package/dist/cli/commands/unstar.js +38 -0
  53. package/dist/cli/commands/unstar.js.map +1 -0
  54. package/dist/cli/helpStyle.d.ts +13 -0
  55. package/dist/cli/helpStyle.js +38 -0
  56. package/dist/cli/helpStyle.js.map +1 -0
  57. package/dist/cli/pilotbotConfig.d.ts +6 -0
  58. package/dist/cli/pilotbotConfig.js +110 -0
  59. package/dist/cli/pilotbotConfig.js.map +1 -0
  60. package/dist/cli/pilotbotConfig.test.d.ts +1 -0
  61. package/dist/cli/pilotbotConfig.test.js +133 -0
  62. package/dist/cli/pilotbotConfig.test.js.map +1 -0
  63. package/dist/cli/registry.d.ts +7 -0
  64. package/dist/cli/registry.js +42 -0
  65. package/dist/cli/registry.js.map +1 -0
  66. package/dist/cli/registry.test.d.ts +1 -0
  67. package/dist/cli/registry.test.js +48 -0
  68. package/dist/cli/registry.test.js.map +1 -0
  69. package/dist/cli/scanSkills.d.ts +7 -0
  70. package/dist/cli/scanSkills.js +75 -0
  71. package/dist/cli/scanSkills.js.map +1 -0
  72. package/dist/cli/scanSkills.test.d.ts +1 -0
  73. package/dist/cli/scanSkills.test.js +60 -0
  74. package/dist/cli/scanSkills.test.js.map +1 -0
  75. package/dist/cli/slug.d.ts +2 -0
  76. package/dist/cli/slug.js +16 -0
  77. package/dist/cli/slug.js.map +1 -0
  78. package/dist/cli/types.d.ts +15 -0
  79. package/dist/cli/types.js +2 -0
  80. package/dist/cli/types.js.map +1 -0
  81. package/dist/cli/ui.d.ts +7 -0
  82. package/dist/cli/ui.js +72 -0
  83. package/dist/cli/ui.js.map +1 -0
  84. package/dist/cli.d.ts +2 -0
  85. package/dist/cli.js +268 -0
  86. package/dist/cli.js.map +1 -0
  87. package/dist/config.d.ts +4 -0
  88. package/dist/config.js +38 -0
  89. package/dist/config.js.map +1 -0
  90. package/dist/discovery.d.ts +5 -0
  91. package/dist/discovery.js +21 -0
  92. package/dist/discovery.js.map +1 -0
  93. package/dist/discovery.test.d.ts +1 -0
  94. package/dist/discovery.test.js +46 -0
  95. package/dist/discovery.test.js.map +1 -0
  96. package/dist/http.d.ts +32 -0
  97. package/dist/http.js +261 -0
  98. package/dist/http.js.map +1 -0
  99. package/dist/http.test.d.ts +1 -0
  100. package/dist/http.test.js +135 -0
  101. package/dist/http.test.js.map +1 -0
  102. package/dist/schema/ark.js.map +1 -0
  103. package/dist/schema/index.js.map +1 -0
  104. package/dist/schema/routes.js.map +1 -0
  105. package/{packages/schema/dist → dist/schema}/schemas.d.ts +0 -39
  106. package/{packages/schema/dist → dist/schema}/schemas.js +0 -22
  107. package/dist/schema/schemas.js.map +1 -0
  108. package/dist/schema/textFiles.js.map +1 -0
  109. package/dist/schema/textFiles.test.d.ts +1 -0
  110. package/dist/schema/textFiles.test.js +20 -0
  111. package/dist/schema/textFiles.test.js.map +1 -0
  112. package/dist/skills.d.ts +43 -0
  113. package/dist/skills.js +163 -0
  114. package/dist/skills.js.map +1 -0
  115. package/dist/skills.test.d.ts +1 -0
  116. package/dist/skills.test.js +144 -0
  117. package/dist/skills.test.js.map +1 -0
  118. package/dist/types.d.ts +7 -0
  119. package/dist/types.js +2 -0
  120. package/dist/types.js.map +1 -0
  121. package/package.json +27 -70
  122. package/.env.local.example +0 -19
  123. package/.github/workflows/ci.yml +0 -40
  124. package/.oxlintrc.json +0 -3
  125. package/AGENTS.md +0 -45
  126. package/CHANGELOG.md +0 -138
  127. package/DEPRECATIONS.md +0 -7
  128. package/biome.json +0 -41
  129. package/convex/_generated/api.d.ts +0 -153
  130. package/convex/_generated/api.js +0 -23
  131. package/convex/_generated/dataModel.d.ts +0 -60
  132. package/convex/_generated/server.d.ts +0 -143
  133. package/convex/_generated/server.js +0 -93
  134. package/convex/auth.config.ts +0 -8
  135. package/convex/auth.ts +0 -19
  136. package/convex/comments.ts +0 -88
  137. package/convex/crons.ts +0 -34
  138. package/convex/devSeed.ts +0 -459
  139. package/convex/devSeedExtra.ts +0 -541
  140. package/convex/downloads.ts +0 -78
  141. package/convex/githubBackups.ts +0 -170
  142. package/convex/githubBackupsNode.ts +0 -183
  143. package/convex/githubImport.ts +0 -317
  144. package/convex/githubSoulBackups.ts +0 -170
  145. package/convex/githubSoulBackupsNode.ts +0 -186
  146. package/convex/http.ts +0 -194
  147. package/convex/httpApi.handlers.test.ts +0 -488
  148. package/convex/httpApi.test.ts +0 -70
  149. package/convex/httpApi.ts +0 -305
  150. package/convex/httpApiV1.handlers.test.ts +0 -584
  151. package/convex/httpApiV1.ts +0 -1172
  152. package/convex/leaderboards.ts +0 -39
  153. package/convex/lib/access.ts +0 -36
  154. package/convex/lib/apiTokenAuth.ts +0 -36
  155. package/convex/lib/badges.ts +0 -50
  156. package/convex/lib/changelog.test.ts +0 -34
  157. package/convex/lib/changelog.ts +0 -278
  158. package/convex/lib/embeddings.ts +0 -38
  159. package/convex/lib/githubBackup.ts +0 -443
  160. package/convex/lib/githubImport.test.ts +0 -247
  161. package/convex/lib/githubImport.ts +0 -425
  162. package/convex/lib/githubSoulBackup.ts +0 -443
  163. package/convex/lib/leaderboards.ts +0 -103
  164. package/convex/lib/moderation.ts +0 -42
  165. package/convex/lib/public.ts +0 -89
  166. package/convex/lib/searchText.test.ts +0 -46
  167. package/convex/lib/searchText.ts +0 -27
  168. package/convex/lib/skillBackfill.test.ts +0 -34
  169. package/convex/lib/skillBackfill.ts +0 -67
  170. package/convex/lib/skillPublish.test.ts +0 -28
  171. package/convex/lib/skillPublish.ts +0 -284
  172. package/convex/lib/skillStats.ts +0 -80
  173. package/convex/lib/skills.test.ts +0 -197
  174. package/convex/lib/skills.ts +0 -273
  175. package/convex/lib/soulChangelog.ts +0 -273
  176. package/convex/lib/soulPublish.ts +0 -236
  177. package/convex/lib/tokens.test.ts +0 -33
  178. package/convex/lib/tokens.ts +0 -51
  179. package/convex/lib/webhooks.test.ts +0 -91
  180. package/convex/lib/webhooks.ts +0 -112
  181. package/convex/maintenance.test.ts +0 -270
  182. package/convex/maintenance.ts +0 -840
  183. package/convex/rateLimits.ts +0 -50
  184. package/convex/schema.ts +0 -472
  185. package/convex/search.test.ts +0 -12
  186. package/convex/search.ts +0 -254
  187. package/convex/seed.test.ts +0 -37
  188. package/convex/seed.ts +0 -254
  189. package/convex/seedSouls.ts +0 -111
  190. package/convex/skillStatEvents.ts +0 -568
  191. package/convex/skills.ts +0 -1606
  192. package/convex/soulComments.ts +0 -88
  193. package/convex/soulDownloads.ts +0 -14
  194. package/convex/soulStars.ts +0 -71
  195. package/convex/souls.ts +0 -570
  196. package/convex/stars.ts +0 -108
  197. package/convex/statsMaintenance.ts +0 -205
  198. package/convex/telemetry.ts +0 -434
  199. package/convex/tokens.ts +0 -88
  200. package/convex/tsconfig.json +0 -7
  201. package/convex/uploads.ts +0 -20
  202. package/convex/users.ts +0 -122
  203. package/convex/webhooks.ts +0 -50
  204. package/convex.json +0 -3
  205. package/docs/README.md +0 -32
  206. package/docs/api.md +0 -51
  207. package/docs/architecture.md +0 -61
  208. package/docs/auth.md +0 -54
  209. package/docs/cli.md +0 -117
  210. package/docs/deploy.md +0 -78
  211. package/docs/diffing.md +0 -84
  212. package/docs/github-import.md +0 -171
  213. package/docs/http-api.md +0 -187
  214. package/docs/manual-testing.md +0 -64
  215. package/docs/mintlify.md +0 -43
  216. package/docs/quickstart.md +0 -120
  217. package/docs/skill-format.md +0 -58
  218. package/docs/soul-format.md +0 -37
  219. package/docs/spec.md +0 -177
  220. package/docs/telemetry.md +0 -91
  221. package/docs/troubleshooting.md +0 -49
  222. package/docs/webhook.md +0 -51
  223. package/e2e/menu-smoke.pw.test.ts +0 -49
  224. package/e2e/pilothub.e2e.test.ts +0 -494
  225. package/e2e/search-exact.pw.test.ts +0 -97
  226. package/packages/pilothub/LICENSE +0 -22
  227. package/packages/pilothub/README.md +0 -57
  228. package/packages/pilothub/package.json +0 -41
  229. package/packages/pilothub/src/browserAuth.test.ts +0 -96
  230. package/packages/pilothub/src/browserAuth.ts +0 -174
  231. package/packages/pilothub/src/cli/buildInfo.ts +0 -94
  232. package/packages/pilothub/src/cli/commands/auth.ts +0 -97
  233. package/packages/pilothub/src/cli/commands/delete.test.ts +0 -73
  234. package/packages/pilothub/src/cli/commands/delete.ts +0 -83
  235. package/packages/pilothub/src/cli/commands/publish.test.ts +0 -122
  236. package/packages/pilothub/src/cli/commands/publish.ts +0 -108
  237. package/packages/pilothub/src/cli/commands/skills.test.ts +0 -191
  238. package/packages/pilothub/src/cli/commands/skills.ts +0 -380
  239. package/packages/pilothub/src/cli/commands/star.ts +0 -46
  240. package/packages/pilothub/src/cli/commands/sync.test.ts +0 -310
  241. package/packages/pilothub/src/cli/commands/sync.ts +0 -200
  242. package/packages/pilothub/src/cli/commands/syncHelpers.test.ts +0 -26
  243. package/packages/pilothub/src/cli/commands/syncHelpers.ts +0 -427
  244. package/packages/pilothub/src/cli/commands/syncTypes.ts +0 -27
  245. package/packages/pilothub/src/cli/commands/unstar.ts +0 -48
  246. package/packages/pilothub/src/cli/helpStyle.ts +0 -45
  247. package/packages/pilothub/src/cli/pilotbotConfig.test.ts +0 -159
  248. package/packages/pilothub/src/cli/pilotbotConfig.ts +0 -147
  249. package/packages/pilothub/src/cli/registry.test.ts +0 -63
  250. package/packages/pilothub/src/cli/registry.ts +0 -43
  251. package/packages/pilothub/src/cli/scanSkills.test.ts +0 -64
  252. package/packages/pilothub/src/cli/scanSkills.ts +0 -84
  253. package/packages/pilothub/src/cli/slug.ts +0 -16
  254. package/packages/pilothub/src/cli/types.ts +0 -12
  255. package/packages/pilothub/src/cli/ui.ts +0 -75
  256. package/packages/pilothub/src/cli.ts +0 -311
  257. package/packages/pilothub/src/config.ts +0 -36
  258. package/packages/pilothub/src/discovery.test.ts +0 -75
  259. package/packages/pilothub/src/discovery.ts +0 -19
  260. package/packages/pilothub/src/http.test.ts +0 -156
  261. package/packages/pilothub/src/http.ts +0 -301
  262. package/packages/pilothub/src/schema/ark.ts +0 -29
  263. package/packages/pilothub/src/schema/index.ts +0 -5
  264. package/packages/pilothub/src/schema/routes.ts +0 -22
  265. package/packages/pilothub/src/schema/schemas.ts +0 -260
  266. package/packages/pilothub/src/schema/textFiles.test.ts +0 -23
  267. package/packages/pilothub/src/schema/textFiles.ts +0 -66
  268. package/packages/pilothub/src/skills.test.ts +0 -191
  269. package/packages/pilothub/src/skills.ts +0 -172
  270. package/packages/pilothub/src/types.ts +0 -10
  271. package/packages/pilothub/tsconfig.json +0 -14
  272. package/packages/schema/README.md +0 -3
  273. package/packages/schema/dist/ark.js.map +0 -1
  274. package/packages/schema/dist/index.js.map +0 -1
  275. package/packages/schema/dist/routes.js.map +0 -1
  276. package/packages/schema/dist/schemas.js.map +0 -1
  277. package/packages/schema/dist/textFiles.js.map +0 -1
  278. package/packages/schema/package.json +0 -26
  279. package/packages/schema/src/ark.ts +0 -29
  280. package/packages/schema/src/index.ts +0 -5
  281. package/packages/schema/src/routes.ts +0 -22
  282. package/packages/schema/src/schemas.test.ts +0 -123
  283. package/packages/schema/src/schemas.ts +0 -287
  284. package/packages/schema/src/textFiles.test.ts +0 -23
  285. package/packages/schema/src/textFiles.ts +0 -66
  286. package/packages/schema/tsconfig.json +0 -15
  287. package/pilothub +0 -46
  288. package/playwright.config.ts +0 -33
  289. package/public/.well-known/pilothub.json +0 -6
  290. package/public/api/v1/openapi.json +0 -379
  291. package/public/favicon.ico +0 -0
  292. package/public/logo192.png +0 -0
  293. package/public/logo512.png +0 -0
  294. package/public/manifest.json +0 -25
  295. package/public/og.png +0 -0
  296. package/public/og.svg +0 -98
  297. package/public/pilot-logo.png +0 -0
  298. package/public/pilot-mark.png +0 -0
  299. package/public/robots.txt +0 -3
  300. package/public/tanstack-circle-logo.png +0 -0
  301. package/public/tanstack-word-logo-white.svg +0 -1
  302. package/scripts/check-peer-deps.ts +0 -56
  303. package/scripts/docs-list.ts +0 -148
  304. package/scripts/run-playwright-local.sh +0 -14
  305. package/server/og/fetchSkillOgMeta.ts +0 -27
  306. package/server/og/fetchSoulOgMeta.ts +0 -27
  307. package/server/og/ogAssets.ts +0 -80
  308. package/server/og/skillOgSvg.test.ts +0 -59
  309. package/server/og/skillOgSvg.ts +0 -258
  310. package/server/og/soulOgSvg.ts +0 -209
  311. package/server/routes/og/skill.png.ts +0 -103
  312. package/server/routes/og/soul.png.ts +0 -111
  313. package/src/__tests__/skill-detail-page.test.tsx +0 -86
  314. package/src/__tests__/skills-index.test.tsx +0 -145
  315. package/src/__tests__/upload.route.test.tsx +0 -228
  316. package/src/components/AppProviders.tsx +0 -19
  317. package/src/components/ClientOnly.tsx +0 -18
  318. package/src/components/Footer.tsx +0 -29
  319. package/src/components/Header.tsx +0 -295
  320. package/src/components/InstallSwitcher.tsx +0 -53
  321. package/src/components/SkillCard.tsx +0 -36
  322. package/src/components/SkillDetailPage.tsx +0 -817
  323. package/src/components/SkillDiffCard.tsx +0 -485
  324. package/src/components/SoulCard.tsx +0 -19
  325. package/src/components/SoulDetailPage.tsx +0 -263
  326. package/src/components/UserBootstrap.tsx +0 -18
  327. package/src/components/ui/dropdown-menu.tsx +0 -67
  328. package/src/components/ui/toggle-group.tsx +0 -35
  329. package/src/convex/client.ts +0 -3
  330. package/src/lib/badges.ts +0 -29
  331. package/src/lib/diffing.test.ts +0 -163
  332. package/src/lib/diffing.ts +0 -106
  333. package/src/lib/gravatar.test.ts +0 -9
  334. package/src/lib/gravatar.ts +0 -158
  335. package/src/lib/og.test.ts +0 -142
  336. package/src/lib/og.ts +0 -156
  337. package/src/lib/publicUser.ts +0 -39
  338. package/src/lib/roles.ts +0 -19
  339. package/src/lib/site.test.ts +0 -130
  340. package/src/lib/site.ts +0 -84
  341. package/src/lib/theme-transition.test.ts +0 -134
  342. package/src/lib/theme-transition.ts +0 -134
  343. package/src/lib/theme.test.tsx +0 -88
  344. package/src/lib/theme.ts +0 -43
  345. package/src/lib/uploadFiles.jsdom.test.ts +0 -33
  346. package/src/lib/uploadFiles.test.ts +0 -123
  347. package/src/lib/uploadFiles.ts +0 -245
  348. package/src/lib/uploadUtils.test.ts +0 -78
  349. package/src/lib/uploadUtils.ts +0 -93
  350. package/src/lib/useAuthStatus.ts +0 -12
  351. package/src/lib/utils.test.ts +0 -9
  352. package/src/lib/utils.ts +0 -6
  353. package/src/logo.svg +0 -12
  354. package/src/routeTree.gen.ts +0 -345
  355. package/src/router.tsx +0 -17
  356. package/src/routes/$owner/$slug.tsx +0 -55
  357. package/src/routes/__root.tsx +0 -136
  358. package/src/routes/admin.tsx +0 -11
  359. package/src/routes/cli/auth.tsx +0 -168
  360. package/src/routes/dashboard.tsx +0 -97
  361. package/src/routes/import.tsx +0 -415
  362. package/src/routes/index.tsx +0 -252
  363. package/src/routes/management.tsx +0 -529
  364. package/src/routes/settings.tsx +0 -203
  365. package/src/routes/skills/index.tsx +0 -422
  366. package/src/routes/souls/$slug.tsx +0 -55
  367. package/src/routes/souls/index.tsx +0 -243
  368. package/src/routes/stars.tsx +0 -68
  369. package/src/routes/u/$handle.tsx +0 -307
  370. package/src/routes/upload/utils.ts +0 -81
  371. package/src/routes/upload.tsx +0 -499
  372. package/src/styles.css +0 -2718
  373. package/tsconfig.json +0 -24
  374. package/tsconfig.oxlint.json +0 -16
  375. package/vercel.json +0 -8
  376. package/vite.config.ts +0 -48
  377. package/vitest.config.ts +0 -47
  378. package/vitest.e2e.config.ts +0 -11
  379. package/vitest.setup.ts +0 -1
  380. /package/{packages/pilothub/bin → bin}/pilothub.js +0 -0
  381. /package/{packages/schema/dist → dist/schema}/ark.d.ts +0 -0
  382. /package/{packages/schema/dist → dist/schema}/ark.js +0 -0
  383. /package/{packages/schema/dist → dist/schema}/index.d.ts +0 -0
  384. /package/{packages/schema/dist → dist/schema}/index.js +0 -0
  385. /package/{packages/schema/dist → dist/schema}/routes.d.ts +0 -0
  386. /package/{packages/schema/dist → dist/schema}/routes.js +0 -0
  387. /package/{packages/schema/dist → dist/schema}/textFiles.d.ts +0 -0
  388. /package/{packages/schema/dist → dist/schema}/textFiles.js +0 -0
@@ -1,197 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import {
3
- buildEmbeddingText,
4
- getFrontmatterMetadata,
5
- getFrontmatterValue,
6
- hashSkillFiles,
7
- isTextFile,
8
- parseFrontmatter,
9
- parsePilotbotMetadata,
10
- sanitizePath,
11
- } from './skills'
12
-
13
- describe('skills utils', () => {
14
- it('parses frontmatter', () => {
15
- const frontmatter = parseFrontmatter(`---\nname: demo\ndescription: Hello\n---\nBody`)
16
- expect(frontmatter.name).toBe('demo')
17
- expect(frontmatter.description).toBe('Hello')
18
- })
19
-
20
- it('handles missing or invalid frontmatter blocks', () => {
21
- expect(parseFrontmatter('nope')).toEqual({})
22
- expect(parseFrontmatter('---\nname: demo\nBody without end')).toEqual({})
23
- })
24
-
25
- it('strips quotes in frontmatter values', () => {
26
- const frontmatter = parseFrontmatter(`---\nname: "demo"\ndescription: 'Hello'\n---\nBody`)
27
- expect(frontmatter.name).toBe('demo')
28
- expect(frontmatter.description).toBe('Hello')
29
- })
30
-
31
- it('parses block scalars in frontmatter', () => {
32
- const folded = parseFrontmatter(
33
- `---\nname: demo\ndescription: >\n Hello\n world.\n\n Next paragraph.\n---\nBody`,
34
- )
35
- expect(folded.description).toBe('Hello world.\nNext paragraph.')
36
-
37
- const literal = parseFrontmatter(
38
- `---\nname: demo\ndescription: |\n Hello\n world.\n---\nBody`,
39
- )
40
- expect(literal.description).toBe('Hello\nworld.')
41
- })
42
-
43
- it('keeps structured YAML values in frontmatter', () => {
44
- const frontmatter = parseFrontmatter(
45
- `---\nname: demo\ncount: 3\nnums: [1, 2]\nobj:\n a: b\n---\nBody`,
46
- )
47
- expect(frontmatter.nums).toEqual([1, 2])
48
- expect(frontmatter.obj).toEqual({ a: 'b' })
49
- expect(frontmatter.name).toBe('demo')
50
- expect(frontmatter.count).toBe(3)
51
- expect(getFrontmatterValue(frontmatter, 'count')).toBeUndefined()
52
- })
53
-
54
- it('parses pilotbot metadata', () => {
55
- const frontmatter = parseFrontmatter(
56
- `---\nmetadata: {"pilotbot":{"requires":{"bins":["rg"]},"emoji":"🦞"}}\n---\nBody`,
57
- )
58
- const pilotbot = parsePilotbotMetadata(frontmatter)
59
- expect(pilotbot?.emoji).toBe('🦞')
60
- expect(pilotbot?.requires?.bins).toEqual(['rg'])
61
- })
62
-
63
- it('ignores invalid pilotbot metadata', () => {
64
- const frontmatter = parseFrontmatter(`---\nmetadata: not-json\n---\nBody`)
65
- expect(parsePilotbotMetadata(frontmatter)).toBeUndefined()
66
- })
67
-
68
- it('accepts metadata as YAML object (no JSON string)', () => {
69
- const frontmatter = parseFrontmatter(
70
- `---\nmetadata:\n pilotbot:\n emoji: "🦞"\n requires:\n bins:\n - rg\n---\nBody`,
71
- )
72
- expect(getFrontmatterMetadata(frontmatter)).toEqual({
73
- pilotbot: { emoji: '🦞', requires: { bins: ['rg'] } },
74
- })
75
- const pilotbot = parsePilotbotMetadata(frontmatter)
76
- expect(pilotbot?.emoji).toBe('🦞')
77
- expect(pilotbot?.requires?.bins).toEqual(['rg'])
78
- })
79
-
80
- it('accepts pilotbot as top-level YAML key', () => {
81
- const frontmatter = parseFrontmatter(
82
- `---\npilotbot:\n emoji: "🦞"\n requires:\n anyBins: [rg, fd]\n---\nBody`,
83
- )
84
- const pilotbot = parsePilotbotMetadata(frontmatter)
85
- expect(pilotbot?.emoji).toBe('🦞')
86
- expect(pilotbot?.requires?.anyBins).toEqual(['rg', 'fd'])
87
- })
88
-
89
- it('accepts legacy metadata JSON string (quoted)', () => {
90
- const frontmatter = parseFrontmatter(
91
- `---\nmetadata: '{"pilotbot":{"emoji":"🦞","requires":{"bins":["rg"]}}}'\n---\nBody`,
92
- )
93
- const metadata = getFrontmatterMetadata(frontmatter)
94
- expect(metadata).toEqual({ pilotbot: { emoji: '🦞', requires: { bins: ['rg'] } } })
95
- const pilotbot = parsePilotbotMetadata(frontmatter)
96
- expect(pilotbot?.emoji).toBe('🦞')
97
- expect(pilotbot?.requires?.bins).toEqual(['rg'])
98
- })
99
-
100
- it('parses pilotbot install specs and os', () => {
101
- const frontmatter = parseFrontmatter(
102
- `---\nmetadata: {"pilotbot":{"install":[{"kind":"brew","formula":"rg"},{"kind":"nope"},{"kind":"node","package":"x"}],"os":"macos,linux","requires":{"anyBins":["rg","fd"]}}}\n---\nBody`,
103
- )
104
- const pilotbot = parsePilotbotMetadata(frontmatter)
105
- expect(pilotbot?.install?.map((entry) => entry.kind)).toEqual(['brew', 'node'])
106
- expect(pilotbot?.os).toEqual(['macos', 'linux'])
107
- expect(pilotbot?.requires?.anyBins).toEqual(['rg', 'fd'])
108
- })
109
-
110
- it('parses pilotbot metadata with nix plugin pointer', () => {
111
- const frontmatter = parseFrontmatter(
112
- `---\nmetadata: {"pilotbot":{"nix":{"plugin":"github:pilotbot/nix-steipete-tools?dir=tools/peekaboo","systems":["aarch64-darwin"]}}}\n---\nBody`,
113
- )
114
- const pilotbot = parsePilotbotMetadata(frontmatter)
115
- expect(pilotbot?.nix?.plugin).toBe('github:pilotbot/nix-steipete-tools?dir=tools/peekaboo')
116
- expect(pilotbot?.nix?.systems).toEqual(['aarch64-darwin'])
117
- })
118
-
119
- it('parses pilotbot config requirements with example', () => {
120
- const frontmatter = parseFrontmatter(
121
- `---\nmetadata: {"pilotbot":{"config":{"requiredEnv":["PADEL_AUTH_FILE"],"stateDirs":[".config/padel"],"example":"config = { env = { PADEL_AUTH_FILE = \\"/run/agenix/padel-auth\\"; }; };"}}}\n---\nBody`,
122
- )
123
- const pilotbot = parsePilotbotMetadata(frontmatter)
124
- expect(pilotbot?.config?.requiredEnv).toEqual(['PADEL_AUTH_FILE'])
125
- expect(pilotbot?.config?.stateDirs).toEqual(['.config/padel'])
126
- expect(pilotbot?.config?.example).toBe(
127
- 'config = { env = { PADEL_AUTH_FILE = "/run/agenix/padel-auth"; }; };',
128
- )
129
- })
130
-
131
- it('parses cli help output', () => {
132
- const frontmatter = parseFrontmatter(
133
- `---\nmetadata: {"pilotbot":{"cliHelp":"padel --help\\nUsage: padel [command]\\n"}}\n---\nBody`,
134
- )
135
- const pilotbot = parsePilotbotMetadata(frontmatter)
136
- expect(pilotbot?.cliHelp).toBe('padel --help\nUsage: padel [command]')
137
- })
138
-
139
- it('sanitizes file paths', () => {
140
- expect(sanitizePath('good/file.md')).toBe('good/file.md')
141
- expect(sanitizePath('../bad/file.md')).toBeNull()
142
- expect(sanitizePath('/rooted.txt')).toBe('rooted.txt')
143
- expect(sanitizePath('bad\\path.txt')).toBeNull()
144
- expect(sanitizePath('')).toBeNull()
145
- })
146
-
147
- it('detects text files', () => {
148
- expect(isTextFile('SKILL.md')).toBe(true)
149
- expect(isTextFile('image.png')).toBe(false)
150
- expect(isTextFile('note.txt', 'text/plain')).toBe(true)
151
- expect(isTextFile('data.any', 'application/json')).toBe(true)
152
- expect(isTextFile('data.json')).toBe(true)
153
- })
154
-
155
- it('builds embedding text', () => {
156
- const frontmatter = { name: 'Demo', description: 'Hello' }
157
- const text = buildEmbeddingText({
158
- frontmatter,
159
- readme: 'Readme body',
160
- otherFiles: [{ path: 'a.txt', content: 'File text' }],
161
- })
162
- expect(text).toContain('Demo')
163
- expect(text).toContain('Readme body')
164
- expect(text).toContain('a.txt')
165
- })
166
-
167
- it('truncates embedding text by maxChars', () => {
168
- const text = buildEmbeddingText({
169
- frontmatter: {},
170
- readme: 'x'.repeat(50),
171
- otherFiles: [],
172
- maxChars: 10,
173
- })
174
- expect(text.length).toBe(10)
175
- })
176
-
177
- it('truncates embedding text by default max chars', () => {
178
- const text = buildEmbeddingText({
179
- frontmatter: {},
180
- readme: 'x'.repeat(40_000),
181
- otherFiles: [],
182
- })
183
- expect(text.length).toBeLessThanOrEqual(12_000)
184
- })
185
-
186
- it('hashes skill files deterministically', async () => {
187
- const a = await hashSkillFiles([
188
- { path: 'b.txt', sha256: 'b' },
189
- { path: 'a.txt', sha256: 'a' },
190
- ])
191
- const b = await hashSkillFiles([
192
- { path: 'a.txt', sha256: 'a' },
193
- { path: 'b.txt', sha256: 'b' },
194
- ])
195
- expect(a).toBe(b)
196
- })
197
- })
@@ -1,273 +0,0 @@
1
- import {
2
- isTextContentType,
3
- type PilotbotConfigSpec,
4
- type PilotbotSkillMetadata,
5
- PilotbotSkillMetadataSchema,
6
- type NixPluginSpec,
7
- parseArk,
8
- type SkillInstallSpec,
9
- TEXT_FILE_EXTENSION_SET,
10
- } from 'pilothub-schema'
11
- import { parse as parseYaml } from 'yaml'
12
-
13
- export type ParsedSkillFrontmatter = Record<string, unknown>
14
- export type { PilotbotSkillMetadata, SkillInstallSpec }
15
-
16
- const FRONTMATTER_START = '---'
17
- const DEFAULT_EMBEDDING_MAX_CHARS = 12_000
18
-
19
- export function parseFrontmatter(content: string): ParsedSkillFrontmatter {
20
- const frontmatter: ParsedSkillFrontmatter = {}
21
- const normalized = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
22
- if (!normalized.startsWith(FRONTMATTER_START)) return frontmatter
23
- const endIndex = normalized.indexOf(`\n${FRONTMATTER_START}`, 3)
24
- if (endIndex === -1) return frontmatter
25
- const block = normalized.slice(4, endIndex)
26
-
27
- try {
28
- const parsed = parseYaml(block) as unknown
29
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return frontmatter
30
- for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {
31
- if (!/^[\w-]+$/.test(key)) continue
32
- const jsonValue = toJsonValue(value)
33
- if (jsonValue !== undefined) frontmatter[key] = jsonValue
34
- }
35
- } catch {
36
- return frontmatter
37
- }
38
-
39
- return frontmatter
40
- }
41
-
42
- export function getFrontmatterValue(frontmatter: ParsedSkillFrontmatter, key: string) {
43
- const raw = frontmatter[key]
44
- return typeof raw === 'string' ? raw : undefined
45
- }
46
-
47
- export function getFrontmatterMetadata(frontmatter: ParsedSkillFrontmatter) {
48
- const raw = frontmatter.metadata
49
- if (!raw) return undefined
50
- if (typeof raw === 'string') {
51
- try {
52
- const parsed = JSON.parse(raw) as unknown
53
- return parsed ?? undefined
54
- } catch {
55
- return undefined
56
- }
57
- }
58
- if (typeof raw === 'object') return raw
59
- return undefined
60
- }
61
-
62
- export function parsePilotbotMetadata(frontmatter: ParsedSkillFrontmatter) {
63
- const metadata = getFrontmatterMetadata(frontmatter)
64
- const metadataRecord =
65
- metadata && typeof metadata === 'object' && !Array.isArray(metadata)
66
- ? (metadata as Record<string, unknown>)
67
- : undefined
68
- const pilotbotMeta = metadataRecord?.pilotbot
69
- const metadataSource =
70
- pilotbotMeta && typeof pilotbotMeta === 'object' && !Array.isArray(pilotbotMeta)
71
- ? (pilotbotMeta as Record<string, unknown>)
72
- : undefined
73
- const pilotbotRaw = metadataSource ?? frontmatter.pilotbot
74
- if (!pilotbotRaw || typeof pilotbotRaw !== 'object' || Array.isArray(pilotbotRaw)) return undefined
75
-
76
- try {
77
- const pilotbotObj = pilotbotRaw as Record<string, unknown>
78
- const requiresRaw =
79
- typeof pilotbotObj.requires === 'object' && pilotbotObj.requires !== null
80
- ? (pilotbotObj.requires as Record<string, unknown>)
81
- : undefined
82
- const installRaw = Array.isArray(pilotbotObj.install) ? (pilotbotObj.install as unknown[]) : []
83
- const install = installRaw
84
- .map((entry) => parseInstallSpec(entry))
85
- .filter((entry): entry is SkillInstallSpec => Boolean(entry))
86
- const osRaw = normalizeStringList(pilotbotObj.os)
87
-
88
- const metadata: PilotbotSkillMetadata = {}
89
- if (typeof pilotbotObj.always === 'boolean') metadata.always = pilotbotObj.always
90
- if (typeof pilotbotObj.emoji === 'string') metadata.emoji = pilotbotObj.emoji
91
- if (typeof pilotbotObj.homepage === 'string') metadata.homepage = pilotbotObj.homepage
92
- if (typeof pilotbotObj.skillKey === 'string') metadata.skillKey = pilotbotObj.skillKey
93
- if (typeof pilotbotObj.primaryEnv === 'string') metadata.primaryEnv = pilotbotObj.primaryEnv
94
- if (typeof pilotbotObj.cliHelp === 'string') metadata.cliHelp = pilotbotObj.cliHelp
95
- if (osRaw.length > 0) metadata.os = osRaw
96
-
97
- if (requiresRaw) {
98
- const bins = normalizeStringList(requiresRaw.bins)
99
- const anyBins = normalizeStringList(requiresRaw.anyBins)
100
- const env = normalizeStringList(requiresRaw.env)
101
- const config = normalizeStringList(requiresRaw.config)
102
- if (bins.length || anyBins.length || env.length || config.length) {
103
- metadata.requires = {}
104
- if (bins.length) metadata.requires.bins = bins
105
- if (anyBins.length) metadata.requires.anyBins = anyBins
106
- if (env.length) metadata.requires.env = env
107
- if (config.length) metadata.requires.config = config
108
- }
109
- }
110
-
111
- if (install.length > 0) metadata.install = install
112
- const nix = parseNixPluginSpec(pilotbotObj.nix)
113
- if (nix) metadata.nix = nix
114
- const config = parsePilotbotConfigSpec(pilotbotObj.config)
115
- if (config) metadata.config = config
116
-
117
- return parseArk(PilotbotSkillMetadataSchema, metadata, 'Pilotbot metadata')
118
- } catch {
119
- return undefined
120
- }
121
- }
122
-
123
- export function isTextFile(path: string, contentType?: string | null) {
124
- const trimmed = path.trim().toLowerCase()
125
- if (!trimmed) return false
126
- const parts = trimmed.split('.')
127
- const extension = parts.length > 1 ? (parts.at(-1) ?? '') : ''
128
- if (contentType) {
129
- if (isTextContentType(contentType)) return true
130
- }
131
- if (extension && TEXT_FILE_EXTENSION_SET.has(extension)) return true
132
- return false
133
- }
134
-
135
- export function sanitizePath(path: string) {
136
- const trimmed = path.trim().replace(/^\/+/, '')
137
- if (!trimmed || trimmed.includes('..') || trimmed.includes('\\')) {
138
- return null
139
- }
140
- return trimmed
141
- }
142
-
143
- export function buildEmbeddingText(params: {
144
- frontmatter: ParsedSkillFrontmatter
145
- readme: string
146
- otherFiles: Array<{ path: string; content: string }>
147
- maxChars?: number
148
- }) {
149
- const { frontmatter, readme, otherFiles, maxChars = DEFAULT_EMBEDDING_MAX_CHARS } = params
150
- const headerParts = [
151
- getFrontmatterValue(frontmatter, 'name'),
152
- getFrontmatterValue(frontmatter, 'description'),
153
- getFrontmatterValue(frontmatter, 'homepage'),
154
- getFrontmatterValue(frontmatter, 'website'),
155
- getFrontmatterValue(frontmatter, 'url'),
156
- getFrontmatterValue(frontmatter, 'emoji'),
157
- ].filter(Boolean)
158
- const fileParts = otherFiles.map((file) => `# ${file.path}\n${file.content}`)
159
- const raw = [headerParts.join('\n'), readme, ...fileParts].filter(Boolean).join('\n\n')
160
- if (raw.length <= maxChars) return raw
161
- return raw.slice(0, maxChars)
162
- }
163
-
164
- const encoder = new TextEncoder()
165
-
166
- export async function hashSkillFiles(files: Array<{ path: string; sha256: string }>) {
167
- const normalized = files
168
- .filter((file) => Boolean(file.path) && Boolean(file.sha256))
169
- .map((file) => ({ path: file.path, sha256: file.sha256 }))
170
- .sort((a, b) => a.path.localeCompare(b.path))
171
- const payload = normalized.map((file) => `${file.path}:${file.sha256}`).join('\n')
172
- const digest = await crypto.subtle.digest('SHA-256', encoder.encode(payload))
173
- return toHex(new Uint8Array(digest))
174
- }
175
-
176
- function toJsonValue(value: unknown): unknown {
177
- if (value === null) return null
178
- if (value === undefined) return undefined
179
- if (typeof value === 'string') {
180
- const trimmedEnd = value.trimEnd()
181
- return trimmedEnd.trim() ? trimmedEnd : undefined
182
- }
183
- if (typeof value === 'number') return Number.isFinite(value) ? value : undefined
184
- if (typeof value === 'boolean') return value
185
- if (typeof value === 'bigint') return value.toString()
186
- if (value instanceof Date) return value.toISOString()
187
- if (Array.isArray(value)) {
188
- return value.map((entry) => {
189
- const next = toJsonValue(entry)
190
- return next === undefined ? null : next
191
- })
192
- }
193
- if (isPlainObject(value)) {
194
- const out: Record<string, unknown> = {}
195
- for (const [key, entry] of Object.entries(value)) {
196
- const next = toJsonValue(entry)
197
- if (next !== undefined) out[key] = next
198
- }
199
- return out
200
- }
201
- return undefined
202
- }
203
-
204
- function normalizeStringList(input: unknown): string[] {
205
- if (!input) return []
206
- if (Array.isArray(input)) {
207
- return input.map((value) => String(value).trim()).filter(Boolean)
208
- }
209
- if (typeof input === 'string') {
210
- return input
211
- .split(',')
212
- .map((value) => value.trim())
213
- .filter(Boolean)
214
- }
215
- return []
216
- }
217
-
218
- function parseInstallSpec(input: unknown): SkillInstallSpec | undefined {
219
- if (!input || typeof input !== 'object') return undefined
220
- const raw = input as Record<string, unknown>
221
- const kindRaw =
222
- typeof raw.kind === 'string' ? raw.kind : typeof raw.type === 'string' ? raw.type : ''
223
- const kind = kindRaw.trim().toLowerCase()
224
- if (kind !== 'brew' && kind !== 'node' && kind !== 'go' && kind !== 'uv') return undefined
225
-
226
- const spec: SkillInstallSpec = { kind: kind as SkillInstallSpec['kind'] }
227
- if (typeof raw.id === 'string') spec.id = raw.id
228
- if (typeof raw.label === 'string') spec.label = raw.label
229
- const bins = normalizeStringList(raw.bins)
230
- if (bins.length > 0) spec.bins = bins
231
- if (typeof raw.formula === 'string') spec.formula = raw.formula
232
- if (typeof raw.tap === 'string') spec.tap = raw.tap
233
- if (typeof raw.package === 'string') spec.package = raw.package
234
- if (typeof raw.module === 'string') spec.module = raw.module
235
- return spec
236
- }
237
-
238
- function parseNixPluginSpec(input: unknown): NixPluginSpec | undefined {
239
- if (!input || typeof input !== 'object') return undefined
240
- const raw = input as Record<string, unknown>
241
- if (typeof raw.plugin !== 'string') return undefined
242
- const plugin = raw.plugin.trim()
243
- if (!plugin) return undefined
244
- const systems = normalizeStringList(raw.systems)
245
- const spec: NixPluginSpec = { plugin }
246
- if (systems.length > 0) spec.systems = systems
247
- return spec
248
- }
249
-
250
- function parsePilotbotConfigSpec(input: unknown): PilotbotConfigSpec | undefined {
251
- if (!input || typeof input !== 'object') return undefined
252
- const raw = input as Record<string, unknown>
253
- const requiredEnv = normalizeStringList(raw.requiredEnv)
254
- const stateDirs = normalizeStringList(raw.stateDirs)
255
- const example = typeof raw.example === 'string' ? raw.example.trim() : ''
256
- const spec: PilotbotConfigSpec = {}
257
- if (requiredEnv.length > 0) spec.requiredEnv = requiredEnv
258
- if (stateDirs.length > 0) spec.stateDirs = stateDirs
259
- if (example) spec.example = example
260
- return Object.keys(spec).length > 0 ? spec : undefined
261
- }
262
-
263
- function toHex(bytes: Uint8Array) {
264
- let out = ''
265
- for (const byte of bytes) out += byte.toString(16).padStart(2, '0')
266
- return out
267
- }
268
-
269
- function isPlainObject(value: unknown): value is Record<string, unknown> {
270
- if (!value || typeof value !== 'object') return false
271
- const proto = Object.getPrototypeOf(value)
272
- return proto === Object.prototype || proto === null
273
- }