productfoo 0.0.0-stage → 1.0.1

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 (498) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +338 -2
  3. package/bin/foo +3 -0
  4. package/bin/foo.mjs +42 -0
  5. package/commands/bug.md +31 -0
  6. package/commands/discovery.md +28 -0
  7. package/commands/ideate.md +27 -0
  8. package/commands/init.md +28 -0
  9. package/commands/pitch.md +27 -0
  10. package/commands/preview.md +25 -0
  11. package/commands/research.md +31 -0
  12. package/commands/roast.md +27 -0
  13. package/commands/spec.md +30 -0
  14. package/config/pricing.yaml +198 -0
  15. package/config/routing-policy.yaml +171 -0
  16. package/dist-public/src/activeModel.d.ts +83 -0
  17. package/dist-public/src/activeModel.js +331 -0
  18. package/dist-public/src/agentAdapters.d.ts +122 -0
  19. package/dist-public/src/agentAdapters.js +178 -0
  20. package/dist-public/src/agentState.d.ts +43 -0
  21. package/dist-public/src/agentState.js +177 -0
  22. package/dist-public/src/bugCapsule.d.ts +94 -0
  23. package/dist-public/src/bugCapsule.js +227 -0
  24. package/dist-public/src/bugCapture.d.ts +26 -0
  25. package/dist-public/src/bugCapture.js +251 -0
  26. package/dist-public/src/bugOwnerLog.d.ts +93 -0
  27. package/dist-public/src/bugOwnerLog.js +304 -0
  28. package/dist-public/src/bugReportStore.d.ts +48 -0
  29. package/dist-public/src/bugReportStore.js +860 -0
  30. package/dist-public/src/bugSpecContext.d.ts +7 -0
  31. package/dist-public/src/bugSpecContext.js +129 -0
  32. package/dist-public/src/channelFreshness.d.ts +25 -0
  33. package/dist-public/src/channelFreshness.js +194 -0
  34. package/dist-public/src/cli.d.ts +30 -0
  35. package/dist-public/src/cli.js +1667 -0
  36. package/dist-public/src/cliArgs.d.ts +7 -0
  37. package/dist-public/src/cliArgs.js +44 -0
  38. package/dist-public/src/codeCatalog.d.ts +41 -0
  39. package/dist-public/src/codeCatalog.js +197 -0
  40. package/dist-public/src/commandContract.d.ts +84 -0
  41. package/dist-public/src/commandContract.js +390 -0
  42. package/dist-public/src/commandUsage.d.ts +3 -0
  43. package/dist-public/src/commandUsage.js +30 -0
  44. package/dist-public/src/commands/agent.d.ts +60 -0
  45. package/dist-public/src/commands/agent.js +1353 -0
  46. package/dist-public/src/commands/asp.d.ts +8 -0
  47. package/dist-public/src/commands/asp.js +669 -0
  48. package/dist-public/src/commands/evalCmd.d.ts +4 -0
  49. package/dist-public/src/commands/evalCmd.js +405 -0
  50. package/dist-public/src/commands/login.d.ts +25 -0
  51. package/dist-public/src/commands/login.js +94 -0
  52. package/dist-public/src/commands/loop.d.ts +16 -0
  53. package/dist-public/src/commands/loop.js +619 -0
  54. package/dist-public/src/commands/reconcile.d.ts +41 -0
  55. package/dist-public/src/commands/reconcile.js +292 -0
  56. package/dist-public/src/commands/runtime.d.ts +1 -0
  57. package/dist-public/src/commands/runtime.js +36 -0
  58. package/dist-public/src/commands/scanRedact.d.ts +8 -0
  59. package/dist-public/src/commands/scanRedact.js +266 -0
  60. package/dist-public/src/commands/setup.d.ts +28 -0
  61. package/dist-public/src/commands/setup.js +398 -0
  62. package/dist-public/src/commands/shared.d.ts +17 -0
  63. package/dist-public/src/commands/shared.js +147 -0
  64. package/dist-public/src/commands/source.d.ts +1 -0
  65. package/dist-public/src/commands/source.js +134 -0
  66. package/dist-public/src/commands/specCmd.d.ts +24 -0
  67. package/dist-public/src/commands/specCmd.js +794 -0
  68. package/dist-public/src/commands/update.d.ts +61 -0
  69. package/dist-public/src/commands/update.js +244 -0
  70. package/dist-public/src/comprehensionSignal.d.ts +20 -0
  71. package/dist-public/src/comprehensionSignal.js +166 -0
  72. package/dist-public/src/conductor/diversity.d.ts +48 -0
  73. package/dist-public/src/conductor/diversity.js +423 -0
  74. package/dist-public/src/conductor/engine.d.ts +7 -0
  75. package/dist-public/src/conductor/engine.js +295 -0
  76. package/dist-public/src/conductor/families/ideate.d.ts +2 -0
  77. package/dist-public/src/conductor/families/ideate.js +255 -0
  78. package/dist-public/src/conductor/families/interview.d.ts +2 -0
  79. package/dist-public/src/conductor/families/interview.js +39 -0
  80. package/dist-public/src/conductor/families/specSwarm.d.ts +2 -0
  81. package/dist-public/src/conductor/families/specSwarm.js +146 -0
  82. package/dist-public/src/conductor/familyDelivery.d.ts +6 -0
  83. package/dist-public/src/conductor/familyDelivery.js +37 -0
  84. package/dist-public/src/conductor/familyPackArtifact.d.ts +4 -0
  85. package/dist-public/src/conductor/familyPackArtifact.js +260 -0
  86. package/dist-public/src/conductor/ideateDecision.d.ts +6 -0
  87. package/dist-public/src/conductor/ideateDecision.js +237 -0
  88. package/dist-public/src/conductor/pack.d.ts +26 -0
  89. package/dist-public/src/conductor/pack.js +75 -0
  90. package/dist-public/src/conductor/types.d.ts +111 -0
  91. package/dist-public/src/conductor/types.js +7 -0
  92. package/dist-public/src/configPath.d.ts +2 -0
  93. package/dist-public/src/configPath.js +7 -0
  94. package/dist-public/src/discoveryArtifacts.d.ts +54 -0
  95. package/dist-public/src/discoveryArtifacts.js +467 -0
  96. package/dist-public/src/discoveryCommand.d.ts +127 -0
  97. package/dist-public/src/discoveryCommand.js +2148 -0
  98. package/dist-public/src/discoveryConnections.d.ts +339 -0
  99. package/dist-public/src/discoveryConnections.js +613 -0
  100. package/dist-public/src/discoveryDocs.d.ts +129 -0
  101. package/dist-public/src/discoveryDocs.js +577 -0
  102. package/dist-public/src/discoveryEvidence.d.ts +59 -0
  103. package/dist-public/src/discoveryEvidence.js +198 -0
  104. package/dist-public/src/discoveryFireflies.d.ts +25 -0
  105. package/dist-public/src/discoveryFireflies.js +551 -0
  106. package/dist-public/src/discoveryGmail.d.ts +12 -0
  107. package/dist-public/src/discoveryGmail.js +466 -0
  108. package/dist-public/src/discoveryGong.d.ts +12 -0
  109. package/dist-public/src/discoveryGong.js +491 -0
  110. package/dist-public/src/discoveryLearningRouting.d.ts +90 -0
  111. package/dist-public/src/discoveryLearningRouting.js +405 -0
  112. package/dist-public/src/discoveryMatching.d.ts +26 -0
  113. package/dist-public/src/discoveryMatching.js +73 -0
  114. package/dist-public/src/discoveryOutlook.d.ts +12 -0
  115. package/dist-public/src/discoveryOutlook.js +492 -0
  116. package/dist-public/src/discoveryProcess.d.ts +221 -0
  117. package/dist-public/src/discoveryProcess.js +703 -0
  118. package/dist-public/src/discoveryProof.d.ts +143 -0
  119. package/dist-public/src/discoveryProof.js +529 -0
  120. package/dist-public/src/discoveryProviders.d.ts +152 -0
  121. package/dist-public/src/discoveryProviders.js +756 -0
  122. package/dist-public/src/discoveryReadiness.d.ts +219 -0
  123. package/dist-public/src/discoveryReadiness.js +672 -0
  124. package/dist-public/src/discoveryReconcile.d.ts +57 -0
  125. package/dist-public/src/discoveryReconcile.js +424 -0
  126. package/dist-public/src/discoverySlack.d.ts +12 -0
  127. package/dist-public/src/discoverySlack.js +452 -0
  128. package/dist-public/src/discoverySources.d.ts +56 -0
  129. package/dist-public/src/discoverySources.js +322 -0
  130. package/dist-public/src/discoverySync.d.ts +52 -0
  131. package/dist-public/src/discoverySync.js +151 -0
  132. package/dist-public/src/discoverySynthesis.d.ts +57 -0
  133. package/dist-public/src/discoverySynthesis.js +215 -0
  134. package/dist-public/src/discoveryTeams.d.ts +12 -0
  135. package/dist-public/src/discoveryTeams.js +516 -0
  136. package/dist-public/src/discoveryZoom.d.ts +12 -0
  137. package/dist-public/src/discoveryZoom.js +523 -0
  138. package/dist-public/src/elicitationSurface.d.ts +13 -0
  139. package/dist-public/src/elicitationSurface.js +77 -0
  140. package/dist-public/src/errorReport.d.ts +99 -0
  141. package/dist-public/src/errorReport.js +443 -0
  142. package/dist-public/src/errorTaxonomy.d.ts +84 -0
  143. package/dist-public/src/errorTaxonomy.js +189 -0
  144. package/dist-public/src/evalCalibration.d.ts +82 -0
  145. package/dist-public/src/evalCalibration.js +169 -0
  146. package/dist-public/src/evalChecks.d.ts +21 -0
  147. package/dist-public/src/evalChecks.js +295 -0
  148. package/dist-public/src/evalCompile.d.ts +56 -0
  149. package/dist-public/src/evalCompile.js +58 -0
  150. package/dist-public/src/evalImprove.d.ts +60 -0
  151. package/dist-public/src/evalImprove.js +175 -0
  152. package/dist-public/src/evalJudge.d.ts +47 -0
  153. package/dist-public/src/evalJudge.js +143 -0
  154. package/dist-public/src/evalLabels.d.ts +100 -0
  155. package/dist-public/src/evalLabels.js +287 -0
  156. package/dist-public/src/evalRecord.d.ts +68 -0
  157. package/dist-public/src/evalRecord.js +165 -0
  158. package/dist-public/src/evalRoast.d.ts +88 -0
  159. package/dist-public/src/evalRoast.js +249 -0
  160. package/dist-public/src/evidenceIntake.d.ts +66 -0
  161. package/dist-public/src/evidenceIntake.js +356 -0
  162. package/dist-public/src/feedback.d.ts +78 -0
  163. package/dist-public/src/feedback.js +294 -0
  164. package/dist-public/src/firefliesClient.d.ts +106 -0
  165. package/dist-public/src/firefliesClient.js +258 -0
  166. package/dist-public/src/firstMoveTensions.d.ts +10 -0
  167. package/dist-public/src/firstMoveTensions.js +93 -0
  168. package/dist-public/src/gitRun.d.ts +21 -0
  169. package/dist-public/src/gitRun.js +50 -0
  170. package/dist-public/src/githubReconcile.d.ts +94 -0
  171. package/dist-public/src/githubReconcile.js +205 -0
  172. package/dist-public/src/gmailNango.d.ts +131 -0
  173. package/dist-public/src/gmailNango.js +329 -0
  174. package/dist-public/src/gongNango.d.ts +118 -0
  175. package/dist-public/src/gongNango.js +308 -0
  176. package/dist-public/src/grammarGate.d.ts +13 -0
  177. package/dist-public/src/grammarGate.js +72 -0
  178. package/dist-public/src/harnessGates.d.ts +65 -0
  179. package/dist-public/src/harnessGates.js +168 -0
  180. package/dist-public/src/humanSurfaceEval.d.ts +19 -0
  181. package/dist-public/src/humanSurfaceEval.js +156 -0
  182. package/dist-public/src/ideate.d.ts +43 -0
  183. package/dist-public/src/ideate.js +255 -0
  184. package/dist-public/src/ideateContext.d.ts +11 -0
  185. package/dist-public/src/ideateContext.js +83 -0
  186. package/dist-public/src/index.d.ts +2 -0
  187. package/dist-public/src/index.js +4 -0
  188. package/dist-public/src/initCommand.d.ts +25 -0
  189. package/dist-public/src/initCommand.js +558 -0
  190. package/dist-public/src/interviewGate.d.ts +25 -0
  191. package/dist-public/src/interviewGate.js +126 -0
  192. package/dist-public/src/jsonFile.d.ts +2 -0
  193. package/dist-public/src/jsonFile.js +22 -0
  194. package/dist-public/src/loopBudget.d.ts +182 -0
  195. package/dist-public/src/loopBudget.js +494 -0
  196. package/dist-public/src/loopContracts.d.ts +35 -0
  197. package/dist-public/src/loopContracts.js +364 -0
  198. package/dist-public/src/machineState.d.ts +1 -0
  199. package/dist-public/src/machineState.js +9 -0
  200. package/dist-public/src/notionNango.d.ts +93 -0
  201. package/dist-public/src/notionNango.js +279 -0
  202. package/dist-public/src/orchestrationResult.d.ts +11 -0
  203. package/dist-public/src/orchestrationResult.js +73 -0
  204. package/dist-public/src/outlookNango.d.ts +124 -0
  205. package/dist-public/src/outlookNango.js +327 -0
  206. package/dist-public/src/packetCorrections.d.ts +40 -0
  207. package/dist-public/src/packetCorrections.js +172 -0
  208. package/dist-public/src/packetGlossary.d.ts +6 -0
  209. package/dist-public/src/packetGlossary.js +42 -0
  210. package/dist-public/src/packetIndex.d.ts +54 -0
  211. package/dist-public/src/packetIndex.js +744 -0
  212. package/dist-public/src/packetIndexEntry.d.ts +3 -0
  213. package/dist-public/src/packetIndexEntry.js +128 -0
  214. package/dist-public/src/packetIndexServe.d.ts +13 -0
  215. package/dist-public/src/packetIndexServe.js +113 -0
  216. package/dist-public/src/packetIndexWorker.d.ts +7 -0
  217. package/dist-public/src/packetIndexWorker.js +45 -0
  218. package/dist-public/src/packetLifecycle.d.ts +30 -0
  219. package/dist-public/src/packetLifecycle.js +127 -0
  220. package/dist-public/src/packetReconcile.d.ts +73 -0
  221. package/dist-public/src/packetReconcile.js +386 -0
  222. package/dist-public/src/packetStatus.d.ts +125 -0
  223. package/dist-public/src/packetStatus.js +770 -0
  224. package/dist-public/src/packetValidation.d.ts +17 -0
  225. package/dist-public/src/packetValidation.js +333 -0
  226. package/dist-public/src/packetVerify.d.ts +73 -0
  227. package/dist-public/src/packetVerify.js +303 -0
  228. package/dist-public/src/packetWrite.d.ts +52 -0
  229. package/dist-public/src/packetWrite.js +448 -0
  230. package/dist-public/src/pitch.d.ts +38 -0
  231. package/dist-public/src/pitch.js +154 -0
  232. package/dist-public/src/processMemo.d.ts +8 -0
  233. package/dist-public/src/processMemo.js +23 -0
  234. package/dist-public/src/productDetect.d.ts +30 -0
  235. package/dist-public/src/productDetect.js +376 -0
  236. package/dist-public/src/profile.d.ts +62 -0
  237. package/dist-public/src/profile.js +334 -0
  238. package/dist-public/src/questionModel.d.ts +46 -0
  239. package/dist-public/src/questionModel.js +74 -0
  240. package/dist-public/src/questionText.d.ts +7 -0
  241. package/dist-public/src/questionText.js +38 -0
  242. package/dist-public/src/redactConfig.d.ts +2 -0
  243. package/dist-public/src/redactConfig.js +11 -0
  244. package/dist-public/src/redactEngine.d.ts +59 -0
  245. package/dist-public/src/redactEngine.js +509 -0
  246. package/dist-public/src/redactGate.d.ts +63 -0
  247. package/dist-public/src/redactGate.js +209 -0
  248. package/dist-public/src/repoRead.d.ts +29 -0
  249. package/dist-public/src/repoRead.js +160 -0
  250. package/dist-public/src/research.d.ts +242 -0
  251. package/dist-public/src/research.js +1314 -0
  252. package/dist-public/src/researchCompletion.d.ts +34 -0
  253. package/dist-public/src/researchCompletion.js +123 -0
  254. package/dist-public/src/researchExecution.d.ts +47 -0
  255. package/dist-public/src/researchExecution.js +309 -0
  256. package/dist-public/src/researchFormat.d.ts +21 -0
  257. package/dist-public/src/researchFormat.js +77 -0
  258. package/dist-public/src/researchGate.d.ts +86 -0
  259. package/dist-public/src/researchGate.js +1036 -0
  260. package/dist-public/src/researchReceipt.d.ts +76 -0
  261. package/dist-public/src/researchReceipt.js +148 -0
  262. package/dist-public/src/researchRender.d.ts +2 -0
  263. package/dist-public/src/researchRender.js +267 -0
  264. package/dist-public/src/researchSigningKey.d.ts +19 -0
  265. package/dist-public/src/researchSigningKey.js +95 -0
  266. package/dist-public/src/riskClassify.d.ts +42 -0
  267. package/dist-public/src/riskClassify.js +149 -0
  268. package/dist-public/src/routines.d.ts +42 -0
  269. package/dist-public/src/routines.js +269 -0
  270. package/dist-public/src/routingPolicy.d.ts +172 -0
  271. package/dist-public/src/routingPolicy.js +646 -0
  272. package/dist-public/src/secretInput.d.ts +8 -0
  273. package/dist-public/src/secretInput.js +89 -0
  274. package/dist-public/src/selfCheck.d.ts +7 -0
  275. package/dist-public/src/selfCheck.js +473 -0
  276. package/dist-public/src/selfCheckCache.d.ts +29 -0
  277. package/dist-public/src/selfCheckCache.js +147 -0
  278. package/dist-public/src/severityClass.d.ts +15 -0
  279. package/dist-public/src/severityClass.js +22 -0
  280. package/dist-public/src/skillClientPolicy.d.ts +8 -0
  281. package/dist-public/src/skillClientPolicy.js +16 -0
  282. package/dist-public/src/skillLocal.d.ts +98 -0
  283. package/dist-public/src/skillLocal.js +463 -0
  284. package/dist-public/src/skillTreeDigest.d.ts +9 -0
  285. package/dist-public/src/skillTreeDigest.js +64 -0
  286. package/dist-public/src/slackNango.d.ts +62 -0
  287. package/dist-public/src/slackNango.js +208 -0
  288. package/dist-public/src/social/arxiv.d.ts +19 -0
  289. package/dist-public/src/social/arxiv.js +96 -0
  290. package/dist-public/src/social/github.d.ts +2 -0
  291. package/dist-public/src/social/github.js +78 -0
  292. package/dist-public/src/social/hackernews.d.ts +2 -0
  293. package/dist-public/src/social/hackernews.js +67 -0
  294. package/dist-public/src/social/index.d.ts +10 -0
  295. package/dist-public/src/social/index.js +79 -0
  296. package/dist-public/src/social/reddit.d.ts +2 -0
  297. package/dist-public/src/social/reddit.js +125 -0
  298. package/dist-public/src/social/types.d.ts +73 -0
  299. package/dist-public/src/social/types.js +61 -0
  300. package/dist-public/src/social/xml.d.ts +10 -0
  301. package/dist-public/src/social/xml.js +49 -0
  302. package/dist-public/src/social/youtube.d.ts +7 -0
  303. package/dist-public/src/social/youtube.js +217 -0
  304. package/dist-public/src/sourceIngress.d.ts +488 -0
  305. package/dist-public/src/sourceIngress.js +1651 -0
  306. package/dist-public/src/specAfter.d.ts +187 -0
  307. package/dist-public/src/specAfter.js +814 -0
  308. package/dist-public/src/specBudget.d.ts +124 -0
  309. package/dist-public/src/specBudget.js +424 -0
  310. package/dist-public/src/specDecisionSignals.d.ts +38 -0
  311. package/dist-public/src/specDecisionSignals.js +121 -0
  312. package/dist-public/src/specLinkedCommits.d.ts +7 -0
  313. package/dist-public/src/specLinkedCommits.js +107 -0
  314. package/dist-public/src/specSections.d.ts +105 -0
  315. package/dist-public/src/specSections.js +718 -0
  316. package/dist-public/src/statusGlyphs.d.ts +18 -0
  317. package/dist-public/src/statusGlyphs.js +75 -0
  318. package/dist-public/src/swarmCommand.d.ts +10 -0
  319. package/dist-public/src/swarmCommand.js +96 -0
  320. package/dist-public/src/teamsNango.d.ts +114 -0
  321. package/dist-public/src/teamsNango.js +301 -0
  322. package/dist-public/src/tokenStore.d.ts +10 -0
  323. package/dist-public/src/tokenStore.js +77 -0
  324. package/dist-public/src/trustEnvelope.d.ts +78 -0
  325. package/dist-public/src/trustEnvelope.js +392 -0
  326. package/dist-public/src/untrustedText.d.ts +6 -0
  327. package/dist-public/src/untrustedText.js +20 -0
  328. package/dist-public/src/updateNotice.d.ts +41 -0
  329. package/dist-public/src/updateNotice.js +290 -0
  330. package/dist-public/src/vendorYaml.d.ts +1 -0
  331. package/dist-public/src/vendorYaml.js +7374 -0
  332. package/dist-public/src/workflowRunActuals.d.ts +67 -0
  333. package/dist-public/src/workflowRunActuals.js +184 -0
  334. package/dist-public/src/zendeskDiscovery.d.ts +12 -0
  335. package/dist-public/src/zendeskDiscovery.js +513 -0
  336. package/dist-public/src/zendeskNango.d.ts +131 -0
  337. package/dist-public/src/zendeskNango.js +344 -0
  338. package/dist-public/src/zoomNango.d.ts +106 -0
  339. package/dist-public/src/zoomNango.js +351 -0
  340. package/docs/CHANGELOG.md +32 -0
  341. package/docs/THIRD_PARTY_NOTICES.md +36 -0
  342. package/package.json +71 -4
  343. package/schemas/bug-capsule-receipt.schema.json +54 -0
  344. package/schemas/bug-capsule.schema.json +189 -0
  345. package/schemas/discovery-evidence-envelope.schema.json +79 -0
  346. package/schemas/discovery-processing-receipt.schema.json +71 -0
  347. package/schemas/discovery-service-contract.schema.json +189 -0
  348. package/skills/AGENTS.md +32 -0
  349. package/skills/README.md +287 -0
  350. package/skills/bug/SKILL.md +49 -0
  351. package/skills/build-preview-spec/SKILL.md +103 -0
  352. package/skills/capture-external-artifact/SKILL.md +23 -0
  353. package/skills/capture-external-artifact/agents/openai.yaml +4 -0
  354. package/skills/capture-external-artifact/references/extractor-libraries.md +27 -0
  355. package/skills/check-spec-health/SKILL.md +45 -0
  356. package/skills/check-spec-health/agents/openai.yaml +4 -0
  357. package/skills/competitive-positioning/SKILL.md +86 -0
  358. package/skills/competitive-positioning/agents/openai.yaml +4 -0
  359. package/skills/cpo-lens/SKILL.md +398 -0
  360. package/skills/cpo-lens/agents/openai.yaml +4 -0
  361. package/skills/create-prd/SKILL.md +26 -0
  362. package/skills/create-prd/agents/openai.yaml +4 -0
  363. package/skills/customer-discovery/SKILL.md +192 -0
  364. package/skills/design-system/SKILL.md +49 -0
  365. package/skills/design-system/agents/openai.yaml +4 -0
  366. package/skills/discussion-guide/SKILL.md +75 -0
  367. package/skills/discussion-guide/agents/openai.yaml +4 -0
  368. package/skills/firecrawl-research-papers/SKILL.md +96 -0
  369. package/skills/firecrawl-research-papers/agents/openai.yaml +4 -0
  370. package/skills/foo-roast/SKILL.md +325 -0
  371. package/skills/generate-gtm-brief/SKILL.md +41 -0
  372. package/skills/generate-gtm-brief/agents/openai.yaml +4 -0
  373. package/skills/generate-gtm-brief/references/gtm-brief-template.md +56 -0
  374. package/skills/gong-quotes/SKILL.md +43 -0
  375. package/skills/gong-quotes/agents/openai.yaml +4 -0
  376. package/skills/grill-me/SKILL.md +27 -0
  377. package/skills/grill-me/agents/openai.yaml +4 -0
  378. package/skills/ideate/SKILL.md +180 -0
  379. package/skills/ideate/references/family-packs.md +5 -0
  380. package/skills/initiative-tracker/SKILL.md +23 -0
  381. package/skills/initiative-tracker/agents/openai.yaml +4 -0
  382. package/skills/interop-review/SKILL.md +25 -0
  383. package/skills/interop-review/agents/openai.yaml +4 -0
  384. package/skills/interview-me/SKILL.md +404 -0
  385. package/skills/interview-me/agents/openai.yaml +4 -0
  386. package/skills/loop-library-audit/SKILL.md +123 -0
  387. package/skills/pitch/SKILL.md +110 -0
  388. package/skills/pitch/references/opportunity-assessment.md +136 -0
  389. package/skills/pitch/references/prfaq-template.md +54 -0
  390. package/skills/pmm-release-briefing/SKILL.md +27 -0
  391. package/skills/pmm-release-briefing/agents/openai.yaml +4 -0
  392. package/skills/pmm-release-briefing/references/variant-generalized.md +16 -0
  393. package/skills/positioning-messaging/SKILL.md +70 -0
  394. package/skills/positioning-messaging/agents/openai.yaml +4 -0
  395. package/skills/positioning-messaging/references/guest-insights.md +1400 -0
  396. package/skills/preview-swarm/SKILL.md +418 -0
  397. package/skills/preview-swarm/agents/openai.yaml +4 -0
  398. package/skills/preview-swarm/references/html-effectiveness-build-previews.md +168 -0
  399. package/skills/product-copywriting/SKILL.md +127 -0
  400. package/skills/product-copywriting/agents/openai.yaml +4 -0
  401. package/skills/product-copywriting/references/foo-voice.md +26 -0
  402. package/skills/product-shape-interview/SKILL.md +219 -0
  403. package/skills/product-shape-interview/agents/openai.yaml +4 -0
  404. package/skills/ready-for-release/SKILL.md +39 -0
  405. package/skills/ready-for-release/agents/openai.yaml +4 -0
  406. package/skills/redact-transcripts/SKILL.md +23 -0
  407. package/skills/redact-transcripts/agents/openai.yaml +4 -0
  408. package/skills/research-brief/SKILL.md +48 -0
  409. package/skills/research-brief/agents/openai.yaml +4 -0
  410. package/skills/research-refresh/SKILL.md +63 -0
  411. package/skills/research-refresh/references/worked-cases.md +44 -0
  412. package/skills/review-loop/SKILL.md +200 -0
  413. package/skills/review-spec/SKILL.md +32 -0
  414. package/skills/review-spec/agents/openai.yaml +4 -0
  415. package/skills/rfc-from-research/SKILL.md +42 -0
  416. package/skills/rfc-from-research/agents/openai.yaml +4 -0
  417. package/skills/rfc-from-research/references/rfc-pattern.md +69 -0
  418. package/skills/skill-registry.yaml +810 -0
  419. package/skills/spec-cleanup/SKILL.md +103 -0
  420. package/skills/spec-cleanup/agents/openai.yaml +4 -0
  421. package/skills/spec-swarm/SKILL.md +87 -0
  422. package/skills/spec-swarm/agents/openai.yaml +4 -0
  423. package/skills/spec-swarm/template/SKILL.template.md +94 -0
  424. package/skills/spec-swarm/template/personas/accessibility-inclusion.md +40 -0
  425. package/skills/spec-swarm/template/personas/ai-technical-pm.md +69 -0
  426. package/skills/spec-swarm/template/personas/analytics-engineering.md +39 -0
  427. package/skills/spec-swarm/template/personas/applied-ai-eval.md +64 -0
  428. package/skills/spec-swarm/template/personas/business-gm-pm.md +67 -0
  429. package/skills/spec-swarm/template/personas/consumer-craft-pm.md +68 -0
  430. package/skills/spec-swarm/template/personas/content-design.md +40 -0
  431. package/skills/spec-swarm/template/personas/cpo.md +42 -0
  432. package/skills/spec-swarm/template/personas/data-science.md +42 -0
  433. package/skills/spec-swarm/template/personas/developer-experience.md +65 -0
  434. package/skills/spec-swarm/template/personas/dx-product.md +70 -0
  435. package/skills/spec-swarm/template/personas/field.md +43 -0
  436. package/skills/spec-swarm/template/personas/finance-cfo.md +40 -0
  437. package/skills/spec-swarm/template/personas/growth-pm.md +67 -0
  438. package/skills/spec-swarm/template/personas/platform-pm.md +67 -0
  439. package/skills/spec-swarm/template/personas/pmm.md +43 -0
  440. package/skills/spec-swarm/template/personas/principal-product-designer.md +42 -0
  441. package/skills/spec-swarm/template/personas/product-ops-program.md +41 -0
  442. package/skills/spec-swarm/template/personas/qa-release.md +41 -0
  443. package/skills/spec-swarm/template/personas/responsible-ai-safety.md +64 -0
  444. package/skills/spec-swarm/template/personas/security-privacy-legal.md +40 -0
  445. package/skills/spec-swarm/template/personas/strategy-competitive.md +41 -0
  446. package/skills/spec-swarm/template/personas/technical-architect.md +42 -0
  447. package/skills/spec-swarm/template/personas/technical-lead.md +43 -0
  448. package/skills/spec-swarm/template/personas/user-research.md +41 -0
  449. package/skills/spec-swarm-accessibility-inclusion/SKILL.md +95 -0
  450. package/skills/spec-swarm-accessibility-inclusion/agents/openai.yaml +4 -0
  451. package/skills/spec-swarm-ai-technical-pm/SKILL.md +124 -0
  452. package/skills/spec-swarm-analytics-engineering/SKILL.md +94 -0
  453. package/skills/spec-swarm-analytics-engineering/agents/openai.yaml +4 -0
  454. package/skills/spec-swarm-applied-ai-eval/SKILL.md +120 -0
  455. package/skills/spec-swarm-business-gm-pm/SKILL.md +122 -0
  456. package/skills/spec-swarm-consumer-craft-pm/SKILL.md +122 -0
  457. package/skills/spec-swarm-content-design/SKILL.md +95 -0
  458. package/skills/spec-swarm-content-design/agents/openai.yaml +4 -0
  459. package/skills/spec-swarm-cpo/SKILL.md +94 -0
  460. package/skills/spec-swarm-cpo/agents/openai.yaml +4 -0
  461. package/skills/spec-swarm-data-science/SKILL.md +95 -0
  462. package/skills/spec-swarm-data-science/agents/openai.yaml +4 -0
  463. package/skills/spec-swarm-developer-experience/SKILL.md +121 -0
  464. package/skills/spec-swarm-dx-product/SKILL.md +126 -0
  465. package/skills/spec-swarm-field/SKILL.md +96 -0
  466. package/skills/spec-swarm-field/agents/openai.yaml +4 -0
  467. package/skills/spec-swarm-finance-cfo/SKILL.md +95 -0
  468. package/skills/spec-swarm-finance-cfo/agents/openai.yaml +4 -0
  469. package/skills/spec-swarm-growth-pm/SKILL.md +122 -0
  470. package/skills/spec-swarm-platform-pm/SKILL.md +122 -0
  471. package/skills/spec-swarm-pmm/SKILL.md +96 -0
  472. package/skills/spec-swarm-pmm/agents/openai.yaml +4 -0
  473. package/skills/spec-swarm-principal-product-designer/SKILL.md +95 -0
  474. package/skills/spec-swarm-principal-product-designer/agents/openai.yaml +4 -0
  475. package/skills/spec-swarm-product-ops-program/SKILL.md +96 -0
  476. package/skills/spec-swarm-product-ops-program/agents/openai.yaml +4 -0
  477. package/skills/spec-swarm-qa-release/SKILL.md +96 -0
  478. package/skills/spec-swarm-qa-release/agents/openai.yaml +4 -0
  479. package/skills/spec-swarm-responsible-ai-safety/SKILL.md +120 -0
  480. package/skills/spec-swarm-security-privacy-legal/SKILL.md +95 -0
  481. package/skills/spec-swarm-security-privacy-legal/agents/openai.yaml +4 -0
  482. package/skills/spec-swarm-strategy-competitive/SKILL.md +96 -0
  483. package/skills/spec-swarm-strategy-competitive/agents/openai.yaml +4 -0
  484. package/skills/spec-swarm-technical-architect/SKILL.md +95 -0
  485. package/skills/spec-swarm-technical-architect/agents/openai.yaml +4 -0
  486. package/skills/spec-swarm-technical-lead/SKILL.md +96 -0
  487. package/skills/spec-swarm-technical-lead/agents/openai.yaml +4 -0
  488. package/skills/spec-swarm-user-research/SKILL.md +95 -0
  489. package/skills/spec-swarm-user-research/agents/openai.yaml +4 -0
  490. package/skills/strategy-check/SKILL.md +170 -0
  491. package/skills/strategy-check/agents/openai.yaml +4 -0
  492. package/skills/study-digest/SKILL.md +41 -0
  493. package/skills/synthetic-customer/SKILL.md +68 -0
  494. package/skills/update-cli-spec/SKILL.md +69 -0
  495. package/skills/update-cli-spec/agents/openai.yaml +4 -0
  496. package/skills/voc-researcher/SKILL.md +45 -0
  497. package/skills/voc-researcher/agents/openai.yaml +4 -0
  498. package/skills-lock.json +35 -0
