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,425 +0,0 @@
1
- import { zipSync } from 'fflate'
2
- import { TEXT_FILE_EXTENSION_SET } from 'pilothub-schema'
3
- import semver from 'semver'
4
- import { parseFrontmatter } from './skills'
5
-
6
- export type GitHubImportUrl = {
7
- owner: string
8
- repo: string
9
- ref?: string
10
- path?: string
11
- originalUrl: string
12
- }
13
-
14
- export type GitHubImportResolved = {
15
- owner: string
16
- repo: string
17
- ref: string
18
- commit: string
19
- path: string
20
- repoUrl: string
21
- originalUrl: string
22
- }
23
-
24
- export type GitHubImportCandidate = {
25
- path: string
26
- readmePath: string
27
- name?: string
28
- description?: string
29
- }
30
-
31
- export type GitHubImportFileEntry = {
32
- path: string
33
- size: number
34
- defaultSelected: boolean
35
- }
36
-
37
- const MAX_REDIRECTS = 6
38
- const GITHUB_HOST = 'github.com'
39
- const CODELOAD_HOST = 'codeload.github.com'
40
- const SKILL_FILENAMES = ['skill.md', 'skills.md']
41
-
42
- export function parseGitHubImportUrl(input: string): GitHubImportUrl {
43
- const originalUrl = input.trim()
44
- let url: URL
45
- try {
46
- url = new URL(originalUrl)
47
- } catch {
48
- throw new Error('Invalid URL')
49
- }
50
- if (url.protocol !== 'https:') throw new Error('Only https:// URLs are supported')
51
- if (url.hostname !== GITHUB_HOST) throw new Error('Only github.com URLs are supported')
52
-
53
- const segments = url.pathname
54
- .split('/')
55
- .map((segment) => segment.trim())
56
- .filter(Boolean)
57
- .map((segment) => {
58
- try {
59
- return decodeURIComponent(segment)
60
- } catch {
61
- throw new Error('Invalid URL')
62
- }
63
- })
64
-
65
- const owner = segments[0] ?? ''
66
- const repo = (segments[1] ?? '').replace(/\.git$/, '')
67
- if (!owner || !repo) throw new Error('GitHub URL must be /<owner>/<repo>')
68
-
69
- const kind = segments[2] ?? ''
70
- if (!kind) return { owner, repo, originalUrl }
71
- if (kind !== 'tree' && kind !== 'blob') {
72
- return { owner, repo, originalUrl }
73
- }
74
-
75
- const ref = segments[3] ?? ''
76
- if (!ref) throw new Error('Missing ref in GitHub URL')
77
-
78
- const rest = segments.slice(4).join('/')
79
- const normalizedRest = normalizeRepoPath(rest)
80
-
81
- if (kind === 'blob') {
82
- if (!rest) throw new Error('Missing path in GitHub URL')
83
- if (!normalizedRest) throw new Error('Invalid path in GitHub URL')
84
- const dir = normalizedRest.split('/').slice(0, -1).join('/')
85
- return { owner, repo, ref, path: dir || undefined, originalUrl }
86
- }
87
-
88
- if (rest && !normalizedRest) throw new Error('Invalid path in GitHub URL')
89
- return { owner, repo, ref, path: normalizedRest || undefined, originalUrl }
90
- }
91
-
92
- export async function resolveGitHubCommit(
93
- parsed: GitHubImportUrl,
94
- fetcher: typeof fetch,
95
- ): Promise<GitHubImportResolved> {
96
- const repoUrl = `https://${GITHUB_HOST}/${parsed.owner}/${parsed.repo}`
97
- const ref = parsed.ref?.trim() || 'HEAD'
98
- const path = normalizeRepoPath(parsed.path ?? '')
99
-
100
- const commit =
101
- ref === 'HEAD'
102
- ? await resolveHeadCommit(parsed, fetcher)
103
- : await resolveRefCommit(parsed, ref, fetcher)
104
-
105
- return {
106
- owner: parsed.owner,
107
- repo: parsed.repo,
108
- ref,
109
- commit,
110
- path,
111
- repoUrl,
112
- originalUrl: parsed.originalUrl,
113
- }
114
- }
115
-
116
- async function resolveRefCommit(parsed: GitHubImportUrl, ref: string, fetcher: typeof fetch) {
117
- const apiUrl = `https://api.github.com/repos/${parsed.owner}/${parsed.repo}/commits/${encodeURIComponent(ref)}`
118
- const response = await fetcher(apiUrl, {
119
- headers: {
120
- Accept: 'application/vnd.github+json',
121
- 'User-Agent': 'pilothub/github-import',
122
- },
123
- })
124
- if (!response.ok) throw new Error('GitHub ref not found')
125
- const body = (await response.json()) as { sha?: unknown }
126
- const sha = typeof body.sha === 'string' ? body.sha : ''
127
- if (!/^[a-f0-9]{40}$/i.test(sha)) throw new Error('GitHub commit sha missing')
128
- return sha.toLowerCase()
129
- }
130
-
131
- async function resolveHeadCommit(parsed: GitHubImportUrl, fetcher: typeof fetch) {
132
- let url = `https://${GITHUB_HOST}/${parsed.owner}/${parsed.repo}/archive/HEAD.zip`
133
- for (let i = 0; i < MAX_REDIRECTS; i += 1) {
134
- const response = await fetcher(url, { redirect: 'manual' })
135
- const location = response.headers.get('location')
136
- if (!location) break
137
- const next = new URL(location, url)
138
- if (next.hostname !== GITHUB_HOST && next.hostname !== CODELOAD_HOST) {
139
- throw new Error('Unexpected redirect host')
140
- }
141
- url = next.toString()
142
- }
143
-
144
- const maybe = url.split('/').at(-1) ?? ''
145
- if (!/^[a-f0-9]{40}$/i.test(maybe)) {
146
- throw new Error('Could not resolve commit for HEAD')
147
- }
148
- return maybe.toLowerCase()
149
- }
150
-
151
- export async function fetchGitHubZipBytes(
152
- resolved: GitHubImportResolved,
153
- fetcher: typeof fetch,
154
- limits?: { maxZipBytes?: number },
155
- ): Promise<Uint8Array> {
156
- const maxZipBytes = limits?.maxZipBytes ?? 25 * 1024 * 1024
157
- const url = `https://${CODELOAD_HOST}/${resolved.owner}/${resolved.repo}/zip/${resolved.commit}`
158
- const response = await fetcher(url, {
159
- headers: { 'User-Agent': 'pilothub/github-import' },
160
- })
161
- if (!response.ok) throw new Error('GitHub archive download failed')
162
-
163
- const lengthHeader = response.headers.get('content-length')
164
- if (lengthHeader) {
165
- const contentLength = Number.parseInt(lengthHeader, 10)
166
- if (Number.isFinite(contentLength) && contentLength > maxZipBytes) {
167
- throw new Error('GitHub archive too large')
168
- }
169
- }
170
-
171
- const reader = response.body?.getReader()
172
- if (!reader) {
173
- const buffer = new Uint8Array(await response.arrayBuffer())
174
- if (buffer.byteLength > maxZipBytes) throw new Error('GitHub archive too large')
175
- return buffer
176
- }
177
-
178
- const chunks: Uint8Array[] = []
179
- let total = 0
180
- while (true) {
181
- const { done, value } = await reader.read()
182
- if (done) break
183
- if (!value) continue
184
- total += value.byteLength
185
- if (total > maxZipBytes) throw new Error('GitHub archive too large')
186
- chunks.push(value)
187
- }
188
-
189
- const out = new Uint8Array(total)
190
- let offset = 0
191
- for (const chunk of chunks) {
192
- out.set(chunk, offset)
193
- offset += chunk.byteLength
194
- }
195
- return out
196
- }
197
-
198
- export type ZipEntryMap = Record<string, Uint8Array>
199
-
200
- export function buildGitHubZipForTests(entries: Record<string, string>) {
201
- const asBytes = Object.fromEntries(
202
- Object.entries(entries).map(([path, text]) => [path, new TextEncoder().encode(text)]),
203
- )
204
- return Uint8Array.from(zipSync(asBytes, { level: 1 }))
205
- }
206
-
207
- export function stripGitHubZipRoot(entries: ZipEntryMap): ZipEntryMap {
208
- const paths = Object.keys(entries)
209
- if (paths.length === 0) return {}
210
- const first = paths[0] ?? ''
211
- const firstRoot = first.split('/')[0] ?? ''
212
- if (!firstRoot) return entries
213
- const prefix = `${firstRoot}/`
214
- if (!paths.every((path) => path.startsWith(prefix))) return entries
215
- const out: ZipEntryMap = {}
216
- for (const [path, data] of Object.entries(entries)) {
217
- const stripped = path.slice(prefix.length)
218
- if (!stripped) continue
219
- out[stripped] = data
220
- }
221
- return out
222
- }
223
-
224
- export function detectGitHubImportCandidates(entries: ZipEntryMap): GitHubImportCandidate[] {
225
- const candidates: GitHubImportCandidate[] = []
226
- for (const path of Object.keys(entries)) {
227
- const normalized = normalizeRepoPath(path)
228
- const lower = normalized.toLowerCase()
229
- const isSkill = SKILL_FILENAMES.some((name) => lower === name || lower.endsWith(`/${name}`))
230
- if (!isSkill) continue
231
- const dir = normalized.split('/').slice(0, -1).join('/')
232
- const readmePath = normalized
233
- const raw = new TextDecoder().decode(entries[path] ?? new Uint8Array())
234
- const frontmatter = parseFrontmatter(raw)
235
- const name = typeof frontmatter.name === 'string' ? frontmatter.name : undefined
236
- const description =
237
- typeof frontmatter.description === 'string' ? frontmatter.description : undefined
238
- candidates.push({
239
- path: normalizeRepoPath(dir),
240
- readmePath,
241
- name: name?.trim() || undefined,
242
- description: description?.trim() || undefined,
243
- })
244
- }
245
- return uniqCandidates(candidates)
246
- }
247
-
248
- function uniqCandidates(candidates: GitHubImportCandidate[]) {
249
- const seen = new Set<string>()
250
- const out: GitHubImportCandidate[] = []
251
- for (const candidate of candidates) {
252
- const key = `${candidate.path}::${candidate.readmePath}`
253
- if (seen.has(key)) continue
254
- seen.add(key)
255
- out.push(candidate)
256
- }
257
- return out.sort((a, b) => a.path.localeCompare(b.path))
258
- }
259
-
260
- export function listTextFilesUnderCandidate(
261
- entries: ZipEntryMap,
262
- candidatePath: string,
263
- ): Array<{ path: string; bytes: Uint8Array }> {
264
- const root = normalizeCandidateRoot(candidatePath)
265
- const out: Array<{ path: string; bytes: Uint8Array }> = []
266
- for (const [path, bytes] of Object.entries(entries)) {
267
- const normalized = normalizeRepoPath(path)
268
- if (!isUnderRoot(normalized, root)) continue
269
- if (!isTextPath(normalized)) continue
270
- out.push({ path: normalized, bytes })
271
- }
272
- return out.sort((a, b) => a.path.localeCompare(b.path))
273
- }
274
-
275
- export function computeDefaultSelectedPaths(params: {
276
- candidate: GitHubImportCandidate
277
- files: Array<{ path: string; bytes: Uint8Array }>
278
- maxDepth?: number
279
- maxAdds?: number
280
- }) {
281
- const maxDepth = params.maxDepth ?? 4
282
- const maxAdds = params.maxAdds ?? 200
283
- const byPath = new Map(params.files.map((file) => [file.path, file.bytes]))
284
- const candidateRoot = normalizeCandidateRoot(params.candidate.path)
285
- const selected = new Set<string>()
286
- let added = 0
287
-
288
- const add = (path: string) => {
289
- const normalized = normalizeRepoPath(path)
290
- if (!isUnderRoot(normalized, candidateRoot)) return
291
- if (!byPath.has(normalized)) return
292
- if (!selected.has(normalized)) {
293
- selected.add(normalized)
294
- added += 1
295
- }
296
- }
297
-
298
- add(params.candidate.readmePath)
299
-
300
- const visited = new Set<string>()
301
- const queue: Array<{ path: string; depth: number }> = [
302
- { path: params.candidate.readmePath, depth: 0 },
303
- ]
304
-
305
- while (queue.length > 0) {
306
- const item = queue.shift()
307
- if (!item) break
308
- if (item.depth >= maxDepth) continue
309
- if (visited.has(item.path)) continue
310
- visited.add(item.path)
311
-
312
- const bytes = byPath.get(item.path)
313
- if (!bytes) continue
314
- if (!item.path.toLowerCase().endsWith('.md')) continue
315
-
316
- const text = new TextDecoder().decode(bytes)
317
- const refs = extractMarkdownRelativeTargets(text)
318
- for (const ref of refs) {
319
- if (added >= maxAdds) break
320
- const resolved = resolveMarkdownTarget(item.path, ref)
321
- if (!resolved) continue
322
- add(resolved)
323
- if (resolved.toLowerCase().endsWith('.md') && byPath.has(resolved)) {
324
- queue.push({ path: resolved, depth: item.depth + 1 })
325
- }
326
- }
327
- if (added >= maxAdds) break
328
- }
329
-
330
- return Array.from(selected).sort()
331
- }
332
-
333
- export function buildGitHubImportFileList(params: {
334
- candidate: GitHubImportCandidate
335
- files: Array<{ path: string; bytes: Uint8Array }>
336
- defaultSelectedPaths: string[]
337
- }): GitHubImportFileEntry[] {
338
- const selected = new Set(params.defaultSelectedPaths)
339
- return params.files.map((file) => ({
340
- path: file.path,
341
- size: file.bytes.byteLength,
342
- defaultSelected: selected.has(file.path),
343
- }))
344
- }
345
-
346
- export function normalizeRepoPath(path: string) {
347
- const stripped = path.replace(/^\/+/, '').trim()
348
- if (!stripped) return ''
349
- const cleaned = stripped.split('/').filter(Boolean).join('/')
350
- if (!cleaned || cleaned.includes('\\') || cleaned.includes('..')) return ''
351
- return cleaned
352
- }
353
-
354
- export function normalizeCandidateRoot(candidatePath: string) {
355
- const normalized = normalizeRepoPath(candidatePath)
356
- return normalized ? `${normalized}/` : ''
357
- }
358
-
359
- function isUnderRoot(path: string, rootWithSlash: string) {
360
- if (!rootWithSlash) return true
361
- return path === rootWithSlash.slice(0, -1) || path.startsWith(rootWithSlash)
362
- }
363
-
364
- function isTextPath(path: string) {
365
- const lower = path.toLowerCase()
366
- const ext = lower.split('.').at(-1) ?? ''
367
- if (!ext) return false
368
- return TEXT_FILE_EXTENSION_SET.has(ext)
369
- }
370
-
371
- export function suggestDisplayName(candidate: GitHubImportCandidate, fallbackBase: string) {
372
- const base = candidate.name?.trim() || fallbackBase.trim()
373
- if (!base) return ''
374
- return base
375
- .replace(/[-_]+/g, ' ')
376
- .replace(/\s+/g, ' ')
377
- .replace(/\b\w/g, (char) => char.toUpperCase())
378
- }
379
-
380
- export function suggestVersion(latestVersion?: string | null) {
381
- const latest = latestVersion?.trim() || ''
382
- if (latest && semver.valid(latest)) {
383
- return semver.inc(latest, 'patch') ?? '0.1.0'
384
- }
385
- return '0.1.0'
386
- }
387
-
388
- export function extractMarkdownRelativeTargets(markdown: string): string[] {
389
- const out: string[] = []
390
- const pattern = /!?\[[^\]]*]\(([^)]+)\)/g
391
- for (const match of markdown.matchAll(pattern)) {
392
- const raw = (match[1] ?? '').trim()
393
- if (!raw) continue
394
- const isAngleWrapped = raw.startsWith('<') && raw.endsWith('>')
395
- const cleaned = raw.replace(/^<|>$/g, '').trim()
396
- if (!cleaned) continue
397
- const target = isAngleWrapped ? cleaned : (cleaned.split(/\s+/)[0] ?? '')
398
- if (!target) continue
399
- if (target.startsWith('#')) continue
400
- const lower = target.toLowerCase()
401
- if (lower.startsWith('http:') || lower.startsWith('https:')) continue
402
- if (lower.startsWith('mailto:')) continue
403
- out.push(target)
404
- }
405
- return out
406
- }
407
-
408
- export function resolveMarkdownTarget(fromPath: string, target: string) {
409
- const withoutHash = target.split('#')[0] ?? ''
410
- const withoutQuery = (withoutHash.split('?')[0] ?? '').trim()
411
- if (!withoutQuery) return null
412
- if (withoutQuery.startsWith('/')) return null
413
- if (withoutQuery.includes('\\') || withoutQuery.includes('..')) return null
414
-
415
- const fromDirParts = normalizeRepoPath(fromPath).split('/').slice(0, -1)
416
- const targetParts = withoutQuery.split('/').filter(Boolean)
417
- const combined = [...fromDirParts, ...targetParts]
418
- const normalized: string[] = []
419
- for (const part of combined) {
420
- if (part === '.') continue
421
- if (part === '..') return null
422
- normalized.push(part)
423
- }
424
- return normalizeRepoPath(normalized.join('/')) || null
425
- }