patchwork-os 0.2.0-beta.1 → 0.2.0-beta.10.canary.95

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 (652) hide show
  1. package/README.bridge.md +14 -14
  2. package/README.md +201 -34
  3. package/deploy/README.md +1 -1
  4. package/deploy/bootstrap-vps.sh +14 -9
  5. package/deploy/deploy-dashboard.sh +25 -1
  6. package/deploy/macos/README.md +153 -0
  7. package/deploy/macos/com.patchwork.bridge.plist.template +54 -0
  8. package/deploy/macos/com.patchwork.tunnel.plist.template +76 -0
  9. package/deploy/macos/install-mac-bridge.sh +244 -0
  10. package/deploy/macos/uninstall-mac-bridge.sh +22 -0
  11. package/dist/activityLog.d.ts +6 -0
  12. package/dist/activityLog.js +63 -26
  13. package/dist/activityLog.js.map +1 -1
  14. package/dist/adapters/claude.js +22 -16
  15. package/dist/adapters/claude.js.map +1 -1
  16. package/dist/adapters/gemini.js +15 -11
  17. package/dist/adapters/gemini.js.map +1 -1
  18. package/dist/adapters/openai.js +9 -9
  19. package/dist/adapters/openai.js.map +1 -1
  20. package/dist/ajv2020.d.ts +25 -0
  21. package/dist/ajv2020.js +33 -0
  22. package/dist/ajv2020.js.map +1 -0
  23. package/dist/analyticsAggregator.js +1 -1
  24. package/dist/analyticsAggregator.js.map +1 -1
  25. package/dist/analyticsConfig.d.ts +29 -0
  26. package/dist/analyticsConfig.js +102 -0
  27. package/dist/analyticsConfig.js.map +1 -0
  28. package/dist/analyticsPrefs.d.ts +38 -2
  29. package/dist/analyticsPrefs.js +148 -23
  30. package/dist/analyticsPrefs.js.map +1 -1
  31. package/dist/analyticsSend.d.ts +17 -1
  32. package/dist/analyticsSend.js +67 -5
  33. package/dist/analyticsSend.js.map +1 -1
  34. package/dist/approvalHttp.d.ts +14 -0
  35. package/dist/approvalHttp.js +212 -9
  36. package/dist/approvalHttp.js.map +1 -1
  37. package/dist/approvalInsights.js +13 -5
  38. package/dist/approvalInsights.js.map +1 -1
  39. package/dist/approvalQueue.d.ts +97 -3
  40. package/dist/approvalQueue.js +193 -19
  41. package/dist/approvalQueue.js.map +1 -1
  42. package/dist/approvalSignals.js +19 -11
  43. package/dist/approvalSignals.js.map +1 -1
  44. package/dist/automation.d.ts +35 -10
  45. package/dist/automation.js +133 -37
  46. package/dist/automation.js.map +1 -1
  47. package/dist/automationSuggestions.js +10 -8
  48. package/dist/automationSuggestions.js.map +1 -1
  49. package/dist/bridge.d.ts +2 -0
  50. package/dist/bridge.js +202 -25
  51. package/dist/bridge.js.map +1 -1
  52. package/dist/bridgeLockDiscovery.d.ts +27 -1
  53. package/dist/bridgeLockDiscovery.js +38 -11
  54. package/dist/bridgeLockDiscovery.js.map +1 -1
  55. package/dist/bridgeToken.js +3 -2
  56. package/dist/bridgeToken.js.map +1 -1
  57. package/dist/claudeDriver.js +23 -8
  58. package/dist/claudeDriver.js.map +1 -1
  59. package/dist/claudeOrchestrator.d.ts +1 -1
  60. package/dist/claudeOrchestrator.js +87 -44
  61. package/dist/claudeOrchestrator.js.map +1 -1
  62. package/dist/commands/analytics.d.ts +8 -0
  63. package/dist/commands/analytics.js +134 -0
  64. package/dist/commands/analytics.js.map +1 -0
  65. package/dist/commands/auditEnv.d.ts +36 -0
  66. package/dist/commands/auditEnv.js +202 -0
  67. package/dist/commands/auditEnv.js.map +1 -0
  68. package/dist/commands/dashboard.js +8 -1
  69. package/dist/commands/dashboard.js.map +1 -1
  70. package/dist/commands/doctor.d.ts +35 -0
  71. package/dist/commands/doctor.js +51 -0
  72. package/dist/commands/doctor.js.map +1 -0
  73. package/dist/commands/install.js +3 -0
  74. package/dist/commands/install.js.map +1 -1
  75. package/dist/commands/launchd.js +15 -16
  76. package/dist/commands/launchd.js.map +1 -1
  77. package/dist/commands/patchworkInit.d.ts +5 -0
  78. package/dist/commands/patchworkInit.js +89 -7
  79. package/dist/commands/patchworkInit.js.map +1 -1
  80. package/dist/commands/recipe.d.ts +110 -0
  81. package/dist/commands/recipe.js +512 -9
  82. package/dist/commands/recipe.js.map +1 -1
  83. package/dist/commands/recipeInstall.js +11 -4
  84. package/dist/commands/recipeInstall.js.map +1 -1
  85. package/dist/commands/shadowScan.d.ts +34 -0
  86. package/dist/commands/shadowScan.js +142 -0
  87. package/dist/commands/shadowScan.js.map +1 -0
  88. package/dist/commands/task.js +2 -2
  89. package/dist/commands/task.js.map +1 -1
  90. package/dist/commands/tools.d.ts +20 -1
  91. package/dist/commands/tools.js +112 -3
  92. package/dist/commands/tools.js.map +1 -1
  93. package/dist/commands/tracesImport.js +21 -4
  94. package/dist/commands/tracesImport.js.map +1 -1
  95. package/dist/commitIssueLinkLog.d.ts +16 -0
  96. package/dist/commitIssueLinkLog.js +110 -24
  97. package/dist/commitIssueLinkLog.js.map +1 -1
  98. package/dist/companions/registry.js +15 -7
  99. package/dist/companions/registry.js.map +1 -1
  100. package/dist/config.d.ts +47 -3
  101. package/dist/config.js +111 -21
  102. package/dist/config.js.map +1 -1
  103. package/dist/connectorRoutes.js +866 -57
  104. package/dist/connectorRoutes.js.map +1 -1
  105. package/dist/connectors/airtable.d.ts +230 -0
  106. package/dist/connectors/airtable.js +700 -0
  107. package/dist/connectors/airtable.js.map +1 -0
  108. package/dist/connectors/asana.js +13 -9
  109. package/dist/connectors/asana.js.map +1 -1
  110. package/dist/connectors/baseConnector.js +25 -3
  111. package/dist/connectors/baseConnector.js.map +1 -1
  112. package/dist/connectors/caldiy.d.ts +137 -0
  113. package/dist/connectors/caldiy.js +424 -0
  114. package/dist/connectors/caldiy.js.map +1 -0
  115. package/dist/connectors/circleci.d.ts +162 -0
  116. package/dist/connectors/circleci.js +576 -0
  117. package/dist/connectors/circleci.js.map +1 -0
  118. package/dist/connectors/cloudflare.d.ts +132 -0
  119. package/dist/connectors/cloudflare.js +622 -0
  120. package/dist/connectors/cloudflare.js.map +1 -0
  121. package/dist/connectors/confluence.js +35 -0
  122. package/dist/connectors/confluence.js.map +1 -1
  123. package/dist/connectors/connectorRedirectUri.d.ts +30 -0
  124. package/dist/connectors/connectorRedirectUri.js +38 -0
  125. package/dist/connectors/connectorRedirectUri.js.map +1 -0
  126. package/dist/connectors/connectorRegistry.d.ts +63 -0
  127. package/dist/connectors/connectorRegistry.js +354 -0
  128. package/dist/connectors/connectorRegistry.js.map +1 -0
  129. package/dist/connectors/datadog.js +33 -4
  130. package/dist/connectors/datadog.js.map +1 -1
  131. package/dist/connectors/discord.js +14 -10
  132. package/dist/connectors/discord.js.map +1 -1
  133. package/dist/connectors/elasticsearch.d.ts +141 -0
  134. package/dist/connectors/elasticsearch.js +601 -0
  135. package/dist/connectors/elasticsearch.js.map +1 -0
  136. package/dist/connectors/figma.d.ts +130 -0
  137. package/dist/connectors/figma.js +387 -0
  138. package/dist/connectors/figma.js.map +1 -0
  139. package/dist/connectors/github.d.ts +17 -0
  140. package/dist/connectors/github.js +53 -2
  141. package/dist/connectors/github.js.map +1 -1
  142. package/dist/connectors/gitlab.js +12 -6
  143. package/dist/connectors/gitlab.js.map +1 -1
  144. package/dist/connectors/gmail.js +72 -21
  145. package/dist/connectors/gmail.js.map +1 -1
  146. package/dist/connectors/googleCalendar.js +8 -8
  147. package/dist/connectors/googleCalendar.js.map +1 -1
  148. package/dist/connectors/googleDocs.d.ts +103 -0
  149. package/dist/connectors/googleDocs.js +409 -0
  150. package/dist/connectors/googleDocs.js.map +1 -0
  151. package/dist/connectors/googleDrive.d.ts +12 -0
  152. package/dist/connectors/googleDrive.js +35 -8
  153. package/dist/connectors/googleDrive.js.map +1 -1
  154. package/dist/connectors/grafana.d.ts +133 -0
  155. package/dist/connectors/grafana.js +478 -0
  156. package/dist/connectors/grafana.js.map +1 -0
  157. package/dist/connectors/jira.d.ts +25 -0
  158. package/dist/connectors/jira.js +227 -1
  159. package/dist/connectors/jira.js.map +1 -1
  160. package/dist/connectors/linear.js +1 -1
  161. package/dist/connectors/linear.js.map +1 -1
  162. package/dist/connectors/mcpOAuth.js +78 -16
  163. package/dist/connectors/mcpOAuth.js.map +1 -1
  164. package/dist/connectors/monday.d.ts +217 -0
  165. package/dist/connectors/monday.js +655 -0
  166. package/dist/connectors/monday.js.map +1 -0
  167. package/dist/connectors/mongodb.d.ts +139 -0
  168. package/dist/connectors/mongodb.js +455 -0
  169. package/dist/connectors/mongodb.js.map +1 -0
  170. package/dist/connectors/oauthStateStore.d.ts +20 -0
  171. package/dist/connectors/oauthStateStore.js +94 -4
  172. package/dist/connectors/oauthStateStore.js.map +1 -1
  173. package/dist/connectors/obsidian.d.ts +83 -0
  174. package/dist/connectors/obsidian.js +441 -0
  175. package/dist/connectors/obsidian.js.map +1 -0
  176. package/dist/connectors/paystack.d.ts +159 -0
  177. package/dist/connectors/paystack.js +607 -0
  178. package/dist/connectors/paystack.js.map +1 -0
  179. package/dist/connectors/pipedrive.d.ts +189 -0
  180. package/dist/connectors/pipedrive.js +559 -0
  181. package/dist/connectors/pipedrive.js.map +1 -0
  182. package/dist/connectors/postgres.d.ts +127 -0
  183. package/dist/connectors/postgres.js +512 -0
  184. package/dist/connectors/postgres.js.map +1 -0
  185. package/dist/connectors/posthog.d.ts +119 -0
  186. package/dist/connectors/posthog.js +479 -0
  187. package/dist/connectors/posthog.js.map +1 -0
  188. package/dist/connectors/redis.d.ts +140 -0
  189. package/dist/connectors/redis.js +571 -0
  190. package/dist/connectors/redis.js.map +1 -0
  191. package/dist/connectors/resend.d.ts +131 -0
  192. package/dist/connectors/resend.js +529 -0
  193. package/dist/connectors/resend.js.map +1 -0
  194. package/dist/connectors/salesforce.d.ts +136 -0
  195. package/dist/connectors/salesforce.js +603 -0
  196. package/dist/connectors/salesforce.js.map +1 -0
  197. package/dist/connectors/secrets.d.ts +51 -0
  198. package/dist/connectors/secrets.js +102 -0
  199. package/dist/connectors/secrets.js.map +1 -0
  200. package/dist/connectors/sendgrid.d.ts +102 -0
  201. package/dist/connectors/sendgrid.js +423 -0
  202. package/dist/connectors/sendgrid.js.map +1 -0
  203. package/dist/connectors/shopify.d.ts +145 -0
  204. package/dist/connectors/shopify.js +502 -0
  205. package/dist/connectors/shopify.js.map +1 -0
  206. package/dist/connectors/slack.d.ts +1 -1
  207. package/dist/connectors/slack.js +61 -9
  208. package/dist/connectors/slack.js.map +1 -1
  209. package/dist/connectors/snowflake.d.ts +119 -0
  210. package/dist/connectors/snowflake.js +615 -0
  211. package/dist/connectors/snowflake.js.map +1 -0
  212. package/dist/connectors/supabase.d.ts +92 -0
  213. package/dist/connectors/supabase.js +630 -0
  214. package/dist/connectors/supabase.js.map +1 -0
  215. package/dist/connectors/todoist.d.ts +117 -0
  216. package/dist/connectors/todoist.js +485 -0
  217. package/dist/connectors/todoist.js.map +1 -0
  218. package/dist/connectors/tokenStorage.js +56 -14
  219. package/dist/connectors/tokenStorage.js.map +1 -1
  220. package/dist/connectors/twilio.d.ts +118 -0
  221. package/dist/connectors/twilio.js +475 -0
  222. package/dist/connectors/twilio.js.map +1 -0
  223. package/dist/connectors/vercel.d.ts +110 -0
  224. package/dist/connectors/vercel.js +479 -0
  225. package/dist/connectors/vercel.js.map +1 -0
  226. package/dist/connectors/webflow.d.ts +118 -0
  227. package/dist/connectors/webflow.js +393 -0
  228. package/dist/connectors/webflow.js.map +1 -0
  229. package/dist/connectors/woocommerce.d.ts +220 -0
  230. package/dist/connectors/woocommerce.js +464 -0
  231. package/dist/connectors/woocommerce.js.map +1 -0
  232. package/dist/crypto.js +7 -2
  233. package/dist/crypto.js.map +1 -1
  234. package/dist/decisionReplay.js +15 -6
  235. package/dist/decisionReplay.js.map +1 -1
  236. package/dist/decisionTraceLog.d.ts +28 -0
  237. package/dist/decisionTraceLog.js +156 -29
  238. package/dist/decisionTraceLog.js.map +1 -1
  239. package/dist/drivers/claude/api.js +5 -4
  240. package/dist/drivers/claude/api.js.map +1 -1
  241. package/dist/drivers/claude/envSanitizer.d.ts +0 -6
  242. package/dist/drivers/claude/envSanitizer.js +17 -2
  243. package/dist/drivers/claude/envSanitizer.js.map +1 -1
  244. package/dist/drivers/claude/streamParser.js +5 -4
  245. package/dist/drivers/claude/streamParser.js.map +1 -1
  246. package/dist/drivers/claude/subprocess.js +22 -3
  247. package/dist/drivers/claude/subprocess.js.map +1 -1
  248. package/dist/drivers/gemini/index.d.ts +22 -0
  249. package/dist/drivers/gemini/index.js +256 -129
  250. package/dist/drivers/gemini/index.js.map +1 -1
  251. package/dist/drivers/local/index.d.ts +17 -0
  252. package/dist/drivers/local/index.js +99 -0
  253. package/dist/drivers/local/index.js.map +1 -1
  254. package/dist/drivers/openai/index.js +30 -2
  255. package/dist/drivers/openai/index.js.map +1 -1
  256. package/dist/extensionClient.d.ts +37 -4
  257. package/dist/extensionClient.js +58 -16
  258. package/dist/extensionClient.js.map +1 -1
  259. package/dist/featureFlags.d.ts +91 -0
  260. package/dist/featureFlags.js +174 -3
  261. package/dist/featureFlags.js.map +1 -1
  262. package/dist/fileLock.js +21 -12
  263. package/dist/fileLock.js.map +1 -1
  264. package/dist/fileLockSync.d.ts +67 -0
  265. package/dist/fileLockSync.js +126 -0
  266. package/dist/fileLockSync.js.map +1 -0
  267. package/dist/fp/activityAnalytics.js +26 -12
  268. package/dist/fp/activityAnalytics.js.map +1 -1
  269. package/dist/fp/automationInterpreter.d.ts +15 -1
  270. package/dist/fp/automationInterpreter.js +217 -82
  271. package/dist/fp/automationInterpreter.js.map +1 -1
  272. package/dist/fp/automationProgram.d.ts +30 -0
  273. package/dist/fp/automationProgram.js.map +1 -1
  274. package/dist/fp/automationState.d.ts +24 -5
  275. package/dist/fp/automationState.js +56 -9
  276. package/dist/fp/automationState.js.map +1 -1
  277. package/dist/fp/automationUtils.js +1 -1
  278. package/dist/fp/automationUtils.js.map +1 -1
  279. package/dist/fp/commandDescription.js +7 -1
  280. package/dist/fp/commandDescription.js.map +1 -1
  281. package/dist/fp/extensionSnapshot.js +8 -2
  282. package/dist/fp/extensionSnapshot.js.map +1 -1
  283. package/dist/fp/interpreterContext.d.ts +66 -1
  284. package/dist/fp/interpreterContext.js +91 -1
  285. package/dist/fp/interpreterContext.js.map +1 -1
  286. package/dist/fp/policyParser.js +29 -1
  287. package/dist/fp/policyParser.js.map +1 -1
  288. package/dist/fsWatchWithFallback.d.ts +36 -0
  289. package/dist/fsWatchWithFallback.js +128 -0
  290. package/dist/fsWatchWithFallback.js.map +1 -0
  291. package/dist/haltPushDispatch.d.ts +33 -0
  292. package/dist/haltPushDispatch.js +103 -0
  293. package/dist/haltPushDispatch.js.map +1 -0
  294. package/dist/httpBodyValidation.d.ts +41 -0
  295. package/dist/httpBodyValidation.js +45 -0
  296. package/dist/httpBodyValidation.js.map +1 -0
  297. package/dist/inboxRoutes.d.ts +22 -0
  298. package/dist/inboxRoutes.js +61 -1
  299. package/dist/inboxRoutes.js.map +1 -1
  300. package/dist/index.js +1053 -76
  301. package/dist/index.js.map +1 -1
  302. package/dist/installGuard.js +6 -2
  303. package/dist/installGuard.js.map +1 -1
  304. package/dist/lockfile.js +31 -4
  305. package/dist/lockfile.js.map +1 -1
  306. package/dist/oauth.d.ts +9 -0
  307. package/dist/oauth.js +33 -0
  308. package/dist/oauth.js.map +1 -1
  309. package/dist/oauthRoutes.d.ts +1 -1
  310. package/dist/oauthRoutes.js +5 -3
  311. package/dist/oauthRoutes.js.map +1 -1
  312. package/dist/orchestrator/childBridgeRegistry.js +29 -11
  313. package/dist/orchestrator/childBridgeRegistry.js.map +1 -1
  314. package/dist/patchworkConfig.d.ts +44 -1
  315. package/dist/patchworkConfig.js +28 -3
  316. package/dist/patchworkConfig.js.map +1 -1
  317. package/dist/pluginLoader.js +10 -1
  318. package/dist/pluginLoader.js.map +1 -1
  319. package/dist/pluginWatcher.js +12 -14
  320. package/dist/pluginWatcher.js.map +1 -1
  321. package/dist/preToolUseHook.js +3 -2
  322. package/dist/preToolUseHook.js.map +1 -1
  323. package/dist/processTree.d.ts +34 -0
  324. package/dist/processTree.js +105 -0
  325. package/dist/processTree.js.map +1 -0
  326. package/dist/prompts.js +3 -3
  327. package/dist/prompts.js.map +1 -1
  328. package/dist/recipeOrchestration.d.ts +9 -0
  329. package/dist/recipeOrchestration.js +341 -24
  330. package/dist/recipeOrchestration.js.map +1 -1
  331. package/dist/recipeRoutes.d.ts +58 -2
  332. package/dist/recipeRoutes.js +744 -115
  333. package/dist/recipeRoutes.js.map +1 -1
  334. package/dist/recipes/RecipeOrchestrator.d.ts +2 -0
  335. package/dist/recipes/RecipeOrchestrator.js +6 -1
  336. package/dist/recipes/RecipeOrchestrator.js.map +1 -1
  337. package/dist/recipes/agentExecutor.d.ts +25 -5
  338. package/dist/recipes/agentExecutor.js.map +1 -1
  339. package/dist/recipes/chainedRunner.d.ts +2 -0
  340. package/dist/recipes/chainedRunner.js +142 -5
  341. package/dist/recipes/chainedRunner.js.map +1 -1
  342. package/dist/recipes/connectorPreflight.d.ts +66 -0
  343. package/dist/recipes/connectorPreflight.js +169 -0
  344. package/dist/recipes/connectorPreflight.js.map +1 -0
  345. package/dist/recipes/dependencyGraph.js +13 -5
  346. package/dist/recipes/dependencyGraph.js.map +1 -1
  347. package/dist/recipes/githubInstallSource.d.ts +128 -0
  348. package/dist/recipes/githubInstallSource.js +206 -0
  349. package/dist/recipes/githubInstallSource.js.map +1 -0
  350. package/dist/recipes/haltCategory.d.ts +119 -0
  351. package/dist/recipes/haltCategory.js +188 -0
  352. package/dist/recipes/haltCategory.js.map +1 -0
  353. package/dist/recipes/idempotencyKey.d.ts +134 -0
  354. package/dist/recipes/idempotencyKey.js +322 -0
  355. package/dist/recipes/idempotencyKey.js.map +1 -0
  356. package/dist/recipes/installer.js +48 -2
  357. package/dist/recipes/installer.js.map +1 -1
  358. package/dist/recipes/judgeSummary.d.ts +50 -0
  359. package/dist/recipes/judgeSummary.js +47 -0
  360. package/dist/recipes/judgeSummary.js.map +1 -0
  361. package/dist/recipes/judgeVerdict.d.ts +48 -0
  362. package/dist/recipes/judgeVerdict.js +174 -0
  363. package/dist/recipes/judgeVerdict.js.map +1 -0
  364. package/dist/recipes/migrations/index.d.ts +9 -0
  365. package/dist/recipes/migrations/index.js +133 -0
  366. package/dist/recipes/migrations/index.js.map +1 -1
  367. package/dist/recipes/names.d.ts +20 -0
  368. package/dist/recipes/names.js +25 -0
  369. package/dist/recipes/names.js.map +1 -1
  370. package/dist/recipes/parser.js +88 -5
  371. package/dist/recipes/parser.js.map +1 -1
  372. package/dist/recipes/replayRun.js +1 -1
  373. package/dist/recipes/replayRun.js.map +1 -1
  374. package/dist/recipes/runBudget.d.ts +70 -0
  375. package/dist/recipes/runBudget.js +109 -0
  376. package/dist/recipes/runBudget.js.map +1 -0
  377. package/dist/recipes/scheduler.d.ts +30 -0
  378. package/dist/recipes/scheduler.js +69 -19
  379. package/dist/recipes/scheduler.js.map +1 -1
  380. package/dist/recipes/schema.d.ts +36 -0
  381. package/dist/recipes/schemaGenerator.js +103 -5
  382. package/dist/recipes/schemaGenerator.js.map +1 -1
  383. package/dist/recipes/stepObservation.js +9 -0
  384. package/dist/recipes/stepObservation.js.map +1 -1
  385. package/dist/recipes/toolRegistry.js +20 -3
  386. package/dist/recipes/toolRegistry.js.map +1 -1
  387. package/dist/recipes/tools/fanOut.d.ts +20 -0
  388. package/dist/recipes/tools/fanOut.js +199 -0
  389. package/dist/recipes/tools/fanOut.js.map +1 -0
  390. package/dist/recipes/tools/file.js +5 -2
  391. package/dist/recipes/tools/file.js.map +1 -1
  392. package/dist/recipes/tools/github.d.ts +1 -1
  393. package/dist/recipes/tools/github.js +75 -1
  394. package/dist/recipes/tools/github.js.map +1 -1
  395. package/dist/recipes/tools/gmail.js +27 -5
  396. package/dist/recipes/tools/gmail.js.map +1 -1
  397. package/dist/recipes/tools/googleDrive.js +64 -0
  398. package/dist/recipes/tools/googleDrive.js.map +1 -1
  399. package/dist/recipes/tools/http.d.ts +10 -0
  400. package/dist/recipes/tools/http.js +176 -0
  401. package/dist/recipes/tools/http.js.map +1 -0
  402. package/dist/recipes/tools/index.d.ts +2 -0
  403. package/dist/recipes/tools/index.js +2 -0
  404. package/dist/recipes/tools/index.js.map +1 -1
  405. package/dist/recipes/tools/slack.js +1 -1
  406. package/dist/recipes/validation.d.ts +17 -0
  407. package/dist/recipes/validation.js +29 -11
  408. package/dist/recipes/validation.js.map +1 -1
  409. package/dist/recipes/workspaceRoot.d.ts +37 -0
  410. package/dist/recipes/workspaceRoot.js +73 -0
  411. package/dist/recipes/workspaceRoot.js.map +1 -0
  412. package/dist/recipes/yamlPositions.d.ts +56 -0
  413. package/dist/recipes/yamlPositions.js +183 -0
  414. package/dist/recipes/yamlPositions.js.map +1 -0
  415. package/dist/recipes/yamlRunner.d.ts +182 -8
  416. package/dist/recipes/yamlRunner.js +877 -217
  417. package/dist/recipes/yamlRunner.js.map +1 -1
  418. package/dist/recipesHttp.d.ts +19 -3
  419. package/dist/recipesHttp.js +67 -11
  420. package/dist/recipesHttp.js.map +1 -1
  421. package/dist/resources.js +21 -13
  422. package/dist/resources.js.map +1 -1
  423. package/dist/runLog.d.ts +58 -5
  424. package/dist/runLog.js +98 -21
  425. package/dist/runLog.js.map +1 -1
  426. package/dist/sanitizeParsedJson.d.ts +39 -0
  427. package/dist/sanitizeParsedJson.js +55 -0
  428. package/dist/sanitizeParsedJson.js.map +1 -0
  429. package/dist/schemas/recipe.v1.json +885 -196
  430. package/dist/server.d.ts +160 -3
  431. package/dist/server.js +1099 -122
  432. package/dist/server.js.map +1 -1
  433. package/dist/sessionCheckpoint.d.ts +8 -0
  434. package/dist/sessionCheckpoint.js +33 -3
  435. package/dist/sessionCheckpoint.js.map +1 -1
  436. package/dist/ssrfGuard.d.ts +16 -0
  437. package/dist/ssrfGuard.js +87 -4
  438. package/dist/ssrfGuard.js.map +1 -1
  439. package/dist/streamableHttp.d.ts +9 -4
  440. package/dist/streamableHttp.js +76 -20
  441. package/dist/streamableHttp.js.map +1 -1
  442. package/dist/telemetry.js +19 -12
  443. package/dist/telemetry.js.map +1 -1
  444. package/dist/testing/shadowRun.d.ts +52 -0
  445. package/dist/testing/shadowRun.js +71 -0
  446. package/dist/testing/shadowRun.js.map +1 -0
  447. package/dist/tools/batchLsp.d.ts +3 -0
  448. package/dist/tools/bridgeDoctor.d.ts +11 -0
  449. package/dist/tools/bridgeDoctor.js +48 -2
  450. package/dist/tools/bridgeDoctor.js.map +1 -1
  451. package/dist/tools/bridgeStatus.d.ts +0 -12
  452. package/dist/tools/bridgeStatus.js +2 -28
  453. package/dist/tools/bridgeStatus.js.map +1 -1
  454. package/dist/tools/cancelClaudeTask.d.ts +1 -0
  455. package/dist/tools/clipboard.d.ts +2 -0
  456. package/dist/tools/closeTabs.d.ts +1 -0
  457. package/dist/tools/codeLens.d.ts +1 -0
  458. package/dist/tools/createIssueFromAIComment.d.ts +1 -0
  459. package/dist/tools/ctxGetTaskContext.d.ts +9 -0
  460. package/dist/tools/ctxGetTaskContext.js +50 -2
  461. package/dist/tools/ctxGetTaskContext.js.map +1 -1
  462. package/dist/tools/ctxQueryTraces.js +32 -24
  463. package/dist/tools/ctxQueryTraces.js.map +1 -1
  464. package/dist/tools/ctxSaveTrace.d.ts +1 -0
  465. package/dist/tools/debug.d.ts +4 -0
  466. package/dist/tools/decorations.d.ts +2 -0
  467. package/dist/tools/detectUnusedCode.js +9 -7
  468. package/dist/tools/detectUnusedCode.js.map +1 -1
  469. package/dist/tools/documentLinks.d.ts +1 -0
  470. package/dist/tools/editText.d.ts +1 -0
  471. package/dist/tools/editText.js +2 -1
  472. package/dist/tools/editText.js.map +1 -1
  473. package/dist/tools/enrichCommit.d.ts +1 -0
  474. package/dist/tools/enrichStackTrace.js +11 -5
  475. package/dist/tools/enrichStackTrace.js.map +1 -1
  476. package/dist/tools/explainDiagnostic.d.ts +1 -0
  477. package/dist/tools/explainSymbol.d.ts +1 -0
  478. package/dist/tools/fileOperations.d.ts +3 -0
  479. package/dist/tools/fileOperations.js +2 -1
  480. package/dist/tools/fileOperations.js.map +1 -1
  481. package/dist/tools/fileWatcher.d.ts +2 -0
  482. package/dist/tools/fileWatcher.js +8 -2
  483. package/dist/tools/fileWatcher.js.map +1 -1
  484. package/dist/tools/findFiles.d.ts +1 -0
  485. package/dist/tools/fixAllLintErrors.d.ts +1 -0
  486. package/dist/tools/fixAllLintErrors.js +10 -5
  487. package/dist/tools/fixAllLintErrors.js.map +1 -1
  488. package/dist/tools/foldingRanges.d.ts +1 -0
  489. package/dist/tools/formatDocument.d.ts +1 -0
  490. package/dist/tools/formatDocument.js +10 -5
  491. package/dist/tools/formatDocument.js.map +1 -1
  492. package/dist/tools/generateTests.d.ts +1 -0
  493. package/dist/tools/getAIComments.d.ts +1 -0
  494. package/dist/tools/getAnalyticsReport.js +5 -1
  495. package/dist/tools/getAnalyticsReport.js.map +1 -1
  496. package/dist/tools/getBufferContent.d.ts +1 -0
  497. package/dist/tools/getChangeImpact.d.ts +1 -0
  498. package/dist/tools/getChangeImpact.js +23 -14
  499. package/dist/tools/getChangeImpact.js.map +1 -1
  500. package/dist/tools/getClaudeTaskStatus.d.ts +1 -0
  501. package/dist/tools/getCodeCoverage.d.ts +1 -0
  502. package/dist/tools/getCodeCoverage.js +32 -14
  503. package/dist/tools/getCodeCoverage.js.map +1 -1
  504. package/dist/tools/getCommitsForIssue.d.ts +1 -0
  505. package/dist/tools/getDebugState.d.ts +1 -0
  506. package/dist/tools/getDiagnostics.js +32 -29
  507. package/dist/tools/getDiagnostics.js.map +1 -1
  508. package/dist/tools/getDiffFromHandoff.js +8 -2
  509. package/dist/tools/getDiffFromHandoff.js.map +1 -1
  510. package/dist/tools/getDocumentSymbols.d.ts +1 -0
  511. package/dist/tools/getGitHotspots.d.ts +1 -0
  512. package/dist/tools/getImportedSignatures.d.ts +1 -0
  513. package/dist/tools/getImportedSignatures.js +18 -14
  514. package/dist/tools/getImportedSignatures.js.map +1 -1
  515. package/dist/tools/getPRTemplate.d.ts +1 -0
  516. package/dist/tools/getProjectContext.js +23 -10
  517. package/dist/tools/getProjectContext.js.map +1 -1
  518. package/dist/tools/getSymbolHistory.d.ts +1 -0
  519. package/dist/tools/getToolCapabilities.d.ts +10 -5
  520. package/dist/tools/getToolCapabilities.js +79 -202
  521. package/dist/tools/getToolCapabilities.js.map +1 -1
  522. package/dist/tools/getTypeSignature.d.ts +1 -0
  523. package/dist/tools/getWorkspaceSettings.d.ts +1 -0
  524. package/dist/tools/gitWrite.d.ts +11 -0
  525. package/dist/tools/github/actions.d.ts +2 -0
  526. package/dist/tools/github/composite.d.ts +3 -0
  527. package/dist/tools/github/issues.d.ts +4 -0
  528. package/dist/tools/github/pr.d.ts +7 -0
  529. package/dist/tools/handoffNote.d.ts +1 -0
  530. package/dist/tools/handoffNote.js +2 -1
  531. package/dist/tools/handoffNote.js.map +1 -1
  532. package/dist/tools/headless/lspClient.js +3 -0
  533. package/dist/tools/headless/lspClient.js.map +1 -1
  534. package/dist/tools/hoverAtCursor.d.ts +1 -0
  535. package/dist/tools/httpClient.d.ts +2 -0
  536. package/dist/tools/httpClient.js +38 -71
  537. package/dist/tools/httpClient.js.map +1 -1
  538. package/dist/tools/inlayHints.d.ts +1 -0
  539. package/dist/tools/launchQuickTask.d.ts +1 -0
  540. package/dist/tools/listClaudeTasks.d.ts +1 -0
  541. package/dist/tools/listClaudeTasks.js +10 -8
  542. package/dist/tools/listClaudeTasks.js.map +1 -1
  543. package/dist/tools/listTerminals.d.ts +1 -0
  544. package/dist/tools/lsp.d.ts +15 -0
  545. package/dist/tools/lsp.js +17 -0
  546. package/dist/tools/lsp.js.map +1 -1
  547. package/dist/tools/navigateToSymbolByName.d.ts +1 -0
  548. package/dist/tools/openDiff.d.ts +1 -0
  549. package/dist/tools/openDiff.js +4 -1
  550. package/dist/tools/openDiff.js.map +1 -1
  551. package/dist/tools/openFile.d.ts +1 -0
  552. package/dist/tools/openFile.js +4 -1
  553. package/dist/tools/openFile.js.map +1 -1
  554. package/dist/tools/openInBrowser.js +6 -1
  555. package/dist/tools/openInBrowser.js.map +1 -1
  556. package/dist/tools/organizeImports.d.ts +1 -0
  557. package/dist/tools/organizeImports.js +5 -3
  558. package/dist/tools/organizeImports.js.map +1 -1
  559. package/dist/tools/performanceReport.js +5 -3
  560. package/dist/tools/performanceReport.js.map +1 -1
  561. package/dist/tools/planPersistence.d.ts +3 -0
  562. package/dist/tools/planPersistence.js +4 -1
  563. package/dist/tools/planPersistence.js.map +1 -1
  564. package/dist/tools/previewEdit.d.ts +1 -0
  565. package/dist/tools/previewEdit.js +15 -4
  566. package/dist/tools/previewEdit.js.map +1 -1
  567. package/dist/tools/recentTracesDigest.js +54 -11
  568. package/dist/tools/recentTracesDigest.js.map +1 -1
  569. package/dist/tools/refactorAnalyze.d.ts +1 -0
  570. package/dist/tools/refactorExtractFunction.js +4 -1
  571. package/dist/tools/refactorExtractFunction.js.map +1 -1
  572. package/dist/tools/refactorPreview.d.ts +1 -0
  573. package/dist/tools/refactorPreview.js +10 -2
  574. package/dist/tools/refactorPreview.js.map +1 -1
  575. package/dist/tools/replaceBlock.d.ts +1 -0
  576. package/dist/tools/replaceBlock.js +2 -1
  577. package/dist/tools/replaceBlock.js.map +1 -1
  578. package/dist/tools/resumeClaudeTask.d.ts +1 -0
  579. package/dist/tools/runClaudeTask.d.ts +1 -0
  580. package/dist/tools/runTests.js +15 -4
  581. package/dist/tools/runTests.js.map +1 -1
  582. package/dist/tools/screenshot.d.ts +1 -0
  583. package/dist/tools/screenshotAndAnnotate.js +6 -2
  584. package/dist/tools/screenshotAndAnnotate.js.map +1 -1
  585. package/dist/tools/searchAndReplace.d.ts +1 -0
  586. package/dist/tools/searchAndReplace.js +17 -7
  587. package/dist/tools/searchAndReplace.js.map +1 -1
  588. package/dist/tools/searchTools.js +20 -19
  589. package/dist/tools/searchTools.js.map +1 -1
  590. package/dist/tools/searchWorkspace.d.ts +1 -0
  591. package/dist/tools/selectionRanges.d.ts +1 -0
  592. package/dist/tools/semanticTokens.d.ts +1 -0
  593. package/dist/tools/signatureHelp.d.ts +1 -0
  594. package/dist/tools/spawnWorkspace.js +15 -7
  595. package/dist/tools/spawnWorkspace.js.map +1 -1
  596. package/dist/tools/terminal.d.ts +6 -0
  597. package/dist/tools/testRunners/pytest.js +6 -2
  598. package/dist/tools/testRunners/pytest.js.map +1 -1
  599. package/dist/tools/testRunners/vitestJest.js +3 -1
  600. package/dist/tools/testRunners/vitestJest.js.map +1 -1
  601. package/dist/tools/testTraceToSource.d.ts +1 -0
  602. package/dist/tools/transaction.d.ts +4 -0
  603. package/dist/tools/transaction.js +4 -1
  604. package/dist/tools/transaction.js.map +1 -1
  605. package/dist/tools/typeHierarchy.d.ts +1 -0
  606. package/dist/tools/utils.d.ts +18 -0
  607. package/dist/tools/utils.js +103 -18
  608. package/dist/tools/utils.js.map +1 -1
  609. package/dist/tools/vscodeCommands.d.ts +2 -0
  610. package/dist/tools/vscodeTasks.d.ts +2 -0
  611. package/dist/tools/workspaceSettings.d.ts +1 -0
  612. package/dist/transport.d.ts +3 -1
  613. package/dist/transport.js +103 -52
  614. package/dist/transport.js.map +1 -1
  615. package/dist/winShim.d.ts +34 -0
  616. package/dist/winShim.js +94 -0
  617. package/dist/winShim.js.map +1 -0
  618. package/dist/wireHaltPushDispatch.d.ts +38 -0
  619. package/dist/wireHaltPushDispatch.js +71 -0
  620. package/dist/wireHaltPushDispatch.js.map +1 -0
  621. package/dist/writeFileAtomic.d.ts +23 -0
  622. package/dist/writeFileAtomic.js +121 -0
  623. package/dist/writeFileAtomic.js.map +1 -0
  624. package/package.json +23 -7
  625. package/scripts/postinstall.mjs +42 -2
  626. package/scripts/smoke/run-all.mjs +213 -0
  627. package/scripts/start-all.mjs +572 -0
  628. package/scripts/start-all.ps1 +209 -0
  629. package/scripts/start-all.sh +73 -17
  630. package/scripts/start-orchestrator.ps1 +158 -0
  631. package/scripts/start-remote.mjs +122 -0
  632. package/templates/automation-policies/recipe-authoring.json +1 -1
  633. package/templates/automation-policies/security-first.json +1 -1
  634. package/templates/automation-policies/strict-lint.json +1 -1
  635. package/templates/automation-policies/test-driven.json +1 -1
  636. package/templates/automation-policy.example.json +1 -1
  637. package/templates/co.patchwork-os.bridge.plist +2 -2
  638. package/templates/recipes/approval-queue-ui-test.yaml +1 -1
  639. package/templates/recipes/ctx-loop-test.yaml +1 -1
  640. package/templates/recipes/fix-errors-on-save.yaml +71 -0
  641. package/templates/recipes/morning-brief.yaml +5 -2
  642. package/templates/recipes/project-health-check.yaml +4 -1
  643. package/templates/recipes/sentry-to-linear.yaml +72 -38
  644. package/templates/recipes/webhook/apple-watch-health-log.yaml +145 -0
  645. package/templates/recipes/webhook/customer-escalation.yaml +8 -9
  646. package/templates/recipes/webhook/meeting-prep.yaml +11 -5
  647. package/dist/commands/marketplace.d.ts +0 -16
  648. package/dist/commands/marketplace.js +0 -32
  649. package/dist/commands/marketplace.js.map +0 -1
  650. package/dist/recipes/legacyRecipeCompat.d.ts +0 -10
  651. package/dist/recipes/legacyRecipeCompat.js +0 -131
  652. package/dist/recipes/legacyRecipeCompat.js.map +0 -1
