@xdelivered/emberflow 0.2.0

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 (414) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +153 -0
  3. package/bin/commands.test.ts +185 -0
  4. package/bin/commands.ts +306 -0
  5. package/bin/emberflow.mjs +38 -0
  6. package/bin/init.test.ts +165 -0
  7. package/bin/init.ts +297 -0
  8. package/bin/runtime.mjs +105 -0
  9. package/bin/runtime.test.ts +167 -0
  10. package/bin/skillTargets.test.ts +108 -0
  11. package/bin/skillTargets.ts +36 -0
  12. package/bin/skillTemplates.test.ts +37 -0
  13. package/bin/tsconfig.json +23 -0
  14. package/dist/bin/commands.d.ts +24 -0
  15. package/dist/bin/commands.js +252 -0
  16. package/dist/bin/init.d.ts +17 -0
  17. package/dist/bin/init.js +241 -0
  18. package/dist/bin/runtime.d.mts +39 -0
  19. package/dist/bin/runtime.mjs +96 -0
  20. package/dist/bin/skillTargets.d.ts +13 -0
  21. package/dist/bin/skillTargets.js +22 -0
  22. package/dist/server/agents/claudeAdapter.d.ts +8 -0
  23. package/dist/server/agents/claudeAdapter.js +190 -0
  24. package/dist/server/agents/claudeParse.d.ts +2 -0
  25. package/dist/server/agents/claudeParse.js +162 -0
  26. package/dist/server/agents/codexAdapter.d.ts +13 -0
  27. package/dist/server/agents/codexAdapter.js +174 -0
  28. package/dist/server/agents/codexParse.d.ts +2 -0
  29. package/dist/server/agents/codexParse.js +74 -0
  30. package/dist/server/agents/detect.d.ts +23 -0
  31. package/dist/server/agents/detect.js +35 -0
  32. package/dist/server/agents/gitScope.d.ts +24 -0
  33. package/dist/server/agents/gitScope.js +95 -0
  34. package/dist/server/agents/modelRejectionHint.d.ts +8 -0
  35. package/dist/server/agents/modelRejectionHint.js +12 -0
  36. package/dist/server/agents/prompt.d.ts +79 -0
  37. package/dist/server/agents/prompt.js +364 -0
  38. package/dist/server/agents/runManager.d.ts +90 -0
  39. package/dist/server/agents/runManager.js +259 -0
  40. package/dist/server/agents/types.d.ts +15 -0
  41. package/dist/server/agents/types.js +1 -0
  42. package/dist/server/apiStore.d.ts +60 -0
  43. package/dist/server/apiStore.js +249 -0
  44. package/dist/server/auth/enforce.d.ts +1 -0
  45. package/dist/server/auth/enforce.js +6 -0
  46. package/dist/server/auth/types.d.ts +1 -0
  47. package/dist/server/auth/types.js +1 -0
  48. package/dist/server/auth/verifiers.d.ts +1 -0
  49. package/dist/server/auth/verifiers.js +4 -0
  50. package/dist/server/authAttach.d.ts +19 -0
  51. package/dist/server/authAttach.js +79 -0
  52. package/dist/server/cli.d.ts +7 -0
  53. package/dist/server/cli.js +804 -0
  54. package/dist/server/client.d.ts +130 -0
  55. package/dist/server/client.js +247 -0
  56. package/dist/server/environments.d.ts +103 -0
  57. package/dist/server/environments.js +486 -0
  58. package/dist/server/flowStore.d.ts +37 -0
  59. package/dist/server/flowStore.js +117 -0
  60. package/dist/server/httpOperations.d.ts +32 -0
  61. package/dist/server/httpOperations.js +62 -0
  62. package/dist/server/index.d.ts +1 -0
  63. package/dist/server/index.js +1022 -0
  64. package/dist/server/infrastructure.d.ts +51 -0
  65. package/dist/server/infrastructure.js +116 -0
  66. package/dist/server/login.d.ts +21 -0
  67. package/dist/server/login.js +44 -0
  68. package/dist/server/mcp.d.ts +1 -0
  69. package/dist/server/mcp.js +182 -0
  70. package/dist/server/migrateFlows.d.ts +8 -0
  71. package/dist/server/migrateFlows.js +25 -0
  72. package/dist/server/mockHandler.d.ts +15 -0
  73. package/dist/server/mockHandler.js +35 -0
  74. package/dist/server/nodesPayload.d.ts +14 -0
  75. package/dist/server/nodesPayload.js +14 -0
  76. package/dist/server/normalizeFlow.d.ts +21 -0
  77. package/dist/server/normalizeFlow.js +48 -0
  78. package/dist/server/openBrowser.d.ts +8 -0
  79. package/dist/server/openBrowser.js +16 -0
  80. package/dist/server/operationResult.d.ts +12 -0
  81. package/dist/server/operationResult.js +26 -0
  82. package/dist/server/pathGuard.d.ts +1 -0
  83. package/dist/server/pathGuard.js +16 -0
  84. package/dist/server/pathSafety.d.ts +20 -0
  85. package/dist/server/pathSafety.js +35 -0
  86. package/dist/server/projectConfig.d.ts +47 -0
  87. package/dist/server/projectConfig.js +61 -0
  88. package/dist/server/projectMode.d.ts +26 -0
  89. package/dist/server/projectMode.js +44 -0
  90. package/dist/server/redact.d.ts +6 -0
  91. package/dist/server/redact.js +37 -0
  92. package/dist/server/runRegistry.d.ts +84 -0
  93. package/dist/server/runRegistry.js +200 -0
  94. package/dist/server/runtime/RuntimeAdapter.d.ts +22 -0
  95. package/dist/server/runtime/RuntimeAdapter.js +1 -0
  96. package/dist/server/runtime/expressAdapter.d.ts +19 -0
  97. package/dist/server/runtime/expressAdapter.js +56 -0
  98. package/dist/server/scenarioTest.d.ts +14 -0
  99. package/dist/server/scenarioTest.js +66 -0
  100. package/dist/server/subflowRunner.d.ts +32 -0
  101. package/dist/server/subflowRunner.js +72 -0
  102. package/dist/server/testRunner.d.ts +70 -0
  103. package/dist/server/testRunner.js +151 -0
  104. package/dist/src/config.d.ts +3 -0
  105. package/dist/src/config.js +2 -0
  106. package/dist/src/engine/authVerify.d.ts +33 -0
  107. package/dist/src/engine/authVerify.js +63 -0
  108. package/dist/src/engine/diagnostics.d.ts +62 -0
  109. package/dist/src/engine/diagnostics.js +148 -0
  110. package/dist/src/engine/executor.d.ts +151 -0
  111. package/dist/src/engine/executor.js +684 -0
  112. package/dist/src/engine/httpError.d.ts +7 -0
  113. package/dist/src/engine/httpError.js +14 -0
  114. package/dist/src/engine/index.d.ts +11 -0
  115. package/dist/src/engine/index.js +11 -0
  116. package/dist/src/engine/output.d.ts +11 -0
  117. package/dist/src/engine/output.js +20 -0
  118. package/dist/src/engine/publish.d.ts +30 -0
  119. package/dist/src/engine/publish.js +95 -0
  120. package/dist/src/engine/registry.d.ts +34 -0
  121. package/dist/src/engine/registry.js +67 -0
  122. package/dist/src/engine/scenarios.d.ts +12 -0
  123. package/dist/src/engine/scenarios.js +19 -0
  124. package/dist/src/engine/schemaCheck.d.ts +4 -0
  125. package/dist/src/engine/schemaCheck.js +34 -0
  126. package/dist/src/engine/trace.d.ts +12 -0
  127. package/dist/src/engine/trace.js +12 -0
  128. package/dist/src/engine/types.d.ts +308 -0
  129. package/dist/src/engine/types.js +1 -0
  130. package/dist/src/engine/validation.d.ts +26 -0
  131. package/dist/src/engine/validation.js +318 -0
  132. package/dist/src/flows/anomaly-flows.d.ts +23 -0
  133. package/dist/src/flows/anomaly-flows.js +151 -0
  134. package/dist/src/flows/city-sweep-flow.d.ts +10 -0
  135. package/dist/src/flows/city-sweep-flow.js +127 -0
  136. package/dist/src/flows/login-flow.d.ts +13 -0
  137. package/dist/src/flows/login-flow.js +145 -0
  138. package/dist/src/flows/pradar-flows.d.ts +5 -0
  139. package/dist/src/flows/pradar-flows.js +375 -0
  140. package/dist/src/flows/weather-flow.d.ts +12 -0
  141. package/dist/src/flows/weather-flow.js +150 -0
  142. package/dist/src/lib/pathParams.d.ts +3 -0
  143. package/dist/src/lib/pathParams.js +6 -0
  144. package/dist/src/nodes/anomaly.d.ts +2 -0
  145. package/dist/src/nodes/anomaly.js +556 -0
  146. package/dist/src/nodes/flow-control.d.ts +7 -0
  147. package/dist/src/nodes/flow-control.js +270 -0
  148. package/dist/src/nodes/index.d.ts +3 -0
  149. package/dist/src/nodes/index.js +20 -0
  150. package/dist/src/nodes/login.d.ts +7 -0
  151. package/dist/src/nodes/login.js +158 -0
  152. package/dist/src/nodes/loops.d.ts +11 -0
  153. package/dist/src/nodes/loops.js +60 -0
  154. package/dist/src/nodes/pradar/index.d.ts +7 -0
  155. package/dist/src/nodes/pradar/index.js +9 -0
  156. package/dist/src/nodes/pradar/logic.d.ts +189 -0
  157. package/dist/src/nodes/pradar/logic.js +370 -0
  158. package/dist/src/nodes/pradar/nodes.d.ts +42 -0
  159. package/dist/src/nodes/pradar/nodes.js +801 -0
  160. package/dist/src/nodes/requireAuth.d.ts +20 -0
  161. package/dist/src/nodes/requireAuth.js +48 -0
  162. package/dist/src/nodes/response.d.ts +8 -0
  163. package/dist/src/nodes/response.js +33 -0
  164. package/dist/src/nodes/weather.d.ts +33 -0
  165. package/dist/src/nodes/weather.js +260 -0
  166. package/package.json +92 -0
  167. package/server/agentRoute.test.ts +157 -0
  168. package/server/agents/__fixtures__/fake-claude-writer.mjs +23 -0
  169. package/server/agents/__fixtures__/fake-claude.mjs +55 -0
  170. package/server/agents/__fixtures__/fake-codex-writer.mjs +27 -0
  171. package/server/agents/__fixtures__/fake-codex.mjs +52 -0
  172. package/server/agents/__fixtures__/fake-version-empty.mjs +3 -0
  173. package/server/agents/__fixtures__/fake-version-fail.mjs +4 -0
  174. package/server/agents/__fixtures__/fake-version-garbage.mjs +4 -0
  175. package/server/agents/__fixtures__/fake-version-good.mjs +4 -0
  176. package/server/agents/__fixtures__/fake-version-hang.mjs +3 -0
  177. package/server/agents/claudeAdapter.test.ts +127 -0
  178. package/server/agents/claudeAdapter.ts +212 -0
  179. package/server/agents/claudeParse.ts +193 -0
  180. package/server/agents/codexAdapter.test.ts +125 -0
  181. package/server/agents/codexAdapter.ts +198 -0
  182. package/server/agents/codexParse.test.ts +58 -0
  183. package/server/agents/codexParse.ts +126 -0
  184. package/server/agents/detect.test.ts +65 -0
  185. package/server/agents/detect.ts +43 -0
  186. package/server/agents/gitScope.test.ts +129 -0
  187. package/server/agents/gitScope.ts +110 -0
  188. package/server/agents/modelRejectionHint.test.ts +30 -0
  189. package/server/agents/modelRejectionHint.ts +14 -0
  190. package/server/agents/prompt.test.ts +613 -0
  191. package/server/agents/prompt.ts +527 -0
  192. package/server/agents/runManager.test.ts +251 -0
  193. package/server/agents/runManager.ts +286 -0
  194. package/server/agents/types.ts +17 -0
  195. package/server/apiMount.test.ts +591 -0
  196. package/server/apiStore.test.ts +324 -0
  197. package/server/apiStore.ts +241 -0
  198. package/server/auth/enforce.test.ts +61 -0
  199. package/server/auth/enforce.ts +6 -0
  200. package/server/auth/types.ts +5 -0
  201. package/server/auth/verifiers.test.ts +37 -0
  202. package/server/auth/verifiers.ts +9 -0
  203. package/server/authAttach.test.ts +175 -0
  204. package/server/authAttach.ts +99 -0
  205. package/server/cli.ts +847 -0
  206. package/server/cliAuth.test.ts +229 -0
  207. package/server/cliCreate.test.ts +131 -0
  208. package/server/cliDoctor.test.ts +326 -0
  209. package/server/cliTest.test.ts +735 -0
  210. package/server/client.ts +314 -0
  211. package/server/demoProject.test.ts +29 -0
  212. package/server/diagnosticsRoute.test.ts +169 -0
  213. package/server/environments.test.ts +620 -0
  214. package/server/environments.ts +603 -0
  215. package/server/environmentsRoute.test.ts +385 -0
  216. package/server/errorWorkflow.test.ts +205 -0
  217. package/server/flowStore.test.ts +84 -0
  218. package/server/flowStore.ts +125 -0
  219. package/server/httpOperations.test.ts +377 -0
  220. package/server/httpOperations.ts +96 -0
  221. package/server/index.ts +1111 -0
  222. package/server/infrastructure.test.ts +154 -0
  223. package/server/infrastructure.ts +161 -0
  224. package/server/infrastructureRoute.test.ts +105 -0
  225. package/server/login.test.ts +153 -0
  226. package/server/login.ts +67 -0
  227. package/server/mcp.ts +278 -0
  228. package/server/migrateFlows.test.ts +55 -0
  229. package/server/migrateFlows.ts +23 -0
  230. package/server/mockHandler.test.ts +138 -0
  231. package/server/mockHandler.ts +52 -0
  232. package/server/mockRun.test.ts +367 -0
  233. package/server/mockServe.test.ts +467 -0
  234. package/server/nodesPayload.test.ts +29 -0
  235. package/server/nodesPayload.ts +26 -0
  236. package/server/normalizeFlow.test.ts +80 -0
  237. package/server/normalizeFlow.ts +59 -0
  238. package/server/openBrowser.test.ts +20 -0
  239. package/server/openBrowser.ts +22 -0
  240. package/server/operationResult.test.ts +67 -0
  241. package/server/operationResult.ts +27 -0
  242. package/server/pathGuard.test.ts +19 -0
  243. package/server/pathGuard.ts +12 -0
  244. package/server/pathSafety.test.ts +38 -0
  245. package/server/pathSafety.ts +33 -0
  246. package/server/projectConfig.test.ts +67 -0
  247. package/server/projectConfig.ts +101 -0
  248. package/server/projectMode.test.ts +106 -0
  249. package/server/projectMode.ts +59 -0
  250. package/server/redact.test.ts +88 -0
  251. package/server/redact.ts +35 -0
  252. package/server/redactEgress.test.ts +281 -0
  253. package/server/runRegistry.ts +270 -0
  254. package/server/runsAuth.test.ts +119 -0
  255. package/server/runtime/RuntimeAdapter.ts +24 -0
  256. package/server/runtime/expressAdapter.test.ts +38 -0
  257. package/server/runtime/expressAdapter.ts +70 -0
  258. package/server/scenarioTest.test.ts +148 -0
  259. package/server/scenarioTest.ts +88 -0
  260. package/server/setupStatus.test.ts +127 -0
  261. package/server/subflowRunner.ts +104 -0
  262. package/server/testRunner.ts +216 -0
  263. package/server/tsconfig.json +20 -0
  264. package/server/workflowTestRoute.test.ts +297 -0
  265. package/src/App.tsx +233 -0
  266. package/src/components/AgentConsole.tsx +461 -0
  267. package/src/components/BranchRulesEditor.tsx +156 -0
  268. package/src/components/CreateModal.tsx +275 -0
  269. package/src/components/Dock.tsx +487 -0
  270. package/src/components/EnvironmentDialog.tsx +569 -0
  271. package/src/components/EnvironmentPicker.tsx +357 -0
  272. package/src/components/EnvironmentsDialog.tsx +144 -0
  273. package/src/components/ExecutionPager.tsx +67 -0
  274. package/src/components/InfraPanel.test.tsx +103 -0
  275. package/src/components/InfraPanel.tsx +171 -0
  276. package/src/components/Inspector.tsx +1182 -0
  277. package/src/components/Json.tsx +50 -0
  278. package/src/components/JsonModal.tsx +52 -0
  279. package/src/components/LogMessage.tsx +61 -0
  280. package/src/components/NodeRunModal.tsx +213 -0
  281. package/src/components/RunLogPanel.tsx +397 -0
  282. package/src/components/RunbookView.tsx +1085 -0
  283. package/src/components/Scenarios.tsx +476 -0
  284. package/src/components/SettingsDialog.tsx +229 -0
  285. package/src/components/Sidebar.tsx +436 -0
  286. package/src/components/StatusBar.tsx +302 -0
  287. package/src/components/Toolbar.tsx +663 -0
  288. package/src/components/WelcomeDialog.test.tsx +107 -0
  289. package/src/components/WelcomeDialog.tsx +412 -0
  290. package/src/components/agentMarkdown.test.tsx +91 -0
  291. package/src/components/agentMarkdown.tsx +158 -0
  292. package/src/components/ui/badge.tsx +31 -0
  293. package/src/components/ui/button.tsx +46 -0
  294. package/src/components/ui/combobox.tsx +114 -0
  295. package/src/components/ui/command.tsx +82 -0
  296. package/src/components/ui/dialog.tsx +73 -0
  297. package/src/components/ui/input.tsx +20 -0
  298. package/src/components/ui/popover.tsx +32 -0
  299. package/src/components/ui/resizable.tsx +44 -0
  300. package/src/components/ui/tabs.tsx +36 -0
  301. package/src/config.ts +4 -0
  302. package/src/engine/authVerify.ts +81 -0
  303. package/src/engine/diagnostics.test.ts +437 -0
  304. package/src/engine/diagnostics.ts +211 -0
  305. package/src/engine/executor.test.ts +1339 -0
  306. package/src/engine/executor.ts +840 -0
  307. package/src/engine/httpError.test.ts +12 -0
  308. package/src/engine/httpError.ts +16 -0
  309. package/src/engine/index.ts +11 -0
  310. package/src/engine/output.ts +21 -0
  311. package/src/engine/publish.test.ts +176 -0
  312. package/src/engine/publish.ts +127 -0
  313. package/src/engine/registry.test.ts +64 -0
  314. package/src/engine/registry.ts +80 -0
  315. package/src/engine/scenarios.test.ts +55 -0
  316. package/src/engine/scenarios.ts +29 -0
  317. package/src/engine/schemaCheck.test.ts +21 -0
  318. package/src/engine/schemaCheck.ts +35 -0
  319. package/src/engine/trace.test.ts +19 -0
  320. package/src/engine/trace.ts +23 -0
  321. package/src/engine/types.test.ts +22 -0
  322. package/src/engine/types.ts +316 -0
  323. package/src/engine/validation.test.ts +314 -0
  324. package/src/engine/validation.ts +354 -0
  325. package/src/flows/anomaly-flows.test.ts +81 -0
  326. package/src/flows/anomaly-flows.ts +202 -0
  327. package/src/flows/city-sweep-flow.ts +131 -0
  328. package/src/flows/flows.test.ts +30 -0
  329. package/src/flows/login-flow.ts +150 -0
  330. package/src/flows/pradar-flows.test.ts +204 -0
  331. package/src/flows/pradar-flows.ts +419 -0
  332. package/src/flows/weather-flow.ts +154 -0
  333. package/src/globals.css +147 -0
  334. package/src/lib/pathParams.test.ts +24 -0
  335. package/src/lib/pathParams.ts +6 -0
  336. package/src/lib/registerLens.test.ts +79 -0
  337. package/src/lib/registerLens.ts +59 -0
  338. package/src/lib/runbookModel.test.ts +61 -0
  339. package/src/lib/runbookModel.ts +290 -0
  340. package/src/lib/runbookProjection.test.ts +417 -0
  341. package/src/lib/runbookProjection.ts +254 -0
  342. package/src/lib/useTailAnchor.ts +53 -0
  343. package/src/lib/utils.ts +7 -0
  344. package/src/main.tsx +13 -0
  345. package/src/nodes/anomaly.test.ts +98 -0
  346. package/src/nodes/anomaly.ts +637 -0
  347. package/src/nodes/effects-scan.test.ts +44 -0
  348. package/src/nodes/flow-control.test.ts +280 -0
  349. package/src/nodes/flow-control.ts +305 -0
  350. package/src/nodes/index.ts +22 -0
  351. package/src/nodes/login.test.ts +81 -0
  352. package/src/nodes/login.ts +186 -0
  353. package/src/nodes/loops.ts +69 -0
  354. package/src/nodes/pradar/index.ts +11 -0
  355. package/src/nodes/pradar/logic.test.ts +288 -0
  356. package/src/nodes/pradar/logic.ts +582 -0
  357. package/src/nodes/pradar/nodes.test.ts +146 -0
  358. package/src/nodes/pradar/nodes.ts +955 -0
  359. package/src/nodes/requireAuth.test.ts +75 -0
  360. package/src/nodes/requireAuth.ts +55 -0
  361. package/src/nodes/response.test.ts +33 -0
  362. package/src/nodes/response.ts +39 -0
  363. package/src/nodes/traceDetail.test.ts +39 -0
  364. package/src/nodes/weather.test.ts +65 -0
  365. package/src/nodes/weather.ts +306 -0
  366. package/src/store/agentClient.test.ts +103 -0
  367. package/src/store/agentClient.ts +131 -0
  368. package/src/store/apiTree.test.ts +17 -0
  369. package/src/store/apiTree.ts +73 -0
  370. package/src/store/builderStore.test.ts +1234 -0
  371. package/src/store/builderStore.ts +1879 -0
  372. package/src/store/infraClient.ts +63 -0
  373. package/src/store/nodeMeta.test.ts +24 -0
  374. package/src/store/nodeMeta.ts +21 -0
  375. package/src/store/persistence.test.ts +15 -0
  376. package/src/store/persistence.ts +54 -0
  377. package/src/store/serverRunner.ts +381 -0
  378. package/src/store/setupClient.ts +57 -0
  379. package/studio-dist/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  380. package/studio-dist/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  381. package/studio-dist/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  382. package/studio-dist/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  383. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  384. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  385. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  386. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  387. package/studio-dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  388. package/studio-dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  389. package/studio-dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  390. package/studio-dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  391. package/studio-dist/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  392. package/studio-dist/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  393. package/studio-dist/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  394. package/studio-dist/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  395. package/studio-dist/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  396. package/studio-dist/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  397. package/studio-dist/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  398. package/studio-dist/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  399. package/studio-dist/assets/index-DNJwW-hM.css +1 -0
  400. package/studio-dist/assets/index-O26dKRjW.js +65 -0
  401. package/studio-dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  402. package/studio-dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  403. package/studio-dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  404. package/studio-dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  405. package/studio-dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  406. package/studio-dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  407. package/studio-dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  408. package/studio-dist/favicon.svg +1 -0
  409. package/studio-dist/icons.svg +24 -0
  410. package/studio-dist/index.html +14 -0
  411. package/templates/skills/emberflow-basics/SKILL.md +316 -0
  412. package/templates/skills/emberflow-model-process/SKILL.md +209 -0
  413. package/templates/skills/emberflow-new-workflow/SKILL.md +211 -0
  414. package/templates/skills/emberflow-review-workflow/SKILL.md +148 -0
