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,243 +0,0 @@
1
- import { createFileRoute, Link } from '@tanstack/react-router'
2
- import { useAction, useQuery } from 'convex/react'
3
- import { useEffect, useMemo, useRef, useState } from 'react'
4
- import { api } from '../../../convex/_generated/api'
5
- import { SoulCard } from '../../components/SoulCard'
6
- import type { PublicSoul } from '../../lib/publicUser'
7
-
8
- const sortKeys = ['newest', 'downloads', 'stars', 'name', 'updated'] as const
9
- type SortKey = (typeof sortKeys)[number]
10
- type SortDir = 'asc' | 'desc'
11
-
12
- function parseSort(value: unknown): SortKey {
13
- if (typeof value !== 'string') return 'newest'
14
- if ((sortKeys as readonly string[]).includes(value)) return value as SortKey
15
- return 'newest'
16
- }
17
-
18
- function parseDir(value: unknown, sort: SortKey): SortDir {
19
- if (value === 'asc' || value === 'desc') return value
20
- return sort === 'name' ? 'asc' : 'desc'
21
- }
22
-
23
- export const Route = createFileRoute('/souls/')({
24
- validateSearch: (search) => {
25
- return {
26
- q: typeof search.q === 'string' && search.q.trim() ? search.q : undefined,
27
- sort: typeof search.sort === 'string' ? parseSort(search.sort) : undefined,
28
- dir: search.dir === 'asc' || search.dir === 'desc' ? search.dir : undefined,
29
- view: search.view === 'cards' || search.view === 'list' ? search.view : undefined,
30
- focus: search.focus === 'search' ? 'search' : undefined,
31
- }
32
- },
33
- component: SoulsIndex,
34
- })
35
-
36
- function SoulsIndex() {
37
- const navigate = Route.useNavigate()
38
- const search = Route.useSearch()
39
- const sort = search.sort ?? 'newest'
40
- const dir = parseDir(search.dir, sort)
41
- const view = search.view ?? 'list'
42
- const [query, setQuery] = useState(search.q ?? '')
43
-
44
- const souls = useQuery(api.souls.list, { limit: 500 }) as PublicSoul[] | undefined
45
- const ensureSoulSeeds = useAction(api.seed.ensureSoulSeeds)
46
- const seedEnsuredRef = useRef(false)
47
- const searchInputRef = useRef<HTMLInputElement>(null)
48
- const isLoadingSouls = souls === undefined
49
-
50
- useEffect(() => {
51
- setQuery(search.q ?? '')
52
- }, [search.q])
53
-
54
- // Auto-focus search input when focus=search param is present
55
- useEffect(() => {
56
- if (search.focus === 'search' && searchInputRef.current) {
57
- searchInputRef.current.focus()
58
- // Clear the focus param from URL to avoid re-focusing on navigation
59
- void navigate({ search: (prev) => ({ ...prev, focus: undefined }), replace: true })
60
- }
61
- }, [search.focus, navigate])
62
-
63
- useEffect(() => {
64
- if (seedEnsuredRef.current) return
65
- seedEnsuredRef.current = true
66
- void ensureSoulSeeds({})
67
- }, [ensureSoulSeeds])
68
-
69
- const filtered = useMemo(() => {
70
- const value = query.trim().toLowerCase()
71
- const all = souls ?? []
72
- if (!value) return all
73
- return all.filter((soul) => {
74
- if (soul.slug.toLowerCase().includes(value)) return true
75
- if (soul.displayName.toLowerCase().includes(value)) return true
76
- return (soul.summary ?? '').toLowerCase().includes(value)
77
- })
78
- }, [query, souls])
79
-
80
- const sorted = useMemo(() => {
81
- const multiplier = dir === 'asc' ? 1 : -1
82
- const results = [...filtered]
83
- results.sort((a, b) => {
84
- switch (sort) {
85
- case 'downloads':
86
- return (a.stats.downloads - b.stats.downloads) * multiplier
87
- case 'stars':
88
- return (a.stats.stars - b.stats.stars) * multiplier
89
- case 'updated':
90
- return (a.updatedAt - b.updatedAt) * multiplier
91
- case 'name':
92
- return (
93
- (a.displayName.localeCompare(b.displayName) || a.slug.localeCompare(b.slug)) *
94
- multiplier
95
- )
96
- default:
97
- return (a.createdAt - b.createdAt) * multiplier
98
- }
99
- })
100
- return results
101
- }, [dir, filtered, sort])
102
-
103
- const showing = sorted.length
104
- const total = souls?.length
105
-
106
- return (
107
- <main className="section">
108
- <header className="skills-header">
109
- <div>
110
- <h1 className="section-title" style={{ marginBottom: 8 }}>
111
- Souls
112
- </h1>
113
- <p className="section-subtitle" style={{ marginBottom: 0 }}>
114
- {isLoadingSouls
115
- ? 'Loading souls…'
116
- : `${showing}${typeof total === 'number' ? ` of ${total}` : ''} souls.`}
117
- </p>
118
- </div>
119
- <div className="skills-toolbar">
120
- <div className="skills-search">
121
- <input
122
- ref={searchInputRef}
123
- className="skills-search-input"
124
- value={query}
125
- onChange={(event) => {
126
- const next = event.target.value
127
- const trimmed = next.trim()
128
- setQuery(next)
129
- void navigate({
130
- search: (prev) => ({ ...prev, q: trimmed ? next : undefined }),
131
- replace: true,
132
- })
133
- }}
134
- placeholder="Filter by name, slug, or summary…"
135
- />
136
- </div>
137
- <div className="skills-toolbar-row">
138
- <select
139
- className="skills-sort"
140
- value={sort}
141
- onChange={(event) => {
142
- const sort = parseSort(event.target.value)
143
- void navigate({
144
- search: (prev) => ({
145
- ...prev,
146
- sort,
147
- dir: parseDir(prev.dir, sort),
148
- }),
149
- replace: true,
150
- })
151
- }}
152
- aria-label="Sort souls"
153
- >
154
- <option value="newest">Newest</option>
155
- <option value="updated">Recently updated</option>
156
- <option value="downloads">Downloads</option>
157
- <option value="stars">Stars</option>
158
- <option value="name">Name</option>
159
- </select>
160
- <button
161
- className="skills-dir"
162
- type="button"
163
- aria-label={`Sort direction ${dir}`}
164
- onClick={() => {
165
- void navigate({
166
- search: (prev) => ({
167
- ...prev,
168
- dir: parseDir(prev.dir, sort) === 'asc' ? 'desc' : 'asc',
169
- }),
170
- replace: true,
171
- })
172
- }}
173
- >
174
- {dir === 'asc' ? '↑' : '↓'}
175
- </button>
176
- <button
177
- className={`skills-view${view === 'cards' ? ' is-active' : ''}`}
178
- type="button"
179
- onClick={() => {
180
- void navigate({
181
- search: (prev) => ({
182
- ...prev,
183
- view: prev.view === 'cards' ? undefined : 'cards',
184
- }),
185
- replace: true,
186
- })
187
- }}
188
- >
189
- {view === 'cards' ? 'List' : 'Cards'}
190
- </button>
191
- </div>
192
- </div>
193
- </header>
194
-
195
- {isLoadingSouls ? (
196
- <div className="card">
197
- <div className="loading-indicator">Loading souls…</div>
198
- </div>
199
- ) : showing === 0 ? (
200
- <div className="card">No souls match that filter.</div>
201
- ) : view === 'cards' ? (
202
- <div className="grid">
203
- {sorted.map((soul) => (
204
- <SoulCard
205
- key={soul._id}
206
- soul={soul}
207
- summaryFallback="A SOUL.md bundle."
208
- meta={
209
- <div className="stat">
210
- ⭐ {soul.stats.stars} · ⤓ {soul.stats.downloads} · {soul.stats.versions} v
211
- </div>
212
- }
213
- />
214
- ))}
215
- </div>
216
- ) : (
217
- <div className="skills-list">
218
- {sorted.map((soul) => (
219
- <Link
220
- key={soul._id}
221
- className="skills-row"
222
- to="/souls/$slug"
223
- params={{ slug: soul.slug }}
224
- >
225
- <div className="skills-row-main">
226
- <div className="skills-row-title">
227
- <span>{soul.displayName}</span>
228
- <span className="skills-row-slug">/{soul.slug}</span>
229
- </div>
230
- <div className="skills-row-summary">{soul.summary ?? 'SOUL.md bundle.'}</div>
231
- </div>
232
- <div className="skills-row-metrics">
233
- <span>⤓ {soul.stats.downloads}</span>
234
- <span>★ {soul.stats.stars}</span>
235
- <span>{soul.stats.versions} v</span>
236
- </div>
237
- </Link>
238
- ))}
239
- </div>
240
- )}
241
- </main>
242
- )
243
- }
@@ -1,68 +0,0 @@
1
- import { createFileRoute, Link } from '@tanstack/react-router'
2
- import { useMutation, useQuery } from 'convex/react'
3
- import { api } from '../../convex/_generated/api'
4
- import type { Doc } from '../../convex/_generated/dataModel'
5
- import type { PublicSkill } from '../lib/publicUser'
6
-
7
- export const Route = createFileRoute('/stars')({
8
- component: Stars,
9
- })
10
-
11
- function Stars() {
12
- const me = useQuery(api.users.me) as Doc<'users'> | null | undefined
13
- const skills =
14
- (useQuery(api.stars.listByUser, me ? { userId: me._id, limit: 50 } : 'skip') as
15
- | PublicSkill[]
16
- | undefined) ?? []
17
-
18
- const toggleStar = useMutation(api.stars.toggle)
19
-
20
- if (!me) {
21
- return (
22
- <main className="section">
23
- <div className="card">Sign in to see your highlights.</div>
24
- </main>
25
- )
26
- }
27
-
28
- return (
29
- <main className="section">
30
- <h1 className="section-title">Your highlights</h1>
31
- <p className="section-subtitle">Skills you’ve starred for quick access.</p>
32
- <div className="grid">
33
- {skills.length === 0 ? (
34
- <div className="card">No stars yet.</div>
35
- ) : (
36
- skills.map((skill) => {
37
- const owner = encodeURIComponent(String(skill.ownerUserId))
38
- return (
39
- <div key={skill._id} className="card skill-card">
40
- <Link to="/$owner/$slug" params={{ owner, slug: skill.slug }}>
41
- <h3 className="skill-card-title">{skill.displayName}</h3>
42
- </Link>
43
- <div className="skill-card-footer skill-card-footer-inline">
44
- <span className="stat">⭐ {skill.stats.stars}</span>
45
- <button
46
- className="star-toggle is-active"
47
- type="button"
48
- onClick={async () => {
49
- try {
50
- await toggleStar({ skillId: skill._id })
51
- } catch (error) {
52
- console.error('Failed to unstar skill:', error)
53
- window.alert('Unable to unstar this skill. Please try again.')
54
- }
55
- }}
56
- aria-label={`Unstar ${skill.displayName}`}
57
- >
58
- <span aria-hidden="true">★</span>
59
- </button>
60
- </div>
61
- </div>
62
- )
63
- })
64
- )}
65
- </div>
66
- </main>
67
- )
68
- }
@@ -1,307 +0,0 @@
1
- import { createFileRoute } from '@tanstack/react-router'
2
- import { useMutation, useQuery } from 'convex/react'
3
- import { useEffect, useState } from 'react'
4
- import { api } from '../../../convex/_generated/api'
5
- import type { Doc } from '../../../convex/_generated/dataModel'
6
- import { SkillCard } from '../../components/SkillCard'
7
- import { getSkillBadges } from '../../lib/badges'
8
- import type { PublicSkill, PublicUser } from '../../lib/publicUser'
9
-
10
- export const Route = createFileRoute('/u/$handle')({
11
- component: UserProfile,
12
- })
13
-
14
- function UserProfile() {
15
- const { handle } = Route.useParams()
16
- const me = useQuery(api.users.me) as Doc<'users'> | null | undefined
17
- const user = useQuery(api.users.getByHandle, { handle }) as PublicUser | null | undefined
18
- const publishedSkills = useQuery(
19
- api.skills.list,
20
- user ? { ownerUserId: user._id, limit: 50 } : 'skip',
21
- ) as PublicSkill[] | undefined
22
- const starredSkills = useQuery(
23
- api.stars.listByUser,
24
- user ? { userId: user._id, limit: 50 } : 'skip',
25
- ) as PublicSkill[] | undefined
26
-
27
- const isSelf = Boolean(me && user && me._id === user._id)
28
- const [tab, setTab] = useState<'stars' | 'installed'>('stars')
29
- const [includeRemoved, setIncludeRemoved] = useState(false)
30
- const installed = useQuery(
31
- api.telemetry.getMyInstalled,
32
- isSelf && tab === 'installed' ? { includeRemoved } : 'skip',
33
- ) as TelemetryResponse | null | undefined
34
-
35
- useEffect(() => {
36
- if (!isSelf && tab === 'installed') setTab('stars')
37
- }, [isSelf, tab])
38
-
39
- if (user === undefined) {
40
- return (
41
- <main className="section">
42
- <div className="card">
43
- <div className="loading-indicator">Loading user…</div>
44
- </div>
45
- </main>
46
- )
47
- }
48
-
49
- if (user === null) {
50
- return (
51
- <main className="section">
52
- <div className="card">User not found.</div>
53
- </main>
54
- )
55
- }
56
-
57
- const avatar = user.image
58
- const displayName = user.displayName ?? user.name ?? user.handle ?? 'User'
59
- const displayHandle = user.handle ?? user.name ?? handle
60
- const initial = displayName.charAt(0).toUpperCase()
61
- const isLoadingSkills = starredSkills === undefined
62
- const skills = starredSkills ?? []
63
- const isLoadingPublished = publishedSkills === undefined
64
- const published = publishedSkills ?? []
65
-
66
- return (
67
- <main className="section">
68
- <div className="card settings-profile" style={{ marginBottom: 22 }}>
69
- <div className="settings-avatar" aria-hidden="true">
70
- {avatar ? <img src={avatar} alt="" /> : <span>{initial}</span>}
71
- </div>
72
- <div className="settings-profile-body">
73
- <div className="settings-name">{displayName}</div>
74
- <div className="settings-handle">@{displayHandle}</div>
75
- </div>
76
- </div>
77
-
78
- {isSelf ? (
79
- <div className="profile-tabs" role="tablist" aria-label="Profile tabs">
80
- <button
81
- className={tab === 'stars' ? 'profile-tab is-active' : 'profile-tab'}
82
- type="button"
83
- role="tab"
84
- aria-selected={tab === 'stars'}
85
- onClick={() => setTab('stars')}
86
- >
87
- Stars
88
- </button>
89
- <button
90
- className={tab === 'installed' ? 'profile-tab is-active' : 'profile-tab'}
91
- type="button"
92
- role="tab"
93
- aria-selected={tab === 'installed'}
94
- onClick={() => setTab('installed')}
95
- >
96
- Installed
97
- </button>
98
- </div>
99
- ) : null}
100
-
101
- {tab === 'installed' && isSelf ? (
102
- <InstalledSection
103
- includeRemoved={includeRemoved}
104
- onToggleRemoved={() => setIncludeRemoved((value) => !value)}
105
- data={installed}
106
- />
107
- ) : (
108
- <>
109
- <h2 className="section-title" style={{ fontSize: '1.3rem' }}>
110
- Published
111
- </h2>
112
- <p className="section-subtitle">Skills published by this user.</p>
113
-
114
- {isLoadingPublished ? (
115
- <div className="card">
116
- <div className="loading-indicator">Loading published skills…</div>
117
- </div>
118
- ) : published.length > 0 ? (
119
- <div className="grid" style={{ marginBottom: 18 }}>
120
- {published.map((skill) => (
121
- <SkillCard
122
- key={skill._id}
123
- skill={skill}
124
- badge={getSkillBadges(skill)}
125
- summaryFallback="Agent-ready skill pack."
126
- meta={
127
- <div className="stat">
128
- ⭐ {skill.stats.stars} · ⤓ {skill.stats.downloads} · ⤒{' '}
129
- {skill.stats.installsAllTime ?? 0}
130
- </div>
131
- }
132
- />
133
- ))}
134
- </div>
135
- ) : null}
136
-
137
- <h2 className="section-title" style={{ fontSize: '1.3rem' }}>
138
- Stars
139
- </h2>
140
- <p className="section-subtitle">Skills this user has starred.</p>
141
-
142
- {isLoadingSkills ? (
143
- <div className="card">
144
- <div className="loading-indicator">Loading stars…</div>
145
- </div>
146
- ) : skills.length === 0 ? (
147
- <div className="card">No stars yet.</div>
148
- ) : (
149
- <div className="grid">
150
- {skills.map((skill) => (
151
- <SkillCard
152
- key={skill._id}
153
- skill={skill}
154
- badge={getSkillBadges(skill)}
155
- summaryFallback="Agent-ready skill pack."
156
- meta={
157
- <div className="stat">
158
- ⭐ {skill.stats.stars} · ⤓ {skill.stats.downloads} · ⤒{' '}
159
- {skill.stats.installsAllTime ?? 0}
160
- </div>
161
- }
162
- />
163
- ))}
164
- </div>
165
- )}
166
- </>
167
- )}
168
- </main>
169
- )
170
- }
171
-
172
- function InstalledSection(props: {
173
- includeRemoved: boolean
174
- onToggleRemoved: () => void
175
- data: TelemetryResponse | null | undefined
176
- }) {
177
- const clearTelemetry = useMutation(api.telemetry.clearMyTelemetry)
178
- const [showRaw, setShowRaw] = useState(false)
179
- const data = props.data
180
- if (data === undefined) {
181
- return (
182
- <>
183
- <h2 className="section-title" style={{ fontSize: '1.3rem' }}>
184
- Installed
185
- </h2>
186
- <div className="card">
187
- <div className="loading-indicator">Loading telemetry…</div>
188
- </div>
189
- </>
190
- )
191
- }
192
-
193
- if (data === null) {
194
- return (
195
- <>
196
- <h2 className="section-title" style={{ fontSize: '1.3rem' }}>
197
- Installed
198
- </h2>
199
- <div className="card">Sign in to view your installed skills.</div>
200
- </>
201
- )
202
- }
203
-
204
- return (
205
- <>
206
- <h2 className="section-title" style={{ fontSize: '1.3rem' }}>
207
- Installed
208
- </h2>
209
- <p className="section-subtitle" style={{ maxWidth: 760 }}>
210
- Private view. Only you can see your folders/roots. Everyone else only sees aggregated
211
- install counts per skill.
212
- </p>
213
- <div className="profile-actions">
214
- <button className="btn" type="button" onClick={props.onToggleRemoved}>
215
- {props.includeRemoved ? 'Hide removed' : 'Show removed'}
216
- </button>
217
- <button className="btn" type="button" onClick={() => setShowRaw((value) => !value)}>
218
- {showRaw ? 'Hide JSON' : 'Show JSON'}
219
- </button>
220
- <button
221
- className="btn"
222
- type="button"
223
- onClick={() => {
224
- if (!window.confirm('Delete all telemetry data?')) return
225
- void clearTelemetry()
226
- }}
227
- >
228
- Delete telemetry
229
- </button>
230
- </div>
231
-
232
- {showRaw ? (
233
- <div className="card telemetry-json" style={{ marginBottom: 18 }}>
234
- <pre className="mono" style={{ margin: 0, whiteSpace: 'pre-wrap' }}>
235
- {JSON.stringify(data, null, 2)}
236
- </pre>
237
- </div>
238
- ) : null}
239
-
240
- {data.roots.length === 0 ? (
241
- <div className="card">No telemetry yet. Run `pilothub sync` from the CLI.</div>
242
- ) : (
243
- <div style={{ display: 'grid', gap: 16 }}>
244
- {data.roots.map((root) => (
245
- <div key={root.rootId} className="card telemetry-root">
246
- <div className="telemetry-root-header">
247
- <div>
248
- <div className="telemetry-root-title">{root.label}</div>
249
- <div className="telemetry-root-meta">
250
- Last sync {new Date(root.lastSeenAt).toLocaleString()}
251
- {root.expiredAt ? ' · stale' : ''}
252
- </div>
253
- </div>
254
- <div className="tag">{root.skills.length} skills</div>
255
- </div>
256
- {root.skills.length === 0 ? (
257
- <div className="stat">No skills found in this root.</div>
258
- ) : (
259
- <div className="telemetry-skill-list">
260
- {root.skills.map((entry) => (
261
- <div key={`${root.rootId}:${entry.skill.slug}`} className="telemetry-skill-row">
262
- <a
263
- className="telemetry-skill-link"
264
- href={`/${encodeURIComponent(String(entry.skill.ownerUserId))}/${entry.skill.slug}`}
265
- >
266
- <span>{entry.skill.displayName}</span>
267
- <span className="telemetry-skill-slug">/{entry.skill.slug}</span>
268
- </a>
269
- <div className="telemetry-skill-meta mono">
270
- {entry.lastVersion ? `v${entry.lastVersion}` : 'v?'}{' '}
271
- {entry.removedAt ? '· removed' : ''}
272
- </div>
273
- </div>
274
- ))}
275
- </div>
276
- )}
277
- </div>
278
- ))}
279
- </div>
280
- )}
281
- </>
282
- )
283
- }
284
-
285
- type TelemetryResponse = {
286
- roots: Array<{
287
- rootId: string
288
- label: string
289
- firstSeenAt: number
290
- lastSeenAt: number
291
- expiredAt?: number
292
- skills: Array<{
293
- skill: {
294
- slug: string
295
- displayName: string
296
- summary?: string
297
- stats: unknown
298
- ownerUserId: string
299
- }
300
- firstSeenAt: number
301
- lastSeenAt: number
302
- lastVersion?: string
303
- removedAt?: number
304
- }>
305
- }>
306
- cutoffDays: number
307
- }
@@ -1,81 +0,0 @@
1
- import { isTextContentType, TEXT_FILE_EXTENSION_SET } from 'pilothub-schema'
2
-
3
- export async function uploadFile(uploadUrl: string, file: File) {
4
- const response = await fetch(uploadUrl, {
5
- method: 'POST',
6
- headers: { 'Content-Type': file.type || 'application/octet-stream' },
7
- body: file,
8
- })
9
- if (!response.ok) {
10
- throw new Error(`Upload failed: ${await response.text()}`)
11
- }
12
- const payload = (await response.json()) as { storageId: string }
13
- return payload.storageId
14
- }
15
-
16
- export async function hashFile(file: File) {
17
- const buffer =
18
- typeof file.arrayBuffer === 'function'
19
- ? await file.arrayBuffer()
20
- : await new Response(file).arrayBuffer()
21
- const hash = await crypto.subtle.digest('SHA-256', new Uint8Array(buffer))
22
- const bytes = new Uint8Array(hash)
23
- return Array.from(bytes)
24
- .map((byte) => byte.toString(16).padStart(2, '0'))
25
- .join('')
26
- }
27
-
28
- export function formatBytes(bytes: number) {
29
- if (!Number.isFinite(bytes)) return '0 B'
30
- const units = ['B', 'KB', 'MB', 'GB']
31
- let size = bytes
32
- let unit = 0
33
- while (size >= 1024 && unit < units.length - 1) {
34
- size /= 1024
35
- unit += 1
36
- }
37
- return `${size.toFixed(size < 10 && unit > 0 ? 1 : 0)} ${units[unit]}`
38
- }
39
-
40
- export function formatPublishError(error: unknown) {
41
- if (error && typeof error === 'object' && 'data' in error) {
42
- const data = (error as { data?: unknown }).data
43
- if (typeof data === 'string' && data.trim()) return data.trim()
44
- if (
45
- data &&
46
- typeof data === 'object' &&
47
- 'message' in data &&
48
- typeof (data as { message?: unknown }).message === 'string'
49
- ) {
50
- const message = (data as { message?: string }).message?.trim()
51
- if (message) return message
52
- }
53
- }
54
- if (error instanceof Error) {
55
- const cleaned = error.message
56
- .replace(/\[CONVEX[^\]]*\]\s*/g, '')
57
- .replace(/\[Request ID:[^\]]*\]\s*/g, '')
58
- .replace(/^Server Error Called by client\s*/i, '')
59
- .replace(/^ConvexError:\s*/i, '')
60
- .trim()
61
- if (cleaned && cleaned !== 'Server Error') return cleaned
62
- }
63
- return 'Publish failed. Please try again.'
64
- }
65
-
66
- export function isTextFile(file: File) {
67
- const path = (file.webkitRelativePath || file.name).trim().toLowerCase()
68
- if (!path) return false
69
- const parts = path.split('.')
70
- const extension = parts.length > 1 ? (parts.at(-1) ?? '') : ''
71
- if (file.type && isTextContentType(file.type)) return true
72
- if (extension && TEXT_FILE_EXTENSION_SET.has(extension)) return true
73
- return false
74
- }
75
-
76
- export async function readText(blob: Blob) {
77
- if (typeof (blob as Blob & { text?: unknown }).text === 'function') {
78
- return (blob as Blob & { text: () => Promise<string> }).text()
79
- }
80
- return new Response(blob as BodyInit).text()
81
- }