@@ -11,10 +11,11 @@ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
11
11
  import "../recipes/tools/index.js";
12
12
  import { loadFixtureLibrary } from "../connectors/fixtureLibrary.js";
13
13
  import { MockConnector } from "../connectors/mockConnector.js";
14
- import { defaultDeprecationWarn, normalizeRecipeForRuntime, } from "../recipes/legacyRecipeCompat.js";
14
+ import { HALT_CATEGORY_HINTS, HALT_CATEGORY_LABELS, } from "../recipes/haltCategory.js";
15
+ import { defaultDeprecationWarn, normalizeRecipeForRuntime, } from "../recipes/migrations/index.js";
15
16
  import { tryResolveRecipePath } from "../recipes/resolveRecipePath.js";
16
17
  import { generateSchemaSet, writeSchemas } from "../recipes/schemaGenerator.js";
17
- import { getTool, isConnectorNamespace, seedToolOutputPreviewContext, } from "../recipes/toolRegistry.js";
18
+ import { getTool, isConnectorNamespace, listConnectorNamespaces, listTools, seedToolOutputPreviewContext, } from "../recipes/toolRegistry.js";
18
19
  import { validateRecipeDefinition, } from "../recipes/validation.js";
19
20
  import { buildChainedDeps, dispatchRecipe, loadYamlRecipe, render, runYamlRecipe, } from "../recipes/yamlRunner.js";
