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,509 @@
1
+ // Prompt Redaction Safety V0 — T-001 (R-001): the deterministic secrets
2
+ // detection engine. Pure text-in / findings-out, offline, no I/O — the CLI owns
3
+ // file reading and directory walking so this module stays fixture-testable
4
+ // (same purity discipline as sourceIngress.ts).
5
+ //
6
+ // R-007 hardening: a finding is METADATA-ONLY. A detected secret value is never
7
+ // stored in a finding, returned, or logged — only its type, location, and
8
+ // length. Detection is in-memory; the value is matched and discarded.
9
+ //
10
+ // Recall bias (R-001): provider patterns catch known credentials at high
11
+ // confidence; an entropy backstop catches unknown-format secrets at low
12
+ // confidence (advisory). Precision is recovered via a human-curated allowlist
13
+ // (R-007: never auto-populated), not by raising thresholds.
14
+ const CONFIDENCE_RANK = { high: 3, medium: 2, low: 1 };
15
+ // Order is fixed for deterministic output. Provider patterns first (high
16
+ // confidence), then credentialed connection strings, then keyed assignments.
17
+ const SECRET_DETECTORS = [
18
+ {
19
+ detector: "aws_access_key_id",
20
+ category: "cloud_credential",
21
+ confidence: "high",
22
+ regex: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|ANPA|ANVA)[A-Z0-9]{16}\b/g,
23
+ },
24
+ {
25
+ detector: "github_token",
26
+ category: "vcs_credential",
27
+ confidence: "high",
28
+ regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36}\b/g,
29
+ },
30
+ {
31
+ detector: "github_pat",
32
+ category: "vcs_credential",
33
+ confidence: "high",
34
+ regex: /\bgithub_pat_[A-Za-z0-9_]{59,}\b/g,
35
+ },
36
+ {
37
+ detector: "slack_token",
38
+ category: "saas_credential",
39
+ confidence: "high",
40
+ regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
41
+ },
42
+ {
43
+ detector: "stripe_secret_key",
44
+ category: "saas_credential",
45
+ confidence: "high",
46
+ regex: /\b(?:sk|rk)_live_[A-Za-z0-9]{16,}\b/g,
47
+ },
48
+ {
49
+ detector: "stripe_test_key",
50
+ category: "saas_credential",
51
+ confidence: "medium",
52
+ regex: /\b(?:sk|rk)_test_[A-Za-z0-9]{16,}\b/g,
53
+ },
54
+ {
55
+ detector: "gcp_api_key",
56
+ category: "cloud_credential",
57
+ confidence: "high",
58
+ regex: /\bAIza[0-9A-Za-z_-]{35}\b/g,
59
+ },
60
+ {
61
+ detector: "openai_key",
62
+ category: "saas_credential",
63
+ confidence: "medium",
64
+ regex: /\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\b/g,
65
+ },
66
+ {
67
+ detector: "anthropic_key",
68
+ category: "saas_credential",
69
+ confidence: "high",
70
+ // productfoo's OWN provider — must not be a blind spot. sk-ant-<kind><n>-<long body>.
71
+ regex: /\bsk-ant-[a-z]+\d*-[A-Za-z0-9_-]{24,}\b/g,
72
+ },
73
+ {
74
+ detector: "azure_storage_key",
75
+ category: "cloud_credential",
76
+ confidence: "high",
77
+ // The AccountKey of an Azure Storage connection string (base64, >= 80 chars).
78
+ regex: /AccountKey=[A-Za-z0-9+/=]{80,}/g,
79
+ },
80
+ {
81
+ detector: "gcp_service_account",
82
+ category: "cloud_credential",
83
+ confidence: "medium",
84
+ // Marker for a GCP service-account key file; the embedded private key is caught
85
+ // at high confidence by private_key_block (which also covers OPENSSH SSH keys).
86
+ regex: /"type"\s*:\s*"service_account"/g,
87
+ },
88
+ {
89
+ detector: "private_key_block",
90
+ category: "private_key",
91
+ confidence: "high",
92
+ regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----/g,
93
+ },
94
+ {
95
+ detector: "jwt",
96
+ category: "token",
97
+ confidence: "medium",
98
+ regex: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
99
+ },
100
+ {
101
+ detector: "db_connection_string",
102
+ category: "connection_string",
103
+ confidence: "high",
104
+ // Only flags when credentials are embedded (user:password@host).
105
+ regex: /\b(?:postgres|postgresql|mysql|mongodb(?:\+srv)?|redis|amqps?):\/\/[^\s:@/]+:[^\s:@/]+@[^\s/]+/g,
106
+ },
107
+ {
108
+ detector: "secret_assignment",
109
+ category: "keyed_secret",
110
+ confidence: "high",
111
+ // KEY=VALUE / KEY: VALUE where the key name signals a secret. The whole
112
+ // match is treated as the span; refine() reads the value for confidence.
113
+ regex: /\b[\w.-]*(?:secret|token|password|passwd|pwd|api[_-]?key|access[_-]?key|client[_-]?secret|auth[_-]?token|credential)[\w.-]*\s*[:=]\s*["']?([^"'\s#]{4,})["']?/gi,
114
+ refine: (value) => (isPlaceholder(value) || !looksLikeSecretValue(value) ? null : "high"),
115
+ },
116
+ ];
117
+ // Obvious non-secrets in assignment values: template/placeholder text.
118
+ function isPlaceholder(value) {
119
+ return (/^[*x.\-_]+$/i.test(value) ||
120
+ /^(changeme|change_me|placeholder|your[_-]?\w+|example|examplekey|todo|none|null|undefined|test|dummy|redacted)$/i.test(value) ||
121
+ /^<.+>$/.test(value) ||
122
+ /^\$\{?\w+\}?$/.test(value) // ${ENV_VAR} / $VAR interpolation
123
+ );
124
+ }
125
+ // Per-character Shannon entropy (bits/char) over the token.
126
+ export function shannonEntropy(value) {
127
+ if (value.length === 0)
128
+ return 0;
129
+ const freq = new Map();
130
+ for (const ch of value)
131
+ freq.set(ch, (freq.get(ch) ?? 0) + 1);
132
+ let entropy = 0;
133
+ for (const count of freq.values()) {
134
+ const p = count / value.length;
135
+ entropy -= p * Math.log2(p);
136
+ }
137
+ return entropy;
138
+ }
139
+ // Distinguish a literal secret value from a code reference. Real secrets are
140
+ // long, high-entropy, and contain a digit; identifiers, member access, and
141
+ // calls are rejected. The gate blocks high-confidence findings only, so the
142
+ // keyed-assignment detector must be precise — this kills source-code FPs like
143
+ // `authToken = req.headers` or `tokensPerCall = estimate`.
144
+ function looksLikeSecretValue(value) {
145
+ if (/[.()[\]{}<>]/.test(value))
146
+ return false; // code: a.b.c, getToken(, arr[1], <T>, {x}
147
+ if (!/[0-9]/.test(value) || !/[A-Za-z]/.test(value))
148
+ return false; // need mixed alnum
149
+ return value.length >= 12 && shannonEntropy(value) >= 3.4;
150
+ }
151
+ // Charset-aware entropy thresholds (detect-secrets defaults: 4.5 base64 / 3.0
152
+ // hex). Pure-numeric runs are treated as IDs, not secrets (false-positive cut).
153
+ function entropyFinding(token, minLen) {
154
+ if (token.length < minLen)
155
+ return { ok: false, entropy: 0 };
156
+ if (/^[0-9]+$/.test(token))
157
+ return { ok: false, entropy: 0 };
158
+ const entropy = shannonEntropy(token);
159
+ if (/^[A-Fa-f0-9]+$/.test(token))
160
+ return { ok: entropy >= 3.0, entropy };
161
+ if (/^[A-Za-z0-9+/=_-]+$/.test(token))
162
+ return { ok: entropy >= 4.5, entropy };
163
+ return { ok: false, entropy };
164
+ }
165
+ function lineStarts(text) {
166
+ const starts = [0];
167
+ for (let i = 0; i < text.length; i++) {
168
+ if (text[i] === "\n")
169
+ starts.push(i + 1);
170
+ }
171
+ return starts;
172
+ }
173
+ // Map a 0-based string offset to a 1-based line/column.
174
+ function locate(starts, offset) {
175
+ // Binary search for the last line start <= offset.
176
+ let lo = 0;
177
+ let hi = starts.length - 1;
178
+ while (lo < hi) {
179
+ const mid = (lo + hi + 1) >> 1;
180
+ if (starts[mid] <= offset)
181
+ lo = mid;
182
+ else
183
+ hi = mid - 1;
184
+ }
185
+ return { line: lo + 1, column: offset - starts[lo] + 1 };
186
+ }
187
+ function isAllowed(value, opts) {
188
+ if (opts.allowlist?.some((entry) => entry === value || value.includes(entry)))
189
+ return true;
190
+ if (opts.allowlistPatterns?.some((re) => re.test(value)))
191
+ return true;
192
+ return false;
193
+ }
194
+ // --- Detection profiles (T-005 / R-006) --------------------------------------
195
+ // A profile bundles pattern detectors and whether the entropy backstop runs.
196
+ // Adding coverage — internal infra, PII, sales-transcript — is a registry entry
197
+ // here, NOT an engine change. v1 runs only "secrets" by default.
198
+ // Opt-in seed profile: internal-infrastructure identifiers. Not active by
199
+ // default; medium confidence so it never gates. Exists to demonstrate that a
200
+ // new profile is purely a registry entry (and to seed the future infra pack).
201
+ const INTERNAL_DETECTORS = [
202
+ {
203
+ detector: "private_ip",
204
+ category: "internal_infra",
205
+ confidence: "medium",
206
+ regex: /\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3})\b/g,
207
+ },
208
+ {
209
+ detector: "internal_hostname",
210
+ category: "internal_infra",
211
+ confidence: "medium",
212
+ regex: /\b[a-z0-9][a-z0-9-]*\.(?:internal|local|corp|intranet)\b/gi,
213
+ },
214
+ ];
215
+ // --- The `pii` profile (feedback_triage_v0 T-001 / RISK-001) -----------------
216
+ // A feedback document is raw private material about a named person, and the
217
+ // engine could not touch it: `secrets` strips API keys, not a person's name,
218
+ // home path, employer, or machine. This profile is the prerequisite that lets
219
+ // feedback content be tracked at all.
220
+ //
221
+ // WHAT IT CAN AND CANNOT DO — read this before trusting it:
222
+ //
223
+ // Reliable (high confidence, blocks): email addresses, home directory paths,
224
+ // MAC addresses, and identity/employer fields that are LABELLED
225
+ // ("Reporter: Jane Doe", "Employer: Acme Corp"). These have a syntax, so a
226
+ // deterministic matcher can be precise about them.
227
+ //
228
+ // Best-effort (medium confidence, redacted at the default threshold but never
229
+ // blocking): device names, `.local` hostnames, install UUIDs, and employer
230
+ // mentions in prose ("works at Acme Corp").
231
+ //
232
+ // NOT DETECTED AT ALL: an arbitrary personal name in free prose. "I asked
233
+ // Jordan and she agreed" is indistinguishable, by regex, from any other
234
+ // capitalised word, and every generic name-matcher is either a name list
235
+ // (fails on the name you have not listed) or a capitalised-token heuristic
236
+ // that fires on every sentence start and product noun. Shipping either would
237
+ // be the failure this repo keeps re-learning: a gate that fires on the shape
238
+ // of a risk rather than the risk is one people override on sight. So this
239
+ // profile catches names where they carry a marker — a field label, an email
240
+ // local part, a home-path segment — and states plainly that free-prose names
241
+ // are the caller's job (pass them via `allowlist`'s inverse: redact them
242
+ // before handing text to the engine, or review the document).
243
+ //
244
+ // Consequence for callers: passing this profile is NOT proof a document is
245
+ // anonymous. It is proof the document carries no *machine-detectable* personal
246
+ // identifier. `foo self check` blocks on that, and says exactly that.
247
+ // Shared account names on shared machines — a home path under one of these
248
+ // identifies a CI runner, not a person. Precision, not a threshold change.
249
+ const IMPERSONAL_ACCOUNTS = new Set([
250
+ "runner", "root", "admin", "administrator", "ubuntu", "ec2-user", "user", "username", "you", "vagrant", "docker", "node", "shared", "public", "default",
251
+ ]);
252
+ // The trailing path segment of a home directory: /Users/<account>, /home/<account>.
253
+ function homeAccountOf(value) {
254
+ const parts = value.split(/[\\/]/);
255
+ return (parts[parts.length - 1] ?? "").toLowerCase();
256
+ }
257
+ // A person's or company's name as written in a labelled field: one to four
258
+ // capitalised words. Deliberately strict — the label already carries the
259
+ // evidence, so the value only has to be name-SHAPED, not name-guessed.
260
+ const PROPER_NOUN = /^[A-Z][A-Za-z'’.-]*(?: (?:&|of|and|de|van|von|der|la|[A-Z][A-Za-z'’.,-]*)){0,3}$/;
261
+ function looksLikeProperNoun(value) {
262
+ const trimmed = value.trim().replace(/[.,;]$/, "");
263
+ if (isPlaceholder(trimmed))
264
+ return false;
265
+ return trimmed.length >= 2 && PROPER_NOUN.test(trimmed);
266
+ }
267
+ const PII_DETECTORS = [
268
+ {
269
+ detector: "pii_email",
270
+ category: "personal_contact",
271
+ confidence: "high",
272
+ regex: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)*\.[A-Za-z]{2,}\b/g,
273
+ },
274
+ {
275
+ detector: "pii_home_path",
276
+ category: "personal_path",
277
+ confidence: "high",
278
+ // The whole `/Users/<account>` prefix is the span; the rest of the path is
279
+ // left intact so the document still reads. Group 1 == whole match so that
280
+ // refine() can inspect the account without changing what gets replaced.
281
+ regex: /((?:\/Users\/|\/home\/|\\Users\\|C:\\Users\\)[A-Za-z0-9._-]+)/g,
282
+ refine: (value) => (IMPERSONAL_ACCOUNTS.has(homeAccountOf(value)) ? null : "high"),
283
+ },
284
+ {
285
+ detector: "pii_identity_field",
286
+ category: "personal_identity",
287
+ confidence: "high",
288
+ // A LABELLED name. The label is the evidence; the value must still be
289
+ // name-shaped so a stray `name: foo` in YAML does not fire.
290
+ regex: /\b(?:reporter|reported by|submitted by|filed by|author|full name|name|from|contact|signed)\s*[:=]\s*([^\n,;<(|]{2,60})/gi,
291
+ refine: (value) => (looksLikeProperNoun(value) ? "high" : null),
292
+ },
293
+ {
294
+ detector: "pii_employer",
295
+ category: "employer",
296
+ confidence: "high",
297
+ regex: /\b(?:employer|company|organi[sz]ation|workplace)\s*[:=]\s*([^\n,;<(|]{2,60})/gi,
298
+ refine: (value) => (looksLikeProperNoun(value) ? "high" : null),
299
+ },
300
+ {
301
+ detector: "pii_employer_mention",
302
+ category: "employer",
303
+ confidence: "medium",
304
+ // Best-effort: an employer named in prose. Medium, so it is redacted at the
305
+ // default threshold but never blocks — the phrasing space is unbounded.
306
+ regex: /\b(?:works? at|worked at|working at|employed by|employer is)\s+([A-Z][A-Za-z0-9&'’.-]*(?: [A-Z][A-Za-z0-9&'’.-]*){0,3})/g,
307
+ refine: (value) => (looksLikeProperNoun(value) ? "medium" : null),
308
+ },
309
+ {
310
+ detector: "pii_mac_address",
311
+ category: "machine_id",
312
+ confidence: "high",
313
+ regex: /\b(?:[0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}\b/g,
314
+ },
315
+ {
316
+ detector: "pii_device_name",
317
+ category: "machine_id",
318
+ confidence: "medium",
319
+ // "Jordans-MacBook-Pro", "jvance-laptop.local" — a device name usually
320
+ // carries its owner's name. Best-effort: naming conventions vary.
321
+ // `.local` first so a full `host.local` is one span, not a device name with
322
+ // a dangling suffix.
323
+ regex: /\b[a-z0-9][a-z0-9-]*\.local\b|\b[A-Za-z0-9]+(?:['’]?s)?-(?:MacBook(?:-Pro|-Air)?|iMac|Mac-mini|Mac-Studio|Laptop|PC)\b/gi,
324
+ },
325
+ {
326
+ detector: "pii_machine_uuid",
327
+ category: "machine_id",
328
+ confidence: "medium",
329
+ // Hardware/install UUID. Medium: a UUID is also a perfectly ordinary
330
+ // record id, so this informs redaction without gating a commit.
331
+ regex: /\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b/g,
332
+ },
333
+ ];
334
+ const PROFILE_REGISTRY = {
335
+ secrets: { name: "secrets", detectors: SECRET_DETECTORS, entropyBackstop: true },
336
+ internal: { name: "internal", detectors: INTERNAL_DETECTORS, entropyBackstop: false },
337
+ pii: { name: "pii", detectors: PII_DETECTORS, entropyBackstop: false },
338
+ };
339
+ // The profile that governs personal material in feedback content (RISK-001).
340
+ export const PII_PROFILE = "pii";
341
+ // Only "secrets" runs unless the caller opts into more (R-006: secrets-first v1).
342
+ export const DEFAULT_PROFILES = ["secrets"];
343
+ export function knownProfiles() {
344
+ return Object.keys(PROFILE_REGISTRY);
345
+ }
346
+ function resolveProfiles(names) {
347
+ return names.map((name) => {
348
+ const profile = PROFILE_REGISTRY[name];
349
+ if (!profile)
350
+ throw new Error(`unknown redaction profile: ${name} (known: ${knownProfiles().join(", ")})`);
351
+ return profile;
352
+ });
353
+ }
354
+ // Deterministic: same input + options => identical detections, ordered by
355
+ // offset then detector.
356
+ function detect(text, options = {}) {
357
+ const opts = { minEntropyLen: 20, ...options };
358
+ const profiles = resolveProfiles(opts.profiles ?? DEFAULT_PROFILES);
359
+ const detectors = profiles.flatMap((p) => p.detectors);
360
+ const runEntropy = profiles.some((p) => p.entropyBackstop);
361
+ const raw = [];
362
+ const covered = []; // [start, end) offsets already claimed
363
+ // Inline allow pragma (OQ2): a line containing `foo-redact: allow` suppresses
364
+ // findings on that line — for deliberate fixtures/examples. Pairs with the
365
+ // repo allowlist file. Human-authored only (R-007).
366
+ const starts = lineStarts(text);
367
+ const pragmaLines = new Set();
368
+ text.split(/\r?\n/).forEach((line, i) => {
369
+ if (/foo-redact:\s*allow/i.test(line))
370
+ pragmaLines.add(i + 1);
371
+ });
372
+ const pragmaAllowed = (offset) => pragmaLines.has(locate(starts, offset).line);
373
+ for (const det of detectors) {
374
+ for (const match of text.matchAll(det.regex)) {
375
+ const whole = match[0];
376
+ const start = match.index ?? 0;
377
+ // For keyed assignments, the secret of interest is the captured value.
378
+ const value = det.refine ? (match[1] ?? whole) : whole;
379
+ let confidence = det.confidence;
380
+ if (det.refine) {
381
+ const refined = det.refine(value);
382
+ if (refined === null)
383
+ continue; // placeholder => not a secret
384
+ confidence = refined;
385
+ }
386
+ const valueStart = det.refine ? start + whole.indexOf(value) : start;
387
+ if (isAllowed(value, opts) || pragmaAllowed(valueStart))
388
+ continue;
389
+ raw.push({ detector: det.detector, category: det.category, confidence, offset: valueStart, length: value.length });
390
+ covered.push([start, start + whole.length]);
391
+ }
392
+ }
393
+ // Entropy backstop over long tokens not already claimed by a pattern. Runs
394
+ // only for profiles that enable it (the secrets profile).
395
+ if (runEntropy) {
396
+ for (const match of text.matchAll(/[A-Za-z0-9+/=_-]{12,}/g)) {
397
+ const token = match[0];
398
+ const start = match.index ?? 0;
399
+ const end = start + token.length;
400
+ if (covered.some(([cs, ce]) => start < ce && end > cs))
401
+ continue;
402
+ if (isAllowed(token, opts) || pragmaAllowed(start))
403
+ continue;
404
+ const { ok, entropy } = entropyFinding(token, opts.minEntropyLen ?? 20);
405
+ if (!ok)
406
+ continue;
407
+ // Advisory only — the gate (T-003) blocks high-confidence findings.
408
+ raw.push({ detector: "high_entropy_string", category: "entropy", confidence: "low", offset: start, length: token.length, entropy: Math.round(entropy * 100) / 100 });
409
+ }
410
+ }
411
+ raw.sort((a, b) => a.offset - b.offset || a.detector.localeCompare(b.detector));
412
+ // Dedupe detections starting at the same offset (e.g. a keyed assignment and
413
+ // a provider pattern over the same value), keeping the highest confidence.
414
+ const deduped = [];
415
+ for (const f of raw) {
416
+ const prior = deduped[deduped.length - 1];
417
+ if (prior && prior.offset === f.offset) {
418
+ if (CONFIDENCE_RANK[f.confidence] > CONFIDENCE_RANK[prior.confidence])
419
+ deduped[deduped.length - 1] = f;
420
+ continue;
421
+ }
422
+ deduped.push(f);
423
+ }
424
+ return deduped;
425
+ }
426
+ // Scan a single text blob and return metadata-only findings (R-007).
427
+ export function scanText(text, options = {}) {
428
+ const starts = lineStarts(text);
429
+ const findings = detect(text, options).map((r) => {
430
+ const { line, column } = locate(starts, r.offset);
431
+ const finding = { detector: r.detector, category: r.category, confidence: r.confidence, line, column, length: r.length };
432
+ if (r.entropy !== undefined)
433
+ finding.entropy = r.entropy;
434
+ return finding;
435
+ });
436
+ const counts = {
437
+ high: findings.filter((f) => f.confidence === "high").length,
438
+ medium: findings.filter((f) => f.confidence === "medium").length,
439
+ low: findings.filter((f) => f.confidence === "low").length,
440
+ total: findings.length,
441
+ };
442
+ return { schema_version: "0.1", findings, counts };
443
+ }
444
+ // Parse an allowlist file: one literal value per line, `#` comments and blank
445
+ // lines ignored. Human-curated trusted config (R-007).
446
+ export function parseAllowlist(text) {
447
+ return text
448
+ .split(/\r?\n/)
449
+ .map((line) => line.trim())
450
+ .filter((line) => line.length > 0 && !line.startsWith("#"));
451
+ }
452
+ // Replace detected secrets with typed, consistent, irreversible placeholders.
453
+ // Same value => same token within the document (readability + diff stability).
454
+ // No value->token map is persisted (D-004): the mapping lives only for this
455
+ // call and is discarded on return, so redaction cannot be reversed.
456
+ export function redactText(text, options = {}) {
457
+ const threshold = options.threshold ?? "medium";
458
+ const minRank = CONFIDENCE_RANK[threshold];
459
+ const starts = lineStarts(text);
460
+ const raw = detect(text, options);
461
+ const tokens = new Map(); // value -> consistent placeholder (in-memory only)
462
+ const perType = new Map();
463
+ const tokenFor = (detector, value) => {
464
+ const existing = tokens.get(value);
465
+ if (existing)
466
+ return existing;
467
+ const n = (perType.get(detector) ?? 0) + 1;
468
+ perType.set(detector, n);
469
+ const token = `[${detector.toUpperCase()}_${n}]`;
470
+ tokens.set(value, token);
471
+ return token;
472
+ };
473
+ const by_type = {};
474
+ const by_confidence = { high: 0, medium: 0, low: 0 };
475
+ const placeholders = new Set();
476
+ const kept = [];
477
+ let out = "";
478
+ let cursor = 0;
479
+ let redactions = 0;
480
+ for (const f of raw) {
481
+ if (f.offset < cursor)
482
+ continue; // overlap guard
483
+ if (CONFIDENCE_RANK[f.confidence] < minRank) {
484
+ const { line, column } = locate(starts, f.offset);
485
+ kept.push({ detector: f.detector, confidence: f.confidence, line, column, reason: `below redaction threshold (${threshold})` });
486
+ continue;
487
+ }
488
+ const value = text.slice(f.offset, f.offset + f.length);
489
+ const token = tokenFor(f.detector, value);
490
+ out += text.slice(cursor, f.offset) + token;
491
+ cursor = f.offset + f.length;
492
+ redactions += 1;
493
+ by_type[f.detector] = (by_type[f.detector] ?? 0) + 1;
494
+ by_confidence[f.confidence] += 1;
495
+ placeholders.add(token);
496
+ }
497
+ out += text.slice(cursor);
498
+ return {
499
+ redactedText: out,
500
+ receipt: {
501
+ schema_version: "0.1",
502
+ redactions,
503
+ by_type,
504
+ by_confidence,
505
+ placeholders: [...placeholders].sort(),
506
+ kept,
507
+ },
508
+ };
509
+ }
@@ -0,0 +1,63 @@
1
+ import { type GitRunOptions, type GitRunResult } from "./gitRun.js";
2
+ export type StagedFile = {
3
+ file: string;
4
+ text: string;
5
+ };
6
+ export type UnscannableReason = "git-index-unavailable" | "unsupported-file-type" | "blob-unreadable" | "blob-too-large" | "binary-or-nul" | "decode-failed";
7
+ export type UnscannableStagedFile = {
8
+ file: string;
9
+ reason: UnscannableReason;
10
+ };
11
+ export type StagedRead = {
12
+ scanned: StagedFile[];
13
+ unscannable: UnscannableStagedFile[];
14
+ };
15
+ export type RedactGateGitSeams = {
16
+ gitRun: (args: string[], opts: GitRunOptions) => GitRunResult;
17
+ gitRunBuffer: (args: string[], opts: GitRunOptions) => {
18
+ ok: boolean;
19
+ stdout: Buffer;
20
+ };
21
+ };
22
+ export type GateFinding = {
23
+ file: string;
24
+ line: number;
25
+ column: number;
26
+ detector: string;
27
+ };
28
+ export type GateDecision = {
29
+ blocked: boolean;
30
+ overridden: boolean;
31
+ high: GateFinding[];
32
+ reason?: string;
33
+ };
34
+ export declare function decideGate(staged: StagedFile[], opts?: {
35
+ allowlist?: string[];
36
+ overrideReason?: string;
37
+ }): GateDecision;
38
+ export declare function readStaged(root: string, seams?: Partial<RedactGateGitSeams>): StagedRead;
39
+ export type UnscannableRefusal = {
40
+ schema_version: "0.1";
41
+ status: "refused";
42
+ reason_code: "staged_file_unscannable";
43
+ message: string;
44
+ safe_next_action: string;
45
+ };
46
+ export declare const UNSCANNABLE_REFUSAL: UnscannableRefusal;
47
+ export type RedactGateResult = {
48
+ output: string;
49
+ exitCode: 0 | 1;
50
+ unscannable: false;
51
+ } | {
52
+ output: string;
53
+ exitCode: 1;
54
+ unscannable: true;
55
+ refusal: UnscannableRefusal;
56
+ files: UnscannableStagedFile[];
57
+ };
58
+ export declare function runRedactGate(root: string, opts?: {
59
+ overrideReason?: string;
60
+ at?: string;
61
+ machine?: boolean;
62
+ seams?: Partial<RedactGateGitSeams>;
63
+ }): RedactGateResult;