dirpush 0.0.0 → 0.1.0

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 (895) hide show
  1. package/.agents/skills/dirpush/SKILL.md +92 -0
  2. package/.agents/skills/dirpush/references/assets.md +48 -0
  3. package/.agents/skills/dirpush/references/authentication.md +59 -0
  4. package/.agents/skills/dirpush/references/recovery.md +34 -0
  5. package/.agents/skills/dirpush/references/submissions.md +43 -0
  6. package/LICENSE +21 -0
  7. package/README.md +63 -5
  8. package/THIRD_PARTY_NOTICES.md +10 -0
  9. package/bin/dirpush.cjs +13 -0
  10. package/drizzle/0000_init.sql +59 -0
  11. package/drizzle/0001_jazzy_masked_marvel.sql +9 -0
  12. package/drizzle/0002_loose_may_parker.sql +9 -0
  13. package/drizzle/0003_modern_lilandra.sql +6 -0
  14. package/drizzle/0004_deep_master_chief.sql +11 -0
  15. package/drizzle/meta/0000_snapshot.json +404 -0
  16. package/drizzle/meta/0001_snapshot.json +466 -0
  17. package/drizzle/meta/0002_snapshot.json +527 -0
  18. package/drizzle/meta/0003_snapshot.json +566 -0
  19. package/drizzle/meta/0004_snapshot.json +642 -0
  20. package/drizzle/meta/_journal.json +41 -0
  21. package/examples/workspace/identity.yaml +6 -0
  22. package/examples/workspace/permissions.yaml +29 -0
  23. package/examples/workspace/product.yaml +29 -0
  24. package/package.json +61 -6
  25. package/registry/LICENSE +396 -0
  26. package/registry/directories/1000-tools/directory.yaml +55 -0
  27. package/registry/directories/1000-tools/playbook.md +5 -0
  28. package/registry/directories/10015-io/directory.yaml +66 -0
  29. package/registry/directories/10015-io/playbook.md +9 -0
  30. package/registry/directories/10words/directory.yaml +53 -0
  31. package/registry/directories/10words/playbook.md +8 -0
  32. package/registry/directories/600-tools/directory.yaml +48 -0
  33. package/registry/directories/600-tools/playbook.md +1 -0
  34. package/registry/directories/a1bookmarks/directory.yaml +42 -0
  35. package/registry/directories/aback-launch/directory.yaml +54 -0
  36. package/registry/directories/aback-launch/playbook.md +1 -0
  37. package/registry/directories/acid-tools/directory.yaml +51 -0
  38. package/registry/directories/acid-tools/playbook.md +8 -0
  39. package/registry/directories/agenthunter/directory.yaml +53 -0
  40. package/registry/directories/ai-agents-directory/directory.yaml +49 -0
  41. package/registry/directories/ai-agents-directory/playbook.md +6 -0
  42. package/registry/directories/ai-agents-list/directory.yaml +46 -0
  43. package/registry/directories/ai-agents-list/playbook.md +1 -0
  44. package/registry/directories/ai-agents-listing/directory.yaml +76 -0
  45. package/registry/directories/ai-directories/directory.yaml +45 -0
  46. package/registry/directories/ai-directories/playbook.md +7 -0
  47. package/registry/directories/ai-dust/directory.yaml +68 -0
  48. package/registry/directories/ai-dust/playbook.md +1 -0
  49. package/registry/directories/ai-nav/directory.yaml +54 -0
  50. package/registry/directories/ai-nav/playbook.md +1 -0
  51. package/registry/directories/ai-respo/directory.yaml +43 -0
  52. package/registry/directories/ai-respo/playbook.md +1 -0
  53. package/registry/directories/ai-superhub/directory.yaml +65 -0
  54. package/registry/directories/ai-tool-list/directory.yaml +44 -0
  55. package/registry/directories/ai-tool-list/playbook.md +1 -0
  56. package/registry/directories/ai-tool-saver/directory.yaml +52 -0
  57. package/registry/directories/ai-tool-saver/playbook.md +1 -0
  58. package/registry/directories/ai-toolex/directory.yaml +46 -0
  59. package/registry/directories/ai-toolex/playbook.md +1 -0
  60. package/registry/directories/ai-toolz-dir/directory.yaml +60 -0
  61. package/registry/directories/ai-website-directory/directory.yaml +66 -0
  62. package/registry/directories/ai-website-directory/playbook.md +1 -0
  63. package/registry/directories/ai-x-collection/directory.yaml +45 -0
  64. package/registry/directories/ai-x-collection/playbook.md +1 -0
  65. package/registry/directories/aiagentstore/directory.yaml +52 -0
  66. package/registry/directories/aiagentstore/playbook.md +1 -0
  67. package/registry/directories/aidirs-best/directory.yaml +46 -0
  68. package/registry/directories/aifindy/directory.yaml +44 -0
  69. package/registry/directories/aifindy/playbook.md +1 -0
  70. package/registry/directories/aihubs/directory.yaml +44 -0
  71. package/registry/directories/aihubs/playbook.md +5 -0
  72. package/registry/directories/aihuntlist/directory.yaml +43 -0
  73. package/registry/directories/aihuntlist/playbook.md +6 -0
  74. package/registry/directories/aihustle/directory.yaml +51 -0
  75. package/registry/directories/aihustle/playbook.md +5 -0
  76. package/registry/directories/ailaunch-space/directory.yaml +44 -0
  77. package/registry/directories/ailaunch-space/playbook.md +6 -0
  78. package/registry/directories/ailib/directory.yaml +64 -0
  79. package/registry/directories/ailib/playbook.md +1 -0
  80. package/registry/directories/aimatch-pro/directory.yaml +42 -0
  81. package/registry/directories/aimatch-pro/playbook.md +1 -0
  82. package/registry/directories/ainave/directory.yaml +71 -0
  83. package/registry/directories/ainave/playbook.md +10 -0
  84. package/registry/directories/aitechviral/directory.yaml +47 -0
  85. package/registry/directories/aitechviral/playbook.md +10 -0
  86. package/registry/directories/aitogrow/directory.yaml +52 -0
  87. package/registry/directories/aitogrow/playbook.md +6 -0
  88. package/registry/directories/aitoolhunt/directory.yaml +43 -0
  89. package/registry/directories/aitoolhunt/playbook.md +5 -0
  90. package/registry/directories/aitools-fyi/directory.yaml +52 -0
  91. package/registry/directories/aitools-inc/directory.yaml +48 -0
  92. package/registry/directories/aitools-inc/playbook.md +1 -0
  93. package/registry/directories/aitoolsdirectory/directory.yaml +73 -0
  94. package/registry/directories/aitoolsdirectory/playbook.md +8 -0
  95. package/registry/directories/aitoolshunt/directory.yaml +64 -0
  96. package/registry/directories/aitoolshunt/playbook.md +6 -0
  97. package/registry/directories/aitoolslist/directory.yaml +59 -0
  98. package/registry/directories/aitoolslist/playbook.md +6 -0
  99. package/registry/directories/aitoolsmagazine/directory.yaml +46 -0
  100. package/registry/directories/aitoolsmagazine/playbook.md +5 -0
  101. package/registry/directories/aitoolsmarketer/directory.yaml +56 -0
  102. package/registry/directories/aitoolsmarketer/playbook.md +7 -0
  103. package/registry/directories/aitooltrek/directory.yaml +47 -0
  104. package/registry/directories/aitooltrek/playbook.md +6 -0
  105. package/registry/directories/aitop10-tools/directory.yaml +44 -0
  106. package/registry/directories/aitop10-tools/playbook.md +5 -0
  107. package/registry/directories/aitoptools/directory.yaml +74 -0
  108. package/registry/directories/aitoptools/playbook.md +1 -0
  109. package/registry/directories/aiwithme/directory.yaml +54 -0
  110. package/registry/directories/aiwithme/playbook.md +10 -0
  111. package/registry/directories/all-in-ai-tools/directory.yaml +51 -0
  112. package/registry/directories/all-in-ai-tools/playbook.md +1 -0
  113. package/registry/directories/allmcps/directory.yaml +55 -0
  114. package/registry/directories/allmcps/playbook.md +7 -0
  115. package/registry/directories/alltopstartups/directory.yaml +43 -0
  116. package/registry/directories/altern-ai/directory.yaml +50 -0
  117. package/registry/directories/altern-ai/playbook.md +6 -0
  118. package/registry/directories/alternative-tools/directory.yaml +46 -0
  119. package/registry/directories/alternative-tools/playbook.md +4 -0
  120. package/registry/directories/alternativeto/directory.yaml +48 -0
  121. package/registry/directories/alternativeto/playbook.md +8 -0
  122. package/registry/directories/ant-directory/directory.yaml +52 -0
  123. package/registry/directories/ant-directory/playbook.md +1 -0
  124. package/registry/directories/app-scout/directory.yaml +60 -0
  125. package/registry/directories/app-scout/playbook.md +5 -0
  126. package/registry/directories/app-stack-builder/directory.yaml +71 -0
  127. package/registry/directories/appalist/directory.yaml +46 -0
  128. package/registry/directories/appalist/playbook.md +4 -0
  129. package/registry/directories/apprater/directory.yaml +65 -0
  130. package/registry/directories/apprater/playbook.md +1 -0
  131. package/registry/directories/appsumo/directory.yaml +45 -0
  132. package/registry/directories/appsumo/playbook.md +1 -0
  133. package/registry/directories/ask-ai-for-it/directory.yaml +45 -0
  134. package/registry/directories/ask-ai-for-it/playbook.md +1 -0
  135. package/registry/directories/auraplusplus/directory.yaml +56 -0
  136. package/registry/directories/auraplusplus/playbook.md +6 -0
  137. package/registry/directories/awesome-mcp-list/directory.yaml +45 -0
  138. package/registry/directories/awesome-mcp-list/playbook.md +7 -0
  139. package/registry/directories/awesome-mcp-lists/directory.yaml +44 -0
  140. package/registry/directories/awesome-mcp-lists/playbook.md +6 -0
  141. package/registry/directories/awesome-remote-mcp-servers/directory.yaml +46 -0
  142. package/registry/directories/awesome-remote-mcp-servers/playbook.md +8 -0
  143. package/registry/directories/awesome-tools/directory.yaml +55 -0
  144. package/registry/directories/awesome-tools/playbook.md +8 -0
  145. package/registry/directories/awesomeindie/directory.yaml +51 -0
  146. package/registry/directories/awesomeindie/playbook.md +7 -0
  147. package/registry/directories/axis-launch/directory.yaml +64 -0
  148. package/registry/directories/b2b-saas-market/directory.yaml +63 -0
  149. package/registry/directories/bai-tools/directory.yaml +53 -0
  150. package/registry/directories/bai-tools/playbook.md +7 -0
  151. package/registry/directories/basedtools/directory.yaml +59 -0
  152. package/registry/directories/basedtools/playbook.md +1 -0
  153. package/registry/directories/best-ai-org/directory.yaml +53 -0
  154. package/registry/directories/best-ai-org/playbook.md +1 -0
  155. package/registry/directories/best-of-ai/directory.yaml +57 -0
  156. package/registry/directories/best-of-ai/playbook.md +1 -0
  157. package/registry/directories/bestaito/directory.yaml +47 -0
  158. package/registry/directories/bestaito/playbook.md +6 -0
  159. package/registry/directories/bestofai/directory.yaml +45 -0
  160. package/registry/directories/bestofai/playbook.md +7 -0
  161. package/registry/directories/bestsky-tools/directory.yaml +44 -0
  162. package/registry/directories/bestsky-tools/playbook.md +5 -0
  163. package/registry/directories/betabound/directory.yaml +63 -0
  164. package/registry/directories/betabound/playbook.md +7 -0
  165. package/registry/directories/betalist/directory.yaml +52 -0
  166. package/registry/directories/betalist/playbook.md +11 -0
  167. package/registry/directories/betterbuys/directory.yaml +55 -0
  168. package/registry/directories/betterbuys/playbook.md +7 -0
  169. package/registry/directories/betterlaunch/directory.yaml +53 -0
  170. package/registry/directories/beyond-ai-tools/directory.yaml +43 -0
  171. package/registry/directories/beyond-ai-tools/playbook.md +1 -0
  172. package/registry/directories/billion-dollar-pitch-decks/directory.yaml +64 -0
  173. package/registry/directories/billion-dollar-pitch-decks/playbook.md +4 -0
  174. package/registry/directories/browserextension-dev/directory.yaml +55 -0
  175. package/registry/directories/browserextension-dev/playbook.md +7 -0
  176. package/registry/directories/bsdirectory/directory.yaml +68 -0
  177. package/registry/directories/bsdirectory/playbook.md +5 -0
  178. package/registry/directories/buildvoyage/directory.yaml +64 -0
  179. package/registry/directories/buildvoyage/playbook.md +1 -0
  180. package/registry/directories/built-here/directory.yaml +50 -0
  181. package/registry/directories/built-here/playbook.md +6 -0
  182. package/registry/directories/builtbyme/directory.yaml +55 -0
  183. package/registry/directories/builtbyme/playbook.md +7 -0
  184. package/registry/directories/builtineu/directory.yaml +70 -0
  185. package/registry/directories/builtineu/playbook.md +7 -0
  186. package/registry/directories/businesssoftware/directory.yaml +42 -0
  187. package/registry/directories/buy-sell-startups/directory.yaml +52 -0
  188. package/registry/directories/buy-sell-startups/playbook.md +5 -0
  189. package/registry/directories/claude-mcp-directory/directory.yaml +45 -0
  190. package/registry/directories/claude-mcp-directory/playbook.md +7 -0
  191. package/registry/directories/claude-plugin-directory/directory.yaml +48 -0
  192. package/registry/directories/claude-plugin-directory/playbook.md +7 -0
  193. package/registry/directories/cocoon-ai/directory.yaml +57 -0
  194. package/registry/directories/codehype/directory.yaml +68 -0
  195. package/registry/directories/codehype/playbook.md +7 -0
  196. package/registry/directories/collectai/directory.yaml +43 -0
  197. package/registry/directories/collectai/playbook.md +5 -0
  198. package/registry/directories/commune-launchpad/directory.yaml +51 -0
  199. package/registry/directories/confettisaas/directory.yaml +51 -0
  200. package/registry/directories/confettisaas/playbook.md +1 -0
  201. package/registry/directories/crowdstax/directory.yaml +54 -0
  202. package/registry/directories/crowdstax/playbook.md +6 -0
  203. package/registry/directories/crozdesk/directory.yaml +72 -0
  204. package/registry/directories/crozdesk/playbook.md +8 -0
  205. package/registry/directories/crypto-tools-directory/directory.yaml +60 -0
  206. package/registry/directories/crypto-tools-directory/playbook.md +6 -0
  207. package/registry/directories/ctrlaltship/directory.yaml +92 -0
  208. package/registry/directories/ctrlaltship/playbook.md +6 -0
  209. package/registry/directories/dachstartups/directory.yaml +49 -0
  210. package/registry/directories/dachstartups/playbook.md +4 -0
  211. package/registry/directories/dailypings/directory.yaml +51 -0
  212. package/registry/directories/dailypings/playbook.md +6 -0
  213. package/registry/directories/dang-ai/directory.yaml +44 -0
  214. package/registry/directories/dang-ai/playbook.md +1 -0
  215. package/registry/directories/daniellaunches/directory.yaml +47 -0
  216. package/registry/directories/daniellaunches/playbook.md +6 -0
  217. package/registry/directories/datalook/directory.yaml +60 -0
  218. package/registry/directories/dayslaunch/directory.yaml +46 -0
  219. package/registry/directories/dayslaunch/playbook.md +5 -0
  220. package/registry/directories/deeplaunch/directory.yaml +46 -0
  221. package/registry/directories/deeplaunch/playbook.md +6 -0
  222. package/registry/directories/desifounder-spotlight/directory.yaml +49 -0
  223. package/registry/directories/desifounder-spotlight/playbook.md +1 -0
  224. package/registry/directories/developers-digest-mcp/directory.yaml +46 -0
  225. package/registry/directories/developers-digest-mcp/playbook.md +7 -0
  226. package/registry/directories/devhunt/directory.yaml +53 -0
  227. package/registry/directories/devhunt/playbook.md +8 -0
  228. package/registry/directories/devpages/directory.yaml +62 -0
  229. package/registry/directories/devpages/playbook.md +7 -0
  230. package/registry/directories/devresourc-es/directory.yaml +49 -0
  231. package/registry/directories/devresourc-es/playbook.md +9 -0
  232. package/registry/directories/devtools-directory/directory.yaml +63 -0
  233. package/registry/directories/devtools-directory/playbook.md +7 -0
  234. package/registry/directories/dir-hub/directory.yaml +62 -0
  235. package/registry/directories/dir-hub/playbook.md +6 -0
  236. package/registry/directories/direct2app/directory.yaml +47 -0
  237. package/registry/directories/direct2app/playbook.md +5 -0
  238. package/registry/directories/directree/directory.yaml +56 -0
  239. package/registry/directories/directree/playbook.md +7 -0
  240. package/registry/directories/dirs-cc/directory.yaml +49 -0
  241. package/registry/directories/dirs-cc/playbook.md +1 -0
  242. package/registry/directories/dododirectory/directory.yaml +46 -0
  243. package/registry/directories/dododirectory/playbook.md +6 -0
  244. package/registry/directories/dofollowtools/directory.yaml +75 -0
  245. package/registry/directories/dofollowtools/playbook.md +7 -0
  246. package/registry/directories/dropyourai/directory.yaml +73 -0
  247. package/registry/directories/dynamite-ai/directory.yaml +59 -0
  248. package/registry/directories/dynamite-ai/playbook.md +1 -0
  249. package/registry/directories/earlyhunt/directory.yaml +44 -0
  250. package/registry/directories/earlyhunt/playbook.md +6 -0
  251. package/registry/directories/ebool/directory.yaml +72 -0
  252. package/registry/directories/ebool/playbook.md +8 -0
  253. package/registry/directories/eliteai-tools/directory.yaml +45 -0
  254. package/registry/directories/eliteai-tools/playbook.md +1 -0
  255. package/registry/directories/emoji-launch/directory.yaml +60 -0
  256. package/registry/directories/emoji-launch/playbook.md +1 -0
  257. package/registry/directories/enlisted/directory.yaml +48 -0
  258. package/registry/directories/enlisted/playbook.md +7 -0
  259. package/registry/directories/eu-software-alternatives/directory.yaml +87 -0
  260. package/registry/directories/eu-software-alternatives/playbook.md +5 -0
  261. package/registry/directories/eu-vetted/directory.yaml +64 -0
  262. package/registry/directories/eu-vetted/playbook.md +1 -0
  263. package/registry/directories/eubuilt/directory.yaml +77 -0
  264. package/registry/directories/eubuilt/playbook.md +5 -0
  265. package/registry/directories/euroalternative/directory.yaml +48 -0
  266. package/registry/directories/euroalternative/playbook.md +1 -0
  267. package/registry/directories/european-tech/directory.yaml +55 -0
  268. package/registry/directories/european-tech/playbook.md +5 -0
  269. package/registry/directories/europeanapps/directory.yaml +52 -0
  270. package/registry/directories/europeanapps/playbook.md +5 -0
  271. package/registry/directories/europeanstack/directory.yaml +66 -0
  272. package/registry/directories/europeanstack/playbook.md +7 -0
  273. package/registry/directories/eurotoolsindex/directory.yaml +68 -0
  274. package/registry/directories/eurotoolsindex/playbook.md +7 -0
  275. package/registry/directories/everydev-ai/directory.yaml +50 -0
  276. package/registry/directories/everydev-ai/playbook.md +6 -0
  277. package/registry/directories/faceless-directory/directory.yaml +52 -0
  278. package/registry/directories/fastd/directory.yaml +45 -0
  279. package/registry/directories/fastd/playbook.md +1 -0
  280. package/registry/directories/fastlaunch/directory.yaml +53 -0
  281. package/registry/directories/fastlaunch/playbook.md +10 -0
  282. package/registry/directories/favors/directory.yaml +72 -0
  283. package/registry/directories/favors/playbook.md +7 -0
  284. package/registry/directories/fazier/directory.yaml +44 -0
  285. package/registry/directories/fazier/playbook.md +8 -0
  286. package/registry/directories/featuredtool/directory.yaml +51 -0
  287. package/registry/directories/featuredtool/playbook.md +1 -0
  288. package/registry/directories/find-seo-tools/directory.yaml +72 -0
  289. package/registry/directories/find-seo-tools/playbook.md +1 -0
  290. package/registry/directories/findly-tools/directory.yaml +45 -0
  291. package/registry/directories/findly-tools/playbook.md +7 -0
  292. package/registry/directories/findstack/directory.yaml +46 -0
  293. package/registry/directories/findstack/playbook.md +1 -0
  294. package/registry/directories/findyoursaas/directory.yaml +47 -0
  295. package/registry/directories/findyoursaas/playbook.md +7 -0
  296. package/registry/directories/firsto/directory.yaml +54 -0
  297. package/registry/directories/firsto/playbook.md +10 -0
  298. package/registry/directories/fosspace/directory.yaml +49 -0
  299. package/registry/directories/founders-today/directory.yaml +52 -0
  300. package/registry/directories/francesaas/directory.yaml +49 -0
  301. package/registry/directories/francesaas/playbook.md +5 -0
  302. package/registry/directories/free-startup-directories/directory.yaml +70 -0
  303. package/registry/directories/free-startup-listing/directory.yaml +59 -0
  304. package/registry/directories/free-startup-listing/playbook.md +6 -0
  305. package/registry/directories/free-tools-directory/directory.yaml +55 -0
  306. package/registry/directories/free-tools-directory/playbook.md +7 -0
  307. package/registry/directories/freeaitools-net/directory.yaml +57 -0
  308. package/registry/directories/from-europe-with-love/directory.yaml +63 -0
  309. package/registry/directories/from-europe-with-love/playbook.md +5 -0
  310. package/registry/directories/fstlaunch/directory.yaml +57 -0
  311. package/registry/directories/fstlaunch/playbook.md +6 -0
  312. package/registry/directories/futuretools/directory.yaml +67 -0
  313. package/registry/directories/futuretools/playbook.md +10 -0
  314. package/registry/directories/glama/directory.yaml +46 -0
  315. package/registry/directories/glama/playbook.md +7 -0
  316. package/registry/directories/goodaitools/directory.yaml +46 -0
  317. package/registry/directories/goodaitools/playbook.md +7 -0
  318. package/registry/directories/gptbot/directory.yaml +56 -0
  319. package/registry/directories/gptdemo/directory.yaml +56 -0
  320. package/registry/directories/gptdemo/playbook.md +7 -0
  321. package/registry/directories/hicyou/directory.yaml +51 -0
  322. package/registry/directories/hicyou/playbook.md +1 -0
  323. package/registry/directories/hightechattic/directory.yaml +49 -0
  324. package/registry/directories/hightechattic/playbook.md +6 -0
  325. package/registry/directories/howtobuysaas/directory.yaml +59 -0
  326. package/registry/directories/howtobuysaas/playbook.md +1 -0
  327. package/registry/directories/hunt0/directory.yaml +81 -0
  328. package/registry/directories/hunt0/playbook.md +1 -0
  329. package/registry/directories/huntscreens/directory.yaml +56 -0
  330. package/registry/directories/huntscreens/playbook.md +11 -0
  331. package/registry/directories/huntstartup/directory.yaml +53 -0
  332. package/registry/directories/huntstartup/playbook.md +8 -0
  333. package/registry/directories/huzzler/directory.yaml +49 -0
  334. package/registry/directories/huzzler/playbook.md +7 -0
  335. package/registry/directories/hype-star/directory.yaml +57 -0
  336. package/registry/directories/hype-star/playbook.md +6 -0
  337. package/registry/directories/idea-kiln/directory.yaml +50 -0
  338. package/registry/directories/idea-kiln/playbook.md +6 -0
  339. package/registry/directories/ignlab-launch/directory.yaml +82 -0
  340. package/registry/directories/ignlab-launch/playbook.md +7 -0
  341. package/registry/directories/indie-deals/directory.yaml +48 -0
  342. package/registry/directories/indie-deals/playbook.md +8 -0
  343. package/registry/directories/indie-hustles/directory.yaml +61 -0
  344. package/registry/directories/indie-hustles/playbook.md +1 -0
  345. package/registry/directories/indie-makers-hub/directory.yaml +50 -0
  346. package/registry/directories/indie-makers-hub/playbook.md +5 -0
  347. package/registry/directories/indie-page/directory.yaml +47 -0
  348. package/registry/directories/indie-page/playbook.md +1 -0
  349. package/registry/directories/indiefounders/directory.yaml +48 -0
  350. package/registry/directories/indiefounders/playbook.md +5 -0
  351. package/registry/directories/indiehackers/directory.yaml +47 -0
  352. package/registry/directories/indiehackers/playbook.md +1 -0
  353. package/registry/directories/indiehackerstacks/directory.yaml +45 -0
  354. package/registry/directories/indiehackerstacks/playbook.md +5 -0
  355. package/registry/directories/indiehub/directory.yaml +45 -0
  356. package/registry/directories/indiehub/playbook.md +8 -0
  357. package/registry/directories/indiehunt/directory.yaml +45 -0
  358. package/registry/directories/indiehunt/playbook.md +6 -0
  359. package/registry/directories/indieneed/directory.yaml +78 -0
  360. package/registry/directories/indieneed/playbook.md +9 -0
  361. package/registry/directories/indieproducts/directory.yaml +47 -0
  362. package/registry/directories/indieproducts/playbook.md +4 -0
  363. package/registry/directories/indiesignal/directory.yaml +47 -0
  364. package/registry/directories/indietool/directory.yaml +44 -0
  365. package/registry/directories/indietool/playbook.md +1 -0
  366. package/registry/directories/indietools/directory.yaml +69 -0
  367. package/registry/directories/insidr-ai/directory.yaml +49 -0
  368. package/registry/directories/insidr-ai/playbook.md +5 -0
  369. package/registry/directories/intelligent-tools/directory.yaml +59 -0
  370. package/registry/directories/intelligent-tools/playbook.md +1 -0
  371. package/registry/directories/inventlist/directory.yaml +71 -0
  372. package/registry/directories/inventlist/playbook.md +5 -0
  373. package/registry/directories/joinly/directory.yaml +68 -0
  374. package/registry/directories/joinly/playbook.md +5 -0
  375. package/registry/directories/junkstartups/directory.yaml +72 -0
  376. package/registry/directories/junkstartups/playbook.md +1 -0
  377. package/registry/directories/justhunt/directory.yaml +55 -0
  378. package/registry/directories/justhunt/playbook.md +7 -0
  379. package/registry/directories/kittylaunch/directory.yaml +70 -0
  380. package/registry/directories/kittylaunch/playbook.md +7 -0
  381. package/registry/directories/lachief/directory.yaml +81 -0
  382. package/registry/directories/lachief/playbook.md +1 -0
  383. package/registry/directories/landbook/directory.yaml +48 -0
  384. package/registry/directories/landbook/playbook.md +1 -0
  385. package/registry/directories/launch-community/directory.yaml +73 -0
  386. package/registry/directories/launch-community/playbook.md +1 -0
  387. package/registry/directories/launch-list/directory.yaml +52 -0
  388. package/registry/directories/launch-list/playbook.md +6 -0
  389. package/registry/directories/launchaf/directory.yaml +51 -0
  390. package/registry/directories/launchbase-india/directory.yaml +53 -0
  391. package/registry/directories/launchboard/directory.yaml +50 -0
  392. package/registry/directories/launchboard/playbook.md +6 -0
  393. package/registry/directories/launchclash/directory.yaml +47 -0
  394. package/registry/directories/launchclash/playbook.md +6 -0
  395. package/registry/directories/launched-tools/directory.yaml +48 -0
  396. package/registry/directories/launchfree/directory.yaml +58 -0
  397. package/registry/directories/launchfree/playbook.md +1 -0
  398. package/registry/directories/launchigniter/directory.yaml +60 -0
  399. package/registry/directories/launchigniter/playbook.md +9 -0
  400. package/registry/directories/launchit/directory.yaml +48 -0
  401. package/registry/directories/launchit/playbook.md +1 -0
  402. package/registry/directories/launchllama/directory.yaml +53 -0
  403. package/registry/directories/launchllama/playbook.md +7 -0
  404. package/registry/directories/launchonit/directory.yaml +74 -0
  405. package/registry/directories/launchonit/playbook.md +7 -0
  406. package/registry/directories/launchpedia-tools/directory.yaml +43 -0
  407. package/registry/directories/launchpedia-tools/playbook.md +5 -0
  408. package/registry/directories/launchscaler/directory.yaml +53 -0
  409. package/registry/directories/launchtory/directory.yaml +47 -0
  410. package/registry/directories/launchtory/playbook.md +5 -0
  411. package/registry/directories/launchvault/directory.yaml +52 -0
  412. package/registry/directories/launchvault/playbook.md +5 -0
  413. package/registry/directories/launchvibe/directory.yaml +45 -0
  414. package/registry/directories/launchvibe/playbook.md +1 -0
  415. package/registry/directories/launchvoid/directory.yaml +45 -0
  416. package/registry/directories/launchvoid/playbook.md +6 -0
  417. package/registry/directories/letslaunch/directory.yaml +53 -0
  418. package/registry/directories/letslaunch/playbook.md +1 -0
  419. package/registry/directories/libhunt/directory.yaml +50 -0
  420. package/registry/directories/libhunt/playbook.md +1 -0
  421. package/registry/directories/linezine/directory.yaml +44 -0
  422. package/registry/directories/linezine/playbook.md +1 -0
  423. package/registry/directories/linkrena/directory.yaml +58 -0
  424. package/registry/directories/linkrena/playbook.md +6 -0
  425. package/registry/directories/list-maro/directory.yaml +74 -0
  426. package/registry/directories/list-your-tool/directory.yaml +76 -0
  427. package/registry/directories/list-your-tool/playbook.md +1 -0
  428. package/registry/directories/listbulb/directory.yaml +48 -0
  429. package/registry/directories/listbulb/playbook.md +1 -0
  430. package/registry/directories/listing-sh/directory.yaml +70 -0
  431. package/registry/directories/llm-relevance/directory.yaml +67 -0
  432. package/registry/directories/llm-relevance/playbook.md +1 -0
  433. package/registry/directories/llms-txt-hub/directory.yaml +47 -0
  434. package/registry/directories/llms-txt-hub/playbook.md +6 -0
  435. package/registry/directories/llms-txt-site/directory.yaml +53 -0
  436. package/registry/directories/llms-txt-site/playbook.md +7 -0
  437. package/registry/directories/macnative/directory.yaml +47 -0
  438. package/registry/directories/macnative/playbook.md +6 -0
  439. package/registry/directories/made-with-bolt/directory.yaml +58 -0
  440. package/registry/directories/made-with-bolt/playbook.md +1 -0
  441. package/registry/directories/made-with-lovable/directory.yaml +64 -0
  442. package/registry/directories/made-with-lovable/playbook.md +1 -0
  443. package/registry/directories/magicbox/directory.yaml +51 -0
  444. package/registry/directories/magicbox/playbook.md +16 -0
  445. package/registry/directories/maidensail/directory.yaml +54 -0
  446. package/registry/directories/makeitlast/directory.yaml +63 -0
  447. package/registry/directories/makeitlast/playbook.md +8 -0
  448. package/registry/directories/makerhunt/directory.yaml +56 -0
  449. package/registry/directories/makerhunt/playbook.md +1 -0
  450. package/registry/directories/makerlist/directory.yaml +49 -0
  451. package/registry/directories/makerlist/playbook.md +7 -0
  452. package/registry/directories/mapbid/directory.yaml +46 -0
  453. package/registry/directories/marketingdb/directory.yaml +50 -0
  454. package/registry/directories/marketingdb/playbook.md +8 -0
  455. package/registry/directories/marketraa/directory.yaml +63 -0
  456. package/registry/directories/marketraa/playbook.md +1 -0
  457. package/registry/directories/mcp-market/directory.yaml +49 -0
  458. package/registry/directories/mcp-market/playbook.md +7 -0
  459. package/registry/directories/mcp-repository/directory.yaml +45 -0
  460. package/registry/directories/mcp-repository/playbook.md +6 -0
  461. package/registry/directories/mcp-so/directory.yaml +45 -0
  462. package/registry/directories/mcp-so/playbook.md +6 -0
  463. package/registry/directories/mcpservers/directory.yaml +53 -0
  464. package/registry/directories/mcpservers/playbook.md +7 -0
  465. package/registry/directories/mf8/directory.yaml +53 -0
  466. package/registry/directories/mf8/playbook.md +5 -0
  467. package/registry/directories/microlaunch/directory.yaml +48 -0
  468. package/registry/directories/microlaunch/playbook.md +1 -0
  469. package/registry/directories/microsaasexamples/directory.yaml +50 -0
  470. package/registry/directories/must-tools/directory.yaml +68 -0
  471. package/registry/directories/must-tools/playbook.md +6 -0
  472. package/registry/directories/my-launch-stash/directory.yaml +47 -0
  473. package/registry/directories/my-launch-stash/playbook.md +8 -0
  474. package/registry/directories/nachonacho/directory.yaml +47 -0
  475. package/registry/directories/navfolders/directory.yaml +48 -0
  476. package/registry/directories/navfolders/playbook.md +5 -0
  477. package/registry/directories/navs/directory.yaml +66 -0
  478. package/registry/directories/navs/playbook.md +6 -0
  479. package/registry/directories/navtools-ai/directory.yaml +51 -0
  480. package/registry/directories/navtools-ai/playbook.md +1 -0
  481. package/registry/directories/neeed-directory/directory.yaml +60 -0
  482. package/registry/directories/neeed-directory/playbook.md +10 -0
  483. package/registry/directories/neonrev/directory.yaml +43 -0
  484. package/registry/directories/neonrev/playbook.md +1 -0
  485. package/registry/directories/newaiforyou/directory.yaml +50 -0
  486. package/registry/directories/newaiforyou/playbook.md +1 -0
  487. package/registry/directories/newtool/directory.yaml +51 -0
  488. package/registry/directories/newtool/playbook.md +1 -0
  489. package/registry/directories/nextbigwhat/directory.yaml +48 -0
  490. package/registry/directories/nextbigwhat/playbook.md +5 -0
  491. package/registry/directories/nextlaunch/directory.yaml +50 -0
  492. package/registry/directories/nextlaunch/playbook.md +8 -0
  493. package/registry/directories/nicklaunches/directory.yaml +70 -0
  494. package/registry/directories/nicklaunches/playbook.md +1 -0
  495. package/registry/directories/nocodefinder/directory.yaml +45 -0
  496. package/registry/directories/nocodefinder/playbook.md +6 -0
  497. package/registry/directories/nocodelist/directory.yaml +48 -0
  498. package/registry/directories/nocodelist/playbook.md +7 -0
  499. package/registry/directories/noonlaunch/directory.yaml +51 -0
  500. package/registry/directories/noonlaunch/playbook.md +6 -0
  501. package/registry/directories/nxgn-tools/directory.yaml +49 -0
  502. package/registry/directories/nxgn-tools/playbook.md +6 -0
  503. package/registry/directories/once-tools/directory.yaml +58 -0
  504. package/registry/directories/once-tools/playbook.md +1 -0
  505. package/registry/directories/open-launch/directory.yaml +46 -0
  506. package/registry/directories/open-launch/playbook.md +1 -0
  507. package/registry/directories/openalternative/directory.yaml +62 -0
  508. package/registry/directories/openalternative/playbook.md +8 -0
  509. package/registry/directories/openhunts/directory.yaml +52 -0
  510. package/registry/directories/openhunts/playbook.md +1 -0
  511. package/registry/directories/openresources/directory.yaml +59 -0
  512. package/registry/directories/openresources/playbook.md +6 -0
  513. package/registry/directories/opensaas-directory/directory.yaml +47 -0
  514. package/registry/directories/opensourcealternative-to/directory.yaml +63 -0
  515. package/registry/directories/opensourcealternative-to/playbook.md +6 -0
  516. package/registry/directories/opentools/directory.yaml +42 -0
  517. package/registry/directories/opentools/playbook.md +4 -0
  518. package/registry/directories/osssoftware/directory.yaml +66 -0
  519. package/registry/directories/osssoftware/playbook.md +5 -0
  520. package/registry/directories/oursoftware-eu/directory.yaml +69 -0
  521. package/registry/directories/oursoftware-eu/playbook.md +1 -0
  522. package/registry/directories/peerpush/directory.yaml +48 -0
  523. package/registry/directories/peerpush/playbook.md +8 -0
  524. package/registry/directories/phyco/directory.yaml +54 -0
  525. package/registry/directories/phygital-ai-library/directory.yaml +47 -0
  526. package/registry/directories/phygital-ai-library/playbook.md +6 -0
  527. package/registry/directories/pitchground/directory.yaml +47 -0
  528. package/registry/directories/pitchground/playbook.md +5 -0
  529. package/registry/directories/pitchwall/directory.yaml +47 -0
  530. package/registry/directories/pitchwall/playbook.md +1 -0
  531. package/registry/directories/postmake/directory.yaml +56 -0
  532. package/registry/directories/postmake/playbook.md +1 -0
  533. package/registry/directories/poweredbyai/directory.yaml +57 -0
  534. package/registry/directories/poweredbyai/playbook.md +1 -0
  535. package/registry/directories/pro-launch/directory.yaml +51 -0
  536. package/registry/directories/pro-launch/playbook.md +9 -0
  537. package/registry/directories/product-hunt/directory.yaml +54 -0
  538. package/registry/directories/product-hunt/playbook.md +1 -0
  539. package/registry/directories/product-rally/directory.yaml +50 -0
  540. package/registry/directories/product-rally/playbook.md +6 -0
  541. package/registry/directories/productivity-directory/directory.yaml +47 -0
  542. package/registry/directories/productivity-directory/playbook.md +7 -0
  543. package/registry/directories/productwatch/directory.yaml +51 -0
  544. package/registry/directories/productwatch/playbook.md +7 -0
  545. package/registry/directories/proofstories/directory.yaml +47 -0
  546. package/registry/directories/proofstories/playbook.md +1 -0
  547. package/registry/directories/prouct/directory.yaml +93 -0
  548. package/registry/directories/prouct/playbook.md +7 -0
  549. package/registry/directories/public-apis/directory.yaml +47 -0
  550. package/registry/directories/public-apis/playbook.md +6 -0
  551. package/registry/directories/publishyoursaas/directory.yaml +53 -0
  552. package/registry/directories/publishyoursaas/playbook.md +7 -0
  553. package/registry/directories/rankinpublic/directory.yaml +50 -0
  554. package/registry/directories/rankinpublic/playbook.md +1 -0
  555. package/registry/directories/rankyourai/directory.yaml +50 -0
  556. package/registry/directories/rankyourai/playbook.md +1 -0
  557. package/registry/directories/resource-fyi/directory.yaml +47 -0
  558. package/registry/directories/resource-fyi/playbook.md +5 -0
  559. package/registry/directories/roozna/directory.yaml +68 -0
  560. package/registry/directories/roozna/playbook.md +1 -0
  561. package/registry/directories/saas-fyi/directory.yaml +73 -0
  562. package/registry/directories/saas-fyi/playbook.md +7 -0
  563. package/registry/directories/saas-italia/directory.yaml +58 -0
  564. package/registry/directories/saas-launches/directory.yaml +47 -0
  565. package/registry/directories/saas-launches/playbook.md +6 -0
  566. package/registry/directories/saasbison/directory.yaml +44 -0
  567. package/registry/directories/saasbison/playbook.md +8 -0
  568. package/registry/directories/saascity/directory.yaml +50 -0
  569. package/registry/directories/saascubes/directory.yaml +49 -0
  570. package/registry/directories/saascubes/playbook.md +6 -0
  571. package/registry/directories/saasfame/directory.yaml +47 -0
  572. package/registry/directories/saasfame/playbook.md +6 -0
  573. package/registry/directories/saasgenius/directory.yaml +50 -0
  574. package/registry/directories/saasgenius/playbook.md +8 -0
  575. package/registry/directories/saasgrave-launches/directory.yaml +53 -0
  576. package/registry/directories/saasgrave-launches/playbook.md +7 -0
  577. package/registry/directories/saashammer/directory.yaml +53 -0
  578. package/registry/directories/saashammer/playbook.md +6 -0
  579. package/registry/directories/saashub/directory.yaml +51 -0
  580. package/registry/directories/saashub/playbook.md +9 -0
  581. package/registry/directories/saashunt/directory.yaml +43 -0
  582. package/registry/directories/saaslineup/directory.yaml +68 -0
  583. package/registry/directories/saaslineup/playbook.md +1 -0
  584. package/registry/directories/saaslist/directory.yaml +55 -0
  585. package/registry/directories/saaslist/playbook.md +5 -0
  586. package/registry/directories/saasmag-saas1000/directory.yaml +71 -0
  587. package/registry/directories/saaspage/directory.yaml +48 -0
  588. package/registry/directories/saasplace/directory.yaml +66 -0
  589. package/registry/directories/saasplace/playbook.md +1 -0
  590. package/registry/directories/saasrow/directory.yaml +51 -0
  591. package/registry/directories/saasrow/playbook.md +1 -0
  592. package/registry/directories/saassy-board/directory.yaml +54 -0
  593. package/registry/directories/saassy-board/playbook.md +1 -0
  594. package/registry/directories/saastore/directory.yaml +56 -0
  595. package/registry/directories/saastore/playbook.md +6 -0
  596. package/registry/directories/saastr-community-agents/directory.yaml +65 -0
  597. package/registry/directories/saastr-community-agents/playbook.md +8 -0
  598. package/registry/directories/saasvault/directory.yaml +48 -0
  599. package/registry/directories/saasvault/playbook.md +6 -0
  600. package/registry/directories/scrolllaunch/directory.yaml +54 -0
  601. package/registry/directories/scrolllaunch/playbook.md +8 -0
  602. package/registry/directories/seewhatnewai/directory.yaml +53 -0
  603. package/registry/directories/sellwithboost/directory.yaml +54 -0
  604. package/registry/directories/sellwithboost/playbook.md +5 -0
  605. package/registry/directories/serchen/directory.yaml +48 -0
  606. package/registry/directories/serchen/playbook.md +8 -0
  607. package/registry/directories/shinylaunch/directory.yaml +48 -0
  608. package/registry/directories/shinylaunch/playbook.md +6 -0
  609. package/registry/directories/shipyard-hq/directory.yaml +58 -0
  610. package/registry/directories/shipyard-hq/playbook.md +1 -0
  611. package/registry/directories/shipybara/directory.yaml +48 -0
  612. package/registry/directories/shipybara/playbook.md +8 -0
  613. package/registry/directories/shipycorn/directory.yaml +47 -0
  614. package/registry/directories/shipycorn/playbook.md +6 -0
  615. package/registry/directories/showmebest-ai/directory.yaml +50 -0
  616. package/registry/directories/showmebest-ai/playbook.md +1 -0
  617. package/registry/directories/showmysites/directory.yaml +52 -0
  618. package/registry/directories/showmysites/playbook.md +7 -0
  619. package/registry/directories/sidehunt/directory.yaml +56 -0
  620. package/registry/directories/sidehunt/playbook.md +1 -0
  621. package/registry/directories/sideprojectors/directory.yaml +55 -0
  622. package/registry/directories/sideprojectors/playbook.md +1 -0
  623. package/registry/directories/sideprojects/directory.yaml +52 -0
  624. package/registry/directories/sideprojects/playbook.md +1 -0
  625. package/registry/directories/sitepatent/directory.yaml +58 -0
  626. package/registry/directories/sitepatent/playbook.md +1 -0
  627. package/registry/directories/small-business-marketing-tools/directory.yaml +67 -0
  628. package/registry/directories/small-business-marketing-tools/playbook.md +1 -0
  629. package/registry/directories/smol-launch/directory.yaml +44 -0
  630. package/registry/directories/smol-launch/playbook.md +9 -0
  631. package/registry/directories/software-como-servicio/directory.yaml +64 -0
  632. package/registry/directories/software-como-servicio/playbook.md +6 -0
  633. package/registry/directories/software-finder/directory.yaml +47 -0
  634. package/registry/directories/software-finder/playbook.md +6 -0
  635. package/registry/directories/softwaresuggest/directory.yaml +68 -0
  636. package/registry/directories/softwaresuggest/playbook.md +6 -0
  637. package/registry/directories/solo-launches/directory.yaml +48 -0
  638. package/registry/directories/solo-launches/playbook.md +6 -0
  639. package/registry/directories/sololaunch/directory.yaml +69 -0
  640. package/registry/directories/sourceforge/directory.yaml +75 -0
  641. package/registry/directories/sourceforge/playbook.md +9 -0
  642. package/registry/directories/spacerrapps/directory.yaml +53 -0
  643. package/registry/directories/spacerrapps/playbook.md +6 -0
  644. package/registry/directories/speartip/directory.yaml +48 -0
  645. package/registry/directories/speartip/playbook.md +1 -0
  646. package/registry/directories/spiff-store/directory.yaml +46 -0
  647. package/registry/directories/spiff-store/playbook.md +6 -0
  648. package/registry/directories/spot-startups/directory.yaml +48 -0
  649. package/registry/directories/spotsaas/directory.yaml +45 -0
  650. package/registry/directories/squidir/directory.yaml +60 -0
  651. package/registry/directories/squidir/playbook.md +5 -0
  652. package/registry/directories/stackboo/directory.yaml +49 -0
  653. package/registry/directories/stackboo/playbook.md +6 -0
  654. package/registry/directories/stackledge/directory.yaml +84 -0
  655. package/registry/directories/stackledge/playbook.md +8 -0
  656. package/registry/directories/startup-alternatives/directory.yaml +68 -0
  657. package/registry/directories/startup-alternatives/playbook.md +5 -0
  658. package/registry/directories/startup-benchmarks/directory.yaml +59 -0
  659. package/registry/directories/startup-benchmarks/playbook.md +1 -0
  660. package/registry/directories/startup-collections/directory.yaml +56 -0
  661. package/registry/directories/startup-collections/playbook.md +5 -0
  662. package/registry/directories/startup-europe/directory.yaml +52 -0
  663. package/registry/directories/startup-europe/playbook.md +1 -0
  664. package/registry/directories/startup-fame/directory.yaml +51 -0
  665. package/registry/directories/startup-fame/playbook.md +8 -0
  666. package/registry/directories/startup-fast/directory.yaml +52 -0
  667. package/registry/directories/startup-fast/playbook.md +9 -0
  668. package/registry/directories/startup-list/directory.yaml +81 -0
  669. package/registry/directories/startup-list/playbook.md +8 -0
  670. package/registry/directories/startup-listing/directory.yaml +77 -0
  671. package/registry/directories/startup-listing/playbook.md +1 -0
  672. package/registry/directories/startup-og/directory.yaml +52 -0
  673. package/registry/directories/startup-ranking/directory.yaml +51 -0
  674. package/registry/directories/startup-ranking/playbook.md +1 -0
  675. package/registry/directories/startup-spotlight/directory.yaml +50 -0
  676. package/registry/directories/startup-spotlight/playbook.md +5 -0
  677. package/registry/directories/startup88/directory.yaml +46 -0
  678. package/registry/directories/startup88/playbook.md +1 -0
  679. package/registry/directories/startupaccelerator/directory.yaml +76 -0
  680. package/registry/directories/startupaccelerator/playbook.md +7 -0
  681. package/registry/directories/startupbase/directory.yaml +44 -0
  682. package/registry/directories/startupbase/playbook.md +1 -0
  683. package/registry/directories/startupbeats/directory.yaml +48 -0
  684. package/registry/directories/startupbeats/playbook.md +6 -0
  685. package/registry/directories/startupdirectory/directory.yaml +50 -0
  686. package/registry/directories/startupdirectory/playbook.md +1 -0
  687. package/registry/directories/startupinspire/directory.yaml +52 -0
  688. package/registry/directories/startupinspire/playbook.md +8 -0
  689. package/registry/directories/startuplibrary/directory.yaml +46 -0
  690. package/registry/directories/startuplibrary/playbook.md +1 -0
  691. package/registry/directories/startuplist-africa/directory.yaml +45 -0
  692. package/registry/directories/startuplist-africa/playbook.md +6 -0
  693. package/registry/directories/startups-gallery/directory.yaml +61 -0
  694. package/registry/directories/startups-gallery/playbook.md +5 -0
  695. package/registry/directories/startupsfm/directory.yaml +64 -0
  696. package/registry/directories/startupsfm/playbook.md +9 -0
  697. package/registry/directories/startupsfyi/directory.yaml +51 -0
  698. package/registry/directories/startupsfyi/playbook.md +4 -0
  699. package/registry/directories/startupslab/directory.yaml +61 -0
  700. package/registry/directories/startupslab/playbook.md +8 -0
  701. package/registry/directories/startuptile/directory.yaml +51 -0
  702. package/registry/directories/startuptile/playbook.md +5 -0
  703. package/registry/directories/startuptrusted/directory.yaml +46 -0
  704. package/registry/directories/startuptrusted/playbook.md +1 -0
  705. package/registry/directories/startuups/directory.yaml +50 -0
  706. package/registry/directories/steemhunt/directory.yaml +46 -0
  707. package/registry/directories/steemhunt/playbook.md +5 -0
  708. package/registry/directories/stellar-launch/directory.yaml +55 -0
  709. package/registry/directories/stellar-launch/playbook.md +1 -0
  710. package/registry/directories/stork-ai/directory.yaml +49 -0
  711. package/registry/directories/stork-ai/playbook.md +3 -0
  712. package/registry/directories/stork-mcp/directory.yaml +52 -0
  713. package/registry/directories/stork-mcp/playbook.md +3 -0
  714. package/registry/directories/submitaitools/directory.yaml +57 -0
  715. package/registry/directories/submitaitools/playbook.md +19 -0
  716. package/registry/directories/submithunt/directory.yaml +49 -0
  717. package/registry/directories/submithunt/playbook.md +9 -0
  718. package/registry/directories/submitmysaas/directory.yaml +51 -0
  719. package/registry/directories/submitmysaas/playbook.md +6 -0
  720. package/registry/directories/submitstartup/directory.yaml +73 -0
  721. package/registry/directories/sumodir/directory.yaml +47 -0
  722. package/registry/directories/sumodir/playbook.md +7 -0
  723. package/registry/directories/superlaunch/directory.yaml +64 -0
  724. package/registry/directories/superlaunch/playbook.md +7 -0
  725. package/registry/directories/techboard-australia/directory.yaml +54 -0
  726. package/registry/directories/techboard-australia/playbook.md +5 -0
  727. package/registry/directories/technologyadvice/directory.yaml +65 -0
  728. package/registry/directories/technologyadvice/playbook.md +8 -0
  729. package/registry/directories/tekpon/directory.yaml +44 -0
  730. package/registry/directories/tensorblock-mcp/directory.yaml +46 -0
  731. package/registry/directories/tensorblock-mcp/playbook.md +7 -0
  732. package/registry/directories/thankjohn/directory.yaml +42 -0
  733. package/registry/directories/thankjohn/playbook.md +1 -0
  734. package/registry/directories/that-ai-collection/directory.yaml +52 -0
  735. package/registry/directories/the-ai-library/directory.yaml +46 -0
  736. package/registry/directories/the-ai-library/playbook.md +1 -0
  737. package/registry/directories/the-next-ai/directory.yaml +62 -0
  738. package/registry/directories/the-popular-apps/directory.yaml +43 -0
  739. package/registry/directories/the-popular-apps/playbook.md +1 -0
  740. package/registry/directories/the-startup-inc/directory.yaml +90 -0
  741. package/registry/directories/the-startup-inc/playbook.md +4 -0
  742. package/registry/directories/the-startup-project/directory.yaml +67 -0
  743. package/registry/directories/the-startup-project/playbook.md +5 -0
  744. package/registry/directories/the-tool-directory/directory.yaml +75 -0
  745. package/registry/directories/thedevtoolsdir/directory.yaml +57 -0
  746. package/registry/directories/thedevtoolsdir/playbook.md +6 -0
  747. package/registry/directories/theres-an-ai/directory.yaml +44 -0
  748. package/registry/directories/theres-an-ai/playbook.md +1 -0
  749. package/registry/directories/theresanaiforthat/directory.yaml +51 -0
  750. package/registry/directories/theresanaiforthat/playbook.md +1 -0
  751. package/registry/directories/thesaasdir/directory.yaml +44 -0
  752. package/registry/directories/thesaasdir/playbook.md +6 -0
  753. package/registry/directories/tinylaunch/directory.yaml +50 -0
  754. package/registry/directories/tinylaunch/playbook.md +8 -0
  755. package/registry/directories/tinystartups/directory.yaml +61 -0
  756. package/registry/directories/tinystartups/playbook.md +6 -0
  757. package/registry/directories/tipseason/directory.yaml +68 -0
  758. package/registry/directories/tolodora/directory.yaml +59 -0
  759. package/registry/directories/tool-commons/directory.yaml +46 -0
  760. package/registry/directories/tool-commons/playbook.md +1 -0
  761. package/registry/directories/tool-directory-ai/directory.yaml +64 -0
  762. package/registry/directories/tool-directory-ai/playbook.md +5 -0
  763. package/registry/directories/tool-finder/directory.yaml +64 -0
  764. package/registry/directories/tool-finder/playbook.md +6 -0
  765. package/registry/directories/tooldirs/directory.yaml +64 -0
  766. package/registry/directories/tooldirs/playbook.md +7 -0
  767. package/registry/directories/toolfame/directory.yaml +47 -0
  768. package/registry/directories/toolfame/playbook.md +6 -0
  769. package/registry/directories/toolfio/directory.yaml +48 -0
  770. package/registry/directories/toolfio/playbook.md +7 -0
  771. package/registry/directories/toolfolio/directory.yaml +54 -0
  772. package/registry/directories/toolfolio/playbook.md +1 -0
  773. package/registry/directories/toolfound/directory.yaml +66 -0
  774. package/registry/directories/toolify/directory.yaml +52 -0
  775. package/registry/directories/toolify/playbook.md +8 -0
  776. package/registry/directories/toolkitly/directory.yaml +46 -0
  777. package/registry/directories/toolkitly/playbook.md +1 -0
  778. package/registry/directories/toollist-ai/directory.yaml +60 -0
  779. package/registry/directories/toollist-ai/playbook.md +1 -0
  780. package/registry/directories/toolomio/directory.yaml +55 -0
  781. package/registry/directories/toolomio/playbook.md +5 -0
  782. package/registry/directories/toolpasta/directory.yaml +46 -0
  783. package/registry/directories/toolpasta/playbook.md +1 -0
  784. package/registry/directories/toolpilot-ai/directory.yaml +77 -0
  785. package/registry/directories/toolpilot-ai/playbook.md +8 -0
  786. package/registry/directories/toolpromote/directory.yaml +81 -0
  787. package/registry/directories/toolrain/directory.yaml +48 -0
  788. package/registry/directories/toolrain/playbook.md +1 -0
  789. package/registry/directories/tools-ai-online/directory.yaml +65 -0
  790. package/registry/directories/tools-ai-online/playbook.md +7 -0
  791. package/registry/directories/tools-cafe/directory.yaml +51 -0
  792. package/registry/directories/tools-cafe/playbook.md +6 -0
  793. package/registry/directories/toolscout/directory.yaml +45 -0
  794. package/registry/directories/toolscout/playbook.md +5 -0
  795. package/registry/directories/toolsfine/directory.yaml +58 -0
  796. package/registry/directories/toolsfine/playbook.md +9 -0
  797. package/registry/directories/toolsnocode/directory.yaml +48 -0
  798. package/registry/directories/toolsnocode/playbook.md +6 -0
  799. package/registry/directories/tooltrove/directory.yaml +71 -0
  800. package/registry/directories/tooltrove/playbook.md +9 -0
  801. package/registry/directories/toolveyor/directory.yaml +44 -0
  802. package/registry/directories/toolveyor/playbook.md +1 -0
  803. package/registry/directories/toolwise/directory.yaml +67 -0
  804. package/registry/directories/toolwise/playbook.md +7 -0
  805. package/registry/directories/toools-design/directory.yaml +64 -0
  806. package/registry/directories/toools-design/playbook.md +8 -0
  807. package/registry/directories/topai-tools/directory.yaml +43 -0
  808. package/registry/directories/topai-tools/playbook.md +1 -0
  809. package/registry/directories/traffiklist/directory.yaml +55 -0
  810. package/registry/directories/traffiklist/playbook.md +6 -0
  811. package/registry/directories/trendy-startups/directory.yaml +49 -0
  812. package/registry/directories/trendy-startups/playbook.md +1 -0
  813. package/registry/directories/trillionagent/directory.yaml +57 -0
  814. package/registry/directories/trillionagent/playbook.md +1 -0
  815. package/registry/directories/truerev/directory.yaml +83 -0
  816. package/registry/directories/truerev/playbook.md +12 -0
  817. package/registry/directories/trustmrr/directory.yaml +43 -0
  818. package/registry/directories/trustradius/directory.yaml +67 -0
  819. package/registry/directories/trustradius/playbook.md +9 -0
  820. package/registry/directories/trylaunch/directory.yaml +51 -0
  821. package/registry/directories/trylaunch/playbook.md +1 -0
  822. package/registry/directories/tulimoa/directory.yaml +65 -0
  823. package/registry/directories/tulimoa/playbook.md +7 -0
  824. package/registry/directories/turbo0/directory.yaml +49 -0
  825. package/registry/directories/turbo0/playbook.md +9 -0
  826. package/registry/directories/twelve-tools/directory.yaml +47 -0
  827. package/registry/directories/twelve-tools/playbook.md +6 -0
  828. package/registry/directories/ultimate-free-tools/directory.yaml +49 -0
  829. package/registry/directories/ultimate-free-tools/playbook.md +8 -0
  830. package/registry/directories/uneed/directory.yaml +55 -0
  831. package/registry/directories/uneed/playbook.md +9 -0
  832. package/registry/directories/unitelist/directory.yaml +49 -0
  833. package/registry/directories/unitelist/playbook.md +7 -0
  834. package/registry/directories/uno-directory/directory.yaml +48 -0
  835. package/registry/directories/uno-directory/playbook.md +1 -0
  836. package/registry/directories/ventureradar/directory.yaml +47 -0
  837. package/registry/directories/ventureradar/playbook.md +5 -0
  838. package/registry/directories/vibe-coding-cloud/directory.yaml +45 -0
  839. package/registry/directories/vibe-coding-cloud/playbook.md +6 -0
  840. package/registry/directories/victrays/directory.yaml +55 -0
  841. package/registry/directories/victrays/playbook.md +8 -0
  842. package/registry/directories/visalytica/directory.yaml +59 -0
  843. package/registry/directories/wavel/directory.yaml +51 -0
  844. package/registry/directories/we-are-founders/directory.yaml +85 -0
  845. package/registry/directories/we-are-founders/playbook.md +1 -0
  846. package/registry/directories/web-store-extensions/directory.yaml +45 -0
  847. package/registry/directories/web-store-extensions/playbook.md +6 -0
  848. package/registry/directories/webcurate/directory.yaml +50 -0
  849. package/registry/directories/website-rating/directory.yaml +66 -0
  850. package/registry/directories/website-rating/playbook.md +9 -0
  851. package/registry/directories/webspot/directory.yaml +46 -0
  852. package/registry/directories/webspot/playbook.md +5 -0
  853. package/registry/directories/weekhack/directory.yaml +56 -0
  854. package/registry/directories/weekhack/playbook.md +5 -0
  855. package/registry/directories/weliketools/directory.yaml +50 -0
  856. package/registry/directories/weliketools/playbook.md +6 -0
  857. package/registry/directories/what-is-ai-tools/directory.yaml +48 -0
  858. package/registry/directories/what-is-ai-tools/playbook.md +1 -0
  859. package/registry/directories/wired-business/directory.yaml +44 -0
  860. package/registry/directories/wired-business/playbook.md +6 -0
  861. package/registry/directories/would-you-pay/directory.yaml +49 -0
  862. package/registry/directories/would-you-pay/playbook.md +6 -0
  863. package/registry/directories/xai-plugin-marketplace/directory.yaml +46 -0
  864. package/registry/directories/xai-plugin-marketplace/playbook.md +5 -0
  865. package/registry/directories/zplatform-ai/directory.yaml +75 -0
  866. package/registry/directories/zumvu/directory.yaml +50 -0
  867. package/scripts/ab +40 -0
  868. package/scripts/check-listings +8 -0
  869. package/scripts/google-login +13 -0
  870. package/scripts/show-browser +17 -0
  871. package/src/accounts.ts +81 -0
  872. package/src/audit.ts +229 -0
  873. package/src/browser.ts +123 -0
  874. package/src/capabilities.ts +62 -0
  875. package/src/cli.ts +741 -0
  876. package/src/db/index.ts +29 -0
  877. package/src/db/schema.ts +152 -0
  878. package/src/directory-kinds.ts +12 -0
  879. package/src/import-hosted.ts +156 -0
  880. package/src/jobs.ts +270 -0
  881. package/src/listings.ts +64 -0
  882. package/src/manual.ts +26 -0
  883. package/src/product-approvals.ts +31 -0
  884. package/src/public.ts +24 -0
  885. package/src/ratings.ts +71 -0
  886. package/src/registry-updates.ts +54 -0
  887. package/src/registry.ts +101 -0
  888. package/src/review.ts +58 -0
  889. package/src/server.ts +186 -0
  890. package/src/views.ts +71 -0
  891. package/src/web/app.tsx +1026 -0
  892. package/src/web/favicon.svg +4 -0
  893. package/src/web/index.html +14 -0
  894. package/src/web/style.css +301 -0
  895. package/src/workspace.ts +97 -0