@@ -0,0 +1,208 @@
1
+ // Slack through Nango (slack-discovery-connector-v0, local worker mode).
2
+ // Nango is the credential broker only: productfoo asks Nango for the company's Slack
3
+ // token and then talks to Slack's Web API directly. Every surface productfoo owns is
4
+ // content-free: provider errors and metadata only — message text is fetched by
5
+ // the worker through the proof boundary and never persisted outside process
6
+ // memory (R-005). Slack API shapes below are beliefs owned by the packet's
7
+ // feasibility task (T-001) and pinned by fixtures in test/discoverySlack.test.ts;
8
+ // the live dogfood run (T-007) is what would promote them from belief to verified.
9
+ // Contract-only: this adapter makes no verified-live claim.
10
+ import { createHash } from "node:crypto";
11
+ import { SLACK_CHANNEL_ID, refuseSourceWrite, SLACK_SCOPE_LIMITS } from "./sourceIngress.js";
12
+ export class SlackRuntimeError extends Error {
13
+ code;
14
+ constructor(code, message) {
15
+ super(message);
16
+ this.code = code;
17
+ this.name = "SlackRuntimeError";
18
+ }
19
+ }
20
+ export const SLACK_POLICY = {
21
+ policy_version: "slack-local-worker-0.1",
22
+ max_response_bytes: 4 * 1024 * 1024,
23
+ fetch_timeout_ms: 90_000,
24
+ max_model_input_tokens: 100_000,
25
+ max_replies_per_thread: 200,
26
+ min_request_interval_ms: 1_100,
27
+ max_channels_probed: SLACK_SCOPE_LIMITS.max_channels_per_scope,
28
+ };
29
+ const DEFAULT_NANGO_HOST = "https://api.nango.dev";
30
+ const DEFAULT_SLACK_BASE = "https://slack.com";
31
+ const SLACK_READ_API = new Set(["conversations.list", "conversations.info", "conversations.history", "conversations.replies"]);
32
+ const SLACK_AUTH_ERRORS = new Set(["not_authed", "invalid_auth", "account_inactive", "token_revoked", "token_expired"]);
33
+ const SLACK_ACCESS_ERRORS = new Set(["missing_scope", "not_in_channel", "not_allowed_type", "method_not_supported_for_channel_type", "access_denied"]);
34
+ export function slackConnectionConfigured(env) {
35
+ const viaNango = Boolean(env.NANGO_SECRET_KEY?.trim() && env.FOO_SLACK_NANGO_CONNECTION?.trim());
36
+ const direct = Boolean(env.FOO_SLACK_BOT_TOKEN?.trim());
37
+ return viaNango || direct;
38
+ }
39
+ export function slackCredentialBroker(env) {
40
+ if (env.NANGO_SECRET_KEY?.trim() && env.FOO_SLACK_NANGO_CONNECTION?.trim())
41
+ return "nango";
42
+ if (env.FOO_SLACK_BOT_TOKEN?.trim())
43
+ return "direct";
44
+ return null;
45
+ }
46
+ function opaqueConnectionRef(integration, connectionId) {
47
+ // Hash, never log, never store the raw connection id in findings provenance.
48
+ return `slack:${createHash("sha256").update(`${integration}\n${connectionId}`).digest("hex").slice(0, 16)}`;
49
+ }
50
+ async function fetchWithTimeout(fetchImpl, url, init, timeoutMs) {
51
+ const controller = new AbortController();
52
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
53
+ try {
54
+ return await fetchImpl(url, { ...init, signal: controller.signal });
55
+ }
56
+ catch (error) {
57
+ if (error.name === "AbortError") {
58
+ throw new SlackRuntimeError("provider_timeout", "the Slack runtime timed out");
59
+ }
60
+ throw new SlackRuntimeError("network", "the Slack runtime could not reach the network");
61
+ }
62
+ finally {
63
+ clearTimeout(timer);
64
+ }
65
+ }
66
+ async function readBounded(response, maxBytes) {
67
+ const declared = response.headers.get("content-length");
68
+ if (declared && Number(declared) > maxBytes) {
69
+ throw new SlackRuntimeError("provider_failure", "the provider response exceeded the size ceiling");
70
+ }
71
+ const body = await response.text();
72
+ if (body.length > maxBytes)
73
+ throw new SlackRuntimeError("provider_failure", "the provider response exceeded the size ceiling");
74
+ return body;
75
+ }
76
+ function parseJson(raw, what) {
77
+ try {
78
+ return JSON.parse(raw);
79
+ }
80
+ catch {
81
+ throw new SlackRuntimeError("provider_failure", `${what} returned something that is not JSON`);
82
+ }
83
+ }
84
+ async function resolveNangoSlackToken(input) {
85
+ const secret = input.env.NANGO_SECRET_KEY.trim();
86
+ const connectionId = input.env.FOO_SLACK_NANGO_CONNECTION.trim();
87
+ const integration = input.env.FOO_SLACK_NANGO_INTEGRATION?.trim() || "slack";
88
+ const host = (input.env.NANGO_HOST?.trim() || DEFAULT_NANGO_HOST).replace(/\/+$/, "");
89
+ const url = `${host}/connection/${encodeURIComponent(connectionId)}?provider_config_key=${encodeURIComponent(integration)}`;
90
+ let response;
91
+ try {
92
+ response = await fetchWithTimeout(input.fetchImpl, url, { method: "GET", headers: { authorization: `Bearer ${secret}`, accept: "application/json" } }, SLACK_POLICY.fetch_timeout_ms);
93
+ }
94
+ catch (error) {
95
+ if (error instanceof SlackRuntimeError)
96
+ throw error;
97
+ throw new SlackRuntimeError("network", "the Slack runtime could not reach the network");
98
+ }
99
+ if (response.status === 401 || response.status === 403) {
100
+ throw new SlackRuntimeError("not_connected", "Nango refused the secret key; check the NANGO_SECRET_KEY for this runtime");
101
+ }
102
+ if (response.status === 404) {
103
+ throw new SlackRuntimeError("not_connected", "Nango has no Slack connection under FOO_SLACK_NANGO_CONNECTION; connect Slack first");
104
+ }
105
+ if (!response.ok)
106
+ throw new SlackRuntimeError("provider_failure", `Nango answered ${response.status} while resolving the Slack connection`);
107
+ const body = parseJson(await readBounded(response, 256 * 1024), "Nango");
108
+ const credentials = (body.credentials ?? {});
109
+ const token = typeof credentials.access_token === "string" ? credentials.access_token : "";
110
+ if (!token)
111
+ throw new SlackRuntimeError("not_connected", "the Nango connection carries no usable Slack token; reconnect Slack");
112
+ return { connection_ref: opaqueConnectionRef(integration, connectionId), broker: "nango", token };
113
+ }
114
+ export async function resolveSlackConnection(input) {
115
+ const broker = slackCredentialBroker(input.env);
116
+ if (broker === null) {
117
+ throw new SlackRuntimeError("not_connected", "Slack is not connected: set NANGO_SECRET_KEY and FOO_SLACK_NANGO_CONNECTION for the company connection through Nango, or FOO_SLACK_BOT_TOKEN for a direct administrator-issued bot token held in this runtime only");
118
+ }
119
+ if (broker === "direct") {
120
+ const token = input.env.FOO_SLACK_BOT_TOKEN.trim();
121
+ return { connection_ref: `slack:direct:${createHash("sha256").update(token).digest("hex").slice(0, 16)}`, broker: "direct", token };
122
+ }
123
+ return resolveNangoSlackToken(input);
124
+ }
125
+ export class SlackClient {
126
+ requests = { conversations_list: 0, conversations_info: 0, conversations_history: 0, conversations_replies: 0 };
127
+ connection;
128
+ options;
129
+ lastRequestAt = 0;
130
+ constructor(connection, options = {}) {
131
+ this.connection = connection;
132
+ this.options = {
133
+ fetchImpl: options.fetchImpl ?? globalThis.fetch,
134
+ baseUrl: (options.baseUrl ?? DEFAULT_SLACK_BASE).replace(/\/+$/, ""),
135
+ now: options.now ?? (() => Date.now()),
136
+ sleep: options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
137
+ fetchTimeoutMs: options.fetchTimeoutMs ?? SLACK_POLICY.fetch_timeout_ms,
138
+ };
139
+ }
140
+ // V0's read surface is exactly four conversations.* methods. Anything else —
141
+ // a POST, a chat.* call, an admin.* call — is refused before it leaves productfoo,
142
+ // so the Slack API can never be mutated from a discovery run.
143
+ async request(api, params, maxBytes = SLACK_POLICY.max_response_bytes) {
144
+ if (!SLACK_READ_API.has(api))
145
+ refuseSourceWrite(`slack ${api}`);
146
+ const wait = this.lastRequestAt + SLACK_POLICY.min_request_interval_ms - this.options.now();
147
+ if (wait > 0)
148
+ await this.options.sleep(wait);
149
+ this.lastRequestAt = this.options.now();
150
+ const url = `${this.options.baseUrl}/api/${api}?${new URLSearchParams(params).toString()}`;
151
+ let response;
152
+ try {
153
+ response = await fetchWithTimeout(this.options.fetchImpl, url, { method: "GET", headers: { authorization: `Bearer ${this.connection.token}`, accept: "application/json" } }, this.options.fetchTimeoutMs);
154
+ }
155
+ catch (error) {
156
+ if (error instanceof SlackRuntimeError)
157
+ throw error;
158
+ throw new SlackRuntimeError("network", "the Slack runtime could not reach the network");
159
+ }
160
+ if (response.status === 401 || response.status === 403)
161
+ throw new SlackRuntimeError("auth_expired", "Slack rejected the token; refresh or reconnect");
162
+ if (response.status === 429)
163
+ throw new SlackRuntimeError("budget_exhausted", "Slack rate limit reached; no retry was attempted");
164
+ if (!response.ok)
165
+ throw new SlackRuntimeError("provider_failure", `Slack answered ${response.status}`);
166
+ const body = parseJson(await readBounded(response, maxBytes), "Slack");
167
+ if (body.ok !== true) {
168
+ const error = typeof body.error === "string" ? body.error : "unknown_error";
169
+ if (SLACK_AUTH_ERRORS.has(error))
170
+ throw new SlackRuntimeError("auth_expired", "Slack rejected the token; refresh or reconnect");
171
+ if (SLACK_ACCESS_ERRORS.has(error))
172
+ throw new SlackRuntimeError("access_denied", "Slack denied this read; the token lacks the required scope or channel access");
173
+ if (error === "rate_limited")
174
+ throw new SlackRuntimeError("budget_exhausted", "Slack rate limit reached; no retry was attempted");
175
+ throw new SlackRuntimeError("provider_failure", "Slack refused the read; the selected channel or thread may no longer exist");
176
+ }
177
+ return body;
178
+ }
179
+ // Readiness probe: one metadata-only page of channels. Message text is never
180
+ // requested here.
181
+ async probeWorkspace() {
182
+ this.requests.conversations_list += 1;
183
+ const body = await this.request("conversations.list", { types: "public_channel,private_channel", limit: "200", exclude_archived: "true" }, 1024 * 1024);
184
+ return { channels: Array.isArray(body.channels) ? body.channels.length : 0 };
185
+ }
186
+ // The one content read in V0: the messages of the explicitly selected
187
+ // thread, one bounded page.
188
+ async threadReplies(channelId, threadTs) {
189
+ if (!SLACK_CHANNEL_ID.test(channelId))
190
+ throw new SlackRuntimeError("provider_failure", "a single well-formed channel identifier is required");
191
+ if (!/^\d{10}\.\d{6}$/.test(threadTs))
192
+ throw new SlackRuntimeError("provider_failure", "a single well-formed thread timestamp is required");
193
+ this.requests.conversations_replies += 1;
194
+ const body = await this.request("conversations.replies", { channel: channelId, ts: threadTs, limit: String(SLACK_POLICY.max_replies_per_thread) });
195
+ const rawMessages = Array.isArray(body.messages) ? body.messages : [];
196
+ const messages = rawMessages.map((message) => {
197
+ const record = (message ?? {});
198
+ return {
199
+ ts: typeof record.ts === "string" ? record.ts : undefined,
200
+ user: typeof record.user === "string" ? record.user : undefined,
201
+ bot_id: typeof record.bot_id === "string" ? record.bot_id : undefined,
202
+ subtype: typeof record.subtype === "string" ? record.subtype : undefined,
203
+ text: typeof record.text === "string" ? record.text : undefined,
204
+ };
205
+ });
206
+ return { messages, has_more: body.has_more === true };
207
+ }
208
+ }
@@ -0,0 +1,19 @@
1
+ import type { AdapterOptions, AdapterResult } from "./types.js";
2
+ /**
3
+ * Build arXiv's `search_query`, AND-joined.
4
+ *
5
+ * This exists because arXiv **silently rewrites bare spaces into OR**. productfoo used to
6
+ * send `all:kubernetes operator patterns` and arXiv answered — in its own echoed
7
+ * query — `all:kubernetes OR all:operator OR all:patterns`. "Operator" is one of the
8
+ * most common words in mathematics, so a Kubernetes question returned quantum
9
+ * information theory, and a query of pure gibberish returned 25 papers. Those became
10
+ * "findings", eligible to be typed as evidence in a research paper.
11
+ *
12
+ * That is fabricated evidence, and it is the worst thing a research tool can do —
13
+ * worse than returning nothing, because nothing is honest.
14
+ *
15
+ * AND-joining makes the query mean what it says. A topic with no recent literature
16
+ * now correctly returns zero.
17
+ */
18
+ export declare function buildArxivQuery(query: string): string;
19
+ export declare function fetchArxiv(query: string, opts?: AdapterOptions): Promise<AdapterResult>;
@@ -0,0 +1,96 @@
1
+ // arXiv adapter — the public query API (export.arxiv.org), keyless, Atom XML.
2
+ // Ranked by relevance to the query (arXiv's own `sortBy=relevance`): a candidate
3
+ // pool is fetched, then the recency window is a post-filter. Newest-first was the
4
+ // bug — it returned the most-recent papers matching the AND-terms, which are
5
+ // often tangential, so a topical query came back as noise. arXiv carries no
6
+ // engagement signal, so `score` is null.
7
+ import { resolveOptions, settleStatus, withinWindow } from "./types.js";
8
+ import { parseAtomEntries } from "./xml.js";
9
+ const QUERY_URL = "https://export.arxiv.org/api/query";
10
+ // Words that carry no topical signal. Left in an AND-chain they only cost recall:
11
+ // "the" appears in every paper, so ANDing it is free, but "vs"/"using" are noise
12
+ // that a title match should not have to satisfy.
13
+ const STOPWORDS = new Set([
14
+ "a", "an", "and", "are", "as", "at", "be", "by", "for", "from", "how", "in",
15
+ "is", "of", "on", "or", "the", "to", "vs", "with", "what", "why", "using",
16
+ ]);
17
+ /**
18
+ * Build arXiv's `search_query`, AND-joined.
19
+ *
20
+ * This exists because arXiv **silently rewrites bare spaces into OR**. productfoo used to
21
+ * send `all:kubernetes operator patterns` and arXiv answered — in its own echoed
22
+ * query — `all:kubernetes OR all:operator OR all:patterns`. "Operator" is one of the
23
+ * most common words in mathematics, so a Kubernetes question returned quantum
24
+ * information theory, and a query of pure gibberish returned 25 papers. Those became
25
+ * "findings", eligible to be typed as evidence in a research paper.
26
+ *
27
+ * That is fabricated evidence, and it is the worst thing a research tool can do —
28
+ * worse than returning nothing, because nothing is honest.
29
+ *
30
+ * AND-joining makes the query mean what it says. A topic with no recent literature
31
+ * now correctly returns zero.
32
+ */
33
+ export function buildArxivQuery(query) {
34
+ const terms = query
35
+ .toLowerCase()
36
+ .split(/[^a-z0-9+#.]+/i)
37
+ .filter((t) => t.length > 1 && !STOPWORDS.has(t));
38
+ // Nothing usable left (punctuation, or all stopwords): fall back to the raw
39
+ // phrase, quoted. Quoting is what stops arXiv reading spaces as OR.
40
+ if (terms.length === 0)
41
+ return `all:"${query.replace(/"/g, "")}"`;
42
+ return terms.map((t) => `all:${t}`).join(" AND ");
43
+ }
44
+ export async function fetchArxiv(query, opts = {}) {
45
+ const { days, limit, now, fetchImpl, signal } = resolveOptions(opts);
46
+ // Fetch a relevance-ranked candidate pool, then keep only the recent ones. The
47
+ // pool is larger than `limit` so the window post-filter has relevant papers to
48
+ // keep rather than only the newest `limit` results.
49
+ const pool = Math.max(limit, 50);
50
+ const url = `${QUERY_URL}?search_query=${encodeURIComponent(buildArxivQuery(query))}` +
51
+ `&sortBy=relevance&sortOrder=descending&max_results=${pool}`;
52
+ try {
53
+ const res = await fetchImpl(url, { headers: { Accept: "application/atom+xml" }, signal });
54
+ if (!res.ok) {
55
+ return {
56
+ source: "arxiv",
57
+ status: "excluded",
58
+ reason: `arXiv query API returned HTTP ${res.status}.`,
59
+ items: [],
60
+ };
61
+ }
62
+ const xml = await res.text();
63
+ const items = parseAtomEntries(xml)
64
+ .map((e) => {
65
+ if (!e.title || !e.id)
66
+ return null;
67
+ return {
68
+ source: "arxiv",
69
+ id: `arxiv:${e.id}`,
70
+ title: e.title.replace(/\s+/g, " ").trim(),
71
+ url: e.link ?? e.id,
72
+ author: e.author,
73
+ createdAt: e.published ?? new Date(0).toISOString(),
74
+ score: null,
75
+ snippet: e.summary ? e.summary.replace(/\s+/g, " ").slice(0, 280) : null,
76
+ };
77
+ })
78
+ .filter((it) => it !== null)
79
+ .filter((it) => withinWindow(it.createdAt, now, days))
80
+ .slice(0, limit);
81
+ return {
82
+ source: "arxiv",
83
+ status: settleStatus("available", items.length),
84
+ reason: `arXiv returned ${items.length} paper(s) within ${days}d.`,
85
+ items,
86
+ };
87
+ }
88
+ catch (err) {
89
+ return {
90
+ source: "arxiv",
91
+ status: "excluded",
92
+ reason: `arXiv fetch failed: ${err.message}`,
93
+ items: [],
94
+ };
95
+ }
96
+ }
@@ -0,0 +1,2 @@
1
+ import type { AdapterOptions, AdapterResult } from "./types.js";
2
+ export declare function fetchGitHub(query: string, opts?: AdapterOptions): Promise<AdapterResult>;
@@ -0,0 +1,78 @@
1
+ // GitHub adapter — the public search/issues API. Works unauthenticated (the
2
+ // free default); an optional GITHUB_TOKEN only raises the rate limit, so it
3
+ // stays a free-tier source (D-004). Unauth search is rate-limited to ~10 req/min
4
+ // and answers 403 when throttled — that degrades honestly with an upsell to set
5
+ // a token, never crashes.
6
+ import { resolveOptions, settleStatus, windowStartMs, withinWindow } from "./types.js";
7
+ const SEARCH_URL = "https://api.github.com/search/issues";
8
+ export async function fetchGitHub(query, opts = {}) {
9
+ const { days, limit, now, fetchImpl, githubToken, signal } = resolveOptions(opts);
10
+ const sinceDate = new Date(windowStartMs(now, days)).toISOString().slice(0, 10);
11
+ const q = `${query} created:>=${sinceDate}`;
12
+ const url = `${SEARCH_URL}?q=${encodeURIComponent(q)}` +
13
+ `&sort=updated&order=desc&per_page=${limit}`;
14
+ const headers = {
15
+ Accept: "application/vnd.github+json",
16
+ // GitHub rejects API requests without a User-Agent.
17
+ "User-Agent": "foo-research",
18
+ };
19
+ if (githubToken)
20
+ headers.Authorization = `Bearer ${githubToken}`;
21
+ try {
22
+ const res = await fetchImpl(url, { headers, signal });
23
+ if (res.status === 403 || res.status === 429) {
24
+ return {
25
+ source: "github",
26
+ status: "degraded",
27
+ reason: githubToken
28
+ ? `GitHub search rate-limited (HTTP ${res.status}) despite a token.`
29
+ : `GitHub search rate-limited (HTTP ${res.status}); set GITHUB_TOKEN to raise the free limit.`,
30
+ items: [],
31
+ };
32
+ }
33
+ if (!res.ok) {
34
+ return {
35
+ source: "github",
36
+ status: "excluded",
37
+ reason: `GitHub search returned HTTP ${res.status}.`,
38
+ items: [],
39
+ };
40
+ }
41
+ const body = (await res.json());
42
+ const issues = Array.isArray(body.items) ? body.items : [];
43
+ const items = issues
44
+ .map((issue) => {
45
+ if (issue.id == null || !issue.title || !issue.html_url || !issue.created_at)
46
+ return null;
47
+ const comments = typeof issue.comments === "number" ? issue.comments : 0;
48
+ const reactions = typeof issue.reactions?.total_count === "number" ? issue.reactions.total_count : 0;
49
+ return {
50
+ source: "github",
51
+ id: `github:${issue.id}`,
52
+ title: issue.title,
53
+ url: issue.html_url,
54
+ author: issue.user?.login ?? null,
55
+ createdAt: issue.created_at,
56
+ score: comments + reactions,
57
+ snippet: null,
58
+ };
59
+ })
60
+ .filter((it) => it !== null)
61
+ .filter((it) => withinWindow(it.createdAt, now, days))
62
+ .slice(0, limit);
63
+ return {
64
+ source: "github",
65
+ status: settleStatus("available", items.length),
66
+ reason: `GitHub returned ${items.length} issue/PR(s) within ${days}d${githubToken ? " (authenticated)" : " (unauthenticated)"}.`,
67
+ items,
68
+ };
69
+ }
70
+ catch (err) {
71
+ return {
72
+ source: "github",
73
+ status: "excluded",
74
+ reason: `GitHub fetch failed: ${err.message}`,
75
+ items: [],
76
+ };
77
+ }
78
+ }
@@ -0,0 +1,2 @@
1
+ import type { AdapterOptions, AdapterResult } from "./types.js";
2
+ export declare function fetchHackerNews(query: string, opts?: AdapterOptions): Promise<AdapterResult>;
@@ -0,0 +1,67 @@
1
+ // Hacker News adapter — the cleanest keyless source. Uses the public Algolia
2
+ // search API (hn.algolia.com/api/v1), no key, no cookies. Uses `search`
3
+ // (relevance-ranked), not `search_by_date` (newest-first): a recency-only sort
4
+ // returned the newest stories that mentioned the terms rather than the ones
5
+ // actually about the topic. The recency window stays as a server-side numeric
6
+ // filter plus a post-filter for safety.
7
+ import { resolveOptions, settleStatus, windowStartMs, withinWindow } from "./types.js";
8
+ const SEARCH = "https://hn.algolia.com/api/v1/search";
9
+ function toItem(hit) {
10
+ if (!hit.objectID || !hit.title || !hit.created_at)
11
+ return null;
12
+ const points = typeof hit.points === "number" ? hit.points : 0;
13
+ const comments = typeof hit.num_comments === "number" ? hit.num_comments : 0;
14
+ return {
15
+ source: "hackernews",
16
+ id: `hn:${hit.objectID}`,
17
+ title: hit.title,
18
+ url: hit.url || `https://news.ycombinator.com/item?id=${hit.objectID}`,
19
+ author: hit.author ?? null,
20
+ createdAt: hit.created_at,
21
+ // Engagement = upvotes plus discussion; both are real signal on HN.
22
+ score: points + comments,
23
+ snippet: hit.story_text ? hit.story_text.slice(0, 280) : null,
24
+ };
25
+ }
26
+ export async function fetchHackerNews(query, opts = {}) {
27
+ const { days, limit, now, fetchImpl, signal } = resolveOptions(opts);
28
+ const sinceSec = Math.floor(windowStartMs(now, days) / 1000);
29
+ // Relevance-ranked within the window; fetch a pool larger than `limit` so the
30
+ // post-filter and slice keep the most relevant in-window stories, not the first
31
+ // `limit` the API happened to return.
32
+ const pool = Math.max(limit, 50);
33
+ const url = `${SEARCH}?query=${encodeURIComponent(query)}` +
34
+ `&tags=story&numericFilters=created_at_i>=${sinceSec}&hitsPerPage=${pool}`;
35
+ try {
36
+ const res = await fetchImpl(url, { headers: { Accept: "application/json" }, signal });
37
+ if (!res.ok) {
38
+ return {
39
+ source: "hackernews",
40
+ status: "excluded",
41
+ reason: `HN Algolia returned HTTP ${res.status}.`,
42
+ items: [],
43
+ };
44
+ }
45
+ const body = (await res.json());
46
+ const hits = Array.isArray(body.hits) ? body.hits : [];
47
+ const items = hits
48
+ .map(toItem)
49
+ .filter((it) => it !== null)
50
+ .filter((it) => withinWindow(it.createdAt, now, days))
51
+ .slice(0, limit);
52
+ return {
53
+ source: "hackernews",
54
+ status: settleStatus("available", items.length),
55
+ reason: `HN Algolia returned ${items.length} story(ies) within ${days}d.`,
56
+ items,
57
+ };
58
+ }
59
+ catch (err) {
60
+ return {
61
+ source: "hackernews",
62
+ status: "excluded",
63
+ reason: `HN Algolia fetch failed: ${err.message}`,
64
+ items: [],
65
+ };
66
+ }
67
+ }
@@ -0,0 +1,10 @@
1
+ import type { AdapterOptions, AdapterResult, SocialItem, SocialSource } from "./types.js";
2
+ export declare const DEFAULT_SOURCES: SocialSource[];
3
+ export interface GatherResult {
4
+ items: SocialItem[];
5
+ reports: AdapterResult[];
6
+ }
7
+ export declare function dedupe(items: SocialItem[]): SocialItem[];
8
+ export declare function gatherSocial(query: string, opts?: AdapterOptions & {
9
+ sources?: SocialSource[];
10
+ }): Promise<GatherResult>;
@@ -0,0 +1,79 @@
1
+ // Social adapter orchestrator — runs the enabled keyless adapters, merges and
2
+ // dedupes their evidence, and returns both the items and honest per-source
3
+ // reports (R-001). The spine (research.ts) reports the `social` class; this is
4
+ // the gather step the agent runs to populate it.
5
+ import { fetchArxiv } from "./arxiv.js";
6
+ import { fetchGitHub } from "./github.js";
7
+ import { fetchHackerNews } from "./hackernews.js";
8
+ import { fetchReddit } from "./reddit.js";
9
+ import { fetchYouTube } from "./youtube.js";
10
+ // V0 sources (D-002). Reddit/HN/arXiv/GitHub are keyless HTTP; YouTube is keyless
11
+ // via the yt-dlp binary (degrades if absent). X and the paid tier are V1 (D-006).
12
+ export const DEFAULT_SOURCES = [
13
+ "reddit",
14
+ "hackernews",
15
+ "arxiv",
16
+ "github",
17
+ "youtube",
18
+ ];
19
+ // Registry of implemented adapters. Requesting a source not present here yields
20
+ // an honest `excluded` report rather than a silent drop.
21
+ const ADAPTERS = {
22
+ reddit: fetchReddit,
23
+ hackernews: fetchHackerNews,
24
+ arxiv: fetchArxiv,
25
+ github: fetchGitHub,
26
+ youtube: fetchYouTube,
27
+ };
28
+ // Dedupe by stable id first, then collapse cross-source near-duplicates that
29
+ // point at the same URL, keeping the higher-scored copy so engagement signal
30
+ // survives the merge.
31
+ export function dedupe(items) {
32
+ const byId = new Map();
33
+ for (const it of items) {
34
+ if (!byId.has(it.id))
35
+ byId.set(it.id, it);
36
+ }
37
+ const byUrl = new Map();
38
+ for (const it of byId.values()) {
39
+ const key = normalizeUrl(it.url);
40
+ const existing = byUrl.get(key);
41
+ if (!existing || (it.score ?? -1) > (existing.score ?? -1)) {
42
+ byUrl.set(key, it);
43
+ }
44
+ }
45
+ return [...byUrl.values()];
46
+ }
47
+ function normalizeUrl(url) {
48
+ try {
49
+ const u = new URL(url);
50
+ // Drop trailing slash + tracking-ish query noise for a stable dedupe key.
51
+ return `${u.hostname.replace(/^www\./, "")}${u.pathname.replace(/\/$/, "")}`.toLowerCase();
52
+ }
53
+ catch {
54
+ return url.toLowerCase();
55
+ }
56
+ }
57
+ export async function gatherSocial(query, opts = {}) {
58
+ const sources = opts.sources ?? DEFAULT_SOURCES;
59
+ const reports = await Promise.all(sources.map((source) => {
60
+ const adapter = ADAPTERS[source];
61
+ if (!adapter) {
62
+ return Promise.resolve({
63
+ source,
64
+ status: "excluded",
65
+ reason: `${source} adapter not yet implemented in this build.`,
66
+ items: [],
67
+ });
68
+ }
69
+ return adapter(query, opts);
70
+ }));
71
+ // "It ran" and "it found something" are different facts, and a research tool
72
+ // exists to keep them apart. An adapter that returns zero items has not
73
+ // contributed evidence, whatever it thinks its status is — so the honest zero is
74
+ // enforced here, once, rather than trusted to each adapter. `degraded` and
75
+ // `excluded` already carry their own reason and are left alone.
76
+ const honest = reports.map((r) => r.status === "available" && r.items.length === 0 ? { ...r, status: "empty" } : r);
77
+ const items = dedupe(honest.flatMap((r) => r.items)).sort((a, b) => (b.score ?? -1) - (a.score ?? -1));
78
+ return { items, reports: honest };
79
+ }
@@ -0,0 +1,2 @@
1
+ import type { AdapterOptions, AdapterResult } from "./types.js";
2
+ export declare function fetchReddit(query: string, opts?: AdapterOptions): Promise<AdapterResult>;