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,452 @@
1
+ // `foo discovery slack …` — the local worker for a company Slack connection
2
+ // (slack-discovery-connector-v0, chat decision_context, D-010 local worker
3
+ // mode).
4
+ //
5
+ // The chat class's worker: V0 lists nothing on its own — the approved scope's
6
+ // explicit thread selection IS the listing, transcribed content-free — and it
7
+ // opens exactly one selected thread into process memory per proof. Message
8
+ // text is rendered through deterministic member labels (never raw workspace
9
+ // user ids, including ids embedded in message bodies), synthesized into a
10
+ // summary-only decision_context envelope in one model request, and applied
11
+ // through the core command; contact capture is suppressed because harvesting
12
+ // identities from chat is out of scope for chat evidence. The transcript never
13
+ // reaches argv, stdin, a file, Git, a log, an error, or the host agent's
14
+ // context; every surface this module writes is content-free (R-005).
15
+ // Contract-only: no verified-live claim anywhere.
16
+ import { createHash } from "node:crypto";
17
+ import { mkdirSync, writeFileSync } from "node:fs";
18
+ import path from "node:path";
19
+ import { flagValue, hasFlag } from "./cliArgs.js";
20
+ import { discoveryTargetDir, inboxDir, inboxScope, existingFingerprint, isInboxTarget, localRoot, localTargetName, runDiscoveryCommand, } from "./discoveryCommand.js";
21
+ import { authorizeDiscoverySource, openAuthorizedTranscript } from "./discoveryProof.js";
22
+ import { slackDiscoveryAdapter } from "./discoveryProviders.js";
23
+ import { sourceFingerprint } from "./discoveryMatching.js";
24
+ import { anthropicSynthesisTransport, defaultSynthesisModel, synthesisCredentialPresent, synthesizeDiscoveryEnvelope } from "./discoverySynthesis.js";
25
+ import { SLACK_THREAD_SELECTOR, slackScopeValidationError } from "./sourceIngress.js";
26
+ import { SLACK_POLICY, SlackClient, SlackRuntimeError, resolveSlackConnection, slackConnectionConfigured, slackCredentialBroker } from "./slackNango.js";
27
+ const SUPPORT_STATE = "contract_only";
28
+ const MODE = "local_worker";
29
+ function result(status, options = {}) {
30
+ return { schema_version: "0.1", status, writes: [], errors: [], handoff: null, next_actions: [], coverage: {}, ...options };
31
+ }
32
+ function exitCodeFor(status) {
33
+ return ["ambiguous_match", "needs_source_approval", "needs_provider_access", "needs_provider_connector", "needs_research_proof", "invalid", "budget_exhausted"].includes(status) ? 1 : 0;
34
+ }
35
+ function outcome(value, json) {
36
+ const exitCode = exitCodeFor(value.status);
37
+ if (json)
38
+ return { output: `${JSON.stringify(value, null, 2)}\n`, exitCode, result: value };
39
+ const lines = [`Discovery (slack): ${value.status}.`];
40
+ for (const error of value.errors)
41
+ lines.push(`- ${error.message}`);
42
+ if (value.handoff)
43
+ lines.push(`Handoff: ${value.handoff}`);
44
+ for (const action of value.next_actions)
45
+ lines.push(`Next: ${action}`);
46
+ return { output: `${lines.join("\n")}\n`, exitCode, result: value };
47
+ }
48
+ function invalid(message, json, code = "E_DISCOVERY_VALIDATION", targetId, coverage = {}) {
49
+ return outcome(result("invalid", { study_id: targetId, errors: [{ code, message }], coverage }), json);
50
+ }
51
+ // Typed provider failures map to the discovery command's closed status set.
52
+ // Messages are productfoo's own words: no provider body, id, or credential.
53
+ function fromRuntimeError(error, json, targetId, coverage) {
54
+ const base = { study_id: targetId, coverage: { ...coverage, provider_error: error.code, mode: MODE } };
55
+ switch (error.code) {
56
+ case "not_connected":
57
+ return outcome(result("needs_provider_connector", { ...base, errors: [{ code: "E_DISCOVERY_PROVIDER_CONNECTOR", message: error.message }], handoff: "Connect the company Slack workspace through Nango and set NANGO_SECRET_KEY and FOO_SLACK_NANGO_CONNECTION (plus NANGO_HOST / FOO_SLACK_NANGO_INTEGRATION when they differ from the defaults); or, for this runtime only, set FOO_SLACK_BOT_TOKEN from an administrator-issued bot token." }), json);
58
+ case "auth_expired":
59
+ case "access_denied":
60
+ return outcome(result("needs_provider_access", { ...base, errors: [{ code: "E_DISCOVERY_PROVIDER_ACCESS", message: error.message }], handoff: "Ask the Slack administrator to refresh the connection or grant the read scope, then rerun." }), json);
61
+ case "budget_exhausted":
62
+ // A rate-limit stop is final for the run: nothing widens, nothing retries.
63
+ return outcome(result("budget_exhausted", { ...base, errors: [{ code: "E_DISCOVERY_VALIDATION", message: error.message }], handoff: "The agreed read ceiling stopped this run; narrow the thread selection and rerun — productfoo never widens a scope or auto-retries." }), json);
64
+ default:
65
+ return outcome(result("invalid", { ...base, errors: [{ code: "E_DISCOVERY_VALIDATION", message: error.message }], handoff: error.code === "thread_too_large" || error.code === "provider_timeout" ? "Nothing was written and no retry was attempted; a new attempt needs a fresh match." : null }), json);
66
+ }
67
+ }
68
+ // Authorization already failed closed (wrong state, missing approval, no
69
+ // proof): the status names the discovery lifecycle to land in.
70
+ class AuthorizationFailure extends Error {
71
+ status;
72
+ constructor(status, message) {
73
+ super(message);
74
+ this.status = status;
75
+ this.name = "AuthorizationFailure";
76
+ }
77
+ }
78
+ function authorizationFailure(error, json, targetId, coverage) {
79
+ const known = ["awaiting_match", "ambiguous_match", "needs_source_approval", "needs_provider_access", "needs_provider_connector", "needs_research_proof"];
80
+ const mapped = known.includes(error.status) ? error.status : "invalid";
81
+ return outcome(result(mapped, { study_id: targetId, errors: [{ code: mapped === "needs_source_approval" ? "E_DISCOVERY_SOURCE_APPROVAL" : "E_DISCOVERY_VALIDATION", message: error.message }], handoff: error.message, coverage }), json);
82
+ }
83
+ // Slack V0 runs against an approved inbox scope only: chat threads have no
84
+ // study-matching counterpart, so the study path does not exist here.
85
+ function loadTarget(root, targetId) {
86
+ if (!isInboxTarget(targetId))
87
+ throw new Error("Slack V0 runs against an approved inbox scope (inbox:<scope-id>); chat threads have no study-matching counterpart");
88
+ const scope = inboxScope(root, targetId);
89
+ if (scope.provider.toLowerCase() !== "slack")
90
+ throw new Error("this inbox scope is not a Slack scope");
91
+ if (scope.state !== "approved")
92
+ throw new Error("this inbox scope is not approved");
93
+ const slackScope = { channel_ids: scope.channel_ids ?? [], thread_ts: scope.thread_ts, window: scope.time_window };
94
+ // R-004: validation is pre-flight — an invalid scope never reaches the provider.
95
+ const scopeError = slackScopeValidationError(slackScope);
96
+ if (scopeError)
97
+ throw new Error(scopeError);
98
+ return { target_id: targetId, lane: "unassigned_call_evidence", dir: discoveryTargetDir(root, targetId), connection_alias: scope.connection_alias, scope: slackScope };
99
+ }
100
+ // Composite `<channel-id>/<ts>` bounds split into the exact pair a read needs;
101
+ // the validator has already checked the channel against the scope's channels,
102
+ // so a read never guesses which approved channel to ask.
103
+ function parseThreadSelector(selector) {
104
+ const match = SLACK_THREAD_SELECTOR.exec(selector);
105
+ if (!match)
106
+ throw new Error("the thread selection must be an explicit <channel-id>/<ts> selector like C0123456789/1679237238.123456");
107
+ return { channelId: match[1], threadTs: match[2] };
108
+ }
109
+ // The selector's parent ts, as an ISO completeness point for provenance.
110
+ function selectorCompletedAt(selector) {
111
+ const { threadTs } = parseThreadSelector(selector);
112
+ return new Date(Number(threadTs.split(".")[0]) * 1000).toISOString();
113
+ }
114
+ // Workspace permalink host (e.g. acme.slack.com) — a runtime env selector the
115
+ // operator sets from their own Slack workspace; sanitized through the declared
116
+ // provider link form before anything is stored. Absent = no stable link.
117
+ function slackPermalink(env, channelId, threadTs) {
118
+ const host = env.FOO_SLACK_WORKSPACE_HOST?.trim();
119
+ if (!host)
120
+ return undefined;
121
+ return `https://${host}/archives/${channelId}/p${threadTs.replace(".", "")}`;
122
+ }
123
+ // Deterministic member labels: each distinct provider user id — from the user
124
+ // field OR embedded in message text — becomes member-<4 hex> for this one
125
+ // read, and bot messages are labeled "bot", so the model request and the
126
+ // rendered content never carry a resolvable workspace identity. Timestamps
127
+ // survive as locators (OQ-002).
128
+ const MESSAGE_USER_ID = /\bU[A-Z0-9]{8,}\b/g;
129
+ function renderThreadContent(messages) {
130
+ const labels = new Map();
131
+ const label = (userId) => {
132
+ let mapped = labels.get(userId);
133
+ if (!mapped) {
134
+ mapped = `member-${createHash("sha256").update(userId).digest("hex").slice(0, 4)}`;
135
+ labels.set(userId, mapped);
136
+ }
137
+ return mapped;
138
+ };
139
+ const lines = [];
140
+ for (const message of messages) {
141
+ if (typeof message.text !== "string" || !message.text)
142
+ continue;
143
+ // Join/leave and channel bookkeeping entries are not conversation content.
144
+ if (message.subtype && message.subtype !== "thread_broadcast")
145
+ continue;
146
+ const speaker = message.user ? label(message.user) : message.bot_id ? "bot" : "unknown";
147
+ const text = message.text.replace(MESSAGE_USER_ID, (id) => label(id));
148
+ lines.push(`[${message.ts ?? "unknown-ts"}] ${speaker}: ${text}`);
149
+ }
150
+ return lines.join("\n");
151
+ }
152
+ // Every raw user id the read observed: speaker fields plus ids embedded in
153
+ // message bodies. The post-synthesis screen checks derived text against this
154
+ // full set — defense in depth behind the render-time scrubbing.
155
+ function observedUserIds(messages) {
156
+ const ids = new Set();
157
+ for (const message of messages) {
158
+ if (typeof message.user === "string")
159
+ ids.add(message.user);
160
+ if (typeof message.text === "string") {
161
+ for (const id of message.text.match(MESSAGE_USER_ID) ?? [])
162
+ ids.add(id);
163
+ }
164
+ }
165
+ return [...ids];
166
+ }
167
+ function containsRawUserId(text, userIds) {
168
+ const haystack = text.toLowerCase();
169
+ return userIds.some((id) => id.length >= 4 && haystack.includes(id.toLowerCase()));
170
+ }
171
+ // Exact-scope admission (OQ-001): the scope's own thread selector is the
172
+ // matcher. Writing the match record and authorizing here keeps the proof,
173
+ // fingerprint, and provenance machinery identical to every other source while
174
+ // chat evidence skips the participant/domain/title matcher it was never shaped
175
+ // for. Nothing content-bearing lands: the session is channel-level metadata.
176
+ function threadSession(target, selector, env) {
177
+ const { channelId, threadTs } = parseThreadSelector(selector);
178
+ const record = { channel_id: channelId, thread_ts: threadTs, permalink: slackPermalink(env, channelId, threadTs) };
179
+ const session = slackDiscoveryAdapter.normalizeSession(record, target.connection_alias);
180
+ session.access = "available";
181
+ return session;
182
+ }
183
+ function selectThread(root, target, session, now) {
184
+ const matchDir = path.join(localRoot(root), localTargetName(target.target_id));
185
+ mkdirSync(matchDir, { recursive: true });
186
+ writeFileSync(path.join(matchDir, "match.json"), `${JSON.stringify({ schema_version: "0.1", candidates: [{ candidate_index: 1, source_fingerprint: sourceFingerprint(session), session }] }, null, 2)}\n`, { mode: 0o600 });
187
+ const authorized = authorizeDiscoverySource({ study_dir: inboxDir(root), local_root: localRoot(root), study_id: target.target_id, study_state: "awaiting_match", match_status: "unique", session, now });
188
+ if (authorized.status !== "ready_to_synthesize")
189
+ throw new AuthorizationFailure(authorized.status, authorized.handoff ?? "the selected thread was not authorized for a read");
190
+ return authorized.source_fingerprint;
191
+ }
192
+ async function readyCommand(root, json, deps) {
193
+ const env = deps.env;
194
+ const coverage = {
195
+ provider: "slack",
196
+ mode: MODE,
197
+ support_state: SUPPORT_STATE,
198
+ connection_owner: "workspace",
199
+ configured: slackConnectionConfigured(env),
200
+ credential_broker: slackCredentialBroker(env),
201
+ synthesis: { available: synthesisCredentialPresent(env), model: defaultSynthesisModel(env) },
202
+ policy: { ...SLACK_POLICY },
203
+ };
204
+ try {
205
+ const connection = await resolveSlackConnection({ env, fetchImpl: deps.fetchImpl });
206
+ const client = new SlackClient(connection, { fetchImpl: deps.fetchImpl, baseUrl: env.FOO_SLACK_API_BASE?.trim() || undefined, sleep: deps.sleep, fetchTimeoutMs: deps.fetchTimeoutMs });
207
+ // The readiness probe is one metadata call: it proves the credential and
208
+ // the listing permission without reading any message text.
209
+ const probe = await client.probeWorkspace();
210
+ coverage.connection = connection.connection_ref;
211
+ coverage.credential_broker = connection.broker;
212
+ coverage.capabilities = { list_sessions: true, open_threads: true, stable_link: Boolean(env.FOO_SLACK_WORKSPACE_HOST?.trim()), timestamps: true };
213
+ coverage.provider_calls = client.requests;
214
+ coverage.workspace_channels_visible = probe.channels;
215
+ return outcome(result("ok", { coverage, next_actions: [
216
+ ...(connection.broker === "direct" ? ["Credential broker is direct: this runtime holds an administrator-issued bot token itself. Refresh and revoke happen in Slack, not centrally; move to Nango for the company connection."] : []),
217
+ "Approve a bounded Slack scope with explicit channel and thread selectors, then run foo discovery slack list <inbox:scope-id>.",
218
+ ...(coverage.synthesis && !coverage.synthesis.available ? ["Set ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) before processing a thread."] : []),
219
+ ] }), json);
220
+ }
221
+ catch (error) {
222
+ if (error instanceof SlackRuntimeError)
223
+ return fromRuntimeError(error, json, undefined, coverage);
224
+ return invalid("Slack readiness failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", undefined, coverage);
225
+ }
226
+ }
227
+ async function listCommand(root, targetId, json, deps) {
228
+ if (!targetId)
229
+ return invalid("Usage: foo discovery slack list <inbox:scope-id>", json);
230
+ const now = deps.now();
231
+ let target;
232
+ try {
233
+ target = loadTarget(root, targetId);
234
+ }
235
+ catch (error) {
236
+ return invalid(error instanceof Error ? error.message : "invalid discovery target", json, "E_DISCOVERY_VALIDATION", targetId);
237
+ }
238
+ const coverage = {
239
+ mode: MODE,
240
+ support_state: SUPPORT_STATE,
241
+ lane: target.lane,
242
+ transcript_opened: false,
243
+ provider_calls: { conversations_list: 0, conversations_info: 0, conversations_history: 0, conversations_replies: 0 },
244
+ selected_threads: target.scope.thread_ts?.length ?? 0,
245
+ };
246
+ // Content-free listing: the approved scope's own explicit thread selection
247
+ // is the listing. No provider call happens; thread existence is proven by
248
+ // the single read in `process`.
249
+ const selected = target.scope.thread_ts ?? [];
250
+ // Fail fast on an unconfigured runtime even where no provider call would
251
+ // happen: an operator learns about the gap before any work runs.
252
+ if (!slackConnectionConfigured(deps.env)) {
253
+ return fromRuntimeError(new SlackRuntimeError("not_connected", "Slack is not connected: set NANGO_SECRET_KEY and FOO_SLACK_NANGO_CONNECTION for the company connection through Nango"), json, targetId, coverage);
254
+ }
255
+ if (selected.length > 1) {
256
+ return outcome(result("ambiguous_match", {
257
+ study_id: targetId,
258
+ coverage,
259
+ errors: [{ code: "E_DISCOVERY_AMBIGUOUS_MATCH", message: `The scope selected ${selected.length} threads; processing reads exactly one per proof, so name one.` }],
260
+ handoff: `foo discovery slack process ${targetId} --thread <channel-id>/<ts>`,
261
+ next_actions: [`Approved thread selectors: ${selected.join(", ")}`],
262
+ }), json);
263
+ }
264
+ const selector = selected[0];
265
+ try {
266
+ const fingerprint = selectThread(root, target, threadSession(target, selector, deps.env), now);
267
+ return outcome(result("ok", {
268
+ study_id: targetId,
269
+ coverage: { ...coverage, matched_thread: selector, source_fingerprint: fingerprint },
270
+ next_actions: [`Run foo discovery slack process ${targetId} to open this one thread and apply the sanitized summary-only evidence.`],
271
+ }), json);
272
+ }
273
+ catch (error) {
274
+ if (error instanceof AuthorizationFailure)
275
+ return authorizationFailure(error, json, targetId, coverage);
276
+ return invalid("Slack listing failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", targetId, coverage);
277
+ }
278
+ }
279
+ async function processCommand(root, targetId, args, json, deps) {
280
+ if (!targetId)
281
+ return invalid("Usage: foo discovery slack process <inbox:scope-id> [--thread <channel-id>/<ts>]", json);
282
+ const now = deps.now();
283
+ let target;
284
+ try {
285
+ target = loadTarget(root, targetId);
286
+ }
287
+ catch (error) {
288
+ return invalid(error instanceof Error ? error.message : "invalid discovery target", json, "E_DISCOVERY_VALIDATION", targetId);
289
+ }
290
+ const coverage = { mode: MODE, support_state: SUPPORT_STATE, lane: target.lane, transcript_opened: false };
291
+ const requested = flagValue(args, "--thread");
292
+ if (requested && !target.scope.thread_ts?.includes(requested.trim())) {
293
+ return invalid("the requested thread is not in the approved scope's explicit thread selection; nothing was opened", json, "E_DISCOVERY_VALIDATION", targetId, coverage);
294
+ }
295
+ const selected = requested?.trim() ?? target.scope.thread_ts?.[0];
296
+ if (!selected) {
297
+ return outcome(result("ambiguous_match", {
298
+ study_id: targetId,
299
+ coverage,
300
+ errors: [{ code: "E_DISCOVERY_AMBIGUOUS_MATCH", message: "The scope selected several threads; name one with --thread <channel-id>/<ts>." }],
301
+ handoff: `foo discovery slack process ${targetId} --thread <channel-id>/<ts>`,
302
+ }), json);
303
+ }
304
+ // Proof and match first, before any provider call or model request: the
305
+ // authorization gates everything downstream.
306
+ // One read per applied source: if this exact thread's evidence is already
307
+ // applied to the inbox, a repeat process must not re-match and re-open it.
308
+ // A completed apply consumes the proof and removes the local match state, so
309
+ // the applied fingerprint in findings.yaml is the only durable record.
310
+ const candidateSession = threadSession(target, selected, deps.env);
311
+ if (existingFingerprint(discoveryTargetDir(root, targetId), sourceFingerprint(candidateSession))) {
312
+ return outcome(result("needs_research_proof", {
313
+ study_id: targetId,
314
+ coverage,
315
+ errors: [{ code: "E_DISCOVERY_RESEARCH_PROOF", message: "This thread's evidence was already applied; reopening it would read the source a second time." }],
316
+ handoff: "Run foo discovery slack list to select a different approved thread, or review the applied evidence.",
317
+ }), json);
318
+ }
319
+ let fingerprint;
320
+ try {
321
+ fingerprint = selectThread(root, target, candidateSession, now);
322
+ }
323
+ catch (error) {
324
+ if (error instanceof AuthorizationFailure)
325
+ return authorizationFailure(error, json, targetId, coverage);
326
+ // Fail closed, but never silently: the operator needs the validator's
327
+ // reason on stderr. Validator and matcher messages are policy text and
328
+ // carry no source content.
329
+ console.error(`Slack select failed: ${error instanceof Error ? error.message : String(error)}`);
330
+ return invalid("Slack processing failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", targetId, coverage);
331
+ }
332
+ // Synthesis credential BEFORE opening: no content is fetched if the run
333
+ // cannot synthesize it.
334
+ if (!synthesisCredentialPresent(deps.env))
335
+ return outcome(result("needs_research_proof", { study_id: targetId, coverage, errors: [{ code: "E_DISCOVERY_RESEARCH_PROOF", message: "No model credential is available for server-side synthesis, so the thread was not opened." }], handoff: "Set ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) for this runtime, then rerun process." }), json);
336
+ const model = defaultSynthesisModel(deps.env);
337
+ const { channelId, threadTs } = parseThreadSelector(selected);
338
+ let connection;
339
+ try {
340
+ connection = await resolveSlackConnection({ env: deps.env, fetchImpl: deps.fetchImpl });
341
+ }
342
+ catch (error) {
343
+ if (error instanceof SlackRuntimeError)
344
+ return fromRuntimeError(error, json, targetId, coverage);
345
+ throw error;
346
+ }
347
+ const client = new SlackClient(connection, { fetchImpl: deps.fetchImpl, baseUrl: deps.env.FOO_SLACK_API_BASE?.trim() || undefined, sleep: deps.sleep, fetchTimeoutMs: deps.fetchTimeoutMs });
348
+ coverage.connection = connection.connection_ref;
349
+ // Exactly one content read happens inside the proof boundary (the reply
350
+ // ceiling is enforced inside the client); rendering through member labels
351
+ // happens immediately, and the raw read never leaves this scope.
352
+ let openError;
353
+ let read;
354
+ try {
355
+ read = await openAuthorizedTranscript({ study_dir: inboxDir(root), local_root: localRoot(root), study_id: targetId, source_fingerprint: fingerprint, now }, async () => {
356
+ try {
357
+ return await client.threadReplies(channelId, threadTs);
358
+ }
359
+ catch (error) {
360
+ if (error instanceof SlackRuntimeError)
361
+ openError = error;
362
+ throw error;
363
+ }
364
+ });
365
+ }
366
+ catch (error) {
367
+ if (openError)
368
+ return fromRuntimeError(openError, json, targetId, { ...coverage, transcript_body_requests: client.requests.conversations_replies });
369
+ return outcome(result("needs_research_proof", { study_id: targetId, coverage, errors: [{ code: "E_DISCOVERY_RESEARCH_PROOF", message: "The thread was not opened: the discovery proof is missing, expired, already used, or no longer matches the approval." }], handoff: `Run foo discovery slack list ${targetId} again to re-match and mint a fresh proof.` }), json);
370
+ }
371
+ coverage.transcript_body_requests = client.requests.conversations_replies;
372
+ const permalink = slackPermalink(deps.env, channelId, threadTs);
373
+ const rendered = renderThreadContent(read.messages);
374
+ const rawUserIds = observedUserIds(read.messages);
375
+ const truncated = read.has_more;
376
+ const estimatedTokens = Math.ceil(rendered.length / 4);
377
+ if (estimatedTokens > SLACK_POLICY.max_model_input_tokens) {
378
+ return fromRuntimeError(new SlackRuntimeError("thread_too_large", "the thread exceeds the model-input ceiling; nothing was synthesized"), json, targetId, { ...coverage, transcript_body_requests: client.requests.conversations_replies });
379
+ }
380
+ let synthesized;
381
+ try {
382
+ synthesized = await synthesizeDiscoveryEnvelope({
383
+ target_id: targetId,
384
+ lane: target.lane,
385
+ // The permalink is channel-level provenance (no member identity); when
386
+ // the workspace host is unset there is no stable link, and the
387
+ // envelope must say so explicitly rather than imply one exists.
388
+ source: { provider: "slack", source_fingerprint: fingerprint, completed_at: selectorCompletedAt(selected), locator_capability: "timestamp", ...(permalink ? { stable_url: permalink } : {}) },
389
+ title: `Slack thread ${channelId} ${threadTs}`,
390
+ hypotheses: [],
391
+ transcript_text: rendered,
392
+ // A thread longer than the one-page reply ceiling was not fully read;
393
+ // the envelope says so instead of implying completeness.
394
+ transcript_complete: !truncated,
395
+ caveats: [
396
+ ...(truncated ? ["The thread exceeded the reply ceiling; later replies were not read and the summary covers the first page only."] : []),
397
+ ...(permalink ? [] : ["No stable link is available for this thread; provenance is the channel and thread ids only."]),
398
+ ],
399
+ }, { model, transport: deps.transport });
400
+ }
401
+ catch {
402
+ return invalid("Synthesis did not produce a valid closed envelope; nothing was written and the thread content was discarded.", json, "E_DISCOVERY_INVALID_ENVELOPE", targetId, coverage);
403
+ }
404
+ coverage.synthesis_usage = synthesized.usage ?? null;
405
+ coverage.model = model;
406
+ // Privacy screen beyond the envelope validator: raw workspace user ids must
407
+ // not survive into derived text even as prose; member labels are the only
408
+ // speaker identity the envelope ever carries.
409
+ const derivedText = [
410
+ ...synthesized.envelope.findings.flatMap((finding) => [finding.headline, finding.paraphrase, finding.dissent]),
411
+ ...synthesized.envelope.coverage.caveats,
412
+ ].join("\n");
413
+ if (containsRawUserId(derivedText, rawUserIds))
414
+ return invalid("Synthesis leaked a member identity into derived text; nothing was written.", json, "E_DISCOVERY_INVALID_ENVELOPE", targetId, coverage);
415
+ // Chat class: no contact capture from chat in V0 — identity harvesting from
416
+ // message text is out of scope for decision_context evidence.
417
+ const envelope = { ...synthesized.envelope };
418
+ delete envelope.contact_candidates;
419
+ const applied = await runDiscoveryCommand(root, ["apply", targetId, "--evidence-stdin", "--json"], JSON.stringify(envelope), now);
420
+ return outcome({ ...applied.result, coverage: { ...applied.result.coverage, ...coverage } }, json);
421
+ }
422
+ export async function runSlackDiscovery(root, args, deps = {}) {
423
+ const json = hasFlag(args, "--json");
424
+ const env = deps.env ?? process.env;
425
+ const required = {
426
+ fetchImpl: deps.fetchImpl ?? globalThis.fetch,
427
+ env,
428
+ now: deps.now ?? (() => new Date().toISOString()),
429
+ sleep: deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
430
+ transport: deps.transport ?? anthropicSynthesisTransport({ fetchImpl: deps.fetchImpl, env: deps.env }),
431
+ fetchTimeoutMs: deps.fetchTimeoutMs ?? SLACK_POLICY.fetch_timeout_ms,
432
+ };
433
+ const [command, targetId] = args;
434
+ try {
435
+ if (command === "ready")
436
+ return await readyCommand(root, json, required);
437
+ if (command === "list")
438
+ return await listCommand(root, targetId, json, required);
439
+ if (command === "process")
440
+ return await processCommand(root, targetId, args, json, required);
441
+ return invalid("Usage: foo discovery slack <ready|list|process> …", json);
442
+ }
443
+ catch (error) {
444
+ if (error instanceof SlackRuntimeError)
445
+ return fromRuntimeError(error, json, targetId, { mode: MODE, support_state: SUPPORT_STATE });
446
+ // Fail closed, but never silently: the operator needs the real reason on
447
+ // stderr. Internal validator/command messages are policy text and carry no
448
+ // source content.
449
+ console.error(`Slack discovery failed: ${error instanceof Error ? error.message : String(error)}`);
450
+ return invalid("Slack discovery failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", targetId);
451
+ }
452
+ }
@@ -0,0 +1,56 @@
1
+ export type DiscoveryCapabilities = {
2
+ list_sessions: boolean;
3
+ open_transcript: boolean;
4
+ stable_link: boolean;
5
+ timestamps: boolean;
6
+ };
7
+ export type DiscoveryAccess = "available" | "denied" | "unknown";
8
+ export type DiscoveryCompletionState = "completed" | "in_progress" | "scheduled" | "cancelled";
9
+ export type ContactHintField = "full_name" | "company" | "title" | "linkedin";
10
+ export type SessionContactHint = {
11
+ field: ContactHintField;
12
+ value: string;
13
+ source: string;
14
+ };
15
+ export type DiscoverySession = {
16
+ schema_version: "0.1";
17
+ provider: string;
18
+ connection_alias: string;
19
+ runtime_handle: string;
20
+ completed_at: string;
21
+ completion_state: DiscoveryCompletionState;
22
+ title: string;
23
+ participants: string[];
24
+ stable_url?: string;
25
+ capabilities: DiscoveryCapabilities;
26
+ access: DiscoveryAccess;
27
+ contact_hints?: SessionContactHint[];
28
+ };
29
+ export interface DiscoverySourceAdapter<TRaw> {
30
+ provider: string;
31
+ normalizeSession(raw: TRaw, connectionAlias: string): DiscoverySession;
32
+ }
33
+ export type ProviderLinkForm = {
34
+ hosts: string[];
35
+ host_suffix?: string;
36
+ path: string;
37
+ query: {
38
+ name: string;
39
+ pattern: RegExp;
40
+ };
41
+ };
42
+ export declare function providerLinkForm(provider: string): ProviderLinkForm | undefined;
43
+ export type ProviderPathTemplateForm = {
44
+ hosts: string[];
45
+ host_suffix?: string;
46
+ template: string;
47
+ placeholders: Record<string, RegExp>;
48
+ };
49
+ export declare function sanitizeDiscoveryStableUrl(provider: string, value: string): string;
50
+ export declare function isValidLinkedinUrl(value: string): boolean;
51
+ export declare function companyFromEmailDomain(email: string): string | null;
52
+ export declare function contactHintsFromDisplayLabel(label: string, source: string): SessionContactHint[];
53
+ export declare function dedupeContactHints(hints: SessionContactHint[]): SessionContactHint[];
54
+ export declare function adapterCapabilityState(capabilities: DiscoveryCapabilities): "needs_provider_connector" | "ready_with_coverage_caveat" | "ready";
55
+ export declare function validateNormalizedSession(session: DiscoverySession): string[];
56
+ export declare function assertNormalizedSessionShape(value: unknown): asserts value is DiscoverySession;