package/src/audit.ts ADDED
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Measures what a listing is actually worth, without AI: the rel attribute on outbound product links of real
3
+ * listing pages, whether those pages are indexable, and which bot check guards the submit form.
4
+ * Every verdict carries the page it was measured on.
5
+ */
6
+ import { locs } from "./listings";
7
+ import { normalizeUrl, type Directory } from "./registry";
8
+
9
+ const UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130 Safari/537.36";
10
+ export type Rel = "dofollow" | "nofollow" | "ugc" | "sponsored" | "mixed" | "unknown";
11
+ export type LinkAudit = {
12
+ rel: Rel;
13
+ method: "own_listing" | "sample_listing" | "platform_rule" | "none";
14
+ evidence: string | null; // the listing page the rel was read on
15
+ samples: number; // listing pages that had outbound product links
16
+ noindex: boolean | null; // listing page tells search engines not to index it
17
+ claimed: "dofollow" | "nofollow" | null; // what the directory's own text says
18
+ bot_check: "recaptcha" | "turnstile" | "hcaptcha" | "math" | "none" | "unknown";
19
+ checked_at: string;
20
+ };
21
+
22
+ async function get(url: string, timeout = 15000) {
23
+ try {
24
+ const r = await fetch(url, { redirect: "follow", headers: { "user-agent": UA, accept: "text/html,application/xhtml+xml,application/xml" }, signal: AbortSignal.timeout(timeout) });
25
+ const ct = r.headers.get("content-type") ?? "";
26
+ const body = r.ok && /html|xml|text/.test(ct) ? (await r.text()).slice(0, 2_000_000) : (await r.body?.cancel(), "");
27
+ return { status: r.status, url: r.url, body, robots: r.headers.get("x-robots-tag") ?? "" };
28
+ } catch {
29
+ return { status: 0, url, body: "", robots: "" };
30
+ }
31
+ }
32
+
33
+ // Hosts that are never "the product's website" on a listing page.
34
+ const NOISE = /(^|\.)(twitter|x|facebook|linkedin|instagram|youtube|youtu|tiktok|pinterest|reddit|discord|t|telegram|medium|google|googletagmanager|googleapis|gstatic|doubleclick|apple|microsoft|cloudflare|gravatar|wp|wordpress|w3|schema|ahrefs|semrush|producthunt|github|gitlab|vercel|netlify|stripe|paypal|buymeacoffee|ko-fi|patreon|substack|beehiiv|mailchimp|typeform|tally|notion|calendly|cal|crisp|intercom|hotjar|plausible|umami|fonts|jsdelivr|unpkg|cdnjs)\.[a-z.]+$/;
35
+
36
+ /** rel of each outbound link on a page, keyed by host; nofollow wins over ugc/sponsored when combined. */
37
+ export function outboundRels(html: string, pageHost: string) {
38
+ const out: { host: string; href: string; rel: Exclude<Rel, "mixed" | "unknown">; visit: boolean }[] = [];
39
+ for (const m of html.matchAll(/<a\b([^>]*)>([\s\S]{0,600}?)<\/a>/gi)) {
40
+ const attrs = m[1]!;
41
+ const label = m[2]!.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
42
+ const href = /\bhref\s*=\s*["']([^"']+)["']/i.exec(attrs)?.[1];
43
+ if (!href || !/^https?:\/\//i.test(href)) continue;
44
+ let host: string;
45
+ try { host = new URL(href).hostname.toLowerCase().replace(/^www\./, ""); } catch { continue; }
46
+ if (host === pageHost || host.endsWith(`.${pageHost}`) || pageHost.endsWith(`.${host}`) || NOISE.test(host)) continue;
47
+ const rel = (/\brel\s*=\s*["']([^"']*)["']/i.exec(attrs)?.[1] ?? "").toLowerCase();
48
+ // The listing's "go to the product" link: visit/website wording, or a ref/utm parameter naming the directory.
49
+ const visit = /\b(visit|website|try|get started|go to|open|homepage|view site|official)\b/i.test(label) || /visit|website|outbound|external-link/i.test(attrs) || /[?&](ref|utm_source|via|source)=/i.test(href);
50
+ out.push({ host, href, visit, rel: /nofollow/.test(rel) ? "nofollow" : /sponsored/.test(rel) ? "sponsored" : /ugc/.test(rel) ? "ugc" : "dofollow" });
51
+ }
52
+ return out;
53
+ }
54
+
55
+ export const isNoindex = (html: string, header: string) =>
56
+ /noindex/i.test(header) || /<meta[^>]+name=["'](robots|googlebot)["'][^>]+content=["'][^"']*noindex/i.test(html) || /<meta[^>]+content=["'][^"']*noindex[^"']*["'][^>]+name=["'](robots|googlebot)["']/i.test(html);
57
+
58
+ export function botCheck(html: string): LinkAudit["bot_check"] {
59
+ if (!html) return "unknown";
60
+ if (/challenges\.cloudflare\.com\/turnstile|cf-turnstile/i.test(html)) return "turnstile";
61
+ if (/hcaptcha\.com|h-captcha/i.test(html)) return "hcaptcha";
62
+ if (/google\.com\/recaptcha|recaptcha\/api|g-recaptcha|grecaptcha/i.test(html)) return "recaptcha";
63
+ if (/what is \d+\s*[+\-x×*]\s*\d+|anti-?spam (question|check)|solve.{0,20}(sum|math)/i.test(html)) return "math";
64
+ return "none";
65
+ }
66
+
67
+ /** What the directory says about its links, from the registry's own notes and conditions. */
68
+ export function claimedRel(text: string): LinkAudit["claimed"] {
69
+ const t = text.toLowerCase();
70
+ const no = /no-?follow/.test(t), dof = /do-?follow/.test(t);
71
+ if (dof && !no) return "dofollow";
72
+ if (no && !dof) return "nofollow";
73
+ if (dof && no) return /free[^.]{0,60}no-?follow|no-?follow[^.]{0,40}free/.test(t) ? "nofollow" : "dofollow";
74
+ return null;
75
+ }
76
+
77
+ // Paths that look like a single product's page on a directory.
78
+ const PRODUCT_PATH = /\/(tools?|products?|p|startups?|listings?|apps?|software|items?|launch(es)?|projects?|ai|saas|companies|company|directory|site|sites|s)\/[^/?#]{2,}\/?$/i;
79
+
80
+ async function sampleListings(site: string, max = 4) {
81
+ const origin = new URL(site).origin;
82
+ const robots = (await get(`${origin}/robots.txt`, 8000)).body;
83
+ const queue = [...robots.matchAll(/^sitemap:\s*(\S+)/gim)].map((m) => m[1]!);
84
+ if (!queue.length) queue.push(`${origin}/sitemap.xml`);
85
+ const found: string[] = [];
86
+ for (let i = 0; i < queue.length && i < 6 && found.length < 40; i++) {
87
+ const xml = (await get(queue[i]!, 12000)).body;
88
+ const urls = locs(xml);
89
+ if (/<sitemapindex/i.test(xml)) {
90
+ // Prefer child sitemaps that sound like products.
91
+ queue.push(...urls.sort((a, b) => Number(/tool|product|startup|listing|app|item|launch|project/i.test(b)) - Number(/tool|product|startup|listing|app|item|launch|project/i.test(a))));
92
+ } else found.push(...urls.filter((u) => PRODUCT_PATH.test(new URL(u, origin).pathname)));
93
+ }
94
+ // Spread the picks across the list: newest and oldest listings can differ.
95
+ const step = Math.max(1, Math.floor(found.length / max));
96
+ return [...new Set(found.filter((_, i) => i % step === 0))].slice(0, max);
97
+ }
98
+
99
+ const tally = (rels: Rel[]): Rel => {
100
+ const kinds = new Set(rels);
101
+ if (!kinds.size) return "unknown";
102
+ if (kinds.size === 1) return rels[0]!;
103
+ // A page with both is usually "dofollow for paid/featured, nofollow for the rest": call it mixed.
104
+ return "mixed";
105
+ };
106
+
107
+ export async function auditDirectory(d: Directory, ownListings: { url: string; productHost: string }[], playbook = ""): Promise<LinkAudit> {
108
+ const today = new Date().toISOString().slice(0, 10);
109
+ const site = normalizeUrl(d.website).domain;
110
+ const claimed = claimedRel([d.cost.notes ?? "", ...d.submission.conditions, playbook].join(" "));
111
+ const submit = d.submission_url ? await get(d.submission_url) : null;
112
+ const base = { claimed, bot_check: submit ? botCheck(submit.body) : ("unknown" as const), checked_at: today };
113
+
114
+ // GitHub renders every user-content link with rel="nofollow".
115
+ if (/^github\.com$|\.github\.io$/.test(site) && /github\.com/.test(d.website))
116
+ return { ...base, rel: "nofollow", method: "platform_rule", evidence: d.website, samples: 0, noindex: false };
117
+
118
+ // 1. Our own live listings: the exact link we got.
119
+ for (const own of ownListings) {
120
+ const page = await get(own.url);
121
+ const links = outboundRels(page.body, site).filter((l) => l.host === own.productHost || l.host.endsWith(`.${own.productHost}`));
122
+ // The URL stays on the submission: the shared registry never names a product.
123
+ if (links.length) return { ...base, rel: tally(links.map((l) => l.rel)), method: "own_listing", evidence: null, samples: 1, noindex: isNoindex(page.body, page.robots) };
124
+ }
125
+
126
+ // 2. Other products' listing pages from the sitemap: the dominant rel on outbound product links.
127
+ const rels: Rel[] = [];
128
+ let evidence: string | null = null, noindex: boolean | null = null, samples = 0;
129
+ for (const url of await sampleListings(d.website)) {
130
+ const page = await get(url);
131
+ // Only pages with a visit-style link to one external product count as listings.
132
+ const links = outboundRels(page.body, site).filter((l) => l.visit);
133
+ const hosts = new Set(links.map((l) => l.host));
134
+ if (!links.length || hosts.size > 3) continue; // none, or a page of many products (a category page)
135
+ const counts = new Map<string, number>();
136
+ for (const l of links) counts.set(l.host, (counts.get(l.host) ?? 0) + 1);
137
+ const top = [...counts].sort((a, b) => b[1] - a[1])[0]![0];
138
+ rels.push(tally(links.filter((l) => l.host === top).map((l) => l.rel)));
139
+ evidence ??= url;
140
+ noindex ??= isNoindex(page.body, page.robots);
141
+ samples++;
142
+ }
143
+ return { ...base, rel: tally(rels), method: samples ? "sample_listing" : "none", evidence, samples, noindex };
144
+ }
145
+
146
+ const NOT_LISTING = /^(c|cat|category|categories|tag|tags|topics?|collections?|directories|blog|posts?|news|articles?|guides?|alternatives?|compare|vs|best|top|page|search|login|log-in|signin|sign-in|signup|sign-up|register|account|dashboard|pricing|plans|about|contact|submit|add|faq|help|docs|terms|privacy|legal|cookies?|sitemap|rss|feed|advertise|sponsor|newsletter|api|users?|u|profile|makers?|authors?|en|de|fr|es|it)$/i;
147
+
148
+ // ---- browser pass: real Chrome via agent-browser, for pages a plain fetch can't read -------------------------
149
+
150
+ type Anchor = { href: string; rel: string; text: string; cls: string };
151
+ type Rendered = { finalUrl: string; anchors: Anchor[]; robots: string; challenge: boolean };
152
+
153
+ /** Renders a page in a throwaway, logged-out browser session and returns its links. Read-only. */
154
+ export function renderAnchors(url: string, session: string): Rendered | null {
155
+ const ab = (...args: string[]) => Bun.spawnSync(["agent-browser", "--session", session, ...args], { stderr: "pipe", timeout: 60_000 });
156
+ if (ab("open", url).exitCode) return null;
157
+ ab("wait", "2500");
158
+ const script = `JSON.stringify({ finalUrl: location.href,
159
+ robots: (document.querySelector('meta[name=robots]') || {}).content || "",
160
+ challenge: /just a moment|checking your browser|verify you are human|ci siamo quasi/i.test(document.title + " " + document.body.innerText.slice(0, 400)),
161
+ anchors: [...document.querySelectorAll("a[href]")].slice(0, 600).map(a => ({ href: a.href, rel: a.rel || "", text: (a.innerText || a.getAttribute("aria-label") || "").trim().slice(0, 60), cls: String(a.className || "").slice(0, 120) })) })`;
162
+ const out = ab("eval", script);
163
+ try {
164
+ let v = JSON.parse(out.stdout.toString());
165
+ if (typeof v === "string") v = JSON.parse(v);
166
+ return v as Rendered;
167
+ } catch {
168
+ return null;
169
+ }
170
+ }
171
+
172
+ const relOfAttr = (rel: string): Exclude<Rel, "mixed" | "unknown"> =>
173
+ /nofollow/i.test(rel) ? "nofollow" : /sponsored/i.test(rel) ? "sponsored" : /ugc/i.test(rel) ? "ugc" : "dofollow";
174
+ const hostOf = (u: string) => { try { return new URL(u).hostname.toLowerCase().replace(/^www\./, ""); } catch { return ""; } };
175
+
176
+ /** Same rules as the fetch audit, applied to rendered pages: listing links from the homepage, visit links on each. */
177
+ export function auditDirectoryBrowser(d: Directory, session: string): { link: LinkAudit; note: string } {
178
+ const today = new Date().toISOString().slice(0, 10);
179
+ const site = normalizeUrl(d.website).domain;
180
+ const base = { claimed: d.link?.claimed ?? null, bot_check: d.link?.bot_check ?? ("unknown" as const), checked_at: today };
181
+ const home = renderAnchors(d.website, session);
182
+ if (!home) return { link: { ...base, rel: "unknown", method: "none", evidence: null, samples: 0, noindex: null }, note: "homepage did not load" };
183
+ if (home.challenge) return { link: { ...base, rel: "unknown", method: "none", evidence: null, samples: 0, noindex: null }, note: "bot challenge on the homepage" };
184
+ // Listing pages are the biggest family of internal links sharing a path pattern. Which family is the listings
185
+ // (vs categories or tags) is tested, not guessed: the right one has a single "visit" link per page.
186
+ const groups = new Map<string, string[]>();
187
+ for (const h of new Set(home.anchors.map((a) => a.href.split(/[?#]/)[0]!))) {
188
+ const host = hostOf(h);
189
+ if (!(host === site || host.endsWith(`.${site}`))) continue;
190
+ const parts = new URL(h).pathname.split("/").filter(Boolean);
191
+ if (!parts.length || parts.some((p) => NOT_LISTING.test(p))) continue;
192
+ const key = `/${parts.slice(0, -1).join("/")}${parts.length > 1 ? "/" : ""}*`;
193
+ groups.set(key, [...(groups.get(key) ?? []), h]);
194
+ }
195
+ const families = [...groups].filter(([, urls]) => urls.length >= 5).sort((a, b) => b[1].length - a[1].length).slice(0, 3);
196
+ const listings = families.flatMap(([, urls]) => urls);
197
+ const rels: Rel[] = [];
198
+ let evidence: string | null = null, noindex: boolean | null = null, samples = 0;
199
+ const read = (url: string) => {
200
+ const page = renderAnchors(url, session);
201
+ if (!page || page.challenge) return false;
202
+ const outbound = page.anchors.filter((a) => {
203
+ const host = hostOf(a.href);
204
+ return !!host && host !== site && !host.endsWith(`.${site}`) && !site.endsWith(`.${host}`) && !NOISE.test(host);
205
+ });
206
+ // A button that says Visit/Website is the listing's own link; ref-tagged links alone may be sidebar promos.
207
+ const worded = outbound.filter((a) => /\b(visit|website|try|get started|go to|open|homepage|view site|official)\b/i.test(a.text) || /visit|website|outbound|external-link/i.test(a.cls));
208
+ const links = worded.length ? worded : outbound.filter((a) => /[?&](ref|utm_source|via|source)=/i.test(a.href));
209
+ const hosts = new Set(links.map((l) => hostOf(l.href)));
210
+ if (!links.length || hosts.size > 3) return false;
211
+ const counts = new Map<string, number>();
212
+ for (const l of links) counts.set(hostOf(l.href), (counts.get(hostOf(l.href)) ?? 0) + 1);
213
+ const top = [...counts].sort((x, y) => y[1] - x[1])[0]![0];
214
+ rels.push(tally(links.filter((l) => hostOf(l.href) === top).map((l) => relOfAttr(l.rel))));
215
+ evidence ??= page.finalUrl;
216
+ noindex ??= /noindex/i.test(page.robots);
217
+ samples++;
218
+ return true;
219
+ };
220
+ for (const [, urls] of families) {
221
+ const step = Math.max(1, Math.floor(urls.length / 4));
222
+ const spread = urls.filter((_, i) => i % step === 0);
223
+ if (!spread.slice(0, 2).map(read).some(Boolean)) continue; // not this family
224
+ for (const url of spread.slice(2, 4)) read(url);
225
+ break;
226
+ }
227
+ const note = !listings.length ? "no listing-like link families on the homepage" : samples ? `${samples} listing page${samples === 1 ? "" : "s"} read` : `tried ${families.length} link famil${families.length === 1 ? "y" : "ies"}, no page with a single visit link`;
228
+ return { link: { ...base, rel: tally(rels), method: samples ? "sample_listing" : "none", evidence, samples, noindex }, note };
229
+ }
package/src/browser.ts ADDED
@@ -0,0 +1,123 @@
1
+ /**
2
+ * The agents' browser: a dedicated, real Google Chrome profile (data/browser-profile) with remote debugging on
3
+ * a local port. Any agent that can run shell commands drives it with agent-browser (`scripts/ab` attaches to it);
4
+ * the CLI starts it and handles secrets, so passwords never pass through a model.
5
+ *
6
+ * Google services other than sign-in are unreachable from this profile: a PAC script sends them to a dead proxy.
7
+ * That holds for every client attached over CDP, not only for one wrapper.
8
+ */
9
+ import { randomBytes } from "node:crypto";
10
+ import { existsSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { HOME } from "./db";
13
+
14
+ export const BROWSER_PORT = Number(process.env.LAUNCH_BROWSER_PORT ?? 9222);
15
+ export const PROFILE = join(HOME, "data/browser-profile");
16
+ const CHROME = process.env.LAUNCH_CHROME ?? "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
17
+ const BASE = `http://127.0.0.1:${BROWSER_PORT}`;
18
+ /** One agent-browser session for secret handling, attached to the same Chrome. */
19
+ const SECRETS_SESSION = "dirpush-secrets";
20
+
21
+ /** Same list as scripts/ab: the profile is signed into the human's Google account; only sign-in pages may load. */
22
+ export const BLOCKED_HOSTS = [
23
+ "mail.google.com", "gmail.com", "www.gmail.com", "inbox.google.com",
24
+ "drive.google.com", "drive.usercontent.google.com", "docs.google.com", "sheets.google.com", "slides.google.com", "forms.google.com",
25
+ "photos.google.com", "keep.google.com", "calendar.google.com", "contacts.google.com",
26
+ "myaccount.google.com", "security.google.com", "myactivity.google.com", "passwords.google.com",
27
+ "pay.google.com", "payments.google.com", "wallet.google.com", "takeout.google.com", "admin.google.com",
28
+ "script.google.com", "console.cloud.google.com", "cloud.google.com", "console.developers.google.com",
29
+ "chromewebstore.google.com", "chrome.google.com",
30
+ ];
31
+
32
+ export function pacScript(hosts = BLOCKED_HOSTS) {
33
+ return `function FindProxyForURL(url, host) { var b = ${JSON.stringify(hosts)}; for (var i = 0; i < b.length; i++) if (host === b[i]) return "PROXY 127.0.0.1:9"; return "DIRECT"; }`;
34
+ }
35
+
36
+ type Target = { id: string; type: string; url: string; title: string; webSocketDebuggerUrl?: string };
37
+ const json = async <T>(path: string) => (await fetch(`${BASE}${path}`, { signal: AbortSignal.timeout(1500) })).json() as Promise<T>;
38
+
39
+ export async function browserStatus() {
40
+ try {
41
+ const v = await json<{ Browser: string }>("/json/version");
42
+ const pages = (await json<Target[]>("/json/list")).filter((t) => t.type === "page");
43
+ return { running: true, port: BROWSER_PORT, browserUrl: BASE, chrome: v.Browser, pages: pages.map((p) => ({ title: p.title.slice(0, 80), url: p.url.slice(0, 200) })) };
44
+ } catch {
45
+ return { running: false, port: BROWSER_PORT, browserUrl: BASE };
46
+ }
47
+ }
48
+
49
+ export async function startBrowser() {
50
+ const now = await browserStatus();
51
+ if (now.running) return { ...now, started: false };
52
+ if (!existsSync(CHROME)) throw new Error(`Google Chrome not found at ${CHROME} (set LAUNCH_CHROME)`);
53
+ const pac = `data:application/x-ns-proxy-autoconfig;base64,${Buffer.from(pacScript()).toString("base64")}`;
54
+ // Plain Chrome, headed, no automation flags: Google sign-in and bot checks see a normal browser. The keychain
55
+ // flags match agent-browser and scripts/google-login: the profile's cookies are encrypted with that key, so
56
+ // without them every saved session would be unreadable.
57
+ Bun.spawn([CHROME, `--user-data-dir=${PROFILE}`, `--remote-debugging-port=${BROWSER_PORT}`, `--proxy-pac-url=${pac}`, "--use-mock-keychain", "--password-store=basic", "--no-first-run", "--no-default-browser-check"], {
58
+ stdout: "ignore", stderr: "ignore", stdin: "ignore",
59
+ }).unref();
60
+ for (let i = 0; i < 40; i++) {
61
+ await Bun.sleep(250);
62
+ const s = await browserStatus();
63
+ if (s.running) return { ...s, started: true };
64
+ }
65
+ throw new Error(`Chrome did not open port ${BROWSER_PORT}. Is this profile already open without remote debugging? Quit that window and retry.`);
66
+ }
67
+
68
+ export async function stopBrowser() {
69
+ const s = await browserStatus();
70
+ if (!s.running) return { stopped: false, running: false };
71
+ const { webSocketDebuggerUrl } = await json<{ webSocketDebuggerUrl: string }>("/json/version");
72
+ await new Promise<void>((resolve) => {
73
+ const ws = new WebSocket(webSocketDebuggerUrl);
74
+ ws.onopen = () => ws.send(JSON.stringify({ id: 1, method: "Browser.close" }));
75
+ ws.onclose = ws.onerror = () => resolve();
76
+ setTimeout(resolve, 3000);
77
+ });
78
+ return { stopped: true };
79
+ }
80
+
81
+ function ab(args: string[], stdin?: string) {
82
+ const p = Bun.spawnSync(["agent-browser", "--session", SECRETS_SESSION, "--cdp", String(BROWSER_PORT), ...args], { stdin: stdin === undefined ? "ignore" : Buffer.from(stdin), stdout: "pipe", stderr: "pipe", timeout: 60_000 });
83
+ const out = p.stdout.toString().trim();
84
+ if (p.exitCode !== 0) throw new Error(`agent-browser ${args[0]} failed: ${(p.stderr.toString() || out).trim().slice(0, 300)}`);
85
+ return out;
86
+ }
87
+
88
+ /** The open tab on `site`'s host (the one the agent is working in); the most recently active one if several. */
89
+ async function pageOn(site: string) {
90
+ const host = new URL(site.includes("://") ? site : `https://${site}`).hostname.replace(/^www\./, "");
91
+ if (!(await browserStatus()).running) throw new Error("the browser is not running: launch browser start");
92
+ const page = (await json<Target[]>("/json/list")).find((t) => t.type === "page" && /^https?:/.test(t.url) && new URL(t.url).hostname.replace(/^www\./, "").endsWith(host));
93
+ if (!page) throw new Error(`no open tab on ${host}; open the login/sign-up page first`);
94
+ ab(["tab", page.id]);
95
+ return page;
96
+ }
97
+
98
+ /** Fills a saved login (agent-browser vault) into the open tab on that site. The password never leaves the vault. */
99
+ export async function vaultLogin(name: string) {
100
+ const meta = JSON.parse(ab(["auth", "show", name, "--json"])) as { data?: { profile?: { url?: string } } };
101
+ const url = meta.data?.profile?.url;
102
+ if (!url) throw new Error(`vault entry ${name} has no login URL`);
103
+ const page = await pageOn(url);
104
+ ab(["auth", "login", name]);
105
+ return { vault: name, page: page.url.slice(0, 200), filled: true };
106
+ }
107
+
108
+ /**
109
+ * New account password: generated here, typed into every given field of the open tab on `url`'s site, checked,
110
+ * then saved to the vault as `name`. Returns no secret.
111
+ */
112
+ export async function newPassword(name: string, url: string, username: string, selectors: string[]) {
113
+ if (!selectors.length) throw new Error("pass the password field selector(s), e.g. 'input[type=password]'");
114
+ const page = await pageOn(url);
115
+ const pw = randomBytes(18).toString("base64");
116
+ for (const s of selectors) ab(["fill", s, pw]);
117
+ // A fill can "succeed" on a selector that matched nothing; never save a password the site didn't get.
118
+ const lengths = JSON.parse(ab(["eval", `JSON.stringify(${JSON.stringify(selectors)}.map((s) => document.querySelector(s)?.value.length ?? -1))`]));
119
+ const got = (typeof lengths === "string" ? JSON.parse(lengths) : lengths) as number[];
120
+ if (got.some((n) => n !== pw.length)) throw new Error(`password fields not filled (lengths ${got.join(", ")}); nothing saved`);
121
+ ab(["auth", "save", name, "--url", url, "--username", username, "--password-stdin"], pw);
122
+ return { vault: name, username, fields: selectors.length, page: page.url.slice(0, 200), saved: true };
123
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * What the agent working this workspace can actually use: a browser driver, an email tool for verification
3
+ * links and codes, anything else. The CLI detects what it can see (binaries on PATH); the agent records the rest
4
+ * (its own MCP/connector tools) during setup, because only the agent knows which tools its harness gives it.
5
+ * Stored in workspace/capabilities.yaml; no secrets.
6
+ */
7
+ import { existsSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { z } from "zod";
10
+ import { WORKSPACE } from "./workspace";
11
+
12
+ export const Capabilities = z.object({
13
+ checked_at: z.string().optional(),
14
+ checked_by: z.string().optional(), // agent/harness that recorded it, e.g. "claude-code"
15
+ browser: z
16
+ .object({
17
+ tool: z.string().default("agent-browser"),
18
+ notes: z.string().optional(),
19
+ })
20
+ .default({ tool: "agent-browser" }),
21
+ email: z
22
+ .object({
23
+ available: z.boolean(),
24
+ // How to reach it, in the agent's own words: "Executor Gmail app (tools.gmail.search, readMessage)",
25
+ // "claude.ai Gmail connector", "IMAP via himalaya CLI"…
26
+ tool: z.string().optional(),
27
+ address: z.string().optional(), // the inbox it reads; should match identity.yaml email
28
+ read_only: z.boolean().optional(), // true when the tool cannot send/delete/forward
29
+ notes: z.string().optional(),
30
+ })
31
+ .default({ available: false }),
32
+ notes: z.string().optional(),
33
+ });
34
+ export type Capabilities = z.infer<typeof Capabilities>;
35
+
36
+ export const CAPABILITIES = join(WORKSPACE, "capabilities.yaml");
37
+
38
+ export async function loadCapabilities(): Promise<Capabilities | null> {
39
+ if (!existsSync(CAPABILITIES)) return null;
40
+ return Capabilities.parse(Bun.YAML.parse(await Bun.file(CAPABILITIES).text()) ?? {});
41
+ }
42
+
43
+ /** Shallow-merges per section, so `set --input '{"email":{...}}'` leaves the browser entry alone. */
44
+ export async function saveCapabilities(patch: Record<string, unknown>, by?: string) {
45
+ const prev = (await loadCapabilities()) ?? Capabilities.parse({});
46
+ const merged: Record<string, unknown> = { ...prev };
47
+ for (const [k, v] of Object.entries(patch)) {
48
+ const old = (prev as Record<string, unknown>)[k];
49
+ merged[k] = v && typeof v === "object" && !Array.isArray(v) && old && typeof old === "object" ? { ...old, ...v } : v;
50
+ }
51
+ const next = Capabilities.parse({ ...merged, checked_at: new Date().toISOString().slice(0, 10), ...(by && { checked_by: by }) });
52
+ await Bun.write(CAPABILITIES, Bun.YAML.stringify(next, null, 2));
53
+ return next;
54
+ }
55
+
56
+ /** What the CLI can see without the agent: browser driver binaries on PATH. */
57
+ export function detect() {
58
+ const which = (bin: string) => Bun.which(bin);
59
+ const ab = which("agent-browser");
60
+ const version = ab ? Bun.spawnSync([ab, "--version"], { stdout: "pipe", stderr: "ignore" }).stdout.toString().trim() : null;
61
+ return { agentBrowser: ab ? { path: ab, version } : null };
62
+ }