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,860 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { closeSync, constants, existsSync, fchmodSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, unlinkSync, writeFileSync, } from "node:fs";
3
+ import path from "node:path";
4
+ import { isDeepStrictEqual } from "node:util";
5
+ import { canonicalBytes, parseBugCapsule, parseDeliveryReceipt, payloadDigest, } from "./bugCapsule.js";
6
+ const UUID_V7 = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
7
+ const SHA_256_DIGEST = /^sha256:[0-9a-f]{64}$/;
8
+ const RFC_3339_UTC = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?Z$/;
9
+ function capsuleId(value) {
10
+ if (!UUID_V7.test(value))
11
+ throw new Error("capsule ID must be a UUIDv7");
12
+ return value;
13
+ }
14
+ function timestamp(value, label) {
15
+ if (typeof value !== "string")
16
+ throw new Error(`${label} must be RFC 3339 UTC`);
17
+ const match = RFC_3339_UTC.exec(value);
18
+ const date = new Date(value);
19
+ if (match === null ||
20
+ Number.isNaN(date.valueOf()) ||
21
+ date.getUTCFullYear() !== Number(match[1]) ||
22
+ date.getUTCMonth() + 1 !== Number(match[2]) ||
23
+ date.getUTCDate() !== Number(match[3]) ||
24
+ date.getUTCHours() !== Number(match[4]) ||
25
+ date.getUTCMinutes() !== Number(match[5]) ||
26
+ date.getUTCSeconds() !== Number(match[6])) {
27
+ throw new Error(`${label} must be RFC 3339 UTC`);
28
+ }
29
+ return value;
30
+ }
31
+ function object(value, label) {
32
+ if (value === null || typeof value !== "object" || Array.isArray(value))
33
+ throw new Error(`${label} must be an object`);
34
+ return value;
35
+ }
36
+ function exactObject(value, label, keys) {
37
+ const parsed = object(value, label);
38
+ for (const key of Object.keys(parsed)) {
39
+ if (!keys.includes(key))
40
+ throw new Error(`unknown ${label} field: ${key}`);
41
+ }
42
+ for (const key of keys) {
43
+ if (!(key in parsed))
44
+ throw new Error(`${label} field is required: ${key}`);
45
+ }
46
+ return parsed;
47
+ }
48
+ function revalidateDirectoryPath(directory, opened) {
49
+ let current;
50
+ try {
51
+ current = lstatSync(directory);
52
+ }
53
+ catch (error) {
54
+ if (error.code === "ENOENT") {
55
+ throw new Error(`local report directory changed while in use: ${path.basename(directory)}`);
56
+ }
57
+ throw error;
58
+ }
59
+ if (current.isSymbolicLink())
60
+ throw new Error(`local report directory cannot be a symbolic link: ${path.basename(directory)}`);
61
+ if (!current.isDirectory())
62
+ throw new Error(`local report path is not a directory: ${path.basename(directory)}`);
63
+ if (current.dev !== opened.dev || current.ino !== opened.ino) {
64
+ throw new Error(`local report directory changed while in use: ${path.basename(directory)}`);
65
+ }
66
+ }
67
+ function pathEntryExists(entryPath) {
68
+ try {
69
+ lstatSync(entryPath);
70
+ return true;
71
+ }
72
+ catch (error) {
73
+ if (error.code === "ENOENT")
74
+ return false;
75
+ throw error;
76
+ }
77
+ }
78
+ function ensurePrivateDirectory(directory) {
79
+ let created = false;
80
+ try {
81
+ mkdirSync(directory, { mode: 0o700 });
82
+ created = true;
83
+ }
84
+ catch (error) {
85
+ if (error.code !== "EEXIST")
86
+ throw error;
87
+ }
88
+ let descriptor = null;
89
+ try {
90
+ descriptor = openSync(directory, constants.O_RDONLY | constants.O_NOFOLLOW);
91
+ const opened = fstatSync(descriptor);
92
+ if (!opened.isDirectory())
93
+ throw new Error(`local report path is not a directory: ${path.basename(directory)}`);
94
+ fchmodSync(descriptor, 0o700);
95
+ if (created)
96
+ fsyncSync(descriptor);
97
+ revalidateDirectoryPath(directory, opened);
98
+ }
99
+ catch (error) {
100
+ const code = error.code;
101
+ if (code === "ELOOP")
102
+ throw new Error(`local report directory cannot be a symbolic link: ${path.basename(directory)}`);
103
+ if (code === "ENOTDIR")
104
+ throw new Error(`local report path is not a directory: ${path.basename(directory)}`);
105
+ throw error;
106
+ }
107
+ finally {
108
+ if (descriptor !== null)
109
+ closeSync(descriptor);
110
+ }
111
+ if (created)
112
+ durableDirectory(path.dirname(directory));
113
+ }
114
+ function storeDirectory(root, kind, create) {
115
+ const base = path.resolve(root);
116
+ const directories = [path.join(base, ".foo"), path.join(base, ".foo", "reports"), path.join(base, ".foo", "reports", kind)];
117
+ for (const directory of directories) {
118
+ if (!pathEntryExists(directory) && !create)
119
+ return null;
120
+ ensurePrivateDirectory(directory);
121
+ }
122
+ return directories.at(-1);
123
+ }
124
+ function recordPath(root, kind, id, createDirectory) {
125
+ const directory = storeDirectory(root, kind, createDirectory);
126
+ return directory === null ? null : path.join(directory, `${capsuleId(id)}.json`);
127
+ }
128
+ function durableDirectory(directory) {
129
+ const descriptor = openSync(directory, constants.O_RDONLY | constants.O_NOFOLLOW);
130
+ try {
131
+ const opened = fstatSync(descriptor);
132
+ if (!opened.isDirectory())
133
+ throw new Error(`local report path is not a directory: ${path.basename(directory)}`);
134
+ fsyncSync(descriptor);
135
+ revalidateDirectoryPath(directory, opened);
136
+ }
137
+ finally {
138
+ closeSync(descriptor);
139
+ }
140
+ }
141
+ function publishOnce(file, value) {
142
+ ensurePrivateDirectory(path.dirname(file));
143
+ const temporary = `${file}.${process.pid}.${randomBytes(12).toString("hex")}.tmp`;
144
+ let descriptor = null;
145
+ let result = null;
146
+ try {
147
+ descriptor = openSync(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
148
+ fchmodSync(descriptor, 0o600);
149
+ writeFileSync(descriptor, `${JSON.stringify(value, null, 2)}\n`, "utf8");
150
+ fsyncSync(descriptor);
151
+ closeSync(descriptor);
152
+ descriptor = null;
153
+ try {
154
+ linkSync(temporary, file);
155
+ result = "created";
156
+ }
157
+ catch (error) {
158
+ if (error.code !== "EEXIST")
159
+ throw error;
160
+ result = "exists";
161
+ }
162
+ unlinkSync(temporary);
163
+ durableDirectory(path.dirname(file));
164
+ return result;
165
+ }
166
+ finally {
167
+ if (descriptor !== null)
168
+ closeSync(descriptor);
169
+ if (existsSync(temporary))
170
+ unlinkSync(temporary);
171
+ }
172
+ }
173
+ function pathExists(file) {
174
+ if (file === null)
175
+ return false;
176
+ return pathEntryExists(file);
177
+ }
178
+ function readJsonWithoutFollowingLinks(file) {
179
+ const pathStat = lstatSync(file);
180
+ if (pathStat.isSymbolicLink())
181
+ throw new Error(`local bug record cannot be a symbolic link: ${path.basename(file)}`);
182
+ if (!pathStat.isFile())
183
+ throw new Error(`local bug record is not a regular file: ${path.basename(file)}`);
184
+ const descriptor = openSync(file, constants.O_RDONLY | constants.O_NOFOLLOW);
185
+ try {
186
+ if (!fstatSync(descriptor).isFile())
187
+ throw new Error(`local bug record is not a regular file: ${path.basename(file)}`);
188
+ const serialized = readFileSync(descriptor, "utf8");
189
+ try {
190
+ return JSON.parse(serialized);
191
+ }
192
+ catch {
193
+ throw new Error(`corrupt local bug record: ${path.basename(file)}`);
194
+ }
195
+ }
196
+ finally {
197
+ closeSync(descriptor);
198
+ }
199
+ }
200
+ function parseLocalBugRecord(value, expectedId) {
201
+ const source = exactObject(value, "LocalBugRecord", ["schema_version", "capsule", "payload_digest", "state", "preview", "receipt"]);
202
+ if (source.schema_version !== "0.1")
203
+ throw new Error("LocalBugRecord.schema_version must be 0.1");
204
+ const capsule = parseBugCapsule(source.capsule);
205
+ if (expectedId !== undefined && capsule.capsule_id !== expectedId)
206
+ throw new Error("stored capsule ID mismatch");
207
+ if (typeof source.payload_digest !== "string" || !SHA_256_DIGEST.test(source.payload_digest)) {
208
+ throw new Error("LocalBugRecord.payload_digest must be a lowercase SHA-256 digest");
209
+ }
210
+ const computedDigest = payloadDigest(capsule);
211
+ if (source.payload_digest !== computedDigest)
212
+ throw new Error("stored payload digest mismatch");
213
+ if (source.state !== "pending" && source.state !== "accepted" && source.state !== "delete_retryable") {
214
+ throw new Error("LocalBugRecord.state is invalid");
215
+ }
216
+ let preview = null;
217
+ if (source.preview !== null) {
218
+ const loosePreview = object(source.preview, "LocalBugRecord.preview");
219
+ const parsed = exactObject(source.preview, "LocalBugRecord.preview", "destination" in loosePreview ? ["payload_digest", "destination", "previewed_at"] : ["payload_digest", "previewed_at"]);
220
+ if (parsed.payload_digest !== computedDigest)
221
+ throw new Error("stored preview digest mismatch");
222
+ if (parsed.destination !== undefined && (typeof parsed.destination !== "string" || parsed.destination.length === 0)) {
223
+ throw new Error("stored preview destination is invalid");
224
+ }
225
+ preview = {
226
+ payload_digest: parsed.payload_digest,
227
+ destination: typeof parsed.destination === "string" ? parsed.destination : null,
228
+ previewed_at: timestamp(parsed.previewed_at, "LocalBugRecord.preview.previewed_at"),
229
+ };
230
+ }
231
+ const receipt = source.receipt === null ? null : parseDeliveryReceipt(source.receipt);
232
+ if (source.state === "pending" && receipt !== null)
233
+ throw new Error("pending record cannot contain a receipt");
234
+ if (source.state !== "pending") {
235
+ if (receipt === null || receipt.status !== "accepted")
236
+ throw new Error(`${source.state} record requires an accepted receipt`);
237
+ if (receipt.capsule_id !== capsule.capsule_id)
238
+ throw new Error("stored receipt capsule ID mismatch");
239
+ if (receipt.payload_digest !== computedDigest)
240
+ throw new Error("stored receipt payload digest mismatch");
241
+ }
242
+ return {
243
+ schema_version: "0.1",
244
+ capsule,
245
+ payload_digest: computedDigest,
246
+ state: source.state,
247
+ preview,
248
+ receipt,
249
+ };
250
+ }
251
+ function readRecord(file, expectedId) {
252
+ const value = readJsonWithoutFollowingLinks(file);
253
+ try {
254
+ return parseLocalBugRecord(value, expectedId);
255
+ }
256
+ catch {
257
+ throw new Error(`corrupt local bug record: ${path.basename(file)}`);
258
+ }
259
+ }
260
+ function metadata(record) {
261
+ return {
262
+ capsule_id: record.capsule.capsule_id,
263
+ payload_digest: record.payload_digest,
264
+ state: record.state,
265
+ previewed_at: record.preview?.previewed_at ?? null,
266
+ receipt_id: record.receipt?.receipt_id ?? null,
267
+ };
268
+ }
269
+ export function getArchiveDeletionMarker(root, id, digest) {
270
+ capsuleId(id);
271
+ if (digest !== undefined && !SHA_256_DIGEST.test(digest)) {
272
+ throw new Error("payload digest must be a lowercase SHA-256 digest");
273
+ }
274
+ const file = recordPath(root, "remote-deleted", id, false);
275
+ if (!pathExists(file))
276
+ return null;
277
+ const confirmation = readRemoteDeletionConfirmation(file, id);
278
+ if (digest !== undefined && confirmation.payload_digest !== digest) {
279
+ throw new Error(`remote deletion digest mismatch: ${id}`);
280
+ }
281
+ return confirmation;
282
+ }
283
+ function readRemoteDeletionConfirmation(file, expectedId, expectedDigest) {
284
+ const value = readJsonWithoutFollowingLinks(file);
285
+ try {
286
+ const source = exactObject(value, "RemoteDeletionConfirmation", [
287
+ "schema_version", "kind", "capsule_id", "payload_digest", "receipt_id", "destination",
288
+ ]);
289
+ if (source.schema_version !== "0.1")
290
+ throw new Error("RemoteDeletionConfirmation.schema_version must be 0.1");
291
+ if (source.kind !== "remote_deletion_confirmed" && source.kind !== "local_archive_deleted")
292
+ throw new Error("RemoteDeletionConfirmation.kind is invalid");
293
+ if (source.capsule_id !== expectedId)
294
+ throw new Error("remote deletion capsule ID mismatch");
295
+ if (typeof source.payload_digest !== "string" || !SHA_256_DIGEST.test(source.payload_digest)) {
296
+ throw new Error("RemoteDeletionConfirmation.payload_digest must be a lowercase SHA-256 digest");
297
+ }
298
+ if (expectedDigest !== undefined && source.payload_digest !== expectedDigest) {
299
+ throw new Error(`remote deletion digest mismatch: ${expectedId}`);
300
+ }
301
+ if (typeof source.receipt_id !== "string" || source.receipt_id.length === 0) {
302
+ throw new Error("RemoteDeletionConfirmation.receipt_id must be a string");
303
+ }
304
+ if (typeof source.destination !== "string" || source.destination.length === 0) {
305
+ throw new Error("RemoteDeletionConfirmation.destination must be a string");
306
+ }
307
+ return source;
308
+ }
309
+ catch {
310
+ throw new Error(`corrupt local bug record: ${path.basename(file)}`);
311
+ }
312
+ }
313
+ function readPreviewMetadata(file, expectedId, expectedDigest) {
314
+ const value = readJsonWithoutFollowingLinks(file);
315
+ try {
316
+ const loose = object(value, "PreviewMetadata");
317
+ const source = exactObject(value, "PreviewMetadata", "destination" in loose
318
+ ? ["schema_version", "capsule_id", "payload_digest", "destination", "previewed_at"]
319
+ : ["schema_version", "capsule_id", "payload_digest", "previewed_at"]);
320
+ if (source.schema_version !== "0.1")
321
+ throw new Error("PreviewMetadata.schema_version must be 0.1");
322
+ if (source.capsule_id !== expectedId)
323
+ throw new Error("stored preview capsule ID mismatch");
324
+ if (source.payload_digest !== expectedDigest)
325
+ throw new Error("stored preview digest mismatch");
326
+ if (source.destination !== undefined && (typeof source.destination !== "string" || source.destination.length === 0)) {
327
+ throw new Error("stored preview destination is invalid");
328
+ }
329
+ return {
330
+ schema_version: "0.1",
331
+ capsule_id: expectedId,
332
+ payload_digest: expectedDigest,
333
+ destination: typeof source.destination === "string" ? source.destination : null,
334
+ previewed_at: timestamp(source.previewed_at, "PreviewMetadata.previewed_at"),
335
+ };
336
+ }
337
+ catch {
338
+ throw new Error(`corrupt local bug record: ${path.basename(file)}`);
339
+ }
340
+ }
341
+ function readTerminalDecision(file, expectedId, expectedDigest) {
342
+ const value = readJsonWithoutFollowingLinks(file);
343
+ try {
344
+ const loose = object(value, "TerminalDecision");
345
+ const keys = loose.kind === "accepting"
346
+ ? ["schema_version", "kind", "capsule_id", "payload_digest", "receipt"]
347
+ : ["schema_version", "kind", "capsule_id", "payload_digest"];
348
+ const source = exactObject(value, "TerminalDecision", keys);
349
+ if (source.schema_version !== "0.1")
350
+ throw new Error("TerminalDecision.schema_version must be 0.1");
351
+ if (source.kind !== "accepting" && source.kind !== "deleted")
352
+ throw new Error("TerminalDecision.kind is invalid");
353
+ if (source.capsule_id !== expectedId)
354
+ throw new Error("stored terminal capsule ID mismatch");
355
+ if (typeof source.payload_digest !== "string" || !SHA_256_DIGEST.test(source.payload_digest)) {
356
+ throw new Error("TerminalDecision.payload_digest must be a lowercase SHA-256 digest");
357
+ }
358
+ if (expectedDigest !== undefined && source.payload_digest !== expectedDigest) {
359
+ throw new Error("stored terminal digest mismatch");
360
+ }
361
+ if (source.kind === "accepting") {
362
+ const receipt = parseDeliveryReceipt(source.receipt);
363
+ if (receipt.status !== "accepted")
364
+ throw new Error("accepting decision requires an accepted receipt");
365
+ if (receipt.capsule_id !== expectedId)
366
+ throw new Error("terminal receipt capsule ID mismatch");
367
+ if (receipt.payload_digest !== source.payload_digest)
368
+ throw new Error("terminal receipt payload digest mismatch");
369
+ return {
370
+ schema_version: "0.1",
371
+ kind: "accepting",
372
+ capsule_id: expectedId,
373
+ payload_digest: source.payload_digest,
374
+ receipt,
375
+ };
376
+ }
377
+ return {
378
+ schema_version: "0.1",
379
+ kind: "deleted",
380
+ capsule_id: expectedId,
381
+ payload_digest: source.payload_digest,
382
+ };
383
+ }
384
+ catch {
385
+ throw new Error(`corrupt local bug record: ${path.basename(file)}`);
386
+ }
387
+ }
388
+ function pendingRecord(root, id) {
389
+ const file = recordPath(root, "pending", id, false);
390
+ if (!pathExists(file))
391
+ throw new Error(`pending capsule not found: ${capsuleId(id)}`);
392
+ const record = readRecord(file, id);
393
+ if (record.state !== "pending" || record.preview !== null || record.receipt !== null) {
394
+ throw new Error(`corrupt local bug record: ${path.basename(file)}`);
395
+ }
396
+ return { file, record };
397
+ }
398
+ function withPreview(root, record) {
399
+ const id = record.capsule.capsule_id;
400
+ const previewFile = recordPath(root, "previews", id, false);
401
+ if (!pathExists(previewFile))
402
+ return record;
403
+ const preview = readPreviewMetadata(previewFile, id, record.payload_digest);
404
+ return {
405
+ ...record,
406
+ preview: { payload_digest: preview.payload_digest, destination: preview.destination, previewed_at: preview.previewed_at },
407
+ };
408
+ }
409
+ function recoverableAcceptedRecord(root, decision) {
410
+ const { file, record } = pendingRecord(root, decision.capsule_id);
411
+ if (record.payload_digest !== decision.payload_digest) {
412
+ throw new Error(`corrupt local bug record: ${path.basename(file)}`);
413
+ }
414
+ return {
415
+ ...withPreview(root, record),
416
+ state: "accepted",
417
+ receipt: decision.receipt,
418
+ };
419
+ }
420
+ function unlinkDurably(file) {
421
+ try {
422
+ unlinkSync(file);
423
+ }
424
+ catch (error) {
425
+ if (error.code === "ENOENT")
426
+ return;
427
+ throw error;
428
+ }
429
+ durableDirectory(path.dirname(file));
430
+ }
431
+ function cleanupPending(root, id, digest) {
432
+ const pendingFile = recordPath(root, "pending", id, false);
433
+ if (!pathExists(pendingFile))
434
+ return;
435
+ try {
436
+ const pending = readRecord(pendingFile, id);
437
+ if (pending.state !== "pending" || pending.preview !== null || pending.receipt !== null || pending.payload_digest !== digest) {
438
+ throw new Error(`archive conflict for capsule: ${id}`);
439
+ }
440
+ unlinkDurably(pendingFile);
441
+ }
442
+ catch (error) {
443
+ if (error.code === "ENOENT")
444
+ return;
445
+ throw error;
446
+ }
447
+ }
448
+ function cleanupPreview(root, id, digest) {
449
+ const previewFile = recordPath(root, "previews", id, false);
450
+ if (!pathExists(previewFile))
451
+ return;
452
+ try {
453
+ readPreviewMetadata(previewFile, id, digest);
454
+ unlinkDurably(previewFile);
455
+ }
456
+ catch (error) {
457
+ if (error.code === "ENOENT")
458
+ return;
459
+ throw error;
460
+ }
461
+ }
462
+ function acceptedRecord(root, id) {
463
+ const archiveFile = recordPath(root, "archive", id, false);
464
+ if (!pathExists(archiveFile))
465
+ return null;
466
+ const record = readRecord(archiveFile, id);
467
+ if (record.state !== "accepted")
468
+ throw new Error(`corrupt local bug record: ${path.basename(archiveFile)}`);
469
+ return record;
470
+ }
471
+ export function savePending(root, capsule) {
472
+ const parsed = parseBugCapsule(capsule);
473
+ const stableCapsule = parseBugCapsule(JSON.parse(canonicalBytes(parsed).toString("utf8")));
474
+ const id = stableCapsule.capsule_id;
475
+ const record = {
476
+ schema_version: "0.1",
477
+ capsule: stableCapsule,
478
+ payload_digest: payloadDigest(stableCapsule),
479
+ state: "pending",
480
+ preview: null,
481
+ receipt: null,
482
+ };
483
+ const pendingFile = recordPath(root, "pending", id, true);
484
+ const archiveFile = recordPath(root, "archive", id, false);
485
+ const deletedFile = recordPath(root, "deleted", id, false);
486
+ const remoteDeletedFile = recordPath(root, "remote-deleted", id, false);
487
+ if (pathExists(pendingFile) || pathExists(archiveFile) || pathExists(deletedFile) || pathExists(remoteDeletedFile)) {
488
+ throw new Error(`capsule already exists: ${id}`);
489
+ }
490
+ if (publishOnce(pendingFile, record) === "exists")
491
+ throw new Error(`capsule already exists: ${id}`);
492
+ const concurrentArchive = recordPath(root, "archive", id, false);
493
+ const concurrentDecision = recordPath(root, "deleted", id, false);
494
+ const concurrentRemoteDeletion = recordPath(root, "remote-deleted", id, false);
495
+ if (pathExists(concurrentArchive) || pathExists(concurrentDecision) || pathExists(concurrentRemoteDeletion)) {
496
+ const archive = pathExists(concurrentArchive) ? acceptedRecord(root, id) : null;
497
+ const decision = pathExists(concurrentDecision) ? readTerminalDecision(concurrentDecision, id) : null;
498
+ const remoteDeletion = pathExists(concurrentRemoteDeletion)
499
+ ? readRemoteDeletionConfirmation(concurrentRemoteDeletion, id, record.payload_digest)
500
+ : null;
501
+ if (archive !== null || decision?.kind === "deleted" || remoteDeletion !== null)
502
+ cleanupPending(root, id, record.payload_digest);
503
+ throw new Error(`capsule already exists: ${id}`);
504
+ }
505
+ return record;
506
+ }
507
+ export function listLocalBugRecords(root) {
508
+ const records = new Map();
509
+ const corrupt = [];
510
+ const archiveIds = new Set();
511
+ const terminalIds = new Set();
512
+ const decisions = new Map();
513
+ const resolvedAccepting = new Set();
514
+ const remoteDeletedIds = new Set();
515
+ const remoteDeletedDirectory = storeDirectory(root, "remote-deleted", false);
516
+ if (remoteDeletedDirectory !== null) {
517
+ for (const entry of readdirSync(remoteDeletedDirectory, { withFileTypes: true })) {
518
+ if (!entry.name.endsWith(".json"))
519
+ continue;
520
+ const id = entry.name.slice(0, -".json".length);
521
+ if (!UUID_V7.test(id)) {
522
+ corrupt.push(entry.name);
523
+ continue;
524
+ }
525
+ remoteDeletedIds.add(id);
526
+ if (!entry.isFile()) {
527
+ corrupt.push(entry.name);
528
+ continue;
529
+ }
530
+ try {
531
+ readRemoteDeletionConfirmation(path.join(remoteDeletedDirectory, entry.name), id);
532
+ }
533
+ catch {
534
+ corrupt.push(entry.name);
535
+ }
536
+ }
537
+ }
538
+ const archiveDirectory = storeDirectory(root, "archive", false);
539
+ if (archiveDirectory !== null) {
540
+ for (const entry of readdirSync(archiveDirectory, { withFileTypes: true })) {
541
+ if (!entry.name.endsWith(".json"))
542
+ continue;
543
+ const id = entry.name.slice(0, -".json".length);
544
+ if (!UUID_V7.test(id)) {
545
+ corrupt.push(entry.name);
546
+ continue;
547
+ }
548
+ archiveIds.add(id);
549
+ if (remoteDeletedIds.has(id))
550
+ continue;
551
+ if (!entry.isFile()) {
552
+ corrupt.push(entry.name);
553
+ continue;
554
+ }
555
+ try {
556
+ const archived = readRecord(path.join(archiveDirectory, entry.name), id);
557
+ if (archived.state !== "accepted")
558
+ throw new Error("archive record is not accepted");
559
+ records.set(id, metadata(archived));
560
+ }
561
+ catch {
562
+ corrupt.push(entry.name);
563
+ }
564
+ }
565
+ }
566
+ const decisionDirectory = storeDirectory(root, "deleted", false);
567
+ if (decisionDirectory !== null) {
568
+ for (const entry of readdirSync(decisionDirectory, { withFileTypes: true })) {
569
+ if (!entry.name.endsWith(".json"))
570
+ continue;
571
+ const id = entry.name.slice(0, -".json".length);
572
+ if (!UUID_V7.test(id)) {
573
+ corrupt.push(entry.name);
574
+ continue;
575
+ }
576
+ if (archiveIds.has(id))
577
+ continue;
578
+ if (remoteDeletedIds.has(id))
579
+ continue;
580
+ terminalIds.add(id);
581
+ if (!entry.isFile()) {
582
+ corrupt.push(entry.name);
583
+ continue;
584
+ }
585
+ try {
586
+ decisions.set(id, readTerminalDecision(path.join(decisionDirectory, entry.name), id));
587
+ }
588
+ catch {
589
+ corrupt.push(entry.name);
590
+ }
591
+ }
592
+ }
593
+ const pendingDirectory = storeDirectory(root, "pending", false);
594
+ if (pendingDirectory !== null) {
595
+ for (const entry of readdirSync(pendingDirectory, { withFileTypes: true })) {
596
+ if (!entry.name.endsWith(".json"))
597
+ continue;
598
+ const id = entry.name.slice(0, -".json".length);
599
+ if (!UUID_V7.test(id)) {
600
+ corrupt.push(entry.name);
601
+ continue;
602
+ }
603
+ if (archiveIds.has(id))
604
+ continue;
605
+ if (remoteDeletedIds.has(id))
606
+ continue;
607
+ const decision = decisions.get(id);
608
+ if (decision?.kind === "deleted" || (decision === undefined && terminalIds.has(id)))
609
+ continue;
610
+ if (!entry.isFile()) {
611
+ corrupt.push(entry.name);
612
+ if (decision?.kind === "accepting")
613
+ resolvedAccepting.add(id);
614
+ continue;
615
+ }
616
+ try {
617
+ const record = decision?.kind === "accepting"
618
+ ? recoverableAcceptedRecord(root, decision)
619
+ : withPreview(root, pendingRecord(root, id).record);
620
+ records.set(id, metadata(record));
621
+ }
622
+ catch {
623
+ corrupt.push(entry.name);
624
+ }
625
+ if (decision?.kind === "accepting")
626
+ resolvedAccepting.add(id);
627
+ }
628
+ }
629
+ for (const [id, decision] of decisions) {
630
+ if (decision.kind === "accepting" && !resolvedAccepting.has(id))
631
+ corrupt.push(`${id}.json`);
632
+ }
633
+ const orderedRecords = [...records.values()].sort((left, right) => left.capsule_id.localeCompare(right.capsule_id));
634
+ corrupt.sort();
635
+ return { records: orderedRecords, corrupt };
636
+ }
637
+ export function getLocalBugRecord(root, id) {
638
+ capsuleId(id);
639
+ const remoteDeletedFile = recordPath(root, "remote-deleted", id, false);
640
+ if (pathExists(remoteDeletedFile)) {
641
+ readRemoteDeletionConfirmation(remoteDeletedFile, id);
642
+ return null;
643
+ }
644
+ const archived = acceptedRecord(root, id);
645
+ if (archived !== null)
646
+ return archived;
647
+ const decisionFile = recordPath(root, "deleted", id, false);
648
+ if (pathExists(decisionFile)) {
649
+ const decision = readTerminalDecision(decisionFile, id);
650
+ return decision.kind === "deleted" ? null : recoverableAcceptedRecord(root, decision);
651
+ }
652
+ const pendingFile = recordPath(root, "pending", id, false);
653
+ if (pathExists(pendingFile))
654
+ return withPreview(root, pendingRecord(root, id).record);
655
+ return null;
656
+ }
657
+ export function markPreviewed(root, id, digest, destination, previewedAt) {
658
+ const previewTime = timestamp(previewedAt, "previewed_at");
659
+ if (!destination.trim())
660
+ throw new Error("preview destination must be a non-empty string");
661
+ const remoteDeletedFile = recordPath(root, "remote-deleted", id, false);
662
+ if (pathExists(remoteDeletedFile)) {
663
+ readRemoteDeletionConfirmation(remoteDeletedFile, id, digest);
664
+ throw new Error(`cannot preview deleted capsule: ${id}`);
665
+ }
666
+ const archived = acceptedRecord(root, id);
667
+ if (archived !== null)
668
+ throw new Error(`cannot preview accepted capsule: ${id}`);
669
+ const decisionFile = recordPath(root, "deleted", id, false);
670
+ if (pathExists(decisionFile)) {
671
+ const decision = readTerminalDecision(decisionFile, id);
672
+ throw new Error(`cannot preview ${decision.kind === "accepting" ? "accepted" : "deleted"} capsule: ${id}`);
673
+ }
674
+ const { record } = pendingRecord(root, id);
675
+ const currentDigest = payloadDigest(record.capsule);
676
+ if (digest !== currentDigest)
677
+ throw new Error(`capsule digest changed: ${id}`);
678
+ const previewFile = recordPath(root, "previews", id, true);
679
+ const sidecar = {
680
+ schema_version: "0.1",
681
+ capsule_id: id,
682
+ payload_digest: currentDigest,
683
+ destination,
684
+ previewed_at: previewTime,
685
+ };
686
+ if (pathExists(previewFile)) {
687
+ const existing = readPreviewMetadata(previewFile, id, currentDigest);
688
+ if (existing.destination !== destination)
689
+ unlinkDurably(previewFile);
690
+ }
691
+ const preview = publishOnce(previewFile, sidecar) === "created"
692
+ ? sidecar
693
+ : readPreviewMetadata(previewFile, id, currentDigest);
694
+ if (preview.destination !== destination)
695
+ throw new Error(`stored preview destination mismatch: ${id}`);
696
+ return {
697
+ ...record,
698
+ preview: { payload_digest: preview.payload_digest, destination: preview.destination, previewed_at: preview.previewed_at },
699
+ };
700
+ }
701
+ export function archiveAccepted(root, id, receipt) {
702
+ const parsedReceipt = parseDeliveryReceipt(receipt);
703
+ const remoteDeletedFile = recordPath(root, "remote-deleted", id, false);
704
+ if (pathExists(remoteDeletedFile)) {
705
+ readRemoteDeletionConfirmation(remoteDeletedFile, id, parsedReceipt.payload_digest);
706
+ throw new Error(`capsule was deleted: ${id}`);
707
+ }
708
+ const existing = acceptedRecord(root, id);
709
+ if (existing !== null) {
710
+ if (!isDeepStrictEqual(existing.receipt, parsedReceipt))
711
+ throw new Error(`archive conflict for capsule: ${id}`);
712
+ cleanupPending(root, id, existing.payload_digest);
713
+ cleanupPreview(root, id, existing.payload_digest);
714
+ return existing;
715
+ }
716
+ const decisionPath = recordPath(root, "deleted", id, false);
717
+ let decision;
718
+ if (pathExists(decisionPath)) {
719
+ decision = readTerminalDecision(decisionPath, id);
720
+ }
721
+ else {
722
+ const { record } = pendingRecord(root, id);
723
+ if (parsedReceipt.status !== "accepted")
724
+ throw new Error(`receipt was not accepted: ${id}`);
725
+ if (parsedReceipt.capsule_id !== id)
726
+ throw new Error(`receipt capsule ID mismatch: ${id}`);
727
+ if (parsedReceipt.payload_digest !== record.payload_digest)
728
+ throw new Error(`receipt payload digest mismatch: ${id}`);
729
+ const proposed = {
730
+ schema_version: "0.1",
731
+ kind: "accepting",
732
+ capsule_id: id,
733
+ payload_digest: record.payload_digest,
734
+ receipt: parsedReceipt,
735
+ };
736
+ const createdDecisionPath = recordPath(root, "deleted", id, true);
737
+ decision = publishOnce(createdDecisionPath, proposed) === "created"
738
+ ? proposed
739
+ : readTerminalDecision(createdDecisionPath, id);
740
+ }
741
+ if (decision.kind === "deleted")
742
+ throw new Error(`capsule was deleted: ${id}`);
743
+ if (!isDeepStrictEqual(decision.receipt, parsedReceipt))
744
+ throw new Error(`archive conflict for capsule: ${id}`);
745
+ const concurrentArchive = acceptedRecord(root, id);
746
+ if (concurrentArchive !== null) {
747
+ if (!isDeepStrictEqual(concurrentArchive.receipt, parsedReceipt))
748
+ throw new Error(`archive conflict for capsule: ${id}`);
749
+ cleanupPending(root, id, concurrentArchive.payload_digest);
750
+ cleanupPreview(root, id, concurrentArchive.payload_digest);
751
+ return concurrentArchive;
752
+ }
753
+ const archived = recoverableAcceptedRecord(root, decision);
754
+ const archiveFile = recordPath(root, "archive", id, true);
755
+ const result = publishOnce(archiveFile, archived);
756
+ const winner = result === "created" ? archived : readRecord(archiveFile, id);
757
+ if (winner.state !== "accepted" || !isDeepStrictEqual(winner.receipt, parsedReceipt)) {
758
+ throw new Error(`archive conflict for capsule: ${id}`);
759
+ }
760
+ cleanupPending(root, id, winner.payload_digest);
761
+ cleanupPreview(root, id, winner.payload_digest);
762
+ return winner;
763
+ }
764
+ export function deletePending(root, id) {
765
+ capsuleId(id);
766
+ if (acceptedRecord(root, id) !== null)
767
+ throw new Error(`cannot delete accepted capsule: ${id}`);
768
+ const decisionPath = recordPath(root, "deleted", id, false);
769
+ let decision;
770
+ if (pathExists(decisionPath)) {
771
+ decision = readTerminalDecision(decisionPath, id);
772
+ }
773
+ else {
774
+ const { record } = pendingRecord(root, id);
775
+ const proposed = {
776
+ schema_version: "0.1",
777
+ kind: "deleted",
778
+ capsule_id: id,
779
+ payload_digest: record.payload_digest,
780
+ };
781
+ const createdDecisionPath = recordPath(root, "deleted", id, true);
782
+ decision = publishOnce(createdDecisionPath, proposed) === "created"
783
+ ? proposed
784
+ : readTerminalDecision(createdDecisionPath, id, record.payload_digest);
785
+ }
786
+ if (decision.kind === "accepting")
787
+ throw new Error(`cannot delete accepted capsule: ${id}`);
788
+ if (acceptedRecord(root, id) !== null) {
789
+ throw new Error(`cannot delete accepted capsule: ${id}`);
790
+ }
791
+ cleanupPending(root, id, decision.payload_digest);
792
+ cleanupPreview(root, id, decision.payload_digest);
793
+ if (acceptedRecord(root, id) !== null)
794
+ throw new Error(`cannot delete accepted capsule: ${id}`);
795
+ }
796
+ export function getRemoteDeletionConfirmation(root, id, digest) {
797
+ const marker = getArchiveDeletionMarker(root, id, digest);
798
+ return marker?.kind === "remote_deletion_confirmed" ? marker : null;
799
+ }
800
+ /** Compatibility for legacy local-store consumers that already hold server confirmation. */
801
+ export function confirmRemoteDeletion(root, id, digest) {
802
+ deleteArchive(root, id, digest, "remote_deletion_confirmed");
803
+ }
804
+ /** Remove an archived report locally, recording no claim about its historical destination. */
805
+ export function deleteLocalArchive(root, id, digest) {
806
+ deleteArchive(root, id, digest, "local_archive_deleted");
807
+ }
808
+ /** Publish a metadata-only deletion marker before cleanup; exact replay finishes after a crash. */
809
+ function deleteArchive(root, id, digest, kind) {
810
+ capsuleId(id);
811
+ if (!SHA_256_DIGEST.test(digest))
812
+ throw new Error("payload digest must be a lowercase SHA-256 digest");
813
+ const markerFile = recordPath(root, "remote-deleted", id, false);
814
+ let marker;
815
+ if (pathExists(markerFile)) {
816
+ marker = readRemoteDeletionConfirmation(markerFile, id, digest);
817
+ }
818
+ else {
819
+ const accepted = acceptedRecord(root, id) ?? (() => {
820
+ const decisionFile = recordPath(root, "deleted", id, false);
821
+ if (!pathExists(decisionFile))
822
+ return null;
823
+ const decision = readTerminalDecision(decisionFile, id, digest);
824
+ return decision.kind === "accepting" ? recoverableAcceptedRecord(root, decision) : null;
825
+ })();
826
+ if (accepted === null || accepted.state !== "accepted" || accepted.receipt === null) {
827
+ throw new Error(`accepted capsule not found: ${id}`);
828
+ }
829
+ if (accepted.payload_digest !== digest)
830
+ throw new Error(`remote deletion digest mismatch: ${id}`);
831
+ marker = {
832
+ schema_version: "0.1",
833
+ kind,
834
+ capsule_id: id,
835
+ payload_digest: digest,
836
+ receipt_id: accepted.receipt.receipt_id,
837
+ destination: accepted.receipt.destination,
838
+ };
839
+ const createdMarkerFile = recordPath(root, "remote-deleted", id, true);
840
+ marker = publishOnce(createdMarkerFile, marker) === "created"
841
+ ? marker
842
+ : readRemoteDeletionConfirmation(createdMarkerFile, id, digest);
843
+ }
844
+ const archiveFile = recordPath(root, "archive", id, false);
845
+ if (pathExists(archiveFile)) {
846
+ const archived = readRecord(archiveFile, id);
847
+ if (archived.state !== "accepted" || archived.payload_digest !== marker.payload_digest) {
848
+ throw new Error(`archive conflict for capsule: ${id}`);
849
+ }
850
+ unlinkDurably(archiveFile);
851
+ }
852
+ cleanupPending(root, id, marker.payload_digest);
853
+ cleanupPreview(root, id, marker.payload_digest);
854
+ const decisionFile = recordPath(root, "deleted", id, false);
855
+ if (pathExists(decisionFile)) {
856
+ const decision = readTerminalDecision(decisionFile, id, marker.payload_digest);
857
+ if (decision.kind === "accepting")
858
+ unlinkDurably(decisionFile);
859
+ }
860
+ }