patchwork-os 0.2.0-beta.9.canary.92 → 1.1.0-beta.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 (739) hide show
  1. package/README.bridge.md +15 -16
  2. package/README.md +46 -458
  3. package/dist/activationMetrics.js +2 -3
  4. package/dist/activationMetrics.js.map +1 -1
  5. package/dist/activityLog.d.ts +1 -0
  6. package/dist/activityLog.js +8 -3
  7. package/dist/activityLog.js.map +1 -1
  8. package/dist/adapters/gemini.js +2 -2
  9. package/dist/adapters/gemini.js.map +1 -1
  10. package/dist/adapters/grok.js +6 -1
  11. package/dist/adapters/grok.js.map +1 -1
  12. package/dist/adapters/openai.js +4 -0
  13. package/dist/adapters/openai.js.map +1 -1
  14. package/dist/approvalHttp.d.ts +9 -0
  15. package/dist/approvalHttp.js +210 -171
  16. package/dist/approvalHttp.js.map +1 -1
  17. package/dist/approvalKpi.d.ts +102 -0
  18. package/dist/approvalKpi.js +282 -0
  19. package/dist/approvalKpi.js.map +1 -0
  20. package/dist/approvalQueue.d.ts +1 -1
  21. package/dist/approvalQueue.js.map +1 -1
  22. package/dist/automation.d.ts +45 -4
  23. package/dist/automation.js +123 -15
  24. package/dist/automation.js.map +1 -1
  25. package/dist/bridge.d.ts +10 -0
  26. package/dist/bridge.js +225 -46
  27. package/dist/bridge.js.map +1 -1
  28. package/dist/bridgeLockDiscovery.d.ts +16 -1
  29. package/dist/bridgeLockDiscovery.js +38 -4
  30. package/dist/bridgeLockDiscovery.js.map +1 -1
  31. package/dist/bridgeToolsRules.js +3 -18
  32. package/dist/bridgeToolsRules.js.map +1 -1
  33. package/dist/claudeAuthHttp.d.ts +25 -0
  34. package/dist/claudeAuthHttp.js +219 -0
  35. package/dist/claudeAuthHttp.js.map +1 -0
  36. package/dist/claudeDriver.js +4 -1
  37. package/dist/claudeDriver.js.map +1 -1
  38. package/dist/claudeOrchestrator.d.ts +15 -0
  39. package/dist/claudeOrchestrator.js +44 -0
  40. package/dist/claudeOrchestrator.js.map +1 -1
  41. package/dist/commands/connect.d.ts +47 -0
  42. package/dist/commands/connect.js +419 -0
  43. package/dist/commands/connect.js.map +1 -0
  44. package/dist/commands/install.js +3 -10
  45. package/dist/commands/install.js.map +1 -1
  46. package/dist/commands/launchd.d.ts +7 -0
  47. package/dist/commands/launchd.js +20 -2
  48. package/dist/commands/launchd.js.map +1 -1
  49. package/dist/commands/patchworkInit.d.ts +7 -0
  50. package/dist/commands/patchworkInit.js +26 -0
  51. package/dist/commands/patchworkInit.js.map +1 -1
  52. package/dist/commands/recipe.d.ts +59 -4
  53. package/dist/commands/recipe.js +206 -5
  54. package/dist/commands/recipe.js.map +1 -1
  55. package/dist/commands/recipeInstall.d.ts +9 -0
  56. package/dist/commands/recipeInstall.js +56 -3
  57. package/dist/commands/recipeInstall.js.map +1 -1
  58. package/dist/commands/task.d.ts +25 -0
  59. package/dist/commands/task.js +61 -41
  60. package/dist/commands/task.js.map +1 -1
  61. package/dist/commands/tokenEfficiency.d.ts +4 -0
  62. package/dist/commands/tokenEfficiency.js +23 -26
  63. package/dist/commands/tokenEfficiency.js.map +1 -1
  64. package/dist/commands/tracesExport.d.ts +15 -1
  65. package/dist/commands/tracesExport.js +39 -5
  66. package/dist/commands/tracesExport.js.map +1 -1
  67. package/dist/config.js +16 -0
  68. package/dist/config.js.map +1 -1
  69. package/dist/connectorRoutes.js +613 -62
  70. package/dist/connectorRoutes.js.map +1 -1
  71. package/dist/connectors/asana.js +9 -3
  72. package/dist/connectors/asana.js.map +1 -1
  73. package/dist/connectors/baseConnector.d.ts +16 -0
  74. package/dist/connectors/baseConnector.js +2 -0
  75. package/dist/connectors/baseConnector.js.map +1 -1
  76. package/dist/connectors/caldiy.js +34 -0
  77. package/dist/connectors/caldiy.js.map +1 -1
  78. package/dist/connectors/confluence.js +9 -2
  79. package/dist/connectors/confluence.js.map +1 -1
  80. package/dist/connectors/connectorActivity.d.ts +24 -0
  81. package/dist/connectors/connectorActivity.js +32 -0
  82. package/dist/connectors/connectorActivity.js.map +1 -0
  83. package/dist/connectors/connectorRedirectUri.js +22 -2
  84. package/dist/connectors/connectorRedirectUri.js.map +1 -1
  85. package/dist/connectors/connectorRegistry.d.ts +0 -3
  86. package/dist/connectors/connectorRegistry.js +8 -8
  87. package/dist/connectors/connectorRegistry.js.map +1 -1
  88. package/dist/connectors/datadog.js +8 -1
  89. package/dist/connectors/datadog.js.map +1 -1
  90. package/dist/connectors/discord.js +4 -3
  91. package/dist/connectors/discord.js.map +1 -1
  92. package/dist/connectors/elasticsearch.js +22 -0
  93. package/dist/connectors/elasticsearch.js.map +1 -1
  94. package/dist/connectors/github.d.ts +38 -0
  95. package/dist/connectors/github.js +91 -5
  96. package/dist/connectors/github.js.map +1 -1
  97. package/dist/connectors/gitlab.js +19 -16
  98. package/dist/connectors/gitlab.js.map +1 -1
  99. package/dist/connectors/gmail.d.ts +5 -0
  100. package/dist/connectors/gmail.js +53 -11
  101. package/dist/connectors/gmail.js.map +1 -1
  102. package/dist/connectors/googleCalendar.js +5 -4
  103. package/dist/connectors/googleCalendar.js.map +1 -1
  104. package/dist/connectors/googleDocs.js +7 -6
  105. package/dist/connectors/googleDocs.js.map +1 -1
  106. package/dist/connectors/googleDrive.js +15 -8
  107. package/dist/connectors/googleDrive.js.map +1 -1
  108. package/dist/connectors/grafana.js +39 -3
  109. package/dist/connectors/grafana.js.map +1 -1
  110. package/dist/connectors/jira.d.ts +1 -1
  111. package/dist/connectors/jira.js +19 -4
  112. package/dist/connectors/jira.js.map +1 -1
  113. package/dist/connectors/linear.js +2 -2
  114. package/dist/connectors/linear.js.map +1 -1
  115. package/dist/connectors/mcpClient.d.ts +29 -1
  116. package/dist/connectors/mcpClient.js +114 -44
  117. package/dist/connectors/mcpClient.js.map +1 -1
  118. package/dist/connectors/mcpOAuth.d.ts +1 -0
  119. package/dist/connectors/mcpOAuth.js +49 -4
  120. package/dist/connectors/mcpOAuth.js.map +1 -1
  121. package/dist/connectors/monday.js +5 -4
  122. package/dist/connectors/monday.js.map +1 -1
  123. package/dist/connectors/mongodb.js +32 -5
  124. package/dist/connectors/mongodb.js.map +1 -1
  125. package/dist/connectors/notion.d.ts +1 -1
  126. package/dist/connectors/notion.js +22 -6
  127. package/dist/connectors/notion.js.map +1 -1
  128. package/dist/connectors/oauthError.d.ts +17 -0
  129. package/dist/connectors/oauthError.js +50 -0
  130. package/dist/connectors/oauthError.js.map +1 -0
  131. package/dist/connectors/postgres.d.ts +7 -0
  132. package/dist/connectors/postgres.js +32 -2
  133. package/dist/connectors/postgres.js.map +1 -1
  134. package/dist/connectors/posthog.js +26 -0
  135. package/dist/connectors/posthog.js.map +1 -1
  136. package/dist/connectors/redis.d.ts +1 -0
  137. package/dist/connectors/redis.js +47 -20
  138. package/dist/connectors/redis.js.map +1 -1
  139. package/dist/connectors/salesforce.js +66 -5
  140. package/dist/connectors/salesforce.js.map +1 -1
  141. package/dist/connectors/sentry.js +2 -2
  142. package/dist/connectors/sentry.js.map +1 -1
  143. package/dist/connectors/slack.js +37 -14
  144. package/dist/connectors/slack.js.map +1 -1
  145. package/dist/connectors/snowflake.js +24 -0
  146. package/dist/connectors/snowflake.js.map +1 -1
  147. package/dist/connectors/stripe.js +9 -2
  148. package/dist/connectors/stripe.js.map +1 -1
  149. package/dist/connectors/supabase.js +34 -0
  150. package/dist/connectors/supabase.js.map +1 -1
  151. package/dist/connectors/tokenStorage.d.ts +20 -0
  152. package/dist/connectors/tokenStorage.js +196 -64
  153. package/dist/connectors/tokenStorage.js.map +1 -1
  154. package/dist/connectors/woocommerce.js +34 -0
  155. package/dist/connectors/woocommerce.js.map +1 -1
  156. package/dist/copilot/parseIntent.d.ts +58 -0
  157. package/dist/copilot/parseIntent.js +149 -0
  158. package/dist/copilot/parseIntent.js.map +1 -0
  159. package/dist/decisionReplay.d.ts +8 -6
  160. package/dist/decisionReplay.js +35 -11
  161. package/dist/decisionReplay.js.map +1 -1
  162. package/dist/decisionTraceLog.d.ts +9 -0
  163. package/dist/decisionTraceLog.js +6 -0
  164. package/dist/decisionTraceLog.js.map +1 -1
  165. package/dist/drivers/claude/api.js +49 -7
  166. package/dist/drivers/claude/api.js.map +1 -1
  167. package/dist/drivers/claude/envSanitizer.d.ts +9 -1
  168. package/dist/drivers/claude/envSanitizer.js +41 -3
  169. package/dist/drivers/claude/envSanitizer.js.map +1 -1
  170. package/dist/drivers/claude/streamParser.d.ts +13 -0
  171. package/dist/drivers/claude/streamParser.js.map +1 -1
  172. package/dist/drivers/claude/subprocess.js +131 -16
  173. package/dist/drivers/claude/subprocess.js.map +1 -1
  174. package/dist/drivers/claude/subprocessSettings.d.ts +1 -1
  175. package/dist/drivers/claude/subprocessSettings.js +18 -4
  176. package/dist/drivers/claude/subprocessSettings.js.map +1 -1
  177. package/dist/drivers/gemini/index.d.ts +13 -0
  178. package/dist/drivers/gemini/index.js +211 -67
  179. package/dist/drivers/gemini/index.js.map +1 -1
  180. package/dist/drivers/local/index.d.ts +2 -17
  181. package/dist/drivers/local/index.js +5 -92
  182. package/dist/drivers/local/index.js.map +1 -1
  183. package/dist/drivers/openai/index.js +69 -9
  184. package/dist/drivers/openai/index.js.map +1 -1
  185. package/dist/drivers/outputCap.d.ts +27 -0
  186. package/dist/drivers/outputCap.js +50 -0
  187. package/dist/drivers/outputCap.js.map +1 -0
  188. package/dist/embeddings/cosine.d.ts +23 -0
  189. package/dist/embeddings/cosine.js +59 -0
  190. package/dist/embeddings/cosine.js.map +1 -0
  191. package/dist/embeddings/index.d.ts +19 -0
  192. package/dist/embeddings/index.js +35 -0
  193. package/dist/embeddings/index.js.map +1 -0
  194. package/dist/embeddings/localEmbeddings.d.ts +29 -0
  195. package/dist/embeddings/localEmbeddings.js +103 -0
  196. package/dist/embeddings/localEmbeddings.js.map +1 -0
  197. package/dist/embeddings/localEndpoints.d.ts +24 -0
  198. package/dist/embeddings/localEndpoints.js +37 -0
  199. package/dist/embeddings/localEndpoints.js.map +1 -0
  200. package/dist/embeddings/types.d.ts +32 -0
  201. package/dist/embeddings/types.js +12 -0
  202. package/dist/embeddings/types.js.map +1 -0
  203. package/dist/featureFlags.d.ts +18 -0
  204. package/dist/featureFlags.js +32 -0
  205. package/dist/featureFlags.js.map +1 -1
  206. package/dist/fp/automationInterpreter.js +118 -39
  207. package/dist/fp/automationInterpreter.js.map +1 -1
  208. package/dist/fp/automationProgram.d.ts +10 -0
  209. package/dist/fp/automationProgram.js.map +1 -1
  210. package/dist/fp/automationState.d.ts +6 -0
  211. package/dist/fp/automationState.js +38 -0
  212. package/dist/fp/automationState.js.map +1 -1
  213. package/dist/fp/commandDescription.d.ts +6 -0
  214. package/dist/fp/commandDescription.js +18 -5
  215. package/dist/fp/commandDescription.js.map +1 -1
  216. package/dist/fp/interpreterContext.d.ts +16 -5
  217. package/dist/fp/interpreterContext.js +59 -3
  218. package/dist/fp/interpreterContext.js.map +1 -1
  219. package/dist/fp/policyParser.js +18 -8
  220. package/dist/fp/policyParser.js.map +1 -1
  221. package/dist/haltPushDispatch.d.ts +4 -0
  222. package/dist/haltPushDispatch.js +7 -22
  223. package/dist/haltPushDispatch.js.map +1 -1
  224. package/dist/inboxRoutes.js +2 -2
  225. package/dist/inboxRoutes.js.map +1 -1
  226. package/dist/index.js +672 -135
  227. package/dist/index.js.map +1 -1
  228. package/dist/installGuard.js +6 -0
  229. package/dist/installGuard.js.map +1 -1
  230. package/dist/localEndpointGuard.d.ts +25 -0
  231. package/dist/localEndpointGuard.js +101 -0
  232. package/dist/localEndpointGuard.js.map +1 -0
  233. package/dist/mcpRoutes.js +1 -1
  234. package/dist/mcpRoutes.js.map +1 -1
  235. package/dist/oauth.d.ts +13 -0
  236. package/dist/oauth.js +53 -9
  237. package/dist/oauth.js.map +1 -1
  238. package/dist/orchestrator/childBridgeClient.d.ts +7 -0
  239. package/dist/orchestrator/childBridgeClient.js +42 -1
  240. package/dist/orchestrator/childBridgeClient.js.map +1 -1
  241. package/dist/orchestrator/orchestratorBridge.d.ts +33 -0
  242. package/dist/orchestrator/orchestratorBridge.js +75 -9
  243. package/dist/orchestrator/orchestratorBridge.js.map +1 -1
  244. package/dist/patchworkConfig.d.ts +4 -0
  245. package/dist/patchworkConfig.js +33 -4
  246. package/dist/patchworkConfig.js.map +1 -1
  247. package/dist/probe.js +39 -5
  248. package/dist/probe.js.map +1 -1
  249. package/dist/recipeOrchestration.d.ts +59 -0
  250. package/dist/recipeOrchestration.js +314 -60
  251. package/dist/recipeOrchestration.js.map +1 -1
  252. package/dist/recipeRoutes.d.ts +93 -0
  253. package/dist/recipeRoutes.js +664 -27
  254. package/dist/recipeRoutes.js.map +1 -1
  255. package/dist/recipes/RecipeOrchestrator.d.ts +14 -0
  256. package/dist/recipes/RecipeOrchestrator.js +42 -2
  257. package/dist/recipes/RecipeOrchestrator.js.map +1 -1
  258. package/dist/recipes/agentExecutor.d.ts +51 -2
  259. package/dist/recipes/agentExecutor.js +86 -13
  260. package/dist/recipes/agentExecutor.js.map +1 -1
  261. package/dist/recipes/chainedRunner.d.ts +111 -3
  262. package/dist/recipes/chainedRunner.js +440 -45
  263. package/dist/recipes/chainedRunner.js.map +1 -1
  264. package/dist/recipes/compiler.js +42 -29
  265. package/dist/recipes/compiler.js.map +1 -1
  266. package/dist/recipes/connectorPreflight.js +31 -0
  267. package/dist/recipes/connectorPreflight.js.map +1 -1
  268. package/dist/recipes/dependencyGraph.d.ts +16 -0
  269. package/dist/recipes/dependencyGraph.js +37 -2
  270. package/dist/recipes/dependencyGraph.js.map +1 -1
  271. package/dist/recipes/eventTriggerPrograms.d.ts +36 -0
  272. package/dist/recipes/eventTriggerPrograms.js +158 -0
  273. package/dist/recipes/eventTriggerPrograms.js.map +1 -0
  274. package/dist/recipes/githubInstallSource.d.ts +9 -2
  275. package/dist/recipes/githubInstallSource.js +36 -6
  276. package/dist/recipes/githubInstallSource.js.map +1 -1
  277. package/dist/recipes/haltCategory.d.ts +10 -0
  278. package/dist/recipes/haltCategory.js +11 -1
  279. package/dist/recipes/haltCategory.js.map +1 -1
  280. package/dist/recipes/idempotencyKey.d.ts +30 -3
  281. package/dist/recipes/idempotencyKey.js +89 -5
  282. package/dist/recipes/idempotencyKey.js.map +1 -1
  283. package/dist/recipes/installer.js +12 -35
  284. package/dist/recipes/installer.js.map +1 -1
  285. package/dist/recipes/judgeVerdict.d.ts +11 -1
  286. package/dist/recipes/judgeVerdict.js +47 -7
  287. package/dist/recipes/judgeVerdict.js.map +1 -1
  288. package/dist/recipes/names.d.ts +5 -0
  289. package/dist/recipes/names.js +10 -5
  290. package/dist/recipes/names.js.map +1 -1
  291. package/dist/recipes/parser.js +74 -10
  292. package/dist/recipes/parser.js.map +1 -1
  293. package/dist/recipes/pricing/costRouter.d.ts +43 -0
  294. package/dist/recipes/pricing/costRouter.js +44 -0
  295. package/dist/recipes/pricing/costRouter.js.map +1 -0
  296. package/dist/recipes/pricing/priceTable.d.ts +76 -0
  297. package/dist/recipes/pricing/priceTable.js +144 -0
  298. package/dist/recipes/pricing/priceTable.js.map +1 -0
  299. package/dist/recipes/replayRun.js +5 -2
  300. package/dist/recipes/replayRun.js.map +1 -1
  301. package/dist/recipes/resolveRecipePath.js +15 -1
  302. package/dist/recipes/resolveRecipePath.js.map +1 -1
  303. package/dist/recipes/runBudget.d.ts +93 -32
  304. package/dist/recipes/runBudget.js +213 -49
  305. package/dist/recipes/runBudget.js.map +1 -1
  306. package/dist/recipes/runRegistry.d.ts +32 -0
  307. package/dist/recipes/runRegistry.js +54 -0
  308. package/dist/recipes/runRegistry.js.map +1 -0
  309. package/dist/recipes/scheduler.js +6 -2
  310. package/dist/recipes/scheduler.js.map +1 -1
  311. package/dist/recipes/schema.d.ts +59 -6
  312. package/dist/recipes/schemaGenerator.d.ts +9 -0
  313. package/dist/recipes/schemaGenerator.js +446 -77
  314. package/dist/recipes/schemaGenerator.js.map +1 -1
  315. package/dist/recipes/simulation/aggregateRunRisk.d.ts +46 -0
  316. package/dist/recipes/simulation/aggregateRunRisk.js +120 -0
  317. package/dist/recipes/simulation/aggregateRunRisk.js.map +1 -0
  318. package/dist/recipes/simulation/costProjector.d.ts +32 -0
  319. package/dist/recipes/simulation/costProjector.js +194 -0
  320. package/dist/recipes/simulation/costProjector.js.map +1 -0
  321. package/dist/recipes/simulation/sideEffects.d.ts +32 -0
  322. package/dist/recipes/simulation/sideEffects.js +62 -0
  323. package/dist/recipes/simulation/sideEffects.js.map +1 -0
  324. package/dist/recipes/simulation/simulate.d.ts +36 -0
  325. package/dist/recipes/simulation/simulate.js +264 -0
  326. package/dist/recipes/simulation/simulate.js.map +1 -0
  327. package/dist/recipes/simulation/simulateMockedRun.d.ts +52 -0
  328. package/dist/recipes/simulation/simulateMockedRun.js +84 -0
  329. package/dist/recipes/simulation/simulateMockedRun.js.map +1 -0
  330. package/dist/recipes/simulation/synthesizeMockedOutputs.d.ts +31 -0
  331. package/dist/recipes/simulation/synthesizeMockedOutputs.js +50 -0
  332. package/dist/recipes/simulation/synthesizeMockedOutputs.js.map +1 -0
  333. package/dist/recipes/simulation/types.d.ts +198 -0
  334. package/dist/recipes/simulation/types.js +30 -0
  335. package/dist/recipes/simulation/types.js.map +1 -0
  336. package/dist/recipes/stepObservation.d.ts +12 -0
  337. package/dist/recipes/stepObservation.js +21 -1
  338. package/dist/recipes/stepObservation.js.map +1 -1
  339. package/dist/recipes/toolRegistry.js +61 -24
  340. package/dist/recipes/toolRegistry.js.map +1 -1
  341. package/dist/recipes/tools/airtable.d.ts +15 -0
  342. package/dist/recipes/tools/airtable.js +240 -0
  343. package/dist/recipes/tools/airtable.js.map +1 -0
  344. package/dist/recipes/tools/caldiy.d.ts +13 -0
  345. package/dist/recipes/tools/caldiy.js +215 -0
  346. package/dist/recipes/tools/caldiy.js.map +1 -0
  347. package/dist/recipes/tools/circleci.d.ts +10 -0
  348. package/dist/recipes/tools/circleci.js +204 -0
  349. package/dist/recipes/tools/circleci.js.map +1 -0
  350. package/dist/recipes/tools/cloudflare.d.ts +13 -0
  351. package/dist/recipes/tools/cloudflare.js +211 -0
  352. package/dist/recipes/tools/cloudflare.js.map +1 -0
  353. package/dist/recipes/tools/confluence.js +11 -10
  354. package/dist/recipes/tools/confluence.js.map +1 -1
  355. package/dist/recipes/tools/datadog.js +13 -12
  356. package/dist/recipes/tools/datadog.js.map +1 -1
  357. package/dist/recipes/tools/docs.d.ts +18 -0
  358. package/dist/recipes/tools/docs.js +95 -0
  359. package/dist/recipes/tools/docs.js.map +1 -0
  360. package/dist/recipes/tools/elasticsearch.d.ts +11 -0
  361. package/dist/recipes/tools/elasticsearch.js +157 -0
  362. package/dist/recipes/tools/elasticsearch.js.map +1 -0
  363. package/dist/recipes/tools/fanOut.js +4 -4
  364. package/dist/recipes/tools/fanOut.js.map +1 -1
  365. package/dist/recipes/tools/figma.d.ts +12 -0
  366. package/dist/recipes/tools/figma.js +195 -0
  367. package/dist/recipes/tools/figma.js.map +1 -0
  368. package/dist/recipes/tools/file.js +43 -22
  369. package/dist/recipes/tools/file.js.map +1 -1
  370. package/dist/recipes/tools/github.js +162 -5
  371. package/dist/recipes/tools/github.js.map +1 -1
  372. package/dist/recipes/tools/gmail.js +45 -33
  373. package/dist/recipes/tools/gmail.js.map +1 -1
  374. package/dist/recipes/tools/grafana.d.ts +11 -0
  375. package/dist/recipes/tools/grafana.js +216 -0
  376. package/dist/recipes/tools/grafana.js.map +1 -0
  377. package/dist/recipes/tools/http.d.ts +1 -1
  378. package/dist/recipes/tools/http.js +10 -28
  379. package/dist/recipes/tools/http.js.map +1 -1
  380. package/dist/recipes/tools/hubspot.js +13 -12
  381. package/dist/recipes/tools/hubspot.js.map +1 -1
  382. package/dist/recipes/tools/index.d.ts +28 -0
  383. package/dist/recipes/tools/index.js +28 -0
  384. package/dist/recipes/tools/index.js.map +1 -1
  385. package/dist/recipes/tools/intercom.js +11 -10
  386. package/dist/recipes/tools/intercom.js.map +1 -1
  387. package/dist/recipes/tools/jira.js +13 -0
  388. package/dist/recipes/tools/jira.js.map +1 -1
  389. package/dist/recipes/tools/meetingNotes.js +2 -2
  390. package/dist/recipes/tools/monday.d.ts +22 -0
  391. package/dist/recipes/tools/monday.js +242 -0
  392. package/dist/recipes/tools/monday.js.map +1 -0
  393. package/dist/recipes/tools/mongodb.d.ts +19 -0
  394. package/dist/recipes/tools/mongodb.js +194 -0
  395. package/dist/recipes/tools/mongodb.js.map +1 -0
  396. package/dist/recipes/tools/notion.js +11 -10
  397. package/dist/recipes/tools/notion.js.map +1 -1
  398. package/dist/recipes/tools/obsidian.d.ts +15 -0
  399. package/dist/recipes/tools/obsidian.js +173 -0
  400. package/dist/recipes/tools/obsidian.js.map +1 -0
  401. package/dist/recipes/tools/outcomes.d.ts +15 -0
  402. package/dist/recipes/tools/outcomes.js +111 -0
  403. package/dist/recipes/tools/outcomes.js.map +1 -0
  404. package/dist/recipes/tools/paystack.d.ts +11 -0
  405. package/dist/recipes/tools/paystack.js +212 -0
  406. package/dist/recipes/tools/paystack.js.map +1 -0
  407. package/dist/recipes/tools/pipedrive.d.ts +16 -0
  408. package/dist/recipes/tools/pipedrive.js +234 -0
  409. package/dist/recipes/tools/pipedrive.js.map +1 -0
  410. package/dist/recipes/tools/postgres.d.ts +15 -0
  411. package/dist/recipes/tools/postgres.js +186 -0
  412. package/dist/recipes/tools/postgres.js.map +1 -0
  413. package/dist/recipes/tools/posthog.d.ts +16 -0
  414. package/dist/recipes/tools/posthog.js +219 -0
  415. package/dist/recipes/tools/posthog.js.map +1 -0
  416. package/dist/recipes/tools/redis.d.ts +9 -0
  417. package/dist/recipes/tools/redis.js +141 -0
  418. package/dist/recipes/tools/redis.js.map +1 -0
  419. package/dist/recipes/tools/resend.d.ts +8 -0
  420. package/dist/recipes/tools/resend.js +153 -0
  421. package/dist/recipes/tools/resend.js.map +1 -0
  422. package/dist/recipes/tools/salesforce.d.ts +16 -0
  423. package/dist/recipes/tools/salesforce.js +184 -0
  424. package/dist/recipes/tools/salesforce.js.map +1 -0
  425. package/dist/recipes/tools/sendgrid.d.ts +9 -0
  426. package/dist/recipes/tools/sendgrid.js +175 -0
  427. package/dist/recipes/tools/sendgrid.js.map +1 -0
  428. package/dist/recipes/tools/shopify.d.ts +16 -0
  429. package/dist/recipes/tools/shopify.js +266 -0
  430. package/dist/recipes/tools/shopify.js.map +1 -0
  431. package/dist/recipes/tools/snowflake.d.ts +16 -0
  432. package/dist/recipes/tools/snowflake.js +173 -0
  433. package/dist/recipes/tools/snowflake.js.map +1 -0
  434. package/dist/recipes/tools/stripe.js +13 -12
  435. package/dist/recipes/tools/stripe.js.map +1 -1
  436. package/dist/recipes/tools/supabase.d.ts +9 -0
  437. package/dist/recipes/tools/supabase.js +133 -0
  438. package/dist/recipes/tools/supabase.js.map +1 -0
  439. package/dist/recipes/tools/todoist.d.ts +15 -0
  440. package/dist/recipes/tools/todoist.js +228 -0
  441. package/dist/recipes/tools/todoist.js.map +1 -0
  442. package/dist/recipes/tools/twilio.d.ts +11 -0
  443. package/dist/recipes/tools/twilio.js +180 -0
  444. package/dist/recipes/tools/twilio.js.map +1 -0
  445. package/dist/recipes/tools/vercel.d.ts +9 -0
  446. package/dist/recipes/tools/vercel.js +146 -0
  447. package/dist/recipes/tools/vercel.js.map +1 -0
  448. package/dist/recipes/tools/webflow.d.ts +11 -0
  449. package/dist/recipes/tools/webflow.js +244 -0
  450. package/dist/recipes/tools/webflow.js.map +1 -0
  451. package/dist/recipes/tools/woocommerce.d.ts +18 -0
  452. package/dist/recipes/tools/woocommerce.js +260 -0
  453. package/dist/recipes/tools/woocommerce.js.map +1 -0
  454. package/dist/recipes/tools/wrapConnectorExecute.d.ts +25 -0
  455. package/dist/recipes/tools/wrapConnectorExecute.js +37 -0
  456. package/dist/recipes/tools/wrapConnectorExecute.js.map +1 -0
  457. package/dist/recipes/tools/zendesk.js +11 -10
  458. package/dist/recipes/tools/zendesk.js.map +1 -1
  459. package/dist/recipes/triggerVars.d.ts +15 -0
  460. package/dist/recipes/triggerVars.js +57 -0
  461. package/dist/recipes/triggerVars.js.map +1 -0
  462. package/dist/recipes/validation.d.ts +1 -1
  463. package/dist/recipes/validation.js +536 -12
  464. package/dist/recipes/validation.js.map +1 -1
  465. package/dist/recipes/yamlPositions.d.ts +1 -1
  466. package/dist/recipes/yamlPositions.js +7 -2
  467. package/dist/recipes/yamlPositions.js.map +1 -1
  468. package/dist/recipes/yamlRunner.d.ts +216 -4
  469. package/dist/recipes/yamlRunner.js +1030 -96
  470. package/dist/recipes/yamlRunner.js.map +1 -1
  471. package/dist/recipesHttp.d.ts +5 -0
  472. package/dist/recipesHttp.js +68 -6
  473. package/dist/recipesHttp.js.map +1 -1
  474. package/dist/resources.js +14 -1
  475. package/dist/resources.js.map +1 -1
  476. package/dist/riskSignals.d.ts +42 -0
  477. package/dist/riskSignals.js +197 -0
  478. package/dist/riskSignals.js.map +1 -0
  479. package/dist/riskTier.d.ts +9 -0
  480. package/dist/riskTier.js +48 -0
  481. package/dist/riskTier.js.map +1 -1
  482. package/dist/runLog.d.ts +53 -0
  483. package/dist/runLog.js +75 -16
  484. package/dist/runLog.js.map +1 -1
  485. package/dist/schemas/recipe.v1.json +360 -16
  486. package/dist/server.d.ts +60 -0
  487. package/dist/server.js +192 -31
  488. package/dist/server.js.map +1 -1
  489. package/dist/sessionCheckpoint.js +4 -1
  490. package/dist/sessionCheckpoint.js.map +1 -1
  491. package/dist/sessionDetail.d.ts +43 -0
  492. package/dist/sessionDetail.js +59 -0
  493. package/dist/sessionDetail.js.map +1 -0
  494. package/dist/settingsEnv.d.ts +47 -0
  495. package/dist/settingsEnv.js +88 -0
  496. package/dist/settingsEnv.js.map +1 -0
  497. package/dist/streamableHttp.js +44 -10
  498. package/dist/streamableHttp.js.map +1 -1
  499. package/dist/ta/backtest/abnday.d.ts +53 -0
  500. package/dist/ta/backtest/abnday.js +123 -0
  501. package/dist/ta/backtest/abnday.js.map +1 -0
  502. package/dist/ta/backtest/directional.d.ts +48 -0
  503. package/dist/ta/backtest/directional.js +103 -0
  504. package/dist/ta/backtest/directional.js.map +1 -0
  505. package/dist/ta/backtest/fundx.d.ts +67 -0
  506. package/dist/ta/backtest/fundx.js +264 -0
  507. package/dist/ta/backtest/fundx.js.map +1 -0
  508. package/dist/ta/backtest/fvg.d.ts +62 -0
  509. package/dist/ta/backtest/fvg.js +236 -0
  510. package/dist/ta/backtest/fvg.js.map +1 -0
  511. package/dist/ta/backtest/ichimoku.d.ts +41 -0
  512. package/dist/ta/backtest/ichimoku.js +146 -0
  513. package/dist/ta/backtest/ichimoku.js.map +1 -0
  514. package/dist/ta/backtest/ingest.d.ts +20 -0
  515. package/dist/ta/backtest/ingest.js +104 -0
  516. package/dist/ta/backtest/ingest.js.map +1 -0
  517. package/dist/ta/backtest/msb.d.ts +49 -0
  518. package/dist/ta/backtest/msb.js +162 -0
  519. package/dist/ta/backtest/msb.js.map +1 -0
  520. package/dist/ta/backtest/scoring.d.ts +33 -0
  521. package/dist/ta/backtest/scoring.js +82 -0
  522. package/dist/ta/backtest/scoring.js.map +1 -0
  523. package/dist/ta/backtest/summary.d.ts +20 -0
  524. package/dist/ta/backtest/summary.js +42 -0
  525. package/dist/ta/backtest/summary.js.map +1 -0
  526. package/dist/ta/backtest/tsmom.d.ts +72 -0
  527. package/dist/ta/backtest/tsmom.js +202 -0
  528. package/dist/ta/backtest/tsmom.js.map +1 -0
  529. package/dist/ta/backtest/volsq.d.ts +34 -0
  530. package/dist/ta/backtest/volsq.js +154 -0
  531. package/dist/ta/backtest/volsq.js.map +1 -0
  532. package/dist/ta/backtest/walkForward.d.ts +45 -0
  533. package/dist/ta/backtest/walkForward.js +136 -0
  534. package/dist/ta/backtest/walkForward.js.map +1 -0
  535. package/dist/ta/cycles.d.ts +15 -0
  536. package/dist/ta/cycles.js +31 -0
  537. package/dist/ta/cycles.js.map +1 -0
  538. package/dist/ta/desk/accrualEmitter.d.ts +59 -0
  539. package/dist/ta/desk/accrualEmitter.js +136 -0
  540. package/dist/ta/desk/accrualEmitter.js.map +1 -0
  541. package/dist/ta/desk/cellBacktest.d.ts +179 -0
  542. package/dist/ta/desk/cellBacktest.js +514 -0
  543. package/dist/ta/desk/cellBacktest.js.map +1 -0
  544. package/dist/ta/desk/cells/wpMaRejection.d.ts +49 -0
  545. package/dist/ta/desk/cells/wpMaRejection.js +141 -0
  546. package/dist/ta/desk/cells/wpMaRejection.js.map +1 -0
  547. package/dist/ta/desk/cells/wpVolumeClimax.d.ts +57 -0
  548. package/dist/ta/desk/cells/wpVolumeClimax.js +193 -0
  549. package/dist/ta/desk/cells/wpVolumeClimax.js.map +1 -0
  550. package/dist/ta/desk/collectors.d.ts +18 -0
  551. package/dist/ta/desk/collectors.js +450 -0
  552. package/dist/ta/desk/collectors.js.map +1 -0
  553. package/dist/ta/desk/contract.d.ts +51 -0
  554. package/dist/ta/desk/contract.js +261 -0
  555. package/dist/ta/desk/contract.js.map +1 -0
  556. package/dist/ta/desk/deskLedger.d.ts +36 -0
  557. package/dist/ta/desk/deskLedger.js +239 -0
  558. package/dist/ta/desk/deskLedger.js.map +1 -0
  559. package/dist/ta/desk/liqTape.d.ts +37 -0
  560. package/dist/ta/desk/liqTape.js +99 -0
  561. package/dist/ta/desk/liqTape.js.map +1 -0
  562. package/dist/ta/desk/post.d.ts +20 -0
  563. package/dist/ta/desk/post.js +65 -0
  564. package/dist/ta/desk/post.js.map +1 -0
  565. package/dist/ta/desk/render.d.ts +25 -0
  566. package/dist/ta/desk/render.js +398 -0
  567. package/dist/ta/desk/render.js.map +1 -0
  568. package/dist/ta/desk/surfaces.d.ts +216 -0
  569. package/dist/ta/desk/surfaces.js +472 -0
  570. package/dist/ta/desk/surfaces.js.map +1 -0
  571. package/dist/ta/desk/types.d.ts +174 -0
  572. package/dist/ta/desk/types.js +104 -0
  573. package/dist/ta/desk/types.js.map +1 -0
  574. package/dist/ta/index.d.ts +23 -0
  575. package/dist/ta/index.js +54 -0
  576. package/dist/ta/index.js.map +1 -0
  577. package/dist/ta/ledger.d.ts +44 -0
  578. package/dist/ta/ledger.js +95 -0
  579. package/dist/ta/ledger.js.map +1 -0
  580. package/dist/ta/levels.d.ts +21 -0
  581. package/dist/ta/levels.js +41 -0
  582. package/dist/ta/levels.js.map +1 -0
  583. package/dist/ta/swings.d.ts +30 -0
  584. package/dist/ta/swings.js +83 -0
  585. package/dist/ta/swings.js.map +1 -0
  586. package/dist/ta/types.d.ts +81 -0
  587. package/dist/ta/types.js +20 -0
  588. package/dist/ta/types.js.map +1 -0
  589. package/dist/telemetry.js +20 -9
  590. package/dist/telemetry.js.map +1 -1
  591. package/dist/tokenUsageTracker.d.ts +2 -1
  592. package/dist/tokenUsageTracker.js +38 -20
  593. package/dist/tokenUsageTracker.js.map +1 -1
  594. package/dist/tools/contextBundle.js +13 -9
  595. package/dist/tools/contextBundle.js.map +1 -1
  596. package/dist/tools/ctxGetTaskContext.js +8 -3
  597. package/dist/tools/ctxGetTaskContext.js.map +1 -1
  598. package/dist/tools/ctxQueryTraces.d.ts +18 -1
  599. package/dist/tools/ctxQueryTraces.js +176 -27
  600. package/dist/tools/ctxQueryTraces.js.map +1 -1
  601. package/dist/tools/enrichCommit.js +5 -2
  602. package/dist/tools/enrichCommit.js.map +1 -1
  603. package/dist/tools/getDependencyTree.js +2 -2
  604. package/dist/tools/getDependencyTree.js.map +1 -1
  605. package/dist/tools/getDiagnostics.js +3 -0
  606. package/dist/tools/getDiagnostics.js.map +1 -1
  607. package/dist/tools/getDocumentSymbols.js +5 -1
  608. package/dist/tools/getDocumentSymbols.js.map +1 -1
  609. package/dist/tools/getGitHotspots.js +12 -1
  610. package/dist/tools/getGitHotspots.js.map +1 -1
  611. package/dist/tools/getGitLog.js +5 -2
  612. package/dist/tools/getGitLog.js.map +1 -1
  613. package/dist/tools/getGitStatus.js +6 -1
  614. package/dist/tools/getGitStatus.js.map +1 -1
  615. package/dist/tools/getProjectContext.js +3 -1
  616. package/dist/tools/getProjectContext.js.map +1 -1
  617. package/dist/tools/getToolCapabilities.js +39 -4
  618. package/dist/tools/getToolCapabilities.js.map +1 -1
  619. package/dist/tools/gitWrite.js +12 -3
  620. package/dist/tools/gitWrite.js.map +1 -1
  621. package/dist/tools/github/composite.d.ts +1 -1
  622. package/dist/tools/github/composite.js +7 -7
  623. package/dist/tools/github/composite.js.map +1 -1
  624. package/dist/tools/github/pr.d.ts +6 -6
  625. package/dist/tools/github/pr.js +18 -11
  626. package/dist/tools/github/pr.js.map +1 -1
  627. package/dist/tools/httpClient.js +55 -3
  628. package/dist/tools/httpClient.js.map +1 -1
  629. package/dist/tools/index.d.ts +2 -1
  630. package/dist/tools/index.js +19 -12
  631. package/dist/tools/index.js.map +1 -1
  632. package/dist/tools/lsp.d.ts +6 -0
  633. package/dist/tools/lsp.js +81 -12
  634. package/dist/tools/lsp.js.map +1 -1
  635. package/dist/tools/previewEdit.js +16 -1
  636. package/dist/tools/previewEdit.js.map +1 -1
  637. package/dist/tools/resumeClaudeTask.js +17 -0
  638. package/dist/tools/resumeClaudeTask.js.map +1 -1
  639. package/dist/tools/searchAndReplace.js +9 -5
  640. package/dist/tools/searchAndReplace.js.map +1 -1
  641. package/dist/tools/searchTools.d.ts +26 -0
  642. package/dist/tools/searchTools.js +42 -2
  643. package/dist/tools/searchTools.js.map +1 -1
  644. package/dist/tools/terminal.js +23 -51
  645. package/dist/tools/terminal.js.map +1 -1
  646. package/dist/tools/transaction.d.ts +9 -1
  647. package/dist/tools/transaction.js +59 -4
  648. package/dist/tools/transaction.js.map +1 -1
  649. package/dist/tools/utils.d.ts +16 -0
  650. package/dist/tools/utils.js +77 -7
  651. package/dist/tools/utils.js.map +1 -1
  652. package/dist/transport.d.ts +31 -0
  653. package/dist/transport.js +117 -14
  654. package/dist/transport.js.map +1 -1
  655. package/dist/wireHaltPushDispatch.js +12 -0
  656. package/dist/wireHaltPushDispatch.js.map +1 -1
  657. package/dist/workerGateDecisionLog.d.ts +141 -0
  658. package/dist/workerGateDecisionLog.js +361 -0
  659. package/dist/workerGateDecisionLog.js.map +1 -0
  660. package/dist/workers/actionClass.d.ts +38 -0
  661. package/dist/workers/actionClass.js +155 -0
  662. package/dist/workers/actionClass.js.map +1 -0
  663. package/dist/workers/backtest.d.ts +68 -0
  664. package/dist/workers/backtest.js +113 -0
  665. package/dist/workers/backtest.js.map +1 -0
  666. package/dist/workers/contextRisk.d.ts +35 -0
  667. package/dist/workers/contextRisk.js +20 -0
  668. package/dist/workers/contextRisk.js.map +1 -0
  669. package/dist/workers/contextRiskScorer.d.ts +48 -0
  670. package/dist/workers/contextRiskScorer.js +94 -0
  671. package/dist/workers/contextRiskScorer.js.map +1 -0
  672. package/dist/workers/graduation.d.ts +61 -0
  673. package/dist/workers/graduation.js +112 -0
  674. package/dist/workers/graduation.js.map +1 -0
  675. package/dist/workers/outcomeStore.d.ts +98 -0
  676. package/dist/workers/outcomeStore.js +146 -0
  677. package/dist/workers/outcomeStore.js.map +1 -0
  678. package/dist/workers/outcomesCli.d.ts +34 -0
  679. package/dist/workers/outcomesCli.js +91 -0
  680. package/dist/workers/outcomesCli.js.map +1 -0
  681. package/dist/workers/runWorkerShadow.d.ts +90 -0
  682. package/dist/workers/runWorkerShadow.js +323 -0
  683. package/dist/workers/runWorkerShadow.js.map +1 -0
  684. package/dist/workers/shadowGate.d.ts +30 -0
  685. package/dist/workers/shadowGate.js +58 -0
  686. package/dist/workers/shadowGate.js.map +1 -0
  687. package/dist/workers/shadowObserver.d.ts +165 -0
  688. package/dist/workers/shadowObserver.js +203 -0
  689. package/dist/workers/shadowObserver.js.map +1 -0
  690. package/dist/workers/shadowReport.d.ts +16 -0
  691. package/dist/workers/shadowReport.js +61 -0
  692. package/dist/workers/shadowReport.js.map +1 -0
  693. package/dist/workers/shadowRun.d.ts +38 -0
  694. package/dist/workers/shadowRun.js +44 -0
  695. package/dist/workers/shadowRun.js.map +1 -0
  696. package/dist/workers/trustLevel.d.ts +82 -0
  697. package/dist/workers/trustLevel.js +100 -0
  698. package/dist/workers/trustLevel.js.map +1 -0
  699. package/dist/workers/worker.d.ts +49 -0
  700. package/dist/workers/worker.js +88 -0
  701. package/dist/workers/worker.js.map +1 -0
  702. package/dist/workers/workerGate.d.ts +101 -0
  703. package/dist/workers/workerGate.js +210 -0
  704. package/dist/workers/workerGate.js.map +1 -0
  705. package/dist/workers/workerLevelStore.d.ts +33 -0
  706. package/dist/workers/workerLevelStore.js +87 -0
  707. package/dist/workers/workerLevelStore.js.map +1 -0
  708. package/dist/workers/workerLoader.d.ts +7 -0
  709. package/dist/workers/workerLoader.js +31 -0
  710. package/dist/workers/workerLoader.js.map +1 -0
  711. package/dist/writeFileAtomic.js +40 -13
  712. package/dist/writeFileAtomic.js.map +1 -1
  713. package/package.json +14 -6
  714. package/scripts/mcp-stdio-shim.cjs +247 -87
  715. package/templates/CLAUDE.bridge.md +1 -1
  716. package/templates/recipes/dependency-bump.yaml +33 -0
  717. package/templates/recipes/incident-to-pr.yaml +187 -0
  718. package/templates/recipes/outcome-ingester.yaml +53 -0
  719. package/templates/recipes/release-notes.yaml +30 -0
  720. package/templates/recipes/triage-failing-tests-autofile.yaml +152 -0
  721. package/templates/recipes/triage-failing-tests.yaml +38 -0
  722. package/templates/workers/dependency-upkeep.worker.yaml +26 -0
  723. package/templates/workers/release-notes.worker.yaml +17 -0
  724. package/templates/workers/test-guardian.worker.yaml +24 -0
  725. package/deploy/README.md +0 -172
  726. package/deploy/bootstrap-new-vps.sh +0 -364
  727. package/deploy/bootstrap-vps.sh +0 -192
  728. package/deploy/claude-ide-bridge.service.template +0 -67
  729. package/deploy/claude-ide-bridge@.service +0 -31
  730. package/deploy/deploy-dashboard.sh +0 -198
  731. package/deploy/deploy-landing.sh +0 -136
  732. package/deploy/ecosystem.config.js.example +0 -36
  733. package/deploy/install-vps-service.sh +0 -240
  734. package/deploy/macos/README.md +0 -153
  735. package/deploy/macos/com.patchwork.bridge.plist.template +0 -54
  736. package/deploy/macos/com.patchwork.tunnel.plist.template +0 -76
  737. package/deploy/macos/install-mac-bridge.sh +0 -244
  738. package/deploy/macos/uninstall-mac-bridge.sh +0 -22
  739. package/deploy/nginx-claude-bridge.conf.template +0 -129
