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,310 +0,0 @@
1
- /* @vitest-environment node */
2
-
3
- import { afterEach, describe, expect, it, vi } from 'vitest'
4
- import type { GlobalOpts } from '../types.js'
5
-
6
- const mockIntro = vi.fn()
7
- const mockOutro = vi.fn()
8
- const mockLog = vi.fn()
9
- const mockMultiselect = vi.fn(async (_args?: unknown) => [] as string[])
10
- let interactive = false
11
-
12
- const defaultFindSkillFolders = async (root: string) => {
13
- if (!root.endsWith('/scan')) return []
14
- return [
15
- { folder: '/scan/new-skill', slug: 'new-skill', displayName: 'New Skill' },
16
- { folder: '/scan/synced-skill', slug: 'synced-skill', displayName: 'Synced Skill' },
17
- { folder: '/scan/update-skill', slug: 'update-skill', displayName: 'Update Skill' },
18
- ]
19
- }
20
-
21
- vi.mock('@clack/prompts', () => ({
22
- intro: (value: string) => mockIntro(value),
23
- outro: (value: string) => mockOutro(value),
24
- multiselect: (args: unknown) => mockMultiselect(args),
25
- text: vi.fn(async () => ''),
26
- isCancel: () => false,
27
- }))
28
-
29
- vi.mock('../../config.js', () => ({
30
- readGlobalConfig: vi.fn(async () => ({ registry: 'https://pilothub.com', token: 'tkn' })),
31
- }))
32
-
33
- const mockGetRegistry = vi.fn(async () => 'https://pilothub.com')
34
- vi.mock('../registry.js', () => ({
35
- getRegistry: () => mockGetRegistry(),
36
- }))
37
-
38
- const mockApiRequest = vi.fn()
39
- vi.mock('../../http.js', () => ({
40
- apiRequest: (registry: unknown, args: unknown, schema?: unknown) =>
41
- mockApiRequest(registry, args, schema),
42
- }))
43
-
44
- const mockFail = vi.fn((message: string) => {
45
- throw new Error(message)
46
- })
47
- const mockSpinner = { succeed: vi.fn(), fail: vi.fn(), stop: vi.fn() }
48
- vi.mock('../ui.js', () => ({
49
- createSpinner: vi.fn(() => mockSpinner),
50
- fail: (message: string) => mockFail(message),
51
- formatError: (error: unknown) => (error instanceof Error ? error.message : String(error)),
52
- isInteractive: () => interactive,
53
- }))
54
-
55
- vi.mock('../scanSkills.js', () => ({
56
- findSkillFolders: vi.fn(defaultFindSkillFolders),
57
- getFallbackSkillRoots: vi.fn(() => []),
58
- }))
59
-
60
- const mockResolvePilotbotSkillRoots = vi.fn(
61
- async () =>
62
- ({
63
- roots: [] as string[],
64
- labels: {} as Record<string, string>,
65
- }) as const,
66
- )
67
- vi.mock('../pilotbotConfig.js', () => ({
68
- resolvePilotbotSkillRoots: () => mockResolvePilotbotSkillRoots(),
69
- }))
70
-
71
- vi.mock('../../skills.js', async () => {
72
- const actual = await vi.importActual<typeof import('../../skills.js')>('../../skills.js')
73
- return {
74
- ...actual,
75
- listTextFiles: vi.fn(async (folder: string) => [
76
- { relPath: 'SKILL.md', bytes: new TextEncoder().encode(folder) },
77
- ]),
78
- }
79
- })
80
-
81
- const mockCmdPublish = vi.fn()
82
- vi.mock('./publish.js', () => ({
83
- cmdPublish: (...args: unknown[]) => mockCmdPublish(...args),
84
- }))
85
-
86
- const { cmdSync } = await import('./sync.js')
87
-
88
- function makeOpts(): GlobalOpts {
89
- return {
90
- workdir: '/work',
91
- dir: '/work/skills',
92
- site: 'https://pilothub.com',
93
- registry: 'https://pilothub.com',
94
- registrySource: 'default',
95
- }
96
- }
97
-
98
- afterEach(async () => {
99
- vi.clearAllMocks()
100
- const { findSkillFolders } = await import('../scanSkills.js')
101
- vi.mocked(findSkillFolders).mockImplementation(defaultFindSkillFolders)
102
- })
103
-
104
- vi.spyOn(console, 'log').mockImplementation((...args) => {
105
- mockLog(args.map(String).join(' '))
106
- })
107
-
108
- describe('cmdSync', () => {
109
- it('classifies skills as new/update/synced (dry-run, mocked HTTP)', async () => {
110
- interactive = false
111
- mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
112
- if (args.path === '/api/v1/whoami') return { user: { handle: 'steipete' } }
113
- if (args.path === '/api/cli/telemetry/sync') return { ok: true }
114
- if (args.path.startsWith('/api/v1/resolve?')) {
115
- const u = new URL(`https://x.test${args.path}`)
116
- const slug = u.searchParams.get('slug')
117
- if (slug === 'new-skill') {
118
- throw new Error('Skill not found')
119
- }
120
- if (slug === 'synced-skill') {
121
- return { match: { version: '1.2.3' }, latestVersion: { version: '1.2.3' } }
122
- }
123
- if (slug === 'update-skill') {
124
- return { match: null, latestVersion: { version: '1.0.0' } }
125
- }
126
- }
127
- throw new Error(`Unexpected apiRequest: ${args.path}`)
128
- })
129
-
130
- await cmdSync(makeOpts(), { root: ['/scan'], all: true, dryRun: true }, true)
131
-
132
- expect(mockCmdPublish).not.toHaveBeenCalled()
133
-
134
- const output = mockLog.mock.calls.map((call) => String(call[0])).join('\n')
135
- expect(output).toMatch(/Already synced/)
136
- expect(output).toMatch(/synced-skill/)
137
-
138
- const dryRunOutro = mockOutro.mock.calls.at(-1)?.[0]
139
- expect(String(dryRunOutro)).toMatch(/Dry run: would upload 2 skill/)
140
- })
141
-
142
- it('prints bullet lists and selects all actionable by default', async () => {
143
- interactive = true
144
- mockMultiselect.mockImplementation(async (args?: unknown) => {
145
- const { initialValues } = args as { initialValues: string[] }
146
- return initialValues
147
- })
148
- mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
149
- if (args.path === '/api/v1/whoami') return { user: { handle: 'steipete' } }
150
- if (args.path === '/api/cli/telemetry/sync') return { ok: true }
151
- if (args.path.startsWith('/api/v1/resolve?')) {
152
- const u = new URL(`https://x.test${args.path}`)
153
- const slug = u.searchParams.get('slug')
154
- if (slug === 'new-skill') {
155
- throw new Error('Skill not found')
156
- }
157
- if (slug === 'synced-skill') {
158
- return { match: { version: '1.2.3' }, latestVersion: { version: '1.2.3' } }
159
- }
160
- if (slug === 'update-skill') {
161
- return { match: null, latestVersion: { version: '1.0.0' } }
162
- }
163
- }
164
- throw new Error(`Unexpected apiRequest: ${args.path}`)
165
- })
166
-
167
- await cmdSync(makeOpts(), { root: ['/scan'], all: false, dryRun: false, bump: 'patch' }, true)
168
-
169
- const output = mockLog.mock.calls.map((call) => String(call[0])).join('\n')
170
- expect(output).toMatch(/To sync/)
171
- expect(output).toMatch(/- new-skill/)
172
- expect(output).toMatch(/- update-skill/)
173
- expect(output).toMatch(/Already synced/)
174
- expect(output).toMatch(/- synced-skill/)
175
-
176
- const lastCall = mockMultiselect.mock.calls.at(-1)
177
- const promptArgs = lastCall ? (lastCall[0] as { initialValues: string[] }) : undefined
178
- expect(promptArgs?.initialValues.length).toBe(2)
179
- expect(mockCmdPublish).toHaveBeenCalledTimes(2)
180
- })
181
-
182
- it('shows condensed synced list when nothing to sync', async () => {
183
- interactive = false
184
- mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
185
- if (args.path === '/api/v1/whoami') return { user: { handle: 'steipete' } }
186
- if (args.path === '/api/cli/telemetry/sync') return { ok: true }
187
- if (args.path.startsWith('/api/v1/resolve?')) {
188
- return { match: { version: '1.0.0' }, latestVersion: { version: '1.0.0' } }
189
- }
190
- throw new Error(`Unexpected apiRequest: ${args.path}`)
191
- })
192
-
193
- await cmdSync(makeOpts(), { root: ['/scan'], all: true, dryRun: false }, true)
194
-
195
- const output = mockLog.mock.calls.map((call) => String(call[0])).join('\n')
196
- expect(output).toMatch(/Already synced/)
197
- expect(output).toMatch(/new-skill@1.0.0/)
198
- expect(output).toMatch(/synced-skill@1.0.0/)
199
- expect(output).not.toMatch(/\n-/)
200
-
201
- const outro = mockOutro.mock.calls.at(-1)?.[0]
202
- expect(String(outro)).toMatch(/Nothing to sync/)
203
- })
204
-
205
- it('dedupes duplicate slugs before publishing', async () => {
206
- interactive = false
207
- const { findSkillFolders } = await import('../scanSkills.js')
208
- vi.mocked(findSkillFolders).mockImplementation(async (root: string) => {
209
- if (!root.endsWith('/scan')) return []
210
- return [
211
- { folder: '/scan/dup-skill', slug: 'dup-skill', displayName: 'Dup Skill' },
212
- { folder: '/scan/dup-skill-copy', slug: 'dup-skill', displayName: 'Dup Skill' },
213
- ]
214
- })
215
-
216
- mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
217
- if (args.path === '/api/v1/whoami') return { user: { handle: 'steipete' } }
218
- if (args.path === '/api/cli/telemetry/sync') return { ok: true }
219
- if (args.path.startsWith('/api/v1/resolve?')) {
220
- return { match: null, latestVersion: null }
221
- }
222
- throw new Error(`Unexpected apiRequest: ${args.path}`)
223
- })
224
-
225
- await cmdSync(makeOpts(), { root: ['/scan'], all: true, dryRun: false }, true)
226
-
227
- expect(mockCmdPublish).toHaveBeenCalledTimes(1)
228
- const output = mockLog.mock.calls.map((call) => String(call[0])).join('\n')
229
- expect(output).toMatch(/Skipped duplicate slugs/)
230
- expect(output).toMatch(/dup-skill/)
231
- })
232
-
233
- it('prints labeled roots when pilotbot roots are detected', async () => {
234
- interactive = false
235
- mockResolvePilotbotSkillRoots.mockResolvedValueOnce({
236
- roots: ['/auto'],
237
- labels: { '/auto': 'Agent: Work' },
238
- })
239
- const { findSkillFolders } = await import('../scanSkills.js')
240
- vi.mocked(findSkillFolders).mockImplementation(async (root: string) => {
241
- if (root === '/auto') {
242
- return [{ folder: '/auto/alpha', slug: 'alpha', displayName: 'Alpha' }]
243
- }
244
- return []
245
- })
246
- mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
247
- if (args.path === '/api/v1/whoami') return { user: { handle: 'steipete' } }
248
- if (args.path === '/api/cli/telemetry/sync') return { ok: true }
249
- if (args.path.startsWith('/api/v1/resolve?')) {
250
- throw new Error('Skill not found')
251
- }
252
- throw new Error(`Unexpected apiRequest: ${args.path}`)
253
- })
254
-
255
- await cmdSync(makeOpts(), { all: true, dryRun: true }, true)
256
-
257
- const output = mockLog.mock.calls.map((call) => String(call[0])).join('\n')
258
- expect(output).toMatch(/Roots with skills/)
259
- expect(output).toMatch(/Agent: Work/)
260
- })
261
-
262
- it('allows empty changelog for updates (interactive)', async () => {
263
- interactive = true
264
- mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
265
- if (args.path === '/api/v1/whoami') return { user: { handle: 'steipete' } }
266
- if (args.path === '/api/cli/telemetry/sync') return { ok: true }
267
- if (args.path.startsWith('/api/v1/resolve?')) {
268
- const u = new URL(`https://x.test${args.path}`)
269
- const slug = u.searchParams.get('slug')
270
- if (slug === 'new-skill') {
271
- throw new Error('Skill not found')
272
- }
273
- if (slug === 'synced-skill') {
274
- return { match: { version: '1.2.3' }, latestVersion: { version: '1.2.3' } }
275
- }
276
- if (slug === 'update-skill') {
277
- return { match: null, latestVersion: { version: '1.0.0' } }
278
- }
279
- }
280
- throw new Error(`Unexpected apiRequest: ${args.path}`)
281
- })
282
-
283
- await cmdSync(makeOpts(), { root: ['/scan'], all: true, dryRun: false, bump: 'patch' }, true)
284
-
285
- const calls = mockCmdPublish.mock.calls.map(
286
- (call) => call[2] as { slug: string; changelog: string },
287
- )
288
- const update = calls.find((c) => c.slug === 'update-skill')
289
- if (!update) throw new Error('Missing update-skill publish')
290
- expect(update.changelog).toBe('')
291
- })
292
-
293
- it('skips telemetry when PILOTHUB_DISABLE_TELEMETRY is set', async () => {
294
- interactive = false
295
- process.env.PILOTHUB_DISABLE_TELEMETRY = '1'
296
- mockApiRequest.mockImplementation(async (_registry: string, args: { path: string }) => {
297
- if (args.path === '/api/v1/whoami') return { user: { handle: 'steipete' } }
298
- if (args.path.startsWith('/api/v1/resolve?')) {
299
- return { match: { version: '1.0.0' }, latestVersion: { version: '1.0.0' } }
300
- }
301
- throw new Error(`Unexpected apiRequest: ${args.path}`)
302
- })
303
-
304
- await cmdSync(makeOpts(), { root: ['/scan'], all: true, dryRun: true }, true)
305
- expect(
306
- mockApiRequest.mock.calls.some((call) => call[1]?.path === '/api/cli/telemetry/sync'),
307
- ).toBe(false)
308
- delete process.env.PILOTHUB_DISABLE_TELEMETRY
309
- })
310
- })
@@ -1,200 +0,0 @@
1
- import { intro, outro } from '@clack/prompts'
2
- import { readGlobalConfig } from '../../config.js'
3
- import { hashSkillFiles, listTextFiles, readSkillOrigin } from '../../skills.js'
4
- import { resolvePilotbotSkillRoots } from '../pilotbotConfig.js'
5
- import { getFallbackSkillRoots } from '../scanSkills.js'
6
- import type { GlobalOpts } from '../types.js'
7
- import { createSpinner, fail, formatError, isInteractive } from '../ui.js'
8
- import { cmdPublish } from './publish.js'
9
- import {
10
- buildScanRoots,
11
- checkRegistrySyncState,
12
- dedupeSkillsBySlug,
13
- formatActionableLine,
14
- formatBulletList,
15
- formatCommaList,
16
- formatList,
17
- formatSyncedDisplay,
18
- formatSyncedSummary,
19
- getRegistryWithAuth,
20
- mapWithConcurrency,
21
- mergeScan,
22
- normalizeConcurrency,
23
- printSection,
24
- reportTelemetryIfEnabled,
25
- resolvePublishMeta,
26
- scanRootsWithLabels,
27
- selectToUpload,
28
- } from './syncHelpers.js'
29
- import type { Candidate, LocalSkill, SyncOptions } from './syncTypes.js'
30
-
31
- export async function cmdSync(opts: GlobalOpts, options: SyncOptions, inputAllowed: boolean) {
32
- const allowPrompt = isInteractive() && inputAllowed !== false
33
- intro('PilotHub sync')
34
-
35
- const cfg = await readGlobalConfig()
36
- const token = cfg?.token
37
- if (!token) fail('Not logged in. Run: pilothub login')
38
-
39
- const registry = await getRegistryWithAuth(opts, token)
40
- const selectedRoots = buildScanRoots(opts, options.root)
41
- const pilotbotRoots = await resolvePilotbotSkillRoots()
42
- const combinedRoots = Array.from(
43
- new Set([...selectedRoots, ...pilotbotRoots.roots].map((root) => root.trim()).filter(Boolean)),
44
- )
45
- const concurrency = normalizeConcurrency(options.concurrency)
46
-
47
- const spinner = createSpinner('Scanning for local skills')
48
- const primaryScan = await scanRootsWithLabels(combinedRoots, pilotbotRoots.labels)
49
- let scan = primaryScan
50
- let telemetryScan = primaryScan
51
- if (primaryScan.skills.length === 0) {
52
- const fallback = getFallbackSkillRoots(opts.workdir)
53
- const fallbackScan = await scanRootsWithLabels(fallback)
54
- spinner.stop()
55
- telemetryScan = mergeScan(primaryScan, fallbackScan)
56
- scan = fallbackScan
57
- if (fallbackScan.skills.length === 0)
58
- fail('No skills found (checked workdir and known Pilotbot/Pilot locations)')
59
- printSection(
60
- `No skills in workdir. Found ${fallbackScan.skills.length} in fallback locations.`,
61
- formatList(fallbackScan.rootsWithSkills, 10),
62
- )
63
- } else {
64
- spinner.stop()
65
- const labeledRoots = primaryScan.rootsWithSkills
66
- .map((root) => {
67
- const label = primaryScan.rootLabels?.[root]
68
- return label ? `${label} (${root})` : root
69
- })
70
- .filter(Boolean)
71
- if (labeledRoots.length > 0) {
72
- printSection('Roots with skills', formatList(labeledRoots, 10))
73
- }
74
- }
75
- const deduped = dedupeSkillsBySlug(scan.skills)
76
- const skills = deduped.skills
77
- if (deduped.duplicates.length > 0) {
78
- printSection('Skipped duplicate slugs', formatCommaList(deduped.duplicates, 16))
79
- }
80
- const parsingSpinner = createSpinner('Parsing local skills')
81
- const locals: LocalSkill[] = []
82
- try {
83
- let done = 0
84
- const parsed = await mapWithConcurrency(skills, Math.min(concurrency, 12), async (skill) => {
85
- const filesOnDisk = await listTextFiles(skill.folder)
86
- const hashed = hashSkillFiles(filesOnDisk)
87
- const origin = await readSkillOrigin(skill.folder)
88
- done += 1
89
- parsingSpinner.text = `Parsing local skills ${done}/${skills.length}`
90
- return {
91
- ...skill,
92
- fingerprint: hashed.fingerprint,
93
- fileCount: filesOnDisk.length,
94
- origin,
95
- }
96
- })
97
- locals.push(...parsed)
98
- } catch (error) {
99
- parsingSpinner.fail(formatError(error))
100
- throw error
101
- } finally {
102
- parsingSpinner.stop()
103
- }
104
-
105
- const candidatesSpinner = createSpinner('Checking registry sync state')
106
- const candidates: Candidate[] = []
107
- const resolveSupport: { value: boolean | null } = { value: null }
108
- try {
109
- let done = 0
110
- const resolved = await mapWithConcurrency(locals, Math.min(concurrency, 16), async (skill) => {
111
- try {
112
- return await checkRegistrySyncState(registry, skill, resolveSupport)
113
- } finally {
114
- done += 1
115
- candidatesSpinner.text = `Checking registry sync state ${done}/${locals.length}`
116
- }
117
- })
118
- candidates.push(...resolved)
119
- } catch (error) {
120
- candidatesSpinner.fail(formatError(error))
121
- throw error
122
- } finally {
123
- candidatesSpinner.stop()
124
- }
125
-
126
- await reportTelemetryIfEnabled({
127
- token,
128
- registry,
129
- scan: telemetryScan,
130
- candidates,
131
- })
132
-
133
- const synced = candidates.filter((candidate) => candidate.status === 'synced')
134
- const actionable = candidates.filter((candidate) => candidate.status !== 'synced')
135
- const bump = options.bump ?? 'patch'
136
-
137
- if (actionable.length === 0) {
138
- if (synced.length > 0) {
139
- printSection('Already synced', formatCommaList(synced.map(formatSyncedSummary), 16))
140
- }
141
- outro('Nothing to sync.')
142
- return
143
- }
144
-
145
- printSection(
146
- 'To sync',
147
- formatBulletList(
148
- actionable.map((candidate) => formatActionableLine(candidate, bump)),
149
- 20,
150
- ),
151
- )
152
- if (synced.length > 0) {
153
- printSection('Already synced', formatSyncedDisplay(synced))
154
- }
155
-
156
- const selected = await selectToUpload(actionable, {
157
- allowPrompt,
158
- all: Boolean(options.all),
159
- bump,
160
- })
161
- if (selected.length === 0) {
162
- outro('Nothing selected.')
163
- return
164
- }
165
-
166
- if (options.dryRun) {
167
- outro(`Dry run: would upload ${selected.length} skill(s).`)
168
- return
169
- }
170
-
171
- const tags = options.tags ?? 'latest'
172
-
173
- for (const skill of selected) {
174
- const { publishVersion, changelog } = await resolvePublishMeta(skill, {
175
- bump,
176
- allowPrompt,
177
- changelogFlag: options.changelog,
178
- })
179
- const forkOf =
180
- skill.origin && normalizeRegistry(skill.origin.registry) === normalizeRegistry(registry)
181
- ? skill.origin.slug !== skill.slug
182
- ? `${skill.origin.slug}@${skill.origin.installedVersion}`
183
- : undefined
184
- : undefined
185
- await cmdPublish(opts, skill.folder, {
186
- slug: skill.slug,
187
- name: skill.displayName,
188
- version: publishVersion,
189
- changelog,
190
- tags,
191
- forkOf,
192
- })
193
- }
194
-
195
- outro(`Uploaded ${selected.length} skill(s).`)
196
- }
197
-
198
- function normalizeRegistry(value: string) {
199
- return value.trim().replace(/\/+$/, '').toLowerCase()
200
- }
@@ -1,26 +0,0 @@
1
- /* @vitest-environment node */
2
- import { describe, expect, it, vi } from 'vitest'
3
-
4
- vi.mock('../scanSkills.js', () => ({
5
- findSkillFolders: vi.fn(async (root: string) => {
6
- if (root.endsWith('/with-skill')) {
7
- return [{ folder: `${root}/demo`, slug: 'demo', displayName: 'Demo' }]
8
- }
9
- return []
10
- }),
11
- }))
12
-
13
- const { scanRootsWithLabels } = await import('./syncHelpers.js')
14
-
15
- describe('scanRootsWithLabels', () => {
16
- it('attaches labels to roots with skills', async () => {
17
- const roots = ['/tmp/with-skill', '/tmp/empty', '/tmp/with-skill']
18
- const labels = { '/tmp/with-skill': 'Agent: Work' }
19
-
20
- const result = await scanRootsWithLabels(roots, labels)
21
-
22
- expect(result.rootsWithSkills).toEqual(['/tmp/with-skill'])
23
- expect(result.rootLabels).toEqual({ '/tmp/with-skill': 'Agent: Work' })
24
- expect(result.skills.map((skill) => skill.slug)).toEqual(['demo'])
25
- })
26
- })