@@ -0,0 +1,162 @@
1
+ /** Best-effort extraction of a shell command from a Bash tool_use input. */
2
+ function bashCommandFrom(block) {
3
+ const input = block.input;
4
+ if (!input)
5
+ return undefined;
6
+ const command = input.command;
7
+ return typeof command === 'string' ? command : undefined;
8
+ }
9
+ /** Fallback label for a tool whose input we don't format specially (or that's
10
+ * missing its expected field): `<name> <compact-json>` capped at 80 chars. */
11
+ function compactLabel(name, input) {
12
+ const json = JSON.stringify(input ?? {});
13
+ return `${name} ${json.length > 80 ? json.slice(0, 80) : json}`;
14
+ }
15
+ /**
16
+ * Human label for a non-mcp tool_use block, used as a `command` event's
17
+ * `command` string so Read/Edit/Grep/… fold into the UI's "Ran N commands"
18
+ * group instead of leaking as reasoning prose. Bash keeps its raw shell command;
19
+ * the common Claude tools get a purpose-built one-liner; anything else gets the
20
+ * compact `<name> <json>` fallback.
21
+ */
22
+ function commandLabelFor(block) {
23
+ const name = block.name ?? 'tool';
24
+ const input = (block.input ?? {});
25
+ const str = (v) => (typeof v === 'string' ? v : undefined);
26
+ switch (name) {
27
+ case 'Bash':
28
+ return bashCommandFrom(block) ?? compactLabel(name, input);
29
+ case 'Read': {
30
+ const fp = str(input.file_path);
31
+ if (!fp)
32
+ return compactLabel(name, input);
33
+ const bits = [];
34
+ if (typeof input.offset === 'number')
35
+ bits.push(`offset ${input.offset}`);
36
+ if (typeof input.limit === 'number')
37
+ bits.push(`limit ${input.limit}`);
38
+ return `Read ${fp}${bits.length ? ` (${bits.join(', ')})` : ''}`;
39
+ }
40
+ case 'Edit':
41
+ case 'Write': {
42
+ const fp = str(input.file_path);
43
+ return fp ? `${name} ${fp}` : compactLabel(name, input);
44
+ }
45
+ case 'Grep': {
46
+ const pattern = str(input.pattern);
47
+ if (!pattern)
48
+ return compactLabel(name, input);
49
+ const path = str(input.path);
50
+ return `Grep ${pattern}${path ? ` ${path}` : ''}`;
51
+ }
52
+ case 'Glob': {
53
+ const pattern = str(input.pattern);
54
+ return pattern ? `Glob ${pattern}` : compactLabel(name, input);
55
+ }
56
+ case 'TodoWrite':
57
+ return 'update todos';
58
+ default:
59
+ return compactLabel(name, input);
60
+ }
61
+ }
62
+ /** Flattens a tool_result's `content` (string, or array of text blocks) to plain text. */
63
+ function toolResultTextFrom(block) {
64
+ const content = block.content;
65
+ if (typeof content === 'string')
66
+ return content;
67
+ if (Array.isArray(content)) {
68
+ return content
69
+ .filter((c) => c.type === 'text' && typeof c.text === 'string')
70
+ .map((c) => c.text)
71
+ .join('');
72
+ }
73
+ return '';
74
+ }
75
+ export function parseClaudeLine(line) {
76
+ const trimmed = line.trim();
77
+ if (!trimmed)
78
+ return [];
79
+ let parsed;
80
+ try {
81
+ parsed = JSON.parse(trimmed);
82
+ }
83
+ catch {
84
+ return [];
85
+ }
86
+ switch (parsed.type) {
87
+ case 'system': {
88
+ if (parsed.subtype === 'init')
89
+ return [{ type: 'started' }];
90
+ // hook_started / hook_response and any other system noise — ignore.
91
+ return [];
92
+ }
93
+ case 'assistant': {
94
+ const blocks = parsed.message?.content ?? [];
95
+ // Claude routinely emits multiple content blocks per message (e.g.
96
+ // explain-then-call `[text, tool_use]`, or parallel tool calls) — emit
97
+ // one AgentEvent per meaningful block, in order, so the console shows the
98
+ // full picture. `thinking` and other block types are skipped.
99
+ const events = [];
100
+ for (const block of blocks) {
101
+ if (block.type === 'text' && block.text) {
102
+ events.push({ type: 'message', text: block.text });
103
+ }
104
+ else if (block.type === 'tool_use') {
105
+ // MCP tools surface as `mcp__<server>__<tool>` — show them as MCP calls.
106
+ if (block.name?.startsWith('mcp__')) {
107
+ const [, server, ...toolParts] = block.name.split('__');
108
+ events.push({
109
+ type: 'mcp',
110
+ mcpServer: server || 'mcp',
111
+ mcpTool: toolParts.join('__') || 'tool',
112
+ mcpStatus: 'in_progress',
113
+ ...(block.id ? { toolUseId: block.id } : {}),
114
+ });
115
+ continue;
116
+ }
117
+ // Every other tool (Bash, Read, Edit, Grep, Glob, TodoWrite, …) is a
118
+ // `command` event with a human label, correlated by tool_use_id like
119
+ // Bash always was — so it folds into the UI's "Ran N commands" group
120
+ // instead of leaking as reasoning prose.
121
+ events.push({
122
+ type: 'command',
123
+ command: commandLabelFor(block),
124
+ commandStatus: 'in_progress',
125
+ ...(block.id ? { toolUseId: block.id } : {}),
126
+ });
127
+ }
128
+ }
129
+ return events;
130
+ }
131
+ case 'user': {
132
+ // Tool results. Emit a partial completion carrying the tool_use_id +
133
+ // output text + status; the adapter looks up the original command text
134
+ // by id (it isn't repeated in the tool_result line) and skips gracefully
135
+ // if the id doesn't correspond to a command it's tracking (e.g. a
136
+ // non-Bash tool result).
137
+ const blocks = parsed.message?.content ?? [];
138
+ const events = [];
139
+ for (const block of blocks) {
140
+ if (block.type === 'tool_result' && block.tool_use_id) {
141
+ events.push({
142
+ type: 'command',
143
+ toolUseId: block.tool_use_id,
144
+ commandStatus: block.is_error ? 'failed' : 'completed',
145
+ output: toolResultTextFrom(block),
146
+ });
147
+ }
148
+ }
149
+ return events;
150
+ }
151
+ case 'result': {
152
+ if (parsed.subtype === 'error' || parsed.is_error) {
153
+ return [{ type: 'error', text: parsed.result ?? 'error' }];
154
+ }
155
+ return [{ type: 'done', usage: parsed.usage }];
156
+ }
157
+ case 'rate_limit_event':
158
+ return [];
159
+ default:
160
+ return [];
161
+ }
162
+ }
@@ -0,0 +1,13 @@
1
+ import type { AgentEvent } from './types.js';
2
+ export interface SpawnedAgent {
3
+ events: AsyncIterable<AgentEvent>;
4
+ cancel(): void;
5
+ }
6
+ export interface CodexOptions {
7
+ model?: string;
8
+ reasoning?: 'low' | 'medium' | 'high';
9
+ bin?: string;
10
+ /** Grace period (ms) between SIGTERM and the SIGKILL escalation in cancel(). Test hook. */
11
+ killGraceMs?: number;
12
+ }
13
+ export declare function spawnCodex(prompt: string, projectDir: string, opts?: CodexOptions): SpawnedAgent;
@@ -0,0 +1,174 @@
1
+ import { spawn } from 'node:child_process';
2
+ import readline from 'node:readline';
3
+ import { parseCodexLine } from './codexParse.js';
4
+ import { modelRejectionHint } from './modelRejectionHint.js';
5
+ const STDERR_TAIL_BYTES = 2048;
6
+ export function spawnCodex(prompt, projectDir, opts = {}) {
7
+ const bin = opts.bin ?? 'codex';
8
+ const args = [
9
+ // Never ask for per-call approval — the studio runs codex non-interactively,
10
+ // so an approval prompt has no one to answer it and codex cancels the call
11
+ // ("user cancelled MCP tool call"). This is a top-level flag (before `exec`).
12
+ // The sandbox (-s workspace-write, below) still constrains what can run.
13
+ '-a',
14
+ 'never',
15
+ 'exec',
16
+ '-C',
17
+ projectDir,
18
+ '-s',
19
+ 'workspace-write',
20
+ // Keep the workspace-write FILE sandbox, but allow network — the agent's CLI
21
+ // ops (node bin/emberflow.mjs run/get-workflow/…) must reach the runner on
22
+ // 127.0.0.1; workspace-write blocks all network by default (curl → 000).
23
+ '-c',
24
+ 'sandbox_workspace_write.network_access=true',
25
+ '-c',
26
+ `model_reasoning_effort=${opts.reasoning ?? 'low'}`,
27
+ '--json',
28
+ ...(opts.model ? ['-m', opts.model] : []),
29
+ prompt,
30
+ ];
31
+ // detached: give codex its own process group so cancel() can tree-kill any
32
+ // subprocesses it spawns (workspace-write), not just the top-level process.
33
+ // stdio ['ignore','pipe','pipe'] → stdin is null, stdout/stderr are Readable.
34
+ const child = bin.endsWith('.mjs')
35
+ ? spawn(process.execPath, [bin, ...args], { stdio: ['ignore', 'pipe', 'pipe'], detached: true })
36
+ : spawn(bin, args, { stdio: ['ignore', 'pipe', 'pipe'], detached: true });
37
+ // Buffer only the last ~2KB of stderr — non-fatal in general (e.g. the user's
38
+ // global MCP servers spew auth errors there), but useful as a diagnostic tail
39
+ // when the process dies without a terminal event (see maybeFinalize below).
40
+ let stderrTail = '';
41
+ child.stderr.on('data', (chunk) => {
42
+ stderrTail = (stderrTail + chunk.toString('utf8')).slice(-STDERR_TAIL_BYTES);
43
+ });
44
+ const queue = [];
45
+ const resolvers = [];
46
+ let done = false;
47
+ let sawTerminal = false;
48
+ function push(event) {
49
+ queue.push(event);
50
+ const resolve = resolvers.shift();
51
+ if (resolve)
52
+ resolve();
53
+ }
54
+ function finish() {
55
+ done = true;
56
+ while (resolvers.length) {
57
+ const resolve = resolvers.shift();
58
+ if (resolve)
59
+ resolve();
60
+ }
61
+ }
62
+ const rl = readline.createInterface({ input: child.stdout });
63
+ rl.on('line', (line) => {
64
+ const event = parseCodexLine(line);
65
+ if (event) {
66
+ if (event.type === 'done' || event.type === 'error')
67
+ sawTerminal = true;
68
+ push(event);
69
+ }
70
+ });
71
+ // Finalize only once BOTH the stdout stream is fully drained (readline 'close')
72
+ // AND the process has exited ('close', which fires after all stdio is flushed —
73
+ // unlike 'exit', which can fire while trailing stdout lines are still buffered).
74
+ let rlClosed = false;
75
+ let procClosed = false;
76
+ let exitCode = null;
77
+ let escalateTimer;
78
+ function clearEscalation() {
79
+ if (escalateTimer) {
80
+ clearTimeout(escalateTimer);
81
+ escalateTimer = undefined;
82
+ }
83
+ }
84
+ function maybeFinalize() {
85
+ if (!rlClosed || !procClosed)
86
+ return;
87
+ clearEscalation();
88
+ if (!sawTerminal) {
89
+ if (exitCode === 0) {
90
+ // Clean exit but the model never emitted turn.completed — synthesize a done.
91
+ push({ type: 'done' });
92
+ }
93
+ else {
94
+ const hint = modelRejectionHint('codex', stderrTail);
95
+ const text = hint ? `codex exited with code ${exitCode} (${hint})` : `codex exited with code ${exitCode}`;
96
+ push({ type: 'error', text });
97
+ }
98
+ sawTerminal = true;
99
+ }
100
+ finish();
101
+ }
102
+ rl.on('close', () => {
103
+ rlClosed = true;
104
+ maybeFinalize();
105
+ });
106
+ child.on('close', (code) => {
107
+ procClosed = true;
108
+ exitCode = code;
109
+ maybeFinalize();
110
+ });
111
+ // A failed spawn (missing binary, bad EMBERFLOW_*_BIN, permissions, ...) emits
112
+ // an 'error' event on the child instead of ever exiting/closing normally. Left
113
+ // unhandled, Node rethrows it and crashes the whole runner process — so we
114
+ // must handle it and finalize the stream ourselves. 'close' still fires after
115
+ // a spawn error (with code null), so guard against double-finalizing there.
116
+ let spawnFailed = false;
117
+ child.on('error', (err) => {
118
+ if (spawnFailed)
119
+ return;
120
+ spawnFailed = true;
121
+ clearEscalation();
122
+ push({ type: 'error', text: `failed to start codex: ${err.message}` });
123
+ sawTerminal = true;
124
+ rlClosed = true;
125
+ procClosed = true;
126
+ finish();
127
+ });
128
+ async function* generate() {
129
+ while (true) {
130
+ if (queue.length > 0) {
131
+ yield queue.shift();
132
+ continue;
133
+ }
134
+ if (done)
135
+ return;
136
+ await new Promise((resolve) => resolvers.push(resolve));
137
+ }
138
+ }
139
+ // Kill the whole process group (negative pid) so codex's own subprocesses die
140
+ // too; fall back to a plain kill if the group signal fails (e.g. already gone
141
+ // or no group). Used by both the initial SIGTERM and the SIGKILL escalation.
142
+ function killGroup(signal) {
143
+ try {
144
+ if (child.pid != null) {
145
+ process.kill(-child.pid, signal);
146
+ return;
147
+ }
148
+ }
149
+ catch {
150
+ // group kill failed — fall through to a direct kill
151
+ }
152
+ try {
153
+ child.kill(signal);
154
+ }
155
+ catch {
156
+ // process already gone — nothing to do
157
+ }
158
+ }
159
+ return {
160
+ events: { [Symbol.asyncIterator]: generate },
161
+ cancel() {
162
+ killGroup('SIGTERM');
163
+ // If the process (or a stubborn grandchild/MCP server) ignores SIGTERM,
164
+ // escalate to SIGKILL after a grace period so a hung agent can't
165
+ // permanently lock the run slot. Cleared once close/error finalizes.
166
+ clearEscalation();
167
+ escalateTimer = setTimeout(() => {
168
+ escalateTimer = undefined;
169
+ killGroup('SIGKILL');
170
+ }, opts.killGraceMs ?? 3000);
171
+ escalateTimer.unref?.();
172
+ },
173
+ };
174
+ }
@@ -0,0 +1,2 @@
1
+ import type { AgentEvent } from './types.js';
2
+ export declare function parseCodexLine(line: string): AgentEvent | null;
@@ -0,0 +1,74 @@
1
+ /** Map a codex mcp_tool_call item → an 'mcp' AgentEvent, tolerating shape drift. */
2
+ function mcpEventFrom(item) {
3
+ const server = item.server ?? item.invocation?.server ?? 'mcp';
4
+ const tool = item.tool ?? item.name ?? item.invocation?.tool ?? 'tool';
5
+ const raw = item.error ?? item.result ?? item.output;
6
+ const output = raw === undefined ? undefined : typeof raw === 'string' ? raw : JSON.stringify(raw, null, 2);
7
+ return { type: 'mcp', mcpServer: server, mcpTool: tool, mcpStatus: item.status, ...(output !== undefined ? { output } : {}) };
8
+ }
9
+ export function parseCodexLine(line) {
10
+ const trimmed = line.trim();
11
+ if (!trimmed)
12
+ return null;
13
+ let parsed;
14
+ try {
15
+ parsed = JSON.parse(trimmed);
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ switch (parsed.type) {
21
+ case 'thread.started':
22
+ case 'turn.started':
23
+ return { type: 'started' };
24
+ case 'item.started': {
25
+ const item = parsed.item;
26
+ if (item && item.type === 'command_execution') {
27
+ const commandItem = item;
28
+ return {
29
+ type: 'command',
30
+ command: commandItem.command,
31
+ commandStatus: commandItem.status,
32
+ };
33
+ }
34
+ if (item && item.type === 'mcp_tool_call')
35
+ return mcpEventFrom(item);
36
+ return null;
37
+ }
38
+ case 'item.completed': {
39
+ const item = parsed.item;
40
+ if (!item)
41
+ return null;
42
+ if (item.type === 'command_execution') {
43
+ const commandItem = item;
44
+ return {
45
+ type: 'command',
46
+ command: commandItem.command,
47
+ exitCode: commandItem.exit_code,
48
+ output: commandItem.aggregated_output,
49
+ commandStatus: commandItem.status,
50
+ };
51
+ }
52
+ if (item.type === 'mcp_tool_call')
53
+ return mcpEventFrom(item);
54
+ if (item.type === 'agent_message') {
55
+ const messageItem = item;
56
+ return { type: 'message', text: messageItem.text };
57
+ }
58
+ if (item.type === 'error') {
59
+ // A codex `error` ITEM is a non-fatal diagnostic (e.g. an MCP server
60
+ // failing to auth) — NOT the run failing. Surface it as a message so it
61
+ // stays visible without terminating the run. A real failure is signalled
62
+ // by the process exiting non-zero with no `done` (see codexAdapter),
63
+ // which is the ONLY thing that yields a terminal AgentEvent 'error'.
64
+ const errorItem = item;
65
+ return { type: 'message', text: `⚠ ${errorItem.text ?? errorItem.message ?? 'error'}` };
66
+ }
67
+ return null;
68
+ }
69
+ case 'turn.completed':
70
+ return { type: 'done', usage: parsed.usage };
71
+ default:
72
+ return null;
73
+ }
74
+ }
@@ -0,0 +1,23 @@
1
+ import type { AgentKind } from './types.js';
2
+ export interface DetectedAgent {
3
+ kind: AgentKind;
4
+ version: string | null;
5
+ }
6
+ /**
7
+ * Real PATH probe: runs `<bin> --version` and parses the first semver-ish
8
+ * token out of stdout. Returns `undefined` when the binary isn't present/
9
+ * runnable (spawn error, timeout, or nonzero exit); `null` version when it
10
+ * ran but produced no parseable version string.
11
+ */
12
+ export declare function probe(bin: string): {
13
+ version: string | null;
14
+ } | undefined;
15
+ /**
16
+ * Detects which coding-agent CLIs are available on PATH, and the version
17
+ * each reports (null when present but unparseable). `probeBin` is
18
+ * injectable so tests can stub PATH presence/version without shelling out
19
+ * for real binaries.
20
+ */
21
+ export declare function detectAgents(probeBin?: (bin: string) => {
22
+ version: string | null;
23
+ } | undefined): DetectedAgent[];
@@ -0,0 +1,35 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ const CANDIDATES = ['codex', 'claude'];
3
+ const VERSION_RE = /\d+\.\d+\.\d+/;
4
+ /**
5
+ * Real PATH probe: runs `<bin> --version` and parses the first semver-ish
6
+ * token out of stdout. Returns `undefined` when the binary isn't present/
7
+ * runnable (spawn error, timeout, or nonzero exit); `null` version when it
8
+ * ran but produced no parseable version string.
9
+ */
10
+ export function probe(bin) {
11
+ const result = spawnSync(bin, ['--version'], {
12
+ stdio: ['ignore', 'pipe', 'pipe'],
13
+ timeout: 3000,
14
+ encoding: 'utf8',
15
+ });
16
+ if (result.error !== undefined || result.status !== 0)
17
+ return undefined;
18
+ const match = result.stdout?.match(VERSION_RE);
19
+ return { version: match ? match[0] : null };
20
+ }
21
+ /**
22
+ * Detects which coding-agent CLIs are available on PATH, and the version
23
+ * each reports (null when present but unparseable). `probeBin` is
24
+ * injectable so tests can stub PATH presence/version without shelling out
25
+ * for real binaries.
26
+ */
27
+ export function detectAgents(probeBin = probe) {
28
+ const found = [];
29
+ for (const kind of CANDIDATES) {
30
+ const result = probeBin(kind);
31
+ if (result)
32
+ found.push({ kind, version: result.version });
33
+ }
34
+ return found;
35
+ }
@@ -0,0 +1,24 @@
1
+ export interface GitSnapshot {
2
+ head: string | null;
3
+ dirty: string[];
4
+ }
5
+ export declare function isGitRepo(dir: string): boolean;
6
+ export declare function snapshot(dir: string): GitSnapshot;
7
+ export declare function diffSince(dir: string, snap: GitSnapshot): string;
8
+ export declare function changedFiles(dir: string, snap: GitSnapshot): string[];
9
+ /**
10
+ * Reverts the given files, which are expected to come from `changedFiles`
11
+ * (and therefore already exclude any untracked files that pre-date the
12
+ * snapshot).
13
+ *
14
+ * Tracked files are restored from the snapshot commit (`git checkout
15
+ * <snap.head> -- <file>`), NOT from the index — so a file that was `git add`ed
16
+ * after the snapshot is still restored to its snapshot content, not its staged
17
+ * (mutated) content. Files that are currently untracked (newly added since the
18
+ * snapshot) are deleted.
19
+ *
20
+ * If `snap.head` is null (a repo with no commits at snapshot time) there is no
21
+ * commit to restore tracked files from, so only the newly-added untracked files
22
+ * are deleted; any tracked-file changes are left in place.
23
+ */
24
+ export declare function revert(dir: string, snap: GitSnapshot, files: string[]): void;
@@ -0,0 +1,95 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { rmSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ function git(dir, args) {
5
+ return execFileSync('git', args, { cwd: dir, encoding: 'utf8' });
6
+ }
7
+ /** Untracked (non-ignored) files, relative to `dir`. */
8
+ function untrackedFiles(dir) {
9
+ const out = git(dir, ['ls-files', '--others', '--exclude-standard']);
10
+ return out.split('\n').filter((line) => line.length > 0);
11
+ }
12
+ /**
13
+ * Tracked files with uncommitted modifications (staged or unstaged), relative
14
+ * to `dir`. Diffs against the given commit; when `head` is null (a repo with no
15
+ * commits) there are no tracked-and-committed files, so returns [].
16
+ */
17
+ function dirtyTrackedFiles(dir, head) {
18
+ if (!head)
19
+ return [];
20
+ const out = git(dir, ['diff', '--name-only', head]);
21
+ return out.split('\n').filter((line) => line.length > 0);
22
+ }
23
+ export function isGitRepo(dir) {
24
+ try {
25
+ const out = execFileSync('git', ['rev-parse', '--is-inside-work-tree'], {
26
+ cwd: dir,
27
+ encoding: 'utf8',
28
+ stdio: ['ignore', 'pipe', 'ignore'],
29
+ }).trim();
30
+ return out === 'true';
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
36
+ export function snapshot(dir) {
37
+ let head = null;
38
+ try {
39
+ head = execFileSync('git', ['rev-parse', 'HEAD'], {
40
+ cwd: dir,
41
+ encoding: 'utf8',
42
+ stdio: ['ignore', 'pipe', 'ignore'],
43
+ }).trim();
44
+ }
45
+ catch {
46
+ head = null;
47
+ }
48
+ const dirty = [...dirtyTrackedFiles(dir, head), ...untrackedFiles(dir)];
49
+ return { head, dirty };
50
+ }
51
+ export function diffSince(dir, snap) {
52
+ if (snap.head) {
53
+ return git(dir, ['diff', snap.head]);
54
+ }
55
+ return git(dir, ['diff']);
56
+ }
57
+ export function changedFiles(dir, snap) {
58
+ // Exclude any tracked file that was ALREADY dirty (uncommitted WIP) at
59
+ // snapshot time — mirrors the untracked filter below. We can't cleanly
60
+ // separate the user's pre-existing edits from the agent's own edits to the
61
+ // same file, so the safest choice is to leave such files out of
62
+ // `changedFiles` entirely: they won't be reported as agent changes, and
63
+ // (critically) `revert` won't touch them, preserving the user's WIP.
64
+ const trackedChanged = dirtyTrackedFiles(dir, snap.head).filter((f) => !snap.dirty.includes(f));
65
+ const newlyUntracked = untrackedFiles(dir).filter((f) => !snap.dirty.includes(f));
66
+ return [...new Set([...trackedChanged, ...newlyUntracked])];
67
+ }
68
+ /**
69
+ * Reverts the given files, which are expected to come from `changedFiles`
70
+ * (and therefore already exclude any untracked files that pre-date the
71
+ * snapshot).
72
+ *
73
+ * Tracked files are restored from the snapshot commit (`git checkout
74
+ * <snap.head> -- <file>`), NOT from the index — so a file that was `git add`ed
75
+ * after the snapshot is still restored to its snapshot content, not its staged
76
+ * (mutated) content. Files that are currently untracked (newly added since the
77
+ * snapshot) are deleted.
78
+ *
79
+ * If `snap.head` is null (a repo with no commits at snapshot time) there is no
80
+ * commit to restore tracked files from, so only the newly-added untracked files
81
+ * are deleted; any tracked-file changes are left in place.
82
+ */
83
+ export function revert(dir, snap, files) {
84
+ if (files.length === 0)
85
+ return;
86
+ const currentUntracked = new Set(untrackedFiles(dir));
87
+ const toDelete = files.filter((f) => currentUntracked.has(f));
88
+ const toCheckout = files.filter((f) => !currentUntracked.has(f));
89
+ for (const file of toDelete) {
90
+ rmSync(join(dir, file), { force: true });
91
+ }
92
+ if (toCheckout.length > 0 && snap.head) {
93
+ git(dir, ['checkout', snap.head, '--', ...toCheckout]);
94
+ }
95
+ }
@@ -0,0 +1,8 @@
1
+ import type { AgentKind } from './types.js';
2
+ /**
3
+ * When an agent CLI exits nonzero without ever emitting a terminal `done`,
4
+ * inspect its buffered stderr tail for known model-rejection shapes (a stale
5
+ * CLI that doesn't recognize the requested model). Returns an actionable
6
+ * hint to append to the error event text, or undefined when nothing matches.
7
+ */
8
+ export declare function modelRejectionHint(kind: AgentKind, stderrTail: string): string | undefined;
@@ -0,0 +1,12 @@
1
+ const MODEL_REJECTION_RE = /unknown model|unsupported model|invalid model|model.*not.*(found|supported)/i;
2
+ /**
3
+ * When an agent CLI exits nonzero without ever emitting a terminal `done`,
4
+ * inspect its buffered stderr tail for known model-rejection shapes (a stale
5
+ * CLI that doesn't recognize the requested model). Returns an actionable
6
+ * hint to append to the error event text, or undefined when nothing matches.
7
+ */
8
+ export function modelRejectionHint(kind, stderrTail) {
9
+ if (!MODEL_REJECTION_RE.test(stderrTail))
10
+ return undefined;
11
+ return `hint: your ${kind} CLI may be too old for the selected model — upgrade it or switch backend in Settings.`;
12
+ }