package/dist/index.js CHANGED
@@ -13,9 +13,24 @@
13
13
  const envPath = candidates.find(existsSync);
14
14
  if (envPath) {
15
15
  for (const line of readFileSync(envPath, "utf-8").split("\n")) {
16
- const m = /^([A-Z_][A-Z0-9_]*)=(.*)$/.exec(line.trim());
16
+ const trimmed = line.trim();
17
+ // Skip blank lines and full-line comments outright.
18
+ if (!trimmed || trimmed.startsWith("#"))
19
+ continue;
20
+ const m = /^([A-Z_][A-Z0-9_]*)=(.*)$/.exec(trimmed);
17
21
  if (m?.[1] && !process.env[m[1]]) {
18
- process.env[m[1]] = m[2]?.replace(/^["']|["']$/g, "");
22
+ let raw = m[2] ?? "";
23
+ // Strip an inline comment, but only when the value is NOT quoted —
24
+ // a quoted value may legitimately contain '#' (cli-commands-6).
25
+ const isQuoted = (raw.startsWith('"') && raw.endsWith('"') && raw.length >= 2) ||
26
+ (raw.startsWith("'") && raw.endsWith("'") && raw.length >= 2);
27
+ if (!isQuoted) {
28
+ const hashIdx = raw.indexOf("#");
29
+ if (hashIdx !== -1)
30
+ raw = raw.slice(0, hashIdx);
31
+ raw = raw.trim();
32
+ }
33
+ process.env[m[1]] = raw.replace(/^["']|["']$/g, "");
19
34
  }
20
35
  }
21
36
  }
@@ -38,6 +53,7 @@ import readline from "node:readline";
38
53
  import { fileURLToPath } from "node:url";
39
54
  import { getAnalyticsPref, setAnalyticsPref } from "./analyticsPrefs.js";
40
55
  import { Bridge } from "./bridge.js";
56
+ import { findAllLiveBridges, findBridgeLock, } from "./bridgeLockDiscovery.js";
41
57
  import { isBridgeToolsFileValid, repairBridgeToolsRulesIfStale, } from "./bridgeToolsRules.js";
42
58
  import { findEditor, parseConfig } from "./config.js";
43
59
  import { detectWorkspaceSymlinkInstall, PATCHWORK_PACKAGE_NAME, SYMLINK_INSTALL_FIX, } from "./installGuard.js";
@@ -124,12 +140,18 @@ const KNOWN_SUBCOMMANDS = [
124
140
  "install",
125
141
  "status",
126
142
  "shim",
143
+ "quick-task",
144
+ "start-task",
145
+ "continue-handoff",
146
+ "token-efficiency",
127
147
  "recipe",
148
+ "connect",
128
149
  "traces",
129
150
  "suggest",
130
151
  "dashboard",
131
152
  "launchd",
132
153
  "start",
154
+ "orchestrator",
133
155
  "kill-switch",
134
156
  "panic",
135
157
  "halts",
@@ -137,6 +159,10 @@ const KNOWN_SUBCOMMANDS = [
137
159
  "analytics",
138
160
  "doctor",
139
161
  "shadow-scan",
162
+ "workers",
163
+ "approvals",
164
+ "gate",
165
+ "outcomes",
140
166
  ];
141
167
  const __invokedSubcommand = (() => {
142
168
  const sub = process.argv[2];
@@ -194,6 +220,11 @@ if (process.argv[2] === "--help" ||
194
220
  ` recipe run <name> [--vars k=v] Run a recipe by name\n` +
195
221
  ` recipe install <source> Install from a path or GitHub source\n` +
196
222
  ` recipe --help Full recipe subcommand index\n\n` +
223
+ `Connectors\n` +
224
+ ` connect [list] List connectors + connection status\n` +
225
+ ` connect <vendor> OAuth: print authorize URL / PAT: connect\n` +
226
+ ` connect test <vendor> Health-probe a connector\n` +
227
+ ` connect disconnect <vendor> Revoke a connector\n\n` +
197
228
  `Operate\n` +
198
229
  ` start [--port N] [--workspace <dir>] Start a single bridge (no tmux)\n` +
199
230
  ` status One-line bridge status (port, uptime)\n` +
@@ -306,20 +337,7 @@ This is a thin wrapper over \`start-all\`. For advanced flags see:
306
337
  process.exit(result.status ?? 1);
307
338
  }
308
339
  function writeRulesFileAtomic(rulesFilePath, content) {
309
- const tmpPath = `${rulesFilePath}.tmp`;
310
- writeFileSync(tmpPath, content, { encoding: "utf-8", flag: "wx" });
311
- try {
312
- renameSync(tmpPath, rulesFilePath);
313
- }
314
- catch (err) {
315
- try {
316
- unlinkSync(tmpPath);
317
- }
318
- catch {
319
- /* best-effort cleanup */
320
- }
321
- throw err;
322
- }
340
+ writeFileAtomicSync(rulesFilePath, content, { encoding: "utf-8" });
323
341
  }
324
342
  /**
325
343
  * Handles errors from rules file write operations. EACCES → warning + instructions.
@@ -335,7 +353,7 @@ function handleRulesWriteError(err, rulesFilePath, indent) {
335
353
  `${indent} Run with elevated permissions or create the file manually.\n\n`);
336
354
  return 0;
337
355
  }
338
- if (code === "ELOOP" || code === "EEXIST") {
356
+ if (code === "ELOOP") {
339
357
  process.stderr.write(`${indent}[error] Bridge rules — suspicious path condition (${code}): ${rulesFilePath}\n\n`);
340
358
  return 1;
341
359
  }
@@ -424,7 +442,25 @@ Options:
424
442
  const backupPath = `${targetPath}.${ts}.bak`;
425
443
  try {
426
444
  renameSync(targetPath, backupPath);
427
- renameSync(tmpPath, targetPath);
445
+ try {
446
+ renameSync(tmpPath, targetPath);
447
+ }
448
+ catch (renameErr) {
449
+ if (renameErr.code === "EEXIST" &&
450
+ process.platform === "win32") {
451
+ // Concurrent writer recreated targetPath between the two renames.
452
+ try {
453
+ unlinkSync(targetPath);
454
+ }
455
+ catch {
456
+ /* best-effort */
457
+ }
458
+ renameSync(tmpPath, targetPath);
459
+ }
460
+ else {
461
+ throw renameErr;
462
+ }
463
+ }
428
464
  }
429
465
  catch (err) {
430
466
  try {
@@ -443,7 +479,30 @@ Options:
443
479
  encoding: "utf-8",
444
480
  flag: "wx",
445
481
  });
446
- renameSync(`${targetPath}.tmp`, targetPath);
482
+ try {
483
+ renameSync(`${targetPath}.tmp`, targetPath);
484
+ }
485
+ catch (renameErr) {
486
+ if (renameErr.code === "EEXIST" &&
487
+ process.platform === "win32") {
488
+ try {
489
+ unlinkSync(targetPath);
490
+ }
491
+ catch {
492
+ /* best-effort */
493
+ }
494
+ renameSync(`${targetPath}.tmp`, targetPath);
495
+ }
496
+ else {
497
+ try {
498
+ unlinkSync(`${targetPath}.tmp`);
499
+ }
500
+ catch {
501
+ /* best-effort */
502
+ }
503
+ throw renameErr;
504
+ }
505
+ }
447
506
  }
448
507
  process.stderr.write(`✓ Bridge workflow section written to ${targetPath} (v${PACKAGE_VERSION})\n`);
449
508
  // Also write bridge-tools rules file alongside CLAUDE.md
@@ -493,50 +552,31 @@ Options:
493
552
  const portIdx = argv.indexOf("--port");
494
553
  const portArg = portIdx !== -1 ? argv[portIdx + 1] : undefined;
495
554
  const lockDir = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "ide");
496
- let lockFile;
555
+ // Select a *running bridge* lock (isBridge:true + live PID) rather than the
556
+ // most-recently-touched lock in ~/.claude/ide, which can be an IDE-owned
557
+ // lock or a dead bridge.
558
+ let bridgeLock;
497
559
  if (portArg) {
498
- lockFile = path.join(lockDir, `${portArg}.lock`);
499
- if (!existsSync(lockFile)) {
500
- process.stderr.write(`Error: No lock file found for port ${portArg} at ${lockFile}\n`);
560
+ const port = Number(portArg);
561
+ bridgeLock = findAllLiveBridges().find((b) => b.port === port) ?? null;
562
+ if (!bridgeLock) {
563
+ process.stderr.write(`Error: No running bridge found for port ${portArg} (no lock file for that port, the lock is IDE-owned, or its process is not alive).\n`);
501
564
  process.exit(1);
502
565
  }
503
566
  }
504
567
  else {
505
- // Find the most recently modified lock file
506
- let bestMtime = 0;
507
- try {
508
- for (const f of readdirSync(lockDir)) {
509
- if (!f.endsWith(".lock"))
510
- continue;
511
- const full = path.join(lockDir, f);
512
- const mtime = statSync(full).mtimeMs;
513
- if (mtime > bestMtime) {
514
- bestMtime = mtime;
515
- lockFile = full;
516
- }
517
- }
518
- }
519
- catch {
520
- // lock dir doesn't exist — handled below
521
- }
568
+ bridgeLock = findBridgeLock();
522
569
  }
523
- if (!lockFile) {
524
- process.stderr.write(`Error: No bridge lock file found in ${lockDir}\n`);
570
+ if (!bridgeLock) {
571
+ process.stderr.write(`Error: No running bridge lock file found in ${lockDir}\n`);
525
572
  process.stderr.write("Make sure the bridge is running first, or pass --port <port>.\n");
526
573
  process.exit(1);
527
574
  }
528
- try {
529
- const data = JSON.parse(readFileSync(lockFile, "utf-8"));
530
- if (!data.authToken) {
531
- process.stderr.write(`Error: Lock file ${lockFile} has no authToken field\n`);
532
- process.exit(1);
533
- }
534
- process.stdout.write(`${data.authToken}\n`);
535
- }
536
- catch {
537
- process.stderr.write(`Error: Could not read lock file ${lockFile}\n`);
575
+ if (!bridgeLock.authToken) {
576
+ process.stderr.write(`Error: Bridge lock for port ${bridgeLock.port} has no authToken field\n`);
538
577
  process.exit(1);
539
578
  }
579
+ process.stdout.write(`${bridgeLock.authToken}\n`);
540
580
  process.exit(0);
541
581
  }
542
582
  // Handle notify subcommand — called from CC hooks to fire bridge automation
@@ -558,63 +598,51 @@ if (process.argv[2] === "notify") {
558
598
  }
559
599
  const notifyRest = notifyArgv.slice(1);
560
600
  const namedArgs = {};
561
- for (let i = 0; i < notifyRest.length - 1; i++) {
601
+ // Iterate the full list (not length-1) so a trailing bare `--flag` with no
602
+ // following value is still recorded rather than silently dropped
603
+ // (cli-commands-5). A flag whose next token is another `--flag` (or which is
604
+ // last) is treated as an empty-valued flag, matching the existing `?? ""`.
605
+ for (let i = 0; i < notifyRest.length; i++) {
562
606
  const arg = notifyRest[i];
563
607
  if (arg?.startsWith("--")) {
564
608
  const key = arg.slice(2);
565
- const val = notifyRest[i + 1] ?? "";
566
- namedArgs[key] = val;
567
- i++;
609
+ const next = notifyRest[i + 1];
610
+ if (next !== undefined && !next.startsWith("--")) {
611
+ namedArgs[key] = next;
612
+ i++;
613
+ }
614
+ else {
615
+ namedArgs[key] = "";
616
+ }
568
617
  }
569
618
  }
570
619
  const notifyLockDir = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "ide");
571
- let notifyLockFile;
620
+ // Select a *running bridge* lock (isBridge:true + live PID) rather than the
621
+ // most-recently-touched lock, which can be an IDE-owned or dead-bridge lock.
572
622
  let notifyPort;
623
+ let notifyToken;
573
624
  if (namedArgs.port) {
574
- notifyPort = Number(namedArgs.port);
575
- notifyLockFile = path.join(notifyLockDir, `${notifyPort}.lock`);
576
- if (!existsSync(notifyLockFile)) {
577
- process.stderr.write(`Error: No lock file found for port ${notifyPort}\n`);
625
+ const port = Number(namedArgs.port);
626
+ const lock = findAllLiveBridges().find((b) => b.port === port);
627
+ if (!lock) {
628
+ process.stderr.write(`Error: No running bridge found for port ${namedArgs.port} (no lock for that port, the lock is IDE-owned, or its process is not alive).\n`);
578
629
  process.exit(1);
579
630
  }
631
+ notifyPort = lock.port;
632
+ notifyToken = lock.authToken;
580
633
  }
581
634
  else {
582
- let bestMtime = 0;
583
- try {
584
- for (const f of readdirSync(notifyLockDir)) {
585
- if (!f.endsWith(".lock"))
586
- continue;
587
- const full = path.join(notifyLockDir, f);
588
- const mtime = statSync(full).mtimeMs;
589
- if (mtime > bestMtime) {
590
- bestMtime = mtime;
591
- notifyLockFile = full;
592
- notifyPort = Number(path.basename(f, ".lock"));
593
- }
594
- }
595
- }
596
- catch {
597
- // lock dir doesn't exist
635
+ const lock = findBridgeLock();
636
+ if (lock) {
637
+ notifyPort = lock.port;
638
+ notifyToken = lock.authToken;
598
639
  }
599
640
  }
600
- if (!notifyLockFile || !notifyPort) {
601
- process.stderr.write(`Error: No bridge lock file found in ${notifyLockDir}\n`);
641
+ if (!notifyPort || !notifyToken) {
642
+ process.stderr.write(`Error: No running bridge lock file found in ${notifyLockDir}\n`);
602
643
  process.stderr.write("Make sure the bridge is running first (claude-ide-bridge --watch ...).\n");
603
644
  process.exit(1);
604
645
  }
605
- let notifyToken;
606
- try {
607
- const data = JSON.parse(readFileSync(notifyLockFile, "utf-8"));
608
- if (!data.authToken) {
609
- process.stderr.write(`Error: Lock file has no authToken field\n`);
610
- process.exit(1);
611
- }
612
- notifyToken = data.authToken;
613
- }
614
- catch {
615
- process.stderr.write(`Error: Could not read lock file ${notifyLockFile}\n`);
616
- process.exit(1);
617
- }
618
646
  try {
619
647
  const resp = await fetch(`http://127.0.0.1:${notifyPort}/notify`, {
620
648
  method: "POST",
@@ -1171,18 +1199,41 @@ if (process.argv[2] === "recipe" && process.argv[3] === "run") {
1171
1199
  const { findBridgeLock } = await import("./bridgeLockDiscovery.js");
1172
1200
  const lock = localFlag ? null : findBridgeLock();
1173
1201
  if (lock && !dryRun && !step && !explicitFile && !attemptId) {
1174
- const res = await fetch(`http://127.0.0.1:${lock.port}/recipes/run`, {
1175
- method: "POST",
1176
- headers: {
1177
- Authorization: `Bearer ${lock.authToken}`,
1178
- "Content-Type": "application/json",
1179
- },
1180
- body: JSON.stringify({
1181
- name: recipeArg,
1182
- ...(seedVars ? { vars: seedVars } : {}),
1183
- }),
1184
- });
1185
- const body = (await res.json());
1202
+ // 30s per-request deadline — the bridge can pass the findBridgeLock
1203
+ // PID check yet be wedged on HTTP. Without this abort the fetch
1204
+ // blocks forever. Mirrors the AbortController pattern used by every
1205
+ // sibling bridge call (halts, judgments, recipe doctor, kill-switch).
1206
+ const controller = new AbortController();
1207
+ const timer = setTimeout(() => controller.abort(), 30_000);
1208
+ let res;
1209
+ let body;
1210
+ try {
1211
+ res = await fetch(`http://127.0.0.1:${lock.port}/recipes/run`, {
1212
+ method: "POST",
1213
+ headers: {
1214
+ Authorization: `Bearer ${lock.authToken}`,
1215
+ "Content-Type": "application/json",
1216
+ },
1217
+ body: JSON.stringify({
1218
+ name: recipeArg,
1219
+ ...(seedVars ? { vars: seedVars } : {}),
1220
+ }),
1221
+ signal: controller.signal,
1222
+ });
1223
+ body = (await res.json());
1224
+ }
1225
+ catch (err) {
1226
+ const aborted = err instanceof Error &&
1227
+ (err.name === "AbortError" || controller.signal.aborted);
1228
+ process.stderr.write(aborted
1229
+ ? `Error: bridge on port ${lock.port} did not respond within 30s — it may be wedged. Restart the bridge or re-run with --local.\n`
1230
+ : `Error: failed to reach bridge on port ${lock.port}: ${err instanceof Error ? err.message : String(err)}\n`);
1231
+ process.exit(1);
1232
+ return;
1233
+ }
1234
+ finally {
1235
+ clearTimeout(timer);
1236
+ }
1186
1237
  if (!body.ok) {
1187
1238
  // Fall through to local YAML runner if bridge doesn't know the recipe.
1188
1239
  if (!(body.error ?? "").includes("not found")) {
@@ -1442,6 +1493,8 @@ if (process.argv[2] === "traces" && process.argv[3] === "export") {
1442
1493
  let output;
1443
1494
  let patchworkDir;
1444
1495
  let activityDir;
1496
+ let mode;
1497
+ let passphrase;
1445
1498
  for (let i = 0; i < args.length; i++) {
1446
1499
  const a = args[i];
1447
1500
  if (a === "--output" || a === "-o") {
@@ -1456,10 +1509,30 @@ if (process.argv[2] === "traces" && process.argv[3] === "export") {
1456
1509
  activityDir = args[i + 1];
1457
1510
  i++;
1458
1511
  }
1512
+ else if (a === "--mode") {
1513
+ const m = args[i + 1];
1514
+ if (m !== "public" && m !== "keyed") {
1515
+ process.stderr.write(`Error: --mode must be "public" or "keyed" (got: ${m})\n`);
1516
+ process.exit(1);
1517
+ }
1518
+ mode = m;
1519
+ i++;
1520
+ }
1521
+ else if (a === "--passphrase") {
1522
+ passphrase = args[i + 1];
1523
+ i++;
1524
+ }
1459
1525
  else if (a === "--help" || a === "-h") {
1460
- process.stdout.write("patchwork traces export [--output <path>] [--patchwork-dir <dir>] [--activity-dir <dir>]\n\n" +
1526
+ process.stdout.write("patchwork traces export [--output <path>] [--mode public|keyed]\n" +
1527
+ " [--passphrase <phrase>]\n" +
1528
+ " [--patchwork-dir <dir>] [--activity-dir <dir>]\n\n" +
1461
1529
  "Bundles ~/.patchwork/{runs,decision_traces,commit_issue_links}.jsonl\n" +
1462
1530
  "and ~/.claude/ide/activity-*.jsonl into a single gzipped JSONL file.\n\n" +
1531
+ "Modes:\n" +
1532
+ " public (default) Plain gzip bundle (.jsonl.gz). Anyone with the file\n" +
1533
+ " can read it.\n" +
1534
+ " keyed AES-256-GCM encrypted bundle (.enc). Requires --passphrase;\n" +
1535
+ " auto-detected and decrypted by `traces import --passphrase`.\n\n" +
1463
1536
  "Output is a manifest line followed by one envelope per row:\n" +
1464
1537
  ' {"type":"manifest", ...}\n' +
1465
1538
  ' {"source":"runs", "entry":{...}}\n' +
@@ -1469,11 +1542,17 @@ if (process.argv[2] === "traces" && process.argv[3] === "export") {
1469
1542
  process.exit(0);
1470
1543
  }
1471
1544
  }
1545
+ if (mode === "keyed" && !passphrase) {
1546
+ process.stderr.write("Error: --mode keyed requires --passphrase <phrase>\n");
1547
+ process.exit(1);
1548
+ }
1472
1549
  const { runTracesExport } = await import("./commands/tracesExport.js");
1473
1550
  const result = await runTracesExport({
1474
1551
  ...(output !== undefined && { output }),
1475
1552
  ...(patchworkDir !== undefined && { patchworkDir }),
1476
1553
  ...(activityDir !== undefined && { activityDir }),
1554
+ ...(mode !== undefined && { mode }),
1555
+ ...(passphrase !== undefined && { passphrase }),
1477
1556
  });
1478
1557
  process.stdout.write(` ✓ Wrote ${result.outputPath}\n`);
1479
1558
  process.stdout.write(` ${result.totalCount} rows from ${result.files.length} file${result.files.length === 1 ? "" : "s"} (${result.totalBytes} bytes read)\n`);
@@ -1569,6 +1648,17 @@ if (process.argv[2] === "traces" && process.argv[3] === "import") {
1569
1648
  writeFileSync(tmp, plain, { mode: 0o600 });
1570
1649
  input = tmp;
1571
1650
  process.stderr.write("Decryption succeeded.\n");
1651
+ // Ensure tmp is removed after import regardless of success/failure.
1652
+ // On Windows mode:0o600 is a no-op (NTFS doesn't honour POSIX bits),
1653
+ // so the plaintext would persist in %TEMP% until the next reboot.
1654
+ process.once("exit", () => {
1655
+ try {
1656
+ unlinkSync(tmp);
1657
+ }
1658
+ catch {
1659
+ /* best-effort */
1660
+ }
1661
+ });
1572
1662
  }
1573
1663
  }
1574
1664
  const { runTracesImport } = await import("./commands/tracesImport.js");
@@ -1722,7 +1812,7 @@ if (process.argv[2] === "kill-switch") {
1722
1812
  ? ` [env-locked: ${j.lockedReason ?? "yes"}]`
1723
1813
  : "";
1724
1814
  const wsLabel = lock.workspace
1725
- ? lock.workspace.split("/").slice(-2).join("/")
1815
+ ? path.join(path.basename(path.dirname(lock.workspace)), path.basename(lock.workspace))
1726
1816
  : `pid=${lock.pid}`;
1727
1817
  process.stdout.write(` ${engaged} port=${lock.port} ${wsLabel}${lockedSuffix}\n`);
1728
1818
  }
@@ -1738,7 +1828,7 @@ if (process.argv[2] === "kill-switch") {
1738
1828
  ...(reason ? { reason } : {}),
1739
1829
  });
1740
1830
  const wsLabel = lock.workspace
1741
- ? lock.workspace.split("/").slice(-2).join("/")
1831
+ ? path.join(path.basename(path.dirname(lock.workspace)), path.basename(lock.workspace))
1742
1832
  : `pid=${lock.pid}`;
1743
1833
  if (result.status === 409) {
1744
1834
  anyFailed = true;
@@ -1948,6 +2038,20 @@ if (process.argv[2] === "halts") {
1948
2038
  }
1949
2039
  })();
1950
2040
  }
2041
+ // `patchwork connect` — connector front-door. Thin CLI→HTTP shim over the
2042
+ // bridge's `/connections/*` routes (list / OAuth auth / PAT connect / test /
2043
+ // disconnect). This is the command the 23 connector "Run: patchwork connect
2044
+ // <vendor>" error messages point at. Lock discovery reuses the same
2045
+ // isBridge:true + live-PID selector as `recipe run`, `quick-task`, etc.
2046
+ if (process.argv[2] === "connect") {
2047
+ (async () => {
2048
+ const { runConnect } = await import("./commands/connect.js");
2049
+ const { findBridgeLockForTask } = await import("./commands/task.js");
2050
+ await runConnect(process.argv.slice(3), {
2051
+ findBridgeLock: (port) => findBridgeLockForTask(port),
2052
+ });
2053
+ })();
2054
+ }
1951
2055
  // `patchwork analytics` — manage the self-hosted telemetry collector config.
1952
2056
  // Replaces the brittle "endpoint+secret in launchd plist" pattern with a
1953
2057
  // proper config file the bridge reads at startup.
@@ -2415,6 +2519,134 @@ if (process.argv[2] === "recipe" && process.argv[3] === "preflight") {
2415
2519
  }
2416
2520
  })();
2417
2521
  }
2522
+ // `recipe simulate <name|file>` — What-If Preview. Statically simulates a
2523
+ // recipe before it is enabled: projected actions, side-effect taxonomy,
2524
+ // blast-radius risk, tier-only approval projection (honestly flagged as NOT
2525
+ // gated on recipe steps today), low-confidence cost, and undetermined
2526
+ // conditional branches. Executes nothing. See runRecipeSimulate in
2527
+ // commands/recipe.ts.
2528
+ if (process.argv[2] === "recipe" && process.argv[3] === "simulate") {
2529
+ const args = process.argv.slice(4);
2530
+ const usage = "Usage: patchwork recipe simulate <name|file.yaml> [--json] [--step <id>] [--var k=v]\n\n" +
2531
+ "What-If Preview: statically simulate a recipe before enabling it.\n" +
2532
+ "Shows projected actions, side effects, risk, approvals (tier-only),\n" +
2533
+ "cost (low-confidence), and undetermined branches. Executes nothing.\n";
2534
+ let json = false;
2535
+ let step;
2536
+ const vars = {};
2537
+ let target;
2538
+ for (let i = 0; i < args.length; i++) {
2539
+ const arg = args[i];
2540
+ if (arg === undefined)
2541
+ continue;
2542
+ if (arg === "--json") {
2543
+ json = true;
2544
+ continue;
2545
+ }
2546
+ if (arg === "--step" || arg.startsWith("--step=")) {
2547
+ const value = arg === "--step" ? args[++i] : arg.slice("--step=".length);
2548
+ if (!value) {
2549
+ process.stderr.write(`Error: --step requires a value\n${usage}`);
2550
+ process.exit(1);
2551
+ }
2552
+ step = value;
2553
+ continue;
2554
+ }
2555
+ if (arg === "--var" || arg.startsWith("--var=")) {
2556
+ const value = arg === "--var" ? args[++i] : arg.slice("--var=".length);
2557
+ if (!value?.includes("=")) {
2558
+ process.stderr.write(`Error: --var requires k=v\n${usage}`);
2559
+ process.exit(1);
2560
+ }
2561
+ const idx = value.indexOf("=");
2562
+ vars[value.slice(0, idx)] = value.slice(idx + 1);
2563
+ continue;
2564
+ }
2565
+ if (!arg.startsWith("--")) {
2566
+ target = arg;
2567
+ }
2568
+ }
2569
+ if (!target) {
2570
+ process.stderr.write(usage);
2571
+ process.exit(1);
2572
+ }
2573
+ (async () => {
2574
+ try {
2575
+ const { runRecipeSimulate } = await import("./commands/recipe.js");
2576
+ const report = await runRecipeSimulate(target, {
2577
+ ...(step ? { step } : {}),
2578
+ ...(Object.keys(vars).length ? { vars } : {}),
2579
+ });
2580
+ if (json) {
2581
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
2582
+ process.exit(0);
2583
+ }
2584
+ const out = process.stdout;
2585
+ const mark = (tier) => tier === "high" ? "●" : tier === "medium" ? "◐" : "○";
2586
+ const fidelityLabel = report.fidelity === "mocked"
2587
+ ? `mocked fidelity, ${report.sampleRuns ?? 0} prior run(s)`
2588
+ : `${report.fidelity} fidelity`;
2589
+ out.write(`\n What-If Preview — ${report.recipe} (${report.topology}, ${fidelityLabel})\n`);
2590
+ out.write(` Trigger: ${report.triggerType} · ${report.summary.totalSteps} step(s)\n\n`);
2591
+ out.write(` Risk: ${report.risk.tier.toUpperCase()} (score ${report.risk.score}/100)\n`);
2592
+ const c = report.risk.components;
2593
+ out.write(` high:${c.highSteps} medium:${c.mediumSteps} writes:${c.writeSteps} connector-writes:${c.connectorWriteSteps} http:${c.externalHttpSteps} unresolved:${c.unresolvedSteps}\n\n`);
2594
+ out.write(" Actions:\n");
2595
+ for (const s of report.steps) {
2596
+ const tool = s.tool ?? s.type;
2597
+ const when = s.condition ? ` when: ${s.condition}` : "";
2598
+ out.write(` ${mark(s.effectiveRisk)} ${s.id} ${tool} [${s.sideEffect}]${when}\n`);
2599
+ }
2600
+ out.write("\n");
2601
+ const ns = report.summary.connectorNamespaces;
2602
+ out.write(` Side effects: ${report.summary.writeSteps} write(s) · ${report.summary.connectorSteps} connector call(s)${ns.length ? ` (${ns.join(", ")})` : ""} · ${report.summary.agentSteps} agent step(s)\n`);
2603
+ const gating = report.approvals.projected.filter((a) => a.wouldRequireApproval).length;
2604
+ out.write(` Approvals: ${gating} step(s) would gate IF recipe steps were gated — they are NOT gated today\n`);
2605
+ {
2606
+ const c = report.cost;
2607
+ const toks = typeof c.estInputTokens === "number"
2608
+ ? ` ~${c.estInputTokens} in / ${c.estOutputTokens ?? 0} out tokens`
2609
+ : typeof c.estPromptTokens === "number"
2610
+ ? ` ~${c.estPromptTokens} input tokens`
2611
+ : "";
2612
+ const usd = typeof c.usd === "number"
2613
+ ? ` ≈$${c.usd.toFixed(4)}${typeof c.minUsd === "number" && typeof c.maxUsd === "number"
2614
+ ? ` ($${c.minUsd.toFixed(4)}–$${c.maxUsd.toFixed(4)})`
2615
+ : ""}`
2616
+ : "";
2617
+ const conf = c.confidence ? `/${c.confidence}` : "";
2618
+ out.write(` Cost [${c.basis}${conf}]:${toks}${usd}\n`);
2619
+ out.write(` ${c.note}\n`);
2620
+ }
2621
+ if (report.branches.length > 0) {
2622
+ if (report.fidelity === "mocked") {
2623
+ const taken = report.branches.filter((b) => b.outcome === "taken").length;
2624
+ const skipped = report.branches.filter((b) => b.outcome === "skipped").length;
2625
+ const undet = report.branches.filter((b) => b.outcome === "undetermined").length;
2626
+ out.write(` Branches: ${report.branches.length} conditional — ${taken} taken, ${skipped} skipped, ${undet} undetermined\n`);
2627
+ for (const b of report.branches) {
2628
+ out.write(` ${b.outcome.padEnd(12)} ${b.stepId} ${b.condition}\n`);
2629
+ }
2630
+ }
2631
+ else {
2632
+ out.write(` Branches: ${report.branches.length} conditional — undetermined (resolve in a later sandbox phase)\n`);
2633
+ }
2634
+ }
2635
+ if (report.lint.errors.length > 0 || report.lint.warnings.length > 0) {
2636
+ out.write(` Lint: ${report.lint.errors.length} error(s), ${report.lint.warnings.length} warning(s)\n`);
2637
+ }
2638
+ out.write("\n Notes:\n");
2639
+ for (const n of report.notes)
2640
+ out.write(` • ${n}\n`);
2641
+ out.write("\n");
2642
+ process.exit(0);
2643
+ }
2644
+ catch (err) {
2645
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
2646
+ process.exit(1);
2647
+ }
2648
+ })();
2649
+ }
2418
2650
  // `recipe doctor <name|file>` — one-screen "why is this recipe unhealthy
2419
2651
  // + how do I fix it" diagnosis. Composes the static preflight check with
2420
2652
  // the recipe-scoped runtime halt summary from a live bridge (fail-soft:
@@ -2473,7 +2705,42 @@ if (process.argv[2] === "recipe" && process.argv[3] === "doctor") {
2473
2705
  }
2474
2706
  return null;
2475
2707
  };
2476
- const result = await runRecipeDoctor(ref, { fetchHalts });
2708
+ // Connector auth fetcher: walk live bridges, GET /connections to read
2709
+ // each connector's status. Returns null when no bridge is reachable so
2710
+ // doctor degrades to static-only (lists required connectors, no auth).
2711
+ const fetchConnections = localOnly
2712
+ ? undefined
2713
+ : async () => {
2714
+ const { findAllLiveBridges } = await import("./bridgeLockDiscovery.js");
2715
+ const liveLocks = findAllLiveBridges();
2716
+ if (liveLocks.length === 0)
2717
+ return null;
2718
+ for (const lock of liveLocks) {
2719
+ const controller = new AbortController();
2720
+ const timer = setTimeout(() => controller.abort(), 10_000);
2721
+ try {
2722
+ const res = await fetch(`http://127.0.0.1:${lock.port}/connections`, {
2723
+ headers: { Authorization: `Bearer ${lock.authToken}` },
2724
+ signal: controller.signal,
2725
+ });
2726
+ if (res.ok) {
2727
+ const body = (await res.json());
2728
+ return body.connectors ?? [];
2729
+ }
2730
+ }
2731
+ catch {
2732
+ /* unreachable lock — try next */
2733
+ }
2734
+ finally {
2735
+ clearTimeout(timer);
2736
+ }
2737
+ }
2738
+ return null;
2739
+ };
2740
+ const result = await runRecipeDoctor(ref, {
2741
+ fetchHalts,
2742
+ fetchConnections,
2743
+ });
2477
2744
  if (wantJson) {
2478
2745
  process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
2479
2746
  }
@@ -2943,9 +3210,19 @@ Steps performed:
2943
3210
  // claude -p spawns the stdio command via Node's child_process, which
2944
3211
  // can't resolve a bare `.cmd` shim on Windows. Record the `.cmd` form
2945
3212
  // on win32 so the bridge binary is findable by the spawned process.
3213
+ //
3214
+ // This global entry stays even though the project also gets a
3215
+ // `patchwork` MCP server (project .mcp.json / project config) covering
3216
+ // this same workspace — VS Code/Windsurf/Cursor launches inject
3217
+ // --mcp-config, which overrides any project .mcp.json entirely, so
3218
+ // this ~/.claude.json entry is the ONLY way bridge tools are wired
3219
+ // under an IDE launch. Pin --workspace so that when multiple bridges
3220
+ // are running (this one plus others elsewhere), the workspace-aware
3221
+ // lock discovery in mcp-stdio-shim.cjs picks THIS project's lock
3222
+ // instead of falling back to cwd-based guessing or the wrong bridge.
2946
3223
  mcpServers["claude-ide-bridge"] = {
2947
3224
  command: ensureCmdShim("claude-ide-bridge"),
2948
- args: ["shim"],
3225
+ args: ["shim", "--workspace", workspace],
2949
3226
  type: "stdio",
2950
3227
  };
2951
3228
  claudeJson.mcpServers = mcpServers;
@@ -2956,10 +3233,21 @@ Steps performed:
2956
3233
  }
2957
3234
  }
2958
3235
  catch {
2959
- process.stderr.write(` [warn] Could not update ${claudeJsonAbs} — add manually:\n { "mcpServers": { "claude-ide-bridge": { "command": "claude-ide-bridge", "args": ["shim"] } } }\n\n`);
3236
+ process.stderr.write(` [warn] Could not update ${claudeJsonAbs} — add manually:\n { "mcpServers": { "claude-ide-bridge": { "command": "claude-ide-bridge", "args": ["shim", "--workspace", "${workspace}"] } } }\n\n`);
2960
3237
  }
2961
3238
  // Step 3b: Wire CC hooks in ~/.claude/settings.json
2962
- const ccSettingsPath = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "settings.json");
3239
+ const ccConfigDir = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
3240
+ const ccSettingsPath = path.join(ccConfigDir, "settings.json");
3241
+ // On a truly fresh machine ~/.claude/ may not exist yet. Every settings.json
3242
+ // writer below (CC hooks, the IDE-skip env step, the PreToolUse hook) does an
3243
+ // atomic write that needs the parent dir to exist — without this, all three
3244
+ // silently fail with ENOENT and the user's onboarding state is never persisted.
3245
+ try {
3246
+ mkdirSync(ccConfigDir, { recursive: true });
3247
+ }
3248
+ catch {
3249
+ // best-effort — the individual writers below surface their own warnings
3250
+ }
2963
3251
  const CC_HOOK_NOTIFY_CMDS = {
2964
3252
  PreCompact: "claude-ide-bridge notify PreCompact",
2965
3253
  PostCompact: "claude-ide-bridge notify PostCompact",
@@ -3021,6 +3309,33 @@ Steps performed:
3021
3309
  process.stderr.write(` [warn] Could not update ${ccSettingsPath} — add CC hook entries manually.\n` +
3022
3310
  ` See CLAUDE.md Automation Policy section for the settings.json snippet.\n\n`);
3023
3311
  }
3312
+ // Step 3c: Persist CLAUDE_CODE_IDE_SKIP_VALID_CHECK into the settings.json
3313
+ // `env` block so `claude --ide` connects without the user prefixing the var
3314
+ // on every launch. The bridge writes its own ~/.claude/ide/<port>.lock, but
3315
+ // Claude Code's `--ide` "valid check" is an in-IDE-terminal detection that a
3316
+ // standalone bridge can't satisfy by lock-file shape alone — so we set the
3317
+ // skip flag where Patchwork already manages CC config (NOT the shell rc).
3318
+ try {
3319
+ const { registerSkipIdeValidCheckEnv } = await import("./settingsEnv.js");
3320
+ const envResult = registerSkipIdeValidCheckEnv(ccSettingsPath);
3321
+ if (envResult.action === "added") {
3322
+ process.stderr.write(` ✓ IDE connect — set CLAUDE_CODE_IDE_SKIP_VALID_CHECK=true in ${ccSettingsPath}\n You can now run a plain \`claude --ide\` — no env-var prefix needed.\n\n`);
3323
+ }
3324
+ else if (envResult.action === "already-present") {
3325
+ process.stderr.write(` ✓ IDE connect — CLAUDE_CODE_IDE_SKIP_VALID_CHECK already set in ${ccSettingsPath}\n\n`);
3326
+ }
3327
+ else if (envResult.action === "preserved-user-value") {
3328
+ process.stderr.write(` [info] IDE connect — left your CLAUDE_CODE_IDE_SKIP_VALID_CHECK="${envResult.existingValue}" untouched.\n` +
3329
+ ` If \`claude --ide\` won't connect, set it to "true" in ${ccSettingsPath}.\n\n`);
3330
+ }
3331
+ else {
3332
+ process.stderr.write(` [warn] IDE connect — could not set CLAUDE_CODE_IDE_SKIP_VALID_CHECK (${envResult.error}).\n` +
3333
+ ` Add { "env": { "CLAUDE_CODE_IDE_SKIP_VALID_CHECK": "true" } } to ${ccSettingsPath} manually.\n\n`);
3334
+ }
3335
+ }
3336
+ catch (err) {
3337
+ process.stderr.write(` [warn] IDE connect — ${err instanceof Error ? err.message : String(err)}\n\n`);
3338
+ }
3024
3339
  // Patchwork: register PreToolUse approval hook so the dashboard can
3025
3340
  // approve/reject CC tool calls in real time.
3026
3341
  try {
@@ -3077,8 +3392,14 @@ Steps performed:
3077
3392
  "Next steps:\n" +
3078
3393
  ` 1. Start the bridge: claude-ide-bridge --watch (runs in this workspace)\n` +
3079
3394
  " 2. Restart your IDE once so it picks up the new extension + MCP config.\n" +
3080
- " 3. Open Claude Code and type /mcp — the claude-ide-bridge server should show as connected.\n" +
3081
- " 4. Type /ide to see live workspace state (open editors, diagnostics, git status).\n\n");
3395
+ " 3. Connect Claude Code: claude --ide (the IDE-detection skip flag is\n" +
3396
+ " already set in ~/.claude/settings.json — no env-var prefix needed).\n" +
3397
+ " 4. Type /mcp — the claude-ide-bridge server should show as connected,\n" +
3398
+ " then /ide to see live workspace state (open editors, diagnostics, git).\n" +
3399
+ " 5. Run your first recipe (no connectors needed — local git + a brief):\n" +
3400
+ " patchwork recipe run daily-status --local\n" +
3401
+ " Writes a morning brief to ~/.patchwork/inbox/. Try --dry-run first to\n" +
3402
+ " see the plan without spending tokens.\n\n");
3082
3403
  if (inWorktree) {
3083
3404
  process.stdout.write("⚠ This workspace is a linked git worktree. If this is a Cowork session,\n" +
3084
3405
  " bridge MCP tools are unreachable from inside Cowork itself — run\n" +
@@ -3126,11 +3447,19 @@ Steps performed:
3126
3447
  ? fallbackDocs
3127
3448
  : null;
3128
3449
  if (target) {
3129
- // Use execFile with argv (no shell) — exec(`code "${target}"`) was
3130
- // shell-evaluated and could be injected via `--workspace '"; ...'`
3131
- // since path.resolve preserves shell metachars. Audit 2026-05-17.
3132
- const { execFile } = await import("node:child_process");
3133
- execFile("code", [target], { timeout: 3000 }, () => { });
3450
+ // Use execFile with argv — exec(`code "${target}"`) was shell-evaluated
3451
+ // and could be injected via `--workspace '"; ...'`. Audit 2026-05-17.
3452
+ // Windows: code is a .cmd shim; shell:true lets cmd.exe resolve it
3453
+ // without needing ensureCmdShim (args-as-array prevents injection).
3454
+ // Wrap in try-catch: in Node 22 on Windows, spawn errors can throw
3455
+ // synchronously before the callback is registered.
3456
+ try {
3457
+ const { execFile } = await import("node:child_process");
3458
+ execFile("code", [target], { timeout: 3000, shell: process.platform === "win32" }, () => { });
3459
+ }
3460
+ catch {
3461
+ // best-effort — VS Code may not be installed
3462
+ }
3134
3463
  }
3135
3464
  }
3136
3465
  // Analytics opt-in prompt — only ask once; skip if preference already set
@@ -3298,12 +3627,25 @@ Options:
3298
3627
  process.exit(0);
3299
3628
  }
3300
3629
  const portIdx = argv.indexOf("--port");
3301
- const portArg = portIdx !== -1 ? argv[portIdx + 1] : undefined;
3630
+ // --port wins over PATCHWORK_BRIDGE_PORT wins over workspace-aware discovery.
3631
+ const portArg = portIdx !== -1 ? argv[portIdx + 1] : process.env.PATCHWORK_BRIDGE_PORT;
3302
3632
  const jsonFlag = argv.includes("--json");
3303
3633
  const lockDir = path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude"), "ide");
3304
3634
  let lockFile;
3305
3635
  let lockPort;
3306
3636
  if (portArg) {
3637
+ // Validate before path.join — path.join does not block traversal, so an
3638
+ // unsanitized `--port ../../../etc/passwd` would read an arbitrary
3639
+ // `*.lock` file outside lockDir and leak its JSON contents
3640
+ // (cli-commands-1).
3641
+ const portNum = Number(portArg);
3642
+ if (!/^\d{1,5}$/.test(portArg) ||
3643
+ !Number.isInteger(portNum) ||
3644
+ portNum < 1 ||
3645
+ portNum > 65535) {
3646
+ process.stderr.write("Error: --port must be a valid port number\n");
3647
+ process.exit(1);
3648
+ }
3307
3649
  lockFile = path.join(lockDir, `${portArg}.lock`);
3308
3650
  lockPort = portArg;
3309
3651
  if (!existsSync(lockFile)) {
@@ -3312,22 +3654,17 @@ Options:
3312
3654
  }
3313
3655
  }
3314
3656
  else {
3315
- let bestMtime = 0;
3316
- try {
3317
- for (const f of readdirSync(lockDir)) {
3318
- if (!f.endsWith(".lock"))
3319
- continue;
3320
- const full = path.join(lockDir, f);
3321
- const mtime = statSync(full).mtimeMs;
3322
- if (mtime > bestMtime) {
3323
- bestMtime = mtime;
3324
- lockFile = full;
3325
- lockPort = f.replace(".lock", "");
3326
- }
3327
- }
3328
- }
3329
- catch {
3330
- // lock dir doesn't exist
3657
+ // Workspace-aware, isBridge-filtered, live-PID-filtered discovery — the
3658
+ // same helper the MCP shim and task runner use (#1052/#1054 fixed the
3659
+ // shim's copy of this bug; this was a separate, unfixed copy that instead
3660
+ // picked the newest-mtime `.lock` file in the directory with no isBridge
3661
+ // check, so an unrelated editor's IDE-owned lock — or a stale bridge from
3662
+ // a different workspace — could shadow the real bridge's status).
3663
+ const { findBridgeLock } = await import("./bridgeLockDiscovery.js");
3664
+ const found = findBridgeLock({ lockDir });
3665
+ if (found) {
3666
+ lockFile = path.join(lockDir, `${found.port}.lock`);
3667
+ lockPort = String(found.port);
3331
3668
  }
3332
3669
  }
3333
3670
  if (!lockFile) {
@@ -3510,6 +3847,206 @@ if (process.argv[2] === "shadow-scan") {
3510
3847
  }
3511
3848
  })();
3512
3849
  }
3850
+ // Handle workers subcommand — read-only worker trust-dial (shadow). Replays the
3851
+ // recipe run log + gate decision log through the (worker × action-class) ramp
3852
+ // and prints the dial + a "ramp would vs gate did" comparison. Changes nothing.
3853
+ if (process.argv[2] === "workers") {
3854
+ const args = process.argv.slice(3);
3855
+ const sub = args[0];
3856
+ if ((sub !== "shadow" && sub !== "backtest") ||
3857
+ args.includes("--help") ||
3858
+ args.includes("-h")) {
3859
+ process.stdout.write("Usage: patchwork workers <shadow|backtest> [--workers-dir <path>]\n\n" +
3860
+ " shadow Read-only worker trust dial: replays ~/.patchwork/runs.jsonl\n" +
3861
+ " + the gate decision log through the (worker × action-class)\n" +
3862
+ " ramp. Computes what the ramp WOULD decide vs what the gate DID.\n" +
3863
+ " backtest Replays each worker's history and reports DIVERGENCE: where the\n" +
3864
+ " ramp would have auto-run a bad action (false-allow, the risk) or\n" +
3865
+ " gated a good one (false-gate, the cost). Calibration, not a\n" +
3866
+ " success rate. Neither command changes a live decision.\n\n" +
3867
+ " --workers-dir <path> Where *.worker.yaml live (default ~/.patchwork/workers)\n");
3868
+ process.exit(0);
3869
+ }
3870
+ (async () => {
3871
+ try {
3872
+ const dirIdx = args.indexOf("--workers-dir");
3873
+ const workersDir = dirIdx !== -1 ? args[dirIdx + 1] : undefined;
3874
+ const { runWorkerShadowReport, runWorkerBacktest } = await import("./workers/runWorkerShadow.js");
3875
+ const opts = workersDir ? { workersDir } : {};
3876
+ process.stdout.write(sub === "backtest"
3877
+ ? runWorkerBacktest(opts)
3878
+ : runWorkerShadowReport(opts));
3879
+ }
3880
+ catch (err) {
3881
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
3882
+ process.exit(1);
3883
+ }
3884
+ })();
3885
+ }
3886
+ // Handle approvals subcommand — considered-approval KPI. Reads the local
3887
+ // approval-decision log directly (like `workers shadow`); no bridge round-trip.
3888
+ // The lens that tells you whether the trust climbing the worker dial was EARNED
3889
+ // or rubber-stamped: reject rate, latency-to-decision, abandoned, channel split.
3890
+ if (process.argv[2] === "approvals") {
3891
+ const args = process.argv.slice(3);
3892
+ if (args.includes("--help") || args.includes("-h")) {
3893
+ process.stdout.write("Usage: patchwork approvals [--window 1h|24h|overnight|7d|any] [--json]\n\n" +
3894
+ "Considered-approval KPI from the local approval-decision log:\n" +
3895
+ " reject rate · latency-to-decision · abandoned · dashboard-vs-phone.\n\n" +
3896
+ " --window <name> lookback (default: any). overnight = since 6pm yesterday.\n" +
3897
+ " --json emit raw JSON (for scripting)\n");
3898
+ process.exit(0);
3899
+ }
3900
+ (async () => {
3901
+ try {
3902
+ const wIdx = args.findIndex((a) => a === "--window" || a === "-w");
3903
+ const win = wIdx >= 0 && wIdx + 1 < args.length
3904
+ ? args[wIdx + 1]
3905
+ : "any";
3906
+ const fixed = {
3907
+ "1h": 3_600_000,
3908
+ "24h": 86_400_000,
3909
+ "7d": 604_800_000,
3910
+ any: null,
3911
+ };
3912
+ let sinceMs;
3913
+ if (win === "overnight") {
3914
+ const d = new Date();
3915
+ d.setHours(18, 0, 0, 0);
3916
+ if (d.getTime() > Date.now())
3917
+ d.setDate(d.getDate() - 1);
3918
+ sinceMs = d.getTime();
3919
+ }
3920
+ else if (Object.hasOwn(fixed, win)) {
3921
+ const dur = fixed[win];
3922
+ sinceMs = dur == null ? undefined : Date.now() - dur;
3923
+ }
3924
+ else {
3925
+ process.stderr.write(`Unknown --window value: "${win}"\n`);
3926
+ process.exit(1);
3927
+ }
3928
+ const { readConsideredDecisions, computeConsideredApprovalKpi, formatConsideredApprovalKpi, } = await import("./approvalKpi.js");
3929
+ const decisions = readConsideredDecisions(sinceMs !== undefined ? { sinceMs } : {});
3930
+ const kpi = computeConsideredApprovalKpi(decisions);
3931
+ process.stdout.write(args.includes("--json")
3932
+ ? `${JSON.stringify(kpi, null, 2)}\n`
3933
+ : `${formatConsideredApprovalKpi(kpi, { windowLabel: win })}\n`);
3934
+ process.exit(0);
3935
+ }
3936
+ catch (err) {
3937
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
3938
+ process.exit(1);
3939
+ }
3940
+ })();
3941
+ }
3942
+ // Handle gate subcommand — "why did the worker allow/gate THIS action" over
3943
+ // the local Decision Record (worker_gate_decisions.jsonl). Reads the log
3944
+ // directly (like `workers shadow` / `approvals`); no bridge round-trip needed
3945
+ // since the record is a local file, not live state.
3946
+ if (process.argv[2] === "gate") {
3947
+ const args = process.argv.slice(3);
3948
+ const sub = args[0];
3949
+ if (sub !== "explain" ||
3950
+ args.includes("--help") ||
3951
+ args.includes("-h") ||
3952
+ args.length < 3) {
3953
+ process.stdout.write("Usage: patchwork gate explain <workerId> <classKey> [--limit N] [--diff] [--json]\n\n" +
3954
+ "Explain the worker-autonomy gate's most recent decision(s) for a given\n" +
3955
+ "worker × action-class, from the local Decision Record\n" +
3956
+ "(~/.patchwork/worker_gate_decisions.jsonl) — no bridge required.\n\n" +
3957
+ ' <classKey> e.g. "issue:compensable:high" (domain:reversibility:blastTier)\n' +
3958
+ " --limit N show the N most recent decisions (default 1, or 2 with --diff)\n" +
3959
+ " --diff compare the 2 most recent decisions and show what changed\n" +
3960
+ " --json emit raw JSON (for scripting)\n");
3961
+ process.exit(sub === "explain" ? 1 : 0);
3962
+ }
3963
+ (async () => {
3964
+ try {
3965
+ // Length checked above (args.length < 3 short-circuits to usage), so
3966
+ // args[1]/[2] are present here — noUncheckedIndexedAccess just can't see
3967
+ // across the `if` block above.
3968
+ const workerId = args[1];
3969
+ const classKey = args[2];
3970
+ const wantDiff = args.includes("--diff");
3971
+ const limitIdx = args.indexOf("--limit");
3972
+ const explicitLimit = limitIdx >= 0 && limitIdx + 1 < args.length
3973
+ ? Number.parseInt(args[limitIdx + 1], 10)
3974
+ : undefined;
3975
+ const limit = explicitLimit ?? (wantDiff ? 2 : 1);
3976
+ if (!Number.isFinite(limit) || limit < 1) {
3977
+ process.stderr.write("Error: --limit must be a positive integer\n");
3978
+ process.exit(1);
3979
+ }
3980
+ const { WorkerGateDecisionLog, formatGateDecisionHistory, formatGateDecisionDiff, } = await import("./workerGateDecisionLog.js");
3981
+ const patchworkDir = process.env.PATCHWORK_HOME ?? path.join(os.homedir(), ".patchwork");
3982
+ const log = new WorkerGateDecisionLog({ dir: patchworkDir });
3983
+ const decisions = log.query({ workerId, classKey, limit });
3984
+ if (args.includes("--json")) {
3985
+ process.stdout.write(`${JSON.stringify(decisions, null, 2)}\n`);
3986
+ process.exit(0);
3987
+ }
3988
+ if (decisions.length === 0) {
3989
+ process.stdout.write(`No gate decisions found for worker "${workerId}" on class "${classKey}".\n` +
3990
+ "Either the worker hasn't acted on this class yet, or worker.autonomy is off.\n");
3991
+ process.exit(0);
3992
+ }
3993
+ if (wantDiff) {
3994
+ // query() sorts most-recent-first: newer is [0], older is [1].
3995
+ const [newer, older] = decisions;
3996
+ if (!newer || !older) {
3997
+ process.stdout.write(`Only one decision found for worker "${workerId}" on class "${classKey}" — nothing to diff against yet.\n`);
3998
+ process.exit(0);
3999
+ }
4000
+ process.stdout.write(`${formatGateDecisionDiff(newer, older)}\n`);
4001
+ process.exit(0);
4002
+ }
4003
+ process.stdout.write(`${formatGateDecisionHistory(decisions)}\n`);
4004
+ process.exit(0);
4005
+ }
4006
+ catch (err) {
4007
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
4008
+ process.exit(1);
4009
+ }
4010
+ })();
4011
+ }
4012
+ // Handle outcomes subcommand — operator positive-act confirmation of worker
4013
+ // filings (confirm/reject/list). Writes to ~/.patchwork/outcome-log.jsonl, the
4014
+ // same store the trust ramp reads; it is NOT a recipe step, so a worker cannot
4015
+ // self-confirm its own filings. No bridge required.
4016
+ if (process.argv[2] === "outcomes") {
4017
+ const args = process.argv.slice(3);
4018
+ (async () => {
4019
+ try {
4020
+ // `outcomes pending` — the confirm queue (join over runs + dispositions).
4021
+ // Handled here (not in the pure runOutcomesCli) because it needs the run
4022
+ // log + worker manifests, which live behind the shadow readers.
4023
+ if (args[0] === "pending") {
4024
+ const { computePendingConfirmations, formatPendingConfirmations } = await import("./workers/runWorkerShadow.js");
4025
+ const pending = computePendingConfirmations();
4026
+ if (args.includes("--json")) {
4027
+ process.stdout.write(`${JSON.stringify(pending, null, 2)}\n`);
4028
+ }
4029
+ else {
4030
+ process.stdout.write(formatPendingConfirmations(pending));
4031
+ }
4032
+ process.exit(0);
4033
+ }
4034
+ const { runOutcomesCli } = await import("./workers/outcomesCli.js");
4035
+ const { OutcomeStore, resolveOutcomeLogDir } = await import("./workers/outcomeStore.js");
4036
+ const store = new OutcomeStore(resolveOutcomeLogDir());
4037
+ const res = runOutcomesCli(args, { store, now: Date.now() });
4038
+ if (res.stdout)
4039
+ process.stdout.write(res.stdout);
4040
+ if (res.stderr)
4041
+ process.stderr.write(res.stderr);
4042
+ process.exit(res.exitCode);
4043
+ }
4044
+ catch (err) {
4045
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
4046
+ process.exit(1);
4047
+ }
4048
+ })();
4049
+ }
3513
4050
  // Handle launchd subcommand — install/uninstall macOS LaunchAgent for auto-start
3514
4051
  if (process.argv[2] === "launchd") {
3515
4052
  const sub = process.argv[3];