20
21
  import { findYamlRecipePath } from "../recipesHttp.js";
@@ -113,6 +114,355 @@ export function runNew(options) {
113
114
  export function listTemplates() {
114
115
  return Object.keys(TEMPLATES);
115
116
  }
117
+ // ============================================================================
118
+ // patchwork recipe new --interactive
119
+ //
120
+ // Connector-aware prompt tree. Writes a valid recipe YAML based on
121
+ // user choices. Use a `node:readline/promises` adapter in real CLI;
122
+ // tests inject a stub `InteractivePromptDeps`.
123
+ // ============================================================================
124
+ const RECIPE_NAME_RE = /^[a-z0-9][a-z0-9_-]{1,63}$/;
125
+ const CRON_SHAPE_RE = /^(@every\s+\d+\s*(ms|s|m|h)|\S+\s+\S+\s+\S+\s+\S+\s+\S+)$/i;
126
+ async function askWithValidation(deps, question, validate) {
127
+ for (let attempt = 0; attempt < 5; attempt++) {
128
+ const answer = await deps.ask(question);
129
+ const error = validate(answer);
130
+ if (!error)
131
+ return answer;
132
+ // Surface error and re-ask via the question prompt itself.
133
+ question = `${error} ${question}`;
134
+ }
135
+ throw new Error("Too many invalid answers");
136
+ }
137
+ function yamlScalar(value) {
138
+ // Quote unless the string is a simple identifier-ish token. Conservative
139
+ // — when in doubt, JSON-encode (always parses as a YAML string).
140
+ if (/^[A-Za-z0-9_.\-/:]+$/.test(value))
141
+ return value;
142
+ return JSON.stringify(value);
143
+ }
144
+ /**
145
+ * Prompt the user for the tool's required JSON Schema fields. Optional
146
+ * properties are skipped — the user can hand-edit the YAML afterwards
147
+ * if they want to set defaults. `into` is excluded since the generator
148
+ * supplies its own name.
149
+ *
150
+ * Returns an ordered list of `[key, value]` pairs so the YAML output is
151
+ * stable across runs (insertion order of object keys is sufficient in
152
+ * V8 for string keys, but an array makes the contract explicit).
153
+ */
154
+ async function collectRequiredParams(deps, tool) {
155
+ const schema = tool.paramsSchema;
156
+ if (!schema || typeof schema !== "object")
157
+ return [];
158
+ const required = Array.isArray(schema.required)
159
+ ? schema.required.filter((k) => typeof k === "string" && k !== "into")
160
+ : [];
161
+ if (required.length === 0)
162
+ return [];
163
+ const out = [];
164
+ const properties = schema.properties ?? {};
165
+ for (const key of required) {
166
+ const propMeta = properties[key];
167
+ const hint = propMeta?.description ? ` — ${propMeta.description}` : "";
168
+ const value = await askWithValidation(deps, `${tool.id} → ${key}${hint}`, (a) => (a.trim().length > 0 ? null : "required, cannot be empty."));
169
+ out.push([key, value.trim()]);
170
+ }
171
+ return out;
172
+ }
173
+ /**
174
+ * AI-suggest path. Discovers the running bridge via lock file, POSTs
175
+ * the user's natural-language goal to `/recipes/generate`, writes the
176
+ * bridge's raw YAML response to disk (no form normalization — per
177
+ * memory project_recipe_audit_2026_05_06_part2), and validates
178
+ * post-hoc as warnings only.
179
+ *
180
+ * Failure modes:
181
+ * - No bridge running → clear actionable error
182
+ * - Bridge returns 503 → "AI generation unavailable — start the
183
+ * bridge with --driver subprocess"
184
+ * - Bridge returns 4xx → surface server error message
185
+ * - Bridge returns 200 but `result.yaml` is empty → write nothing,
186
+ * throw with the raw refusal text
187
+ */
188
+ async function runNewAiSuggest(options) {
189
+ const { deps } = options;
190
+ const name = await askWithValidation(deps, "Recipe name", (a) => RECIPE_NAME_RE.test(a)
191
+ ? null
192
+ : 'must match /^[a-z0-9][a-z0-9_-]{1,63}$/ (e.g. "morning-brief").');
193
+ const goal = await askWithValidation(deps, "Describe what the recipe should do (one-paragraph natural language)", (a) => (a.trim().length > 0 ? null : "goal cannot be empty."));
194
+ // Resolve bridge discovery + fetch — production defaults or test injection.
195
+ const findBridge = options.aiSuggest?.findBridge ??
196
+ (() => {
197
+ // Lazy import so non-AI paths don't pay the cost.
198
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
199
+ const mod = require("../bridgeLockDiscovery.js");
200
+ return mod.findBridgeLock();
201
+ });
202
+ const doFetch = options.aiSuggest?.fetch ?? globalThis.fetch;
203
+ const lock = findBridge();
204
+ if (!lock) {
205
+ throw new Error("AI generation requires a running bridge. Start one with `patchwork start` " +
206
+ "(or `claude-ide-bridge --driver subprocess` for the bridge-only mode), " +
207
+ "then retry.");
208
+ }
209
+ let response;
210
+ try {
211
+ response = await doFetch(`http://127.0.0.1:${lock.port}/recipes/generate`, {
212
+ method: "POST",
213
+ headers: {
214
+ "Content-Type": "application/json",
215
+ Authorization: `Bearer ${lock.authToken}`,
216
+ },
217
+ body: JSON.stringify({ prompt: goal.trim() }),
218
+ });
219
+ }
220
+ catch (err) {
221
+ throw new Error(`Could not reach the bridge at port ${lock.port}: ${err instanceof Error ? err.message : String(err)}`);
222
+ }
223
+ let payload;
224
+ try {
225
+ payload = (await response.json());
226
+ }
227
+ catch {
228
+ throw new Error(`Bridge returned non-JSON response (status ${response.status})`);
229
+ }
230
+ if (response.status === 503 || payload.unavailable) {
231
+ throw new Error("AI generation unavailable — start the bridge with `--driver subprocess`.");
232
+ }
233
+ if (!payload.ok) {
234
+ throw new Error(payload.error ?? `Bridge returned ${response.status}`);
235
+ }
236
+ const yamlBody = (payload.yaml ?? "").trim();
237
+ if (!yamlBody) {
238
+ throw new Error("Bridge returned empty YAML — try a more specific goal.");
239
+ }
240
+ // Ensure the SchemaStore pragma is present at line 1 so the file
241
+ // gets autocomplete on first open. If the bridge already prepended
242
+ // one, leave it.
243
+ const hasPragma = /^#\s*yaml-language-server:/.test(yamlBody);
244
+ const content = hasPragma
245
+ ? `${yamlBody}\n`
246
+ : `${RECIPE_SCHEMA_HEADER}\n${yamlBody}\n`;
247
+ if (deps.preview)
248
+ deps.preview(content);
249
+ const shouldWrite = await deps.confirm("Write to disk?");
250
+ if (!shouldWrite) {
251
+ throw new Error("Cancelled by user");
252
+ }
253
+ const outputDir = options.outputDir ?? RECIPES_DIR;
254
+ if (!existsSync(outputDir)) {
255
+ mkdirSync(outputDir, { recursive: true });
256
+ }
257
+ const outputPath = join(outputDir, `${name}.yaml`);
258
+ if (existsSync(outputPath)) {
259
+ throw new Error(`Recipe already exists: ${outputPath}`);
260
+ }
261
+ writeFileSync(outputPath, content);
262
+ // Lint post-hoc. Surface as warnings; never block — the user asked
263
+ // for a draft, the bridge produced it, the file is on disk.
264
+ let warnings = [];
265
+ try {
266
+ const parsed = parseYaml(content);
267
+ const lintResult = validateRecipeDefinition(parsed);
268
+ warnings = lintResult.issues ?? [];
269
+ }
270
+ catch (err) {
271
+ warnings = [
272
+ {
273
+ level: "warning",
274
+ message: `AI-generated YAML did not parse cleanly: ${err instanceof Error ? err.message : String(err)}`,
275
+ },
276
+ ];
277
+ }
278
+ return { path: outputPath, content, warnings };
279
+ }
280
+ export async function runNewInteractive(options) {
281
+ const { deps } = options;
282
+ // Mode pick — Guided (full prompt tree), Template (existing runNew
283
+ // templates: minimal | daily | inbox), or AI-suggest (asks the running
284
+ // bridge's /recipes/generate endpoint and writes the raw response).
285
+ //
286
+ // AI-suggest skips the form normalizer per memory
287
+ // project_recipe_audit_2026_05_06_part2 — applyAiYaml on the dashboard
288
+ // was lossy. Here we writeFileSync the bridge's `result.yaml` verbatim
289
+ // (only the SchemaStore pragma is prepended if absent) and validate
290
+ // post-hoc as warnings, never blocking the write.
291
+ const templates = listTemplates();
292
+ const modeChoices = [
293
+ "Guided — full prompt tree (recommended)",
294
+ `Template — pick from ${templates.length} starters (${templates.join(", ")})`,
295
+ "AI suggest — describe what you want; the bridge drafts a YAML",
296
+ ];
297
+ const modeIdx = await deps.pickFromList("How do you want to start?", modeChoices);
298
+ // AI-suggest mode: route through the running bridge's /recipes/generate.
299
+ if (modeIdx === 3) {
300
+ return runNewAiSuggest(options);
301
+ }
302
+ // Template mode: re-use the existing runNew() flow with the picked template.
303
+ if (modeIdx === 2) {
304
+ const name = await askWithValidation(deps, "Recipe name", (a) => RECIPE_NAME_RE.test(a)
305
+ ? null
306
+ : 'must match /^[a-z0-9][a-z0-9_-]{1,63}$/ (e.g. "morning-brief").');
307
+ const description = await askWithValidation(deps, "One-line description", (a) => (a.trim().length > 0 ? null : "cannot be empty."));
308
+ const templateIdx = await deps.pickFromList("Pick a template", templates);
309
+ const template = templates[templateIdx - 1];
310
+ if (!template)
311
+ throw new Error("Invalid template selection");
312
+ const result = runNew({
313
+ name,
314
+ description,
315
+ template,
316
+ ...(options.outputDir ? { outputDir: options.outputDir } : {}),
317
+ });
318
+ if (deps.preview)
319
+ deps.preview(result.content);
320
+ const shouldWrite = await deps.confirm("Keep this recipe?");
321
+ if (!shouldWrite) {
322
+ // runNew already wrote the file — clean up to honor "cancel."
323
+ try {
324
+ if (existsSync(result.path)) {
325
+ const { unlinkSync } = await import("node:fs");
326
+ unlinkSync(result.path);
327
+ }
328
+ }
329
+ catch {
330
+ // best effort
331
+ }
332
+ throw new Error("Cancelled by user");
333
+ }
334
+ return { path: result.path, content: result.content, warnings: [] };
335
+ }
336
+ // Guided mode (default — modeIdx === 1).
337
+ const name = await askWithValidation(deps, "Recipe name", (a) => RECIPE_NAME_RE.test(a)
338
+ ? null
339
+ : 'must match /^[a-z0-9][a-z0-9_-]{1,63}$/ (e.g. "morning-brief").');
340
+ const description = await askWithValidation(deps, "One-line description", (a) => (a.trim().length > 0 ? null : "cannot be empty."));
341
+ const triggerTypes = ["manual", "cron"];
342
+ const triggerIdx = await deps.pickFromList("Trigger type", triggerTypes.slice());
343
+ const triggerType = triggerTypes[triggerIdx - 1];
344
+ if (!triggerType)
345
+ throw new Error("Invalid trigger selection");
346
+ const triggerLines = [`trigger:`, ` type: ${triggerType}`];
347
+ if (triggerType === "cron") {
348
+ const cron = await askWithValidation(deps, "Cron expression (e.g. '0 8 * * *' for 8am daily)", (a) => CRON_SHAPE_RE.test(a.trim())
349
+ ? null
350
+ : 'expected 5-field cron or "@every Ns|Nm|Nh".');
351
+ triggerLines.push(` at: ${yamlScalar(cron.trim())}`);
352
+ }
353
+ // Step loop. The user adds tool / agent steps until they pick "done".
354
+ // Each tool pick reads its paramsSchema and prompts for required fields,
355
+ // so the generated recipe is closer to runnable than the MVP slice.
356
+ const namespaces = listConnectorNamespaces();
357
+ const stepLines = ["steps:"];
358
+ const intoNames = [];
359
+ let lastAgentInto = null;
360
+ let toolStepCount = 0;
361
+ let agentStepCount = 0;
362
+ const stepKindChoices = [
363
+ "Add a tool step (calls a registered tool — gmail.fetch_unread, github.list_issues, …)",
364
+ "Add an agent step (LLM prompt — drafts, classifies, summarizes)",
365
+ "Done — preview and write",
366
+ ];
367
+ for (let stepIdx = 0; stepIdx < 20; stepIdx++) {
368
+ const kind = await deps.pickFromList(stepIdx === 0
369
+ ? "Build steps — what's next?"
370
+ : "Add another step? (or pick Done)", stepKindChoices);
371
+ if (kind === 3)
372
+ break;
373
+ if (kind === 1) {
374
+ // Tool step
375
+ const nsIdx = await deps.pickFromList("Pick a connector", namespaces.slice());
376
+ const ns = namespaces[nsIdx - 1];
377
+ if (!ns)
378
+ throw new Error("Invalid connector selection");
379
+ const tools = listTools(ns);
380
+ const labels = tools.map((t) => `${t.id} — ${t.description}`);
381
+ const toolIdx = await deps.pickFromList(`Pick a ${ns} tool`, labels);
382
+ const tool = tools[toolIdx - 1];
383
+ if (!tool)
384
+ throw new Error(`Invalid tool selection for ${ns}`);
385
+ toolStepCount += 1;
386
+ const intoName = toolStepCount === 1 ? `${ns}_result` : `${ns}_result_${toolStepCount}`;
387
+ intoNames.push(intoName);
388
+ stepLines.push(` - tool: ${tool.id}`);
389
+ // Prompt for required params from the tool's JSON schema.
390
+ const params = await collectRequiredParams(deps, tool);
391
+ for (const [key, value] of params) {
392
+ stepLines.push(` ${key}: ${yamlScalar(value)}`);
393
+ }
394
+ stepLines.push(` into: ${intoName}`);
395
+ }
396
+ else if (kind === 2) {
397
+ // Agent step
398
+ const prompt = await askWithValidation(deps, "Agent prompt", (a) => a.trim().length > 0 ? null : "cannot be empty.");
399
+ agentStepCount += 1;
400
+ const intoName = agentStepCount === 1
401
+ ? "agent_output"
402
+ : `agent_output_${agentStepCount}`;
403
+ lastAgentInto = intoName;
404
+ stepLines.push(` - agent: true`);
405
+ stepLines.push(` prompt: |`);
406
+ for (const line of prompt.split("\n")) {
407
+ stepLines.push(` ${line}`);
408
+ }
409
+ stepLines.push(` into: ${intoName}`);
410
+ }
411
+ }
412
+ // Always tail with file.write to inbox so the user sees output somewhere.
413
+ // Reference the most recent agent output when one exists, otherwise fall
414
+ // back to the most recent tool result (or a TODO comment if neither).
415
+ const tailRef = lastAgentInto ?? intoNames[intoNames.length - 1] ?? null;
416
+ stepLines.push(` - tool: file.write`);
417
+ stepLines.push(` path: "~/.patchwork/inbox/${name}-{{date}}.md"`);
418
+ stepLines.push(` content: |`);
419
+ stepLines.push(` # ${name} — {{date}}`);
420
+ stepLines.push(``);
421
+ if (tailRef) {
422
+ stepLines.push(` {{${tailRef}}}`);
423
+ }
424
+ else {
425
+ stepLines.push(` (no steps configured — fill in)`);
426
+ }
427
+ const content = `${RECIPE_SCHEMA_HEADER}\n` +
428
+ `version: 1.0.0\n` +
429
+ `name: ${name}\n` +
430
+ `description: ${yamlScalar(description)}\n` +
431
+ `${triggerLines.join("\n")}\n` +
432
+ `${stepLines.join("\n")}\n`;
433
+ // Lint pre-write. validateRecipeDefinition expects parsed YAML.
434
+ let warnings = [];
435
+ try {
436
+ const parsed = parseYaml(content);
437
+ const lintResult = validateRecipeDefinition(parsed);
438
+ warnings = lintResult.issues ?? [];
439
+ }
440
+ catch {
441
+ // Parse failure here is a bug in the generator — surface but don't block.
442
+ warnings = [
443
+ {
444
+ level: "error",
445
+ message: "Generated YAML failed to parse — please report this.",
446
+ },
447
+ ];
448
+ }
449
+ if (deps.preview)
450
+ deps.preview(content);
451
+ const shouldWrite = await deps.confirm("Write to disk?");
452
+ if (!shouldWrite) {
453
+ throw new Error("Cancelled by user");
454
+ }
455
+ const outputDir = options.outputDir ?? RECIPES_DIR;
456
+ if (!existsSync(outputDir)) {
457
+ mkdirSync(outputDir, { recursive: true });
458
+ }
459
+ const outputPath = join(outputDir, `${name}.yaml`);
460
+ if (existsSync(outputPath)) {
461
+ throw new Error(`Recipe already exists: ${outputPath}`);
462
+ }
463
+ writeFileSync(outputPath, content);
464
+ return { path: outputPath, content, warnings };
465
+ }
116
466
  export async function runSchema(outputDir) {
117
467
  const resolvedOutputDir = resolve(outputDir);
118
468
  const schemas = generateSchemaSet();
@@ -183,8 +533,12 @@ export function runLint(recipePath) {
183
533
  const chainIssues = lintChainRefs(parsed, recipePath);
184
534
  if (chainIssues.length > 0) {
185
535
  result.issues.push(...chainIssues);
186
- result.errors += chainIssues.filter((i) => i.level === "error").length;
187
- result.warnings += chainIssues.filter((i) => i.level === "warning").length;
536
+ for (const i of chainIssues) {
537
+ if (i.level === "error")
538
+ result.errors++;
539
+ else if (i.level === "warning")
540
+ result.warnings++;
541
+ }
188
542
  if (result.errors > 0) {
189
543
  result.valid = false;
190
544
  }
@@ -420,6 +774,8 @@ export async function runRecipe(recipeRef, options = {}) {
420
774
  const runnerDeps = {
421
775
  ...options.deps,
422
776
  workdir: options.workdir ?? options.deps?.workdir ?? process.cwd(),
777
+ ...(options.manualRunId && { manualRunId: options.manualRunId }),
778
+ ...(options.ledgerDir && { ledgerDir: options.ledgerDir }),
423
779
  };
424
780
  if (options.dryRun) {
425
781
  throw new Error("runRecipeDryPlan must be used for dry-run execution");
@@ -832,7 +1188,16 @@ export async function runPreflight(recipeRef, options = {}) {
832
1188
  }
833
1189
  const plan = await runRecipeDryPlan(recipeRef, options);
834
1190
  const requireWriteAck = options.requireWriteAck ?? true;
835
- const allowlist = new Set(options.allowWrites ?? []);
1191
+ // Merge the recipe's declared allowWrites (YAML) with any caller-supplied
1192
+ // entries (e.g. --allow-write CLI flag). Either source is sufficient.
1193
+ const recipeForWrites = loadYamlRecipe(recipePath);
1194
+ const recipeAllowWrites = Array.isArray(recipeForWrites.allowWrites)
1195
+ ? recipeForWrites.allowWrites.filter((entry) => typeof entry === "string")
1196
+ : [];
1197
+ const allowlist = new Set([
1198
+ ...recipeAllowWrites,
1199
+ ...(options.allowWrites ?? []),
1200
+ ]);
836
1201
  for (const step of plan.steps) {
837
1202
  if (step.type === "tool" && step.tool && step.resolved === false) {
838
1203
  issues.push({
@@ -898,6 +1263,134 @@ export function runPreflightWatch(options) {
898
1263
  };
899
1264
  return runWatch(watchOptions);
900
1265
  }
1266
+ /**
1267
+ * `recipe doctor` — one-screen "why is this recipe unhealthy + how do I
1268
+ * fix it" diagnosis. Composes the static `preflight` check (lint + policy
1269
+ * + plan) with the recipe-scoped runtime halt summary, mapping every
1270
+ * finding to an actionable hint. Fail-soft: a missing bridge degrades to
1271
+ * static-only rather than erroring.
1272
+ */
1273
+ export async function runRecipeDoctor(recipeRef, options = {}) {
1274
+ const { fetchHalts, ...preflightOptions } = options;
1275
+ const recipePath = resolveRecipePath(recipeRef);
1276
+ // Lint first — it never throws and returns structured issues even for
1277
+ // recipes that can't load. Then attempt the fuller preflight (policy +
1278
+ // plan); if the recipe is too broken to load/plan, `runPreflight`
1279
+ // throws — fall back to a lint-only static result so doctor still
1280
+ // produces a clean diagnosis instead of a stack trace.
1281
+ const lint = runLint(recipePath);
1282
+ let staticResult;
1283
+ try {
1284
+ const pf = await runPreflight(recipePath, preflightOptions);
1285
+ staticResult = { ok: pf.ok, recipe: pf.recipe, issues: pf.issues };
1286
+ }
1287
+ catch (err) {
1288
+ const issues = lint.issues.map((i) => ({
1289
+ level: i.level,
1290
+ code: i.level === "error" ? "lint-error" : "lint-warning",
1291
+ message: i.message,
1292
+ }));
1293
+ // Guarantee at least one error so the verdict is unhealthy — the
1294
+ // load failure itself is the signal even if lint surfaced only
1295
+ // warnings (or nothing it could attribute to a line).
1296
+ if (!issues.some((i) => i.level === "error")) {
1297
+ issues.push({
1298
+ level: "error",
1299
+ code: "lint-error",
1300
+ message: err instanceof Error ? err.message : String(err),
1301
+ });
1302
+ }
1303
+ staticResult = {
1304
+ ok: false,
1305
+ recipe: basename(recipePath).replace(/\.(ya?ml|json)$/i, ""),
1306
+ issues,
1307
+ planSkipped: true,
1308
+ };
1309
+ }
1310
+ const recipeName = staticResult.recipe;
1311
+ let runtime = null;
1312
+ let runtimeNote;
1313
+ if (fetchHalts) {
1314
+ try {
1315
+ runtime = await fetchHalts(recipeName);
1316
+ if (runtime === null) {
1317
+ runtimeNote = "no running bridge — runtime halt check skipped";
1318
+ }
1319
+ }
1320
+ catch (err) {
1321
+ runtime = null;
1322
+ runtimeNote = `runtime halt check failed: ${err instanceof Error ? err.message : String(err)}`;
1323
+ }
1324
+ }
1325
+ else {
1326
+ runtimeNote = "runtime halt check not requested";
1327
+ }
1328
+ const ok = staticResult.ok && (runtime?.total ?? 0) === 0;
1329
+ return {
1330
+ recipe: recipeName,
1331
+ recipePath,
1332
+ static: staticResult,
1333
+ runtime,
1334
+ runtimeNote,
1335
+ ok,
1336
+ };
1337
+ }
1338
+ /**
1339
+ * Render a `RecipeDoctorResult` as a one-screen human report. Returns the
1340
+ * text so the CLI handler can write it (and tests can assert on it)
1341
+ * without coupling to process.stdout.
1342
+ */
1343
+ export function formatRecipeDoctorReport(result) {
1344
+ const lines = [];
1345
+ const verdict = result.ok ? "✓ healthy" : "✗ needs attention";
1346
+ lines.push(`Recipe doctor — ${result.recipe} (${verdict})`);
1347
+ lines.push(` ${result.recipePath}`);
1348
+ lines.push("");
1349
+ // --- Static: lint + policy + plan ---
1350
+ const s = result.static;
1351
+ const staticErrors = s.issues.filter((i) => i.level === "error");
1352
+ const staticWarnings = s.issues.filter((i) => i.level === "warning");
1353
+ if (s.issues.length === 0) {
1354
+ lines.push("Static checks: ✓ lint + policy clean");
1355
+ }
1356
+ else {
1357
+ const planNote = s.planSkipped
1358
+ ? " (could not plan — lint-only; fix the errors below first)"
1359
+ : "";
1360
+ lines.push(`Static checks: ${staticErrors.length} error(s), ${staticWarnings.length} warning(s)${planNote}`);
1361
+ for (const issue of s.issues) {
1362
+ const icon = issue.level === "error" ? "✗" : "⚠";
1363
+ const where = issue.stepId ? ` [step "${issue.stepId}"]` : "";
1364
+ lines.push(` ${icon} (${issue.code})${where} ${issue.message}`);
1365
+ }
1366
+ }
1367
+ lines.push("");
1368
+ // --- Runtime: recent halts ---
1369
+ if (result.runtime === null) {
1370
+ lines.push(`Runtime halts: — (${result.runtimeNote ?? "unavailable"})`);
1371
+ }
1372
+ else if (result.runtime.total === 0) {
1373
+ lines.push("Runtime halts: ✓ none in the recent window");
1374
+ }
1375
+ else {
1376
+ lines.push(`Runtime halts: ${result.runtime.total} in the recent window`);
1377
+ const entries = Object.entries(result.runtime.byCategory).sort(([, a], [, b]) => (b ?? 0) - (a ?? 0));
1378
+ for (const [cat, count] of entries) {
1379
+ const category = cat;
1380
+ const label = HALT_CATEGORY_LABELS[category] ?? cat;
1381
+ const hint = HALT_CATEGORY_HINTS[category] ?? "open run trace";
1382
+ lines.push(` ✗ ${label}: ${count} → ${hint}`);
1383
+ }
1384
+ if (result.runtime.recent.length > 0) {
1385
+ lines.push(" most recent:");
1386
+ for (const r of result.runtime.recent.slice(0, 5)) {
1387
+ const reason = r.reason.length > 100 ? `${r.reason.slice(0, 99)}…` : r.reason;
1388
+ lines.push(` · [run #${r.runSeq}] ${reason}`);
1389
+ }
1390
+ }
1391
+ }
1392
+ return `${lines.join("\n")}\n`;
1393
+ }
901
1394
  /**
902
1395
  * Exported for the cli-warns-when-out-of-jail.test regression suite —
903
1396
  * the warn path is what we assert on, not the path-resolution behaviour.
@@ -1088,8 +1581,13 @@ export async function runRecord(recipePath, options = {}) {
1088
1581
  });
1089
1582
  }
1090
1583
  }
1091
- const errors = issues.filter((issue) => issue.level === "error").length;
1092
- const warnings = issues.filter((issue) => issue.level === "warning").length;
1584
+ let errors = 0, warnings = 0;
1585
+ for (const issue of issues) {
1586
+ if (issue.level === "error")
1587
+ errors++;
1588
+ else if (issue.level === "warning")
1589
+ warnings++;
1590
+ }
1093
1591
  return {
1094
1592
  valid: errors === 0,
1095
1593
  issues,
@@ -1200,8 +1698,13 @@ export async function runTest(recipePath, options = {}) {
1200
1698
  });
1201
1699
  }
1202
1700
  }
1203
- const errors = issues.filter((issue) => issue.level === "error").length;
1204
- const warnings = issues.filter((issue) => issue.level === "warning").length;
1701
+ let errors = 0, warnings = 0;
1702
+ for (const issue of issues) {
1703
+ if (issue.level === "error")
1704
+ errors++;
1705
+ else if (issue.level === "warning")
1706
+ warnings++;
1707
+ }
1205
1708
  return {
1206
1709
  valid: errors === 0,
1207
1710
  issues,