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,551 @@
1
+ // `foo discovery fireflies …` — the local worker for a per-user Fireflies
2
+ // connection (fireflies-discovery-connector-v0, D-001 keyed direct client
3
+ // under the gong worker contract).
4
+ //
5
+ // The gong worker is the process template (R-001): list bounded transcript
6
+ // metadata inside an approved study or inbox scope, normalize it through the
7
+ // Fireflies adapter, hand the closed session shape to `foo discovery match`,
8
+ // open exactly one proof-authorized transcript into process memory, reduce it
9
+ // to the sanitized envelope in one model request, and apply locally. The
10
+ // transcript never reaches argv, stdin, a file, Git, a log, an error, or the
11
+ // host agent's context; every surface this module writes is content-free.
12
+ //
13
+ // What differs from gong, honestly: the credential is the user's own Fireflies
14
+ // API key (connection_owner "user", credential_broker "direct" — R-010/D-002),
15
+ // not a Nango-brokered company connection; there is no administrator over a
16
+ // personal key, so member isolation is a recorded gap, not a provided control.
17
+ //
18
+ // Nothing here claims live Fireflies access. Fireflies stays contract_only
19
+ // until the feasibility task (T-001) and the dogfood run (T-008) close with
20
+ // receipts; the provider API shapes the client pins are beliefs owned by
21
+ // T-001, held up by fixtures only.
22
+ import { existsSync, readFileSync } from "node:fs";
23
+ import path from "node:path";
24
+ import { hasFlag } from "./cliArgs.js";
25
+ import { loadDiscoveryRun } from "./discoveryArtifacts.js";
26
+ import { discoveryTargetDir, inboxScope, isInboxTarget, localRoot, localTargetName, runDiscoveryCommand, } from "./discoveryCommand.js";
27
+ import { openAuthorizedTranscript } from "./discoveryProof.js";
28
+ import { firefliesDiscoveryAdapter, firefliesInternalOnly } from "./discoveryProviders.js";
29
+ import { planDiscoverySync } from "./discoverySync.js";
30
+ import { anthropicSynthesisTransport, defaultSynthesisModel, synthesisCredentialPresent, synthesizeDiscoveryEnvelope } from "./discoverySynthesis.js";
31
+ import { firefliesConnectionReady } from "./sourceIngress.js";
32
+ import { FirefliesClient, FirefliesRuntimeError, firefliesConnectionConfigured, firefliesCredentialBroker, resolveFirefliesConnection, FIREFLIES_POLICY } from "./firefliesClient.js";
33
+ const MATCHABLE_STUDY_STATES = new Set(["awaiting_match", "ambiguous_match", "needs_source_approval", "needs_provider_access", "needs_provider_connector", "needs_research_proof"]);
34
+ const DEFAULT_LOOKBACK_DAYS = 14;
35
+ function result(status, options = {}) {
36
+ return { schema_version: "0.1", status, writes: [], errors: [], handoff: null, next_actions: [], coverage: {}, ...options };
37
+ }
38
+ function exitCodeFor(status) {
39
+ return ["ambiguous_match", "needs_source_approval", "needs_provider_access", "needs_provider_connector", "needs_research_proof", "invalid", "budget_exhausted"].includes(status) ? 1 : 0;
40
+ }
41
+ function outcome(value, json) {
42
+ const exitCode = exitCodeFor(value.status);
43
+ if (json)
44
+ return { output: `${JSON.stringify(value, null, 2)}\n`, exitCode, result: value };
45
+ const lines = [`Discovery (fireflies): ${value.status}.`];
46
+ for (const error of value.errors)
47
+ lines.push(`- ${error.message}`);
48
+ if (value.handoff)
49
+ lines.push(`Handoff: ${value.handoff}`);
50
+ for (const action of value.next_actions)
51
+ lines.push(`Next: ${action}`);
52
+ return { output: `${lines.join("\n")}\n`, exitCode, result: value };
53
+ }
54
+ function invalid(message, json, code = "E_DISCOVERY_VALIDATION", targetId, coverage = {}) {
55
+ return outcome(result("invalid", { study_id: targetId, errors: [{ code, message }], coverage }), json);
56
+ }
57
+ // Typed provider failures map to the discovery command's closed status set.
58
+ // Messages are productfoo's own words: no provider body, id, or credential.
59
+ function fromRuntimeError(error, json, targetId, coverage) {
60
+ const base = { study_id: targetId, coverage: { ...coverage, provider_error: error.code, mode: FIREFLIES_MODE } };
61
+ switch (error.code) {
62
+ case "not_connected":
63
+ return outcome(result("needs_provider_connector", { ...base, errors: [{ code: "E_DISCOVERY_PROVIDER_CONNECTOR", message: error.message }], handoff: "Connect your own Fireflies account by setting FOO_FIREFLIES_API_KEY to your Fireflies API key for this runtime; a Fireflies connection covers only your own meetings." }), json);
64
+ case "auth_expired":
65
+ case "access_denied":
66
+ return outcome(result("needs_provider_access", { ...base, errors: [{ code: "E_DISCOVERY_PROVIDER_ACCESS", message: error.message }], handoff: "Check that the Fireflies API key is valid and its access grants include the meeting, then rerun." }), json);
67
+ case "budget_exhausted":
68
+ // A rate-limit stop is the quota half of R-004: the run ends, nothing
69
+ // widens, nothing retries. The next run needs a narrower window.
70
+ return outcome(result("budget_exhausted", { ...base, errors: [{ code: "E_DISCOVERY_VALIDATION", message: error.message }], handoff: "The agreed page or quota ceiling stopped this run; narrow the window or selectors and rerun — productfoo never widens the window or auto-retries." }), json);
71
+ default:
72
+ return outcome(result("invalid", { ...base, errors: [{ code: "E_DISCOVERY_VALIDATION", message: error.message }], handoff: error.code === "transcript_too_large" || error.code === "provider_timeout" ? "Nothing was written and no retry was attempted; a new attempt needs a fresh match." : null }), json);
73
+ }
74
+ }
75
+ function isoDaysAgo(now, days) {
76
+ return new Date(Date.parse(now) - days * 86_400_000).toISOString();
77
+ }
78
+ function loadTarget(root, targetId, env, now, phase = "list") {
79
+ if (isInboxTarget(targetId)) {
80
+ const scope = inboxScope(root, targetId);
81
+ if (scope.provider.toLowerCase() !== "fireflies")
82
+ throw new Error("this inbox scope is not a Fireflies scope");
83
+ if (scope.state !== "approved")
84
+ throw new Error("this inbox scope is not approved");
85
+ // R-004: validation is pre-flight — an invalid scope never reaches the
86
+ // provider.
87
+ const scopeError = firefliesScopeValidationError(firefliesSelectorsFromInboxScope(scope));
88
+ if (scopeError)
89
+ throw new Error(scopeError);
90
+ const from = new Date(scope.time_window.start);
91
+ const to = new Date(scope.time_window.end);
92
+ return { target_id: targetId, lane: "unassigned_call_evidence", dir: discoveryTargetDir(root, targetId), connection_alias: scope.connection_alias, window: { from: from.toISOString(), to: to.toISOString() } };
93
+ }
94
+ const dir = discoveryTargetDir(root, targetId);
95
+ const run = loadDiscoveryRun(dir);
96
+ if (run.source.provider.toLowerCase() !== "fireflies")
97
+ throw new Error("this study is not a Fireflies study");
98
+ // Listing needs a matchable study; processing needs the state `match` left
99
+ // behind (ready_to_synthesize) — the proof check decides the rest.
100
+ const allowed = phase === "process" ? run.state === "ready_to_synthesize" || MATCHABLE_STUDY_STATES.has(run.state) : MATCHABLE_STUDY_STATES.has(run.state);
101
+ if (!allowed)
102
+ throw new Error(`study ${targetId} is not ready for provider reads (state ${run.state})`);
103
+ let from;
104
+ let to;
105
+ if (run.match.expected_local_date) {
106
+ const day = Date.parse(`${run.match.expected_local_date}T00:00:00Z`);
107
+ if (!Number.isFinite(day))
108
+ throw new Error("the study's expected date is invalid");
109
+ from = new Date(day - 86_400_000).toISOString();
110
+ to = new Date(day + 2 * 86_400_000).toISOString();
111
+ }
112
+ else {
113
+ const lookback = Number(env.FOO_FIREFLIES_LOOKBACK_DAYS ?? DEFAULT_LOOKBACK_DAYS);
114
+ if (!Number.isFinite(lookback) || lookback <= 0 || lookback > 90)
115
+ throw new Error("FOO_FIREFLIES_LOOKBACK_DAYS must be between 1 and 90");
116
+ from = isoDaysAgo(now, lookback);
117
+ to = now;
118
+ }
119
+ // R-004 applies to study scopes too: a fireflies study names its customer —
120
+ // the study target is the named external participant or company, and the
121
+ // match constraints narrow further. A study with neither is invalid.
122
+ const selectorError = firefliesScopeValidationError({
123
+ window: { start: from, end: to },
124
+ external_names: [...(run.match?.required_participants ?? []), ...(run.target?.name?.trim() ? [run.target.name.trim()] : [])],
125
+ external_domains: run.target?.organization?.trim() ? [run.target.organization.trim()] : [],
126
+ title_tokens: run.match?.required_title_tokens ?? [],
127
+ });
128
+ if (selectorError)
129
+ throw new Error(selectorError);
130
+ return { target_id: targetId, lane: "study_linked", dir, connection_alias: run.source.connection_alias, window: { from, to } };
131
+ }
132
+ // R-005: the canonical Fireflies link is path-based, built from the transcript
133
+ // id under the registry-allowlisted host; the adapter re-validates every link
134
+ // through the shared sanitizer, so a malformed id degrades to no link.
135
+ function firefliesLink(id) {
136
+ return `https://app.fireflies.ai/transcript/${id}`;
137
+ }
138
+ // The connection and the session record describe the same fact with two
139
+ // vocabularies: a connection that has not yet read a transcript is "unknown"
140
+ // at the connection level and "missing" (not observed) at the record level.
141
+ // Only an observed successful read upgrades either to "granted" (R-005).
142
+ function observedRecordAccess(connection) {
143
+ return connection.transcript_access === "granted" ? "granted" : "missing";
144
+ }
145
+ function toRecord(listed, connection) {
146
+ if (!listed.started_at)
147
+ return null;
148
+ return {
149
+ id: listed.id,
150
+ title: listed.title?.trim() || "Untitled transcript",
151
+ started_at: listed.started_at,
152
+ ended: listed.ended,
153
+ status: listed.status,
154
+ participants: listed.participants ?? [],
155
+ url: firefliesLink(listed.id),
156
+ transcript_access: observedRecordAccess(connection),
157
+ };
158
+ }
159
+ async function connect(deps) {
160
+ const connection = resolveFirefliesConnection({ env: deps.env });
161
+ const client = new FirefliesClient(connection.credential, { fetchImpl: deps.fetchImpl, baseUrl: deps.env.FOO_FIREFLIES_API_BASE?.trim() || undefined, sleep: deps.sleep });
162
+ // R-005's observed grant, keyed-direct edition: a personal Fireflies key has
163
+ // no scope system, so the one observable fact is the presented key answering
164
+ // for its owner. One identity query observes it per invocation (gong observes
165
+ // its grant the same way, via OAuth-scope introspection at resolve time).
166
+ // A transcript read that still fails maps to needs_provider_access — the
167
+ // claim is never faked, only grounded where the provider allows.
168
+ await client.probe();
169
+ connection.transcript_access = "granted";
170
+ return { connection, client };
171
+ }
172
+ async function listSessions(target, connection, client) {
173
+ const listing = await client.listTranscripts({ from: target.window.from, to: target.window.to });
174
+ // A ceiling stop is final for the run (R-004/R-010): no further provider
175
+ // reads happen — the run never waits for a wider window.
176
+ if (listing.truncated)
177
+ return { sessions: [], listed: listing.transcripts.length, pages: listing.pages, truncated: true, internal_only_excluded: 0, incomplete: 0 };
178
+ const sessions = [];
179
+ let internalExcluded = 0;
180
+ let incomplete = 0;
181
+ for (const entry of listing.transcripts) {
182
+ const provisional = { id: entry.id, title: entry.title?.trim() || "Untitled transcript", started_at: entry.started_at ?? "", participants: entry.participants ?? [], url: firefliesLink(entry.id), transcript_access: observedRecordAccess(connection) };
183
+ if (firefliesInternalOnly(provisional)) {
184
+ internalExcluded += 1;
185
+ continue;
186
+ }
187
+ const record = toRecord(entry, connection);
188
+ if (!record) {
189
+ incomplete += 1;
190
+ continue;
191
+ }
192
+ const session = firefliesDiscoveryAdapter.normalizeSession(record, target.connection_alias);
193
+ if (session.completion_state !== "completed") {
194
+ incomplete += 1;
195
+ continue;
196
+ }
197
+ // Transcript access is observed, never assumed (R-005): connect() watched
198
+ // the presented key answer for its owner, so the session reports available
199
+ // access; until that observation the session would honestly say unknown.
200
+ sessions.push({ ...session, access: connection.transcript_access === "granted" ? "available" : "unknown" });
201
+ }
202
+ return { sessions, listed: listing.transcripts.length, pages: listing.pages, truncated: listing.truncated, internal_only_excluded: internalExcluded, incomplete };
203
+ }
204
+ async function readyCommand(root, json, deps) {
205
+ const env = deps.env;
206
+ const coverage = {
207
+ provider: "fireflies",
208
+ mode: FIREFLIES_MODE,
209
+ support_state: FIREFLIES_SUPPORT_STATE,
210
+ // R-010/D-002: the connection is the user's own key; there is no
211
+ // administrator over it, and member isolation is not provided.
212
+ connection_owner: "user",
213
+ configured: firefliesConnectionConfigured(env),
214
+ credential_broker: firefliesCredentialBroker(env),
215
+ synthesis: { available: synthesisCredentialPresent(env), model: defaultSynthesisModel(env) },
216
+ policy: { ...FIREFLIES_POLICY },
217
+ };
218
+ try {
219
+ const { connection, client } = await connect(deps);
220
+ // connect() already ran the one identity probe that observes the grant
221
+ // (R-005); readiness adds no further provider read.
222
+ coverage.connection = connection.connection_ref;
223
+ coverage.credential_broker = connection.broker;
224
+ coverage.capabilities = { list_sessions: true, open_transcript: connection.transcript_access === "granted", stable_link: true, timestamps: true, transcript_access: connection.transcript_access };
225
+ coverage.provider_calls = client.requests;
226
+ coverage.readiness = firefliesConnectionReady({ connection_ref: connection.connection_ref });
227
+ return outcome(result("ok", { coverage, next_actions: [
228
+ "A personal Fireflies key sees only its owner's meetings: there is no administrator over it, and productfoo does not provide member isolation between users (OQ-001 workspace-owned access stays open).",
229
+ "Approve a bounded Fireflies scope, then run foo discovery fireflies list <study-id|inbox:scope-id>.",
230
+ ...(coverage.synthesis && !coverage.synthesis.available ? ["Set ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) before processing a transcript."] : []),
231
+ ] }), json);
232
+ }
233
+ catch (error) {
234
+ if (error instanceof FirefliesRuntimeError)
235
+ return fromRuntimeError(error, json, undefined, coverage);
236
+ return invalid("Fireflies readiness failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", undefined, coverage);
237
+ }
238
+ }
239
+ async function listCommand(root, targetId, json, deps) {
240
+ if (!targetId)
241
+ return invalid("Usage: foo discovery fireflies list <study-id|inbox:scope-id>", json);
242
+ const now = deps.now();
243
+ let target;
244
+ try {
245
+ target = loadTarget(root, targetId, deps.env, now);
246
+ }
247
+ catch (error) {
248
+ return invalid(error instanceof Error ? error.message : "invalid discovery target", json, "E_DISCOVERY_VALIDATION", targetId);
249
+ }
250
+ const coverage = { mode: FIREFLIES_MODE, window: target.window, lane: target.lane, transcript_opened: false };
251
+ try {
252
+ const { connection, client } = await connect(deps);
253
+ coverage.connection = connection.connection_ref;
254
+ const listed = await listSessions(target, connection, client);
255
+ Object.assign(coverage, { transcripts_listed: listed.listed, pages: listed.pages, truncated: listed.truncated, internal_only_excluded: listed.internal_only_excluded, incomplete_excluded: listed.incomplete, candidates_evaluated: listed.sessions.length, provider_calls: client.requests });
256
+ if (listed.truncated) {
257
+ return outcome(result("budget_exhausted", {
258
+ study_id: targetId,
259
+ coverage: { ...coverage, provider_error: "budget_exhausted" },
260
+ errors: [{ code: "E_DISCOVERY_VALIDATION", message: `The window holds more transcripts than the ${FIREFLIES_POLICY.max_list_pages}-page ceiling; narrow the window or selectors instead of widening the read.` }],
261
+ handoff: "Narrow the approved window or selectors and list again; the run never widens the window and never retries.",
262
+ }), json);
263
+ }
264
+ if (listed.sessions.length === 0)
265
+ return outcome(result("awaiting_match", { study_id: targetId, coverage, next_actions: ["No completed transcript with an external party was listed inside the approved window."] }), json);
266
+ if (target.lane === "study_linked") {
267
+ const matched = await runDiscoveryCommand(root, ["match", targetId, "--sessions-stdin", "--json"], JSON.stringify(listed.sessions), now);
268
+ return outcome({ ...matched.result, coverage: { ...matched.result.coverage, ...coverage }, next_actions: [...matched.result.next_actions, ...(matched.result.status === "ok" ? [`Run foo discovery fireflies process ${targetId} to open the one matched transcript and apply the sanitized evidence.`] : [])] }, json);
269
+ }
270
+ // Inbox: one session at a time so each source gets its own one-time proof.
271
+ let lastMiss = null;
272
+ let evaluated = 0;
273
+ for (const session of listed.sessions) {
274
+ evaluated += 1;
275
+ const matched = await runDiscoveryCommand(root, ["match", targetId, "--sessions-stdin", "--json"], JSON.stringify([session]), now);
276
+ if (matched.result.status === "awaiting_match") {
277
+ lastMiss = matched.result;
278
+ continue;
279
+ }
280
+ const remaining = listed.sessions.length - evaluated;
281
+ return outcome({
282
+ ...matched.result,
283
+ coverage: { ...matched.result.coverage, ...coverage, candidates_evaluated: evaluated, candidates_remaining: remaining },
284
+ next_actions: [
285
+ ...matched.result.next_actions,
286
+ ...(matched.result.status === "ok" ? [`Run foo discovery fireflies process ${targetId} to open this one transcript and apply the sanitized evidence.`] : []),
287
+ ...(remaining > 0 ? [`${remaining} more listed transcript(s) were not evaluated; run list again after this one is processed.`] : []),
288
+ ],
289
+ }, json);
290
+ }
291
+ return outcome(result("awaiting_match", { study_id: targetId, coverage: { ...coverage, candidates_evaluated: evaluated }, next_actions: lastMiss?.next_actions ?? ["No listed transcript matched the approved scope; nothing was opened."] }), json);
292
+ }
293
+ catch (error) {
294
+ if (error instanceof FirefliesRuntimeError)
295
+ return fromRuntimeError(error, json, targetId, coverage);
296
+ return invalid("Fireflies listing failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", targetId, coverage);
297
+ }
298
+ }
299
+ function readMatch(root, targetId) {
300
+ const file = path.join(localRoot(root), localTargetName(targetId), "match.json");
301
+ if (!existsSync(file))
302
+ return null;
303
+ try {
304
+ return JSON.parse(readFileSync(file, "utf8"));
305
+ }
306
+ catch {
307
+ return null;
308
+ }
309
+ }
310
+ function studyHypotheses(dir) {
311
+ const file = path.join(dir, "study.md");
312
+ if (!existsSync(file))
313
+ return [];
314
+ const out = [];
315
+ for (const line of readFileSync(file, "utf8").split("\n")) {
316
+ const match = /^\s*(?:[-*]|\d+\.)?\s*\**(H-\d{3})\**\s*[:—–-]?\s*(.+?)\s*$/.exec(line);
317
+ if (match && !out.some((entry) => entry.id === match[1]))
318
+ out.push({ id: match[1], text: match[2] });
319
+ }
320
+ return out;
321
+ }
322
+ function mmss(ms) {
323
+ const total = Math.max(0, Math.floor((ms ?? 0) / 1000));
324
+ return `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
325
+ }
326
+ // Neutral speaker ordinals, never names: Fireflies metadata carries no
327
+ // affiliation (OQ-002), so the render cannot honestly say Customer or Company
328
+ // representative — and keeping names out of the model text is stricter than
329
+ // gong's role labels. Timestamps render relative to the first sentence when
330
+ // the provider supplies them.
331
+ function renderFirefliesTranscript(content) {
332
+ const starts = content.sentences?.map((sentence) => Date.parse(sentence.start_date ?? "")).filter((value) => Number.isFinite(value)) ?? [];
333
+ const first = starts.length ? Math.min(...starts) : null;
334
+ const lines = [];
335
+ const ordinals = new Map();
336
+ for (const sentence of content.sentences ?? []) {
337
+ let label = "Speaker";
338
+ if (sentence.speaker_id) {
339
+ if (!ordinals.has(sentence.speaker_id))
340
+ ordinals.set(sentence.speaker_id, `Speaker ${ordinals.size + 1}`);
341
+ label = ordinals.get(sentence.speaker_id);
342
+ }
343
+ const start = Date.parse(sentence.start_date ?? "");
344
+ const offset = first !== null && Number.isFinite(start) ? mmss(start - first) : "00:00";
345
+ lines.push(`[${offset}] ${label}: ${sentence.text}`);
346
+ }
347
+ return lines.join("\n");
348
+ }
349
+ function containsIdentity(envelopeText, identities) {
350
+ const haystack = envelopeText.toLowerCase();
351
+ return identities.some((identity) => identity.length >= 4 && haystack.includes(identity.toLowerCase()));
352
+ }
353
+ async function processCommand(root, targetId, json, deps) {
354
+ if (!targetId)
355
+ return invalid("Usage: foo discovery fireflies process <study-id|inbox:scope-id>", json);
356
+ const now = deps.now();
357
+ let target;
358
+ try {
359
+ target = loadTarget(root, targetId, deps.env, now, "process");
360
+ }
361
+ catch (error) {
362
+ return invalid(error instanceof Error ? error.message : "invalid discovery target", json, "E_DISCOVERY_VALIDATION", targetId);
363
+ }
364
+ const match = readMatch(root, targetId);
365
+ if (!match || !Array.isArray(match.candidates) || match.candidates.length === 0)
366
+ return invalid("No matched Fireflies transcript is recorded for this target; run foo discovery fireflies list first.", json, "E_DISCOVERY_VALIDATION", targetId);
367
+ if (match.candidates.length > 1)
368
+ return outcome(result("ambiguous_match", { study_id: targetId, errors: [{ code: "E_DISCOVERY_AMBIGUOUS_MATCH", message: "Several transcripts matched; a person must select one before any transcript is opened." }], handoff: `foo discovery review ${targetId} select-source --fingerprint <sha256> --by <person>` }), json);
369
+ const candidate = match.candidates[0];
370
+ const session = candidate.session;
371
+ const transcriptId = session.runtime_handle;
372
+ if (!synthesisCredentialPresent(deps.env))
373
+ return outcome(result("needs_research_proof", { study_id: targetId, errors: [{ code: "E_DISCOVERY_RESEARCH_PROOF", message: "No model credential is available for server-side synthesis, so the transcript was not opened." }], handoff: "Set ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) for this runtime, then rerun process." }), json);
374
+ const model = defaultSynthesisModel(deps.env);
375
+ const coverage = { mode: FIREFLIES_MODE, lane: target.lane, policy_version: FIREFLIES_POLICY.policy_version, model };
376
+ const hypotheses = target.lane === "study_linked" ? studyHypotheses(target.dir) : [];
377
+ if (target.lane === "study_linked" && hypotheses.length === 0)
378
+ return invalid("The study records no H-### hypotheses in study.md; study-linked evidence needs one to test.", json, "E_DISCOVERY_VALIDATION", targetId);
379
+ let client;
380
+ let connection;
381
+ try {
382
+ const connected = await connect(deps);
383
+ client = connected.client;
384
+ connection = connected.connection;
385
+ coverage.connection = connection.connection_ref;
386
+ // Identities for the post-synthesis leak screen: the names and domain
387
+ // labels the provider's own metadata carries (raw emails are long gone —
388
+ // the adapter stripped them).
389
+ const identities = [...(session.participants ?? [])];
390
+ let openError;
391
+ let transcriptText;
392
+ try {
393
+ const content = await openAuthorizedTranscript({ study_dir: target.dir, local_root: localRoot(root), study_id: targetId, source_fingerprint: candidate.source_fingerprint, now }, async () => {
394
+ try {
395
+ return await client.transcript(transcriptId);
396
+ }
397
+ catch (error) {
398
+ if (error instanceof FirefliesRuntimeError)
399
+ openError = error;
400
+ throw error;
401
+ }
402
+ });
403
+ // Observed, never assumed (R-005): the connection just read a
404
+ // transcript, so transcript access is now a fact of this connection.
405
+ connection.transcript_access = "granted";
406
+ for (const speaker of content.speakers ?? []) {
407
+ if (speaker.name?.trim())
408
+ identities.push(speaker.name.trim());
409
+ }
410
+ transcriptText = renderFirefliesTranscript(content);
411
+ }
412
+ catch (error) {
413
+ if (openError)
414
+ return fromRuntimeError(openError, json, targetId, { ...coverage, transcript_body_requests: client.requests.transcript });
415
+ return outcome(result("needs_research_proof", { study_id: targetId, errors: [{ code: "E_DISCOVERY_RESEARCH_PROOF", message: "The transcript was not opened: the discovery proof is missing, expired, already used, or no longer matches the approval." }], handoff: `Run foo discovery fireflies list ${targetId} again to re-match and mint a fresh proof.`, coverage }), json);
416
+ }
417
+ coverage.transcript_body_requests = client.requests.transcript;
418
+ const estimatedTokens = Math.ceil(transcriptText.length / 4);
419
+ if (estimatedTokens > FIREFLIES_POLICY.max_model_input_tokens) {
420
+ transcriptText = null;
421
+ return fromRuntimeError(new FirefliesRuntimeError("transcript_too_large", "the transcript exceeds the model-input ceiling; nothing was synthesized"), json, targetId, coverage);
422
+ }
423
+ // OQ-002 is stated inside the envelope, not silently dropped: the model
424
+ // sees neutral speaker ordinals because Fireflies carries no affiliation.
425
+ const caveats = [
426
+ "Speaker roles were not attributed: Fireflies metadata carries no affiliation (OQ-002), so speakers are neutral ordinals.",
427
+ ...(session.stable_url ? [] : ["The provider returned no canonical transcript link for this transcript."]),
428
+ ];
429
+ let synthesized;
430
+ try {
431
+ synthesized = await synthesizeDiscoveryEnvelope({
432
+ target_id: targetId,
433
+ lane: target.lane,
434
+ source: { provider: "fireflies", ...(session.stable_url ? { stable_url: session.stable_url } : {}), source_fingerprint: candidate.source_fingerprint, completed_at: session.completed_at, locator_capability: session.capabilities.timestamps ? "timestamp" : "none" },
435
+ title: session.title,
436
+ hypotheses,
437
+ transcript_text: transcriptText,
438
+ transcript_complete: true,
439
+ caveats,
440
+ }, { model, transport: deps.transport });
441
+ }
442
+ catch {
443
+ return invalid("Synthesis did not produce a valid closed envelope; nothing was written and the transcript was discarded.", json, "E_DISCOVERY_INVALID_ENVELOPE", targetId, coverage);
444
+ }
445
+ finally {
446
+ transcriptText = null;
447
+ }
448
+ coverage.synthesis_usage = synthesized.usage ?? null;
449
+ // Privacy screen beyond the envelope validator: participant names, email
450
+ // domains, and speaker names from the provider's own metadata must not
451
+ // appear in derived text.
452
+ const derivedText = [
453
+ ...synthesized.envelope.findings.flatMap((finding) => [finding.headline, finding.paraphrase, finding.dissent]),
454
+ ...synthesized.envelope.coverage.caveats,
455
+ ].join("\n");
456
+ if (containsIdentity(derivedText, identities))
457
+ return invalid("Synthesis leaked a participant identity into derived text; nothing was written.", json, "E_DISCOVERY_INVALID_ENVELOPE", targetId, coverage);
458
+ const applied = await runDiscoveryCommand(root, ["apply", targetId, "--evidence-stdin", "--json"], JSON.stringify(synthesized.envelope), now);
459
+ return outcome({ ...applied.result, coverage: { ...applied.result.coverage, ...coverage } }, json);
460
+ }
461
+ catch (error) {
462
+ if (error instanceof FirefliesRuntimeError)
463
+ return fromRuntimeError(error, json, targetId, { ...coverage, transcript_body_requests: client?.requests.transcript ?? 0 });
464
+ return invalid("Fireflies processing failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", targetId, coverage);
465
+ }
466
+ }
467
+ async function syncCommand(root, json, deps) {
468
+ const now = deps.now();
469
+ const plan = planDiscoverySync(root, now);
470
+ const lanes = plan.lanes.filter((lane) => lane.provider.toLowerCase() === "fireflies");
471
+ if (lanes.length === 0)
472
+ return outcome(result("clean_no_op", { errors: plan.errors.map((message) => ({ code: "E_DISCOVERY_VALIDATION", message })), next_actions: ["No approved Fireflies lane is active; approve a study source or an inbox scope first."], coverage: { mode: FIREFLIES_MODE, lanes: 0, connector_reads: 0 } }), json);
473
+ const results = [];
474
+ // Metadata and match only: a transcript is opened only by an explicit
475
+ // `process` on one target. The loop stays unarmed (R-010).
476
+ for (const lane of lanes) {
477
+ const targetId = lane.kind === "inbox" ? `inbox:${lane.scope_id}` : lane.study_id;
478
+ const listed = await listCommand(root, targetId, true, deps);
479
+ results.push({ lane_id: lane.lane_id, status: listed.result.status, handoff: listed.result.handoff });
480
+ }
481
+ const ready = results.filter((entry) => entry.status === "ok").map((entry) => entry.lane_id);
482
+ return outcome(result("ok", {
483
+ errors: plan.errors.map((message) => ({ code: "E_DISCOVERY_VALIDATION", message })),
484
+ next_actions: [
485
+ ...ready.map((laneId) => `${laneId}: one transcript matched — run foo discovery fireflies process ${laneId.replace(/^study:/, "")} to open it.`),
486
+ ...results.filter((entry) => entry.status !== "ok").map((entry) => `${entry.lane_id}: ${entry.status}${entry.handoff ? ` — ${entry.handoff}` : ""}`),
487
+ ],
488
+ coverage: { mode: FIREFLIES_MODE, lanes: results.length, transcripts_opened: 0, results },
489
+ }), json);
490
+ }
491
+ export async function runFirefliesDiscovery(root, args, deps = {}) {
492
+ const json = hasFlag(args, "--json");
493
+ const env = deps.env ?? process.env;
494
+ const resolved = {
495
+ fetchImpl: deps.fetchImpl ?? globalThis.fetch,
496
+ env,
497
+ now: deps.now ?? (() => new Date().toISOString()),
498
+ sleep: deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
499
+ transport: deps.transport ?? anthropicSynthesisTransport({ fetchImpl: deps.fetchImpl, env }),
500
+ };
501
+ const command = args[0];
502
+ const target = args[1] && !args[1].startsWith("--") ? args[1] : undefined;
503
+ try {
504
+ if (command === "ready")
505
+ return await readyCommand(root, json, resolved);
506
+ if (command === "list")
507
+ return await listCommand(root, target, json, resolved);
508
+ if (command === "process")
509
+ return await processCommand(root, target, json, resolved);
510
+ if (command === "sync")
511
+ return await syncCommand(root, json, resolved);
512
+ return invalid("Try: ready | list <study-id|inbox:scope-id> | process <study-id|inbox:scope-id> | sync", json);
513
+ }
514
+ catch {
515
+ return invalid("Fireflies discovery failed closed without exposing private source content.", json);
516
+ }
517
+ }
518
+ export const FIREFLIES_SUPPORT_STATE = "contract_only";
519
+ export const FIREFLIES_MODE = "local_worker";
520
+ export function firefliesScopeValidationError(scope) {
521
+ const start = Date.parse(scope.window?.start ?? "");
522
+ const end = Date.parse(scope.window?.end ?? "");
523
+ if (!Number.isFinite(start) || !Number.isFinite(end))
524
+ return "a fireflies scope needs a bounded time window with a valid start and end";
525
+ if (start > end)
526
+ return "a fireflies scope's time window must end after it starts";
527
+ if (scope.include_internal_only)
528
+ return "a fireflies scope cannot include internal-only meetings in V0: they are excluded by default (OQ-001 leaves the workspace-archive question open)";
529
+ const selectorKinds = [
530
+ ["named external participants", scope.external_names],
531
+ ["external companies or email domains", scope.external_domains],
532
+ ["title tokens", scope.title_tokens],
533
+ ];
534
+ const hasSelector = selectorKinds.some(([, list]) => Array.isArray(list) && list.some((entry) => typeof entry === "string" && entry.trim().length > 0));
535
+ if (!hasSelector) {
536
+ return "a fireflies scope needs at least one customer selector (a named external participant, an external company or email domain, or a title token); a time window alone is never enough";
537
+ }
538
+ return null;
539
+ }
540
+ // The inbox scope's generic fields map onto the Fireflies selector kinds; the
541
+ // study lane maps its own (required participants, target organization, title
542
+ // tokens) the same way gong's worker does. A scope whose provider is not
543
+ // fireflies never reaches validation.
544
+ export function firefliesSelectorsFromInboxScope(scope) {
545
+ return {
546
+ window: { start: scope.time_window?.start, end: scope.time_window?.end },
547
+ external_names: scope.participant_allowlist,
548
+ external_domains: scope.domain_allowlist,
549
+ title_tokens: scope.title_tokens,
550
+ };
551
+ }
@@ -0,0 +1,12 @@
1
+ import { type DiscoveryCommandOutcome } from "./discoveryCommand.js";
2
+ import { type SynthesisTransport } from "./discoverySynthesis.js";
3
+ import { type FetchLike } from "./gmailNango.js";
4
+ export type GmailDiscoveryDeps = {
5
+ fetchImpl?: FetchLike;
6
+ env?: NodeJS.ProcessEnv;
7
+ now?: () => string;
8
+ sleep?: (ms: number) => Promise<void>;
9
+ transport?: SynthesisTransport;
10
+ fetchTimeoutMs?: number;
11
+ };
12
+ export declare function runGmailDiscovery(root: string, args: string[], deps?: GmailDiscoveryDeps): Promise<DiscoveryCommandOutcome>;