@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,527 @@
1
+ import { dirname, join, resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import type { InfrastructureManifest } from '../infrastructure';
4
+
5
+ /** Absolute path to the register-API CLI bin (`node <this> <cmd>`), resolved
6
+ * from this file's location. This exact invocation is the ONLY sandbox-safe
7
+ * way to run the CLI: `npx emberflow` / `tsx` spawn a tsx IPC pipe the codex
8
+ * sandbox blocks; this bin runs the CLI in-process under tsx's register API. */
9
+ const EMBERFLOW_BIN = resolve(dirname(fileURLToPath(import.meta.url)), '../../bin/emberflow.mjs');
10
+
11
+ export type AgentIntent =
12
+ | { action: 'new-scenario'; flowId: string; instruction: string }
13
+ | { action: 'edit-node'; flowId: string; nodeId: string; instruction: string }
14
+ | { action: 'edit-flow'; flowId: string; instruction: string; scaffold?: boolean }
15
+ | { action: 'new-operation'; location: string; instruction: string }
16
+ | { action: 'setup-auth'; environment: string; instruction: string }
17
+ | { action: 'setup-environments'; instruction: string }
18
+ | { action: 'scout-infrastructure'; instruction: string }
19
+ | { action: 'cover-operation'; flowId: string; instruction: string }
20
+ | { action: 'ask'; flowId?: string; instruction: string };
21
+
22
+ /** Minimal node metadata the agent needs to know which node types it may build with. */
23
+ export interface AvailableNode {
24
+ type: string;
25
+ label?: string;
26
+ description?: string;
27
+ }
28
+
29
+ /**
30
+ * Builds a natural-language prompt for a coding agent (Codex/Claude) that
31
+ * turns a studio `AgentIntent` into skill-aware instructions for editing
32
+ * Emberflow flow files. Pure and deterministic: same inputs always produce
33
+ * the same prompt string.
34
+ *
35
+ * `relPath` is the operation's on-disk relative path under `apisDir` (e.g.
36
+ * `default/triage`) — NOT necessarily the same as `intent.flowId` (the
37
+ * in-file `id`), since operations are keyed by id but stored by path. For
38
+ * `new-operation` there is no existing flow yet, so `relPath` instead carries
39
+ * the *resolved target directory* under `apisDir` (e.g. `billing` or
40
+ * `default`) that the new operation file should be created inside.
41
+ *
42
+ * `availableNodes` is the full, consumer-specific set of registered node
43
+ * types (domain nodes like `ValidateCredentials`/`FetchUser` plus built-ins
44
+ * like `Input`/`Result`/`Response`/`requireAuth`). It is injected so the agent
45
+ * knows what it can REUSE — but nodes are real code it can also author. The
46
+ * prompt encourages inventing a node (registering its implementation) when no
47
+ * existing one fits; the single guardrail is that a referenced `type` must be
48
+ * registered in the same change, so a made-up type with no implementation
49
+ * (e.g. a bare `CurrentServerTime`) can't slip through validation.
50
+ *
51
+ * `projectLanguage` is the target project's authored language (from its
52
+ * `ProjectConfig.language`, explicit-or-inferred). Defaults to 'typescript' —
53
+ * the Emberflow repo itself, when this function is called without a loaded
54
+ * consumer project.
55
+ *
56
+ * `infrastructure` is the loaded `emberflow/infrastructure.json` manifest (or
57
+ * `null` when the project hasn't been scouted / the file is malformed). When
58
+ * present, a preamble block after the node palette lists what the project
59
+ * already uses so the agent REUSES it (same secretRef names, same systems)
60
+ * instead of inventing parallel config. The `scout-infrastructure` intent
61
+ * itself is what WRITES this manifest, so it does not receive the block.
62
+ */
63
+ export function buildPrompt(
64
+ intent: AgentIntent,
65
+ apisDir: string,
66
+ relPath: string,
67
+ availableNodes: AvailableNode[] = [],
68
+ projectLanguage: 'javascript' | 'typescript' = 'typescript',
69
+ infrastructure: InfrastructureManifest | null = null,
70
+ ): string {
71
+ const lines: string[] = [];
72
+
73
+ /** Shared invariant for every flow-mutating intent: doctor is the mechanical
74
+ * checker (missing param defaults, uncovered path params, missing expects),
75
+ * so the per-intent prose no longer has to spell those rules out. */
76
+ const doctorLine = (id: string) =>
77
+ `Before finishing, run doctor ${id} and resolve its findings — fix warnings by editing the operation/scenarios (doctor --fix seeds missing param defaults), and mention any finding you deliberately leave in your final message.`;
78
+
79
+ lines.push(
80
+ 'Skills available in this repo — read the one(s) relevant to your task; you do NOT need all of them (tap in as needed):',
81
+ );
82
+ lines.push(
83
+ '- emberflow-basics — the project model: the apis/ file layout, how an operation and its optional http trigger work, nodes, scenarios, auth. Read first if unsure how the project is structured.',
84
+ );
85
+ lines.push('- emberflow-new-workflow — building a new operation: choosing the trigger and wiring Input → logic → terminus.');
86
+ lines.push('- emberflow-review-workflow — a rubric to self-check an operation before calling it done (wiring, branches, terminus, auth).');
87
+ lines.push('- emberflow-model-process — porting an existing real process/endpoint into an operation faithfully.');
88
+ lines.push('');
89
+ lines.push(
90
+ 'Nodes are real code you WRITE, not a fixed menu. When the goal needs a capability no existing node provides — calling an external API (e.g. an HTTP fetch), a custom transform, a domain rule — CREATE a new node: add its definition plus an implementation `async (ctx) => output` to the project by calling `registry.register(definition, implementation)` inside `registerNodes(registry)` in the project\'s `emberflow.config.(mjs|js|ts)` (or a nodes module that file imports), then reference its `type` from the operation. Inventing nodes is normal and encouraged — build the operation out with as many nodes as the goal genuinely needs (an HTTP call → parse → shape → respond is four nodes, not two).',
91
+ );
92
+ lines.push('');
93
+ lines.push(
94
+ 'The one hard rule: NEVER reference a node `type` you have not registered. A made-up type with no implementation fails flow validation. So whenever you use a new type, you MUST register its implementation in the SAME change. Reuse an existing registered node only when it genuinely fits; otherwise author the node you need.',
95
+ );
96
+ lines.push('');
97
+ lines.push(
98
+ projectLanguage === 'javascript'
99
+ ? 'This project is javascript-driven: author nodes, config edits, and any new modules in JavaScript (.mjs/.js, JSDoc types) — do not introduce TypeScript files into it.'
100
+ : 'This project is typescript-driven: author nodes, config edits, and any new modules in TypeScript (.ts, typed) — do not introduce JavaScript files into it.',
101
+ );
102
+ lines.push('');
103
+ const traceKindLine =
104
+ 'When you register a new node\'s implementation, set its traceKind honestly: `"db"`, `"http"`, or `"llm"` for anything that touches real infrastructure (a database, an external API/network call, a model call), omitted (or `"compute"`) for pure logic that only transforms its input. `traceKind` is the mock-mode infrastructure boundary — in a mock run, compute nodes execute for real but infra nodes require a canned output (see the mocks guidance for scenario-authoring tasks); a node that touches infrastructure but is left without a `traceKind` will silently execute for real during a mock run instead of being caught, so get this right when you author the node.' +
105
+ ' For mutation nodes (effects: "mutation"): the commit path (config.commit === true && !ctx.safeMode) performs the REAL side effect — these are operational APIs, and a commit branch that logs "[SIMULATED]" and returns success is FORBIDDEN. Mock mode and dry-run already cover design-time needs; if a required secret is missing at commit time, THROW an error naming the exact secretRef the user must set (studio Manage Environment dialog) — never fake success. doctor flags fake commits as simulated-commit.';
106
+ lines.push('Existing registered node types you can reuse before authoring a new one:');
107
+ if (availableNodes.length === 0) {
108
+ lines.push('(none reported yet — author the nodes the goal needs, registering each in registerNodes.)');
109
+ } else {
110
+ for (const node of availableNodes) {
111
+ const bits = [node.type];
112
+ if (node.label) bits.push(`label: ${node.label}`);
113
+ if (node.description) bits.push(`description: ${node.description}`);
114
+ lines.push(`- ${bits.join(' — ')}`);
115
+ }
116
+ }
117
+ lines.push('');
118
+
119
+ // Known-infrastructure preamble: what the project already uses, so the agent
120
+ // REUSES it instead of inventing parallel config. Omitted for the scout
121
+ // intent — it's the one that WRITES the manifest, so priming it with the
122
+ // manifest's own contents would be circular.
123
+ if (intent.action !== 'scout-infrastructure') {
124
+ const INFRASTRUCTURE_ITEM_CAP = 30;
125
+ if (infrastructure && infrastructure.items.length > 0) {
126
+ lines.push('Known project infrastructure (from emberflow/infrastructure.json — what this project already uses):');
127
+ const shown = infrastructure.items.slice(0, INFRASTRUCTURE_ITEM_CAP);
128
+ for (const item of shown) {
129
+ const bits = [`${item.name} (${item.kind})`];
130
+ if (item.suggestedSecretRefs.length > 0) bits.push(`secretRefs: ${item.suggestedSecretRefs.join(', ')}`);
131
+ const firstEvidence = item.evidence.find((e) => e.file)?.file;
132
+ if (firstEvidence) bits.push(`see ${firstEvidence}`);
133
+ lines.push(`- ${bits.join(' — ')}`);
134
+ }
135
+ const remaining = infrastructure.items.length - shown.length;
136
+ if (remaining > 0) {
137
+ lines.push(`(+${remaining} more — see emberflow/infrastructure.json or re-run the scout)`);
138
+ }
139
+ lines.push(
140
+ 'REUSE rule: when an operation needs infrastructure this manifest already names, REUSE it — the same secretRef NAMES, the same systems (database, API, provider) — instead of inventing parallel config. When the manifest is absent or looks stale relative to what you find, SAY so rather than guessing.',
141
+ );
142
+ lines.push('');
143
+ } else if (infrastructure && infrastructure.greenfield) {
144
+ lines.push(
145
+ 'Known project infrastructure: emberflow/infrastructure.json marks this project greenfield (no existing databases/APIs/providers detected). Introduce infrastructure only as the goal genuinely requires it.',
146
+ );
147
+ lines.push('');
148
+ } else if (infrastructure) {
149
+ // Present but empty, and NOT greenfield: distinct from "no manifest at
150
+ // all" — the scout ran and found nothing, which may mean the project
151
+ // grew since the last scan rather than genuinely having no infra.
152
+ lines.push(
153
+ 'Known project infrastructure: the infrastructure manifest exists but lists no items — re-run the scout if the project has grown.',
154
+ );
155
+ lines.push('');
156
+ } else {
157
+ lines.push(
158
+ 'Known project infrastructure: none on record (no emberflow/infrastructure.json manifest). If this operation needs to reuse existing databases/APIs/providers, consider running the infrastructure scout first rather than guessing what the project already uses.',
159
+ );
160
+ lines.push('');
161
+ }
162
+ }
163
+
164
+ lines.push(
165
+ `You control the live runner (the studio canvas) through the Emberflow CLI. Make EDITS by writing files; use the CLI to RUN, INSPECT, and REMOVE. Invoke it EXACTLY like this (a shell command) — this precise form is required, because \`npx emberflow\` and \`tsx\` fail inside this sandbox:`,
166
+ );
167
+ lines.push(` node ${EMBERFLOW_BIN} <command> [args]`);
168
+ lines.push('Commands (each talks to the already-running runner):');
169
+ lines.push('- run <id> [--input \'<json>\' | --scenario <name>] [--env <name>] [--full] — run an operation and print its status, per-node nodeStates, and logs. Output is CONCISE by default (large node inputs/outputs are truncated to keys + a preview, so a flow whose fetch node returns 70KB of raw API JSON stays readable); add --full only when you need a node\'s complete raw output. Use to VERIFY a change, then report the result.');
170
+ lines.push('- get-workflow <id> — print an operation\'s full JSON. Use to inspect what exists.');
171
+ lines.push('- get-node <id> <nodeId> — print ONE node\'s wiring: its config/inputMap/retry/optional, its inbound + outbound edges, and (when the type is registered) a definition summary (input/output field names, its trace kind and effects). Use to zoom into a single node without reading the whole operation JSON.');
172
+ lines.push('- list-workflows — list all operations (id, name, path, http).');
173
+ lines.push('- node-schema <type> — one node\'s exact input/output contract. The registered nodes are ALREADY listed above with descriptions, and the best way to learn how to WIRE a node is to read an operation that already uses it (get-workflow — it shows the node in context with real config + edges). Reach for node-schema only when you need a node\'s precise field names and no existing operation demonstrates them; don\'t look nodes up one-by-one out of habit. (list-nodes also exists but you rarely need it — the palette is above.)');
174
+ lines.push('- list-environments — the configured environments (names, which are protected).');
175
+ lines.push('- login-environment <name> — perform the environment\'s configured login and store its credential runner-side; run this if operations fail 401/unauthorized and list-environments shows auth configured but not authenticated.');
176
+ lines.push('- set-environment-auth <name> --json \'<EnvAuth JSON>\' — set (or, with --json \'null\', clear) an environment\'s auth block; carries no secret values, only refs/names.');
177
+ lines.push('- delete <id> — remove an operation.');
178
+ lines.push('- test <id> — run the operation\'s scenario expectations in-process; exit 0/1.');
179
+ lines.push('- doctor [<id>] [--fix] — report operation diagnostics (missing param defaults, uncovered path params, missing expects); --fix auto-seeds missing param defaults. Run it on any operation you created or edited and resolve every finding before you finish.');
180
+ lines.push('- serving <real|mock> — switch whether mounted HTTP endpoints execute for real or answer from scenario expectations (mock). NEVER switch this yourself: the mode is the user\'s choice, and flipping it mid-task makes their runs hit real infrastructure without them knowing. In mock mode, `run <id>` executes against scenario mocks and `test <id>` always runs hermetically — verify with those; if your verification truly needs a real run, say so in your final message instead of flipping the mode.');
181
+ lines.push('- rename <old-id> <new-id> [--name "<display>"] — rename an operation: moves it to the new apis path/id AND updates its display name + http route to match. Operations often start with a vague auto-generated name derived from the create prompt (e.g. "api-which-uses-different", "another-api-call-like"). NAMING IS PART OF YOUR JOB: before finishing, if the id or display name does not clearly say what the operation does, rename it to a concise descriptive slug (e.g. "world-cup-recent-results").');
182
+ lines.push(
183
+ 'Rule of thumb: edits = files; actions (run, read logs, inspect, delete) = the CLI above. These CLI calls are surfaced as distinct operation steps in the Agent view.',
184
+ );
185
+ lines.push(
186
+ 'INSPECTION ORDER: to understand or locate something, go top-down through the CLI BEFORE opening files — list-workflows (find the op) → get-workflow <id> (see the graph) → get-node <id> <nodeId> (one node\'s wiring) → node-schema <type> (the registered definition) → samples <nodeId> (recorded runs). Open source files directly only once the CLI has told you WHERE to look (e.g. a node implementation you located by type).',
187
+ );
188
+ lines.push('');
189
+ lines.push(
190
+ 'AUTH: environments may carry an auth block (attach cookie/header + optional login request + capture) in emberflow.environments.json. Studio/CLI runs auto-attach the stored credential. You NEVER see secret values — they appear as «secret:KEY» in run output. When asked to set up auth for an environment, scaffold the auth block in emberflow.environments.json (attach.name = the cookie/header the API expects, secretRef naming where the captured credential lands, login.request with bodyRef pointing at a secret holding the credentials JSON) and then run login-environment <name> to verify capture works.',
191
+ );
192
+ lines.push(
193
+ 'Reading an existing operation is your PRIMARY way to understand how nodes are used — the workflow JSON already contains the nodes, their config, and the edges wired together. When your goal is "like operation X but …", or the same nodes appear in a sibling op, READ that operation first (get-workflow <id>, or the .json under the apis directory) and mirror its structure — that\'s faster and more reliable than inspecting nodes one at a time. Reading any operation is never restricted; only EDITING is scoped to your task.',
194
+ );
195
+ lines.push('');
196
+ lines.push(
197
+ `TEST AS YOU BUILD — do not wait until the end. Every time you add or change nodes (or a node's implementation), run the operation (\`node ${EMBERFLOW_BIN} run <id> --input '<json>'\`, or with --scenario) and READ the printed nodeStates + logs: each node's input, output, and status is in nodeStates, so you can see exactly which node misbehaves — not just the final Response. That IS your isolation — inspect the specific node's entry rather than guessing from the graph. When a node errors or returns the wrong value, fix its implementation or wiring and run again. Iterate — run, read, fix, re-run — until the operation runs correctly end-to-end. A flow that VALIDATES is not necessarily a flow that WORKS; running it is how you find out.`,
198
+ );
199
+ lines.push(
200
+ `Once it runs correctly, ALSO make sure it stays correct: for every path you changed, add or update a scenario in the op's scenarios sidecar file with an "expect" ({status?, body?, executedNodes?} — assert at minimum status and the branch's terminal node id) so the behavior is asserted, not just eyeballed. Finish with \`node ${EMBERFLOW_BIN} test <id>\` — exit 0 means done; a failure means the scenario or the flow is still wrong, so keep iterating (run/read nodeStates to debug which node is at fault, fix, re-run test) until it passes.`,
201
+ );
202
+ lines.push('');
203
+
204
+ /** Shared guidance for scenario-authoring intents (new-scenario, cover-operation):
205
+ * scenarios must carry canned output for any infrastructure node they exercise,
206
+ * in the exact op-level ⊕ per-scenario `mocks` shape the mock-run engine reads. */
207
+ const mocksLine = (scenariosFile: string) =>
208
+ `Mock runs: a node whose registered definition sets \`traceKind\` to "db", "http", or "llm" touches real infrastructure, so a mock run of any scenario reaching it needs a canned output for it or it fails. For every such infrastructure node your scenario(s) reach, add a "mocks" entry — { nodeId: output } — giving that node a REALISTIC canned output matching the exact shape its implementation returns (read the node's implementation in emberflow.config.(mjs|js|ts) or the nodes module it imports to learn that shape; a mocks value is the node's OUTPUT verbatim, not a wrapper around it). ${scenariosFile} carries two levels: a top-level "mocks" map (the op-level default, used by a plain Run with no scenario selected) and, inside each entry under "scenarios", an optional per-scenario "mocks" map that overrides the op-level default per nodeId for that scenario only. Author BOTH: the op-level "mocks" so plain Run works, and any per-scenario "mocks" a specific scenario needs to diverge (e.g. an error-path scenario mocking a lookup node to return "not found"). NEVER put secret values in a mocks entry — mocked output must not contain real credentials, tokens, or other secret material, even fake-looking ones that resemble real formats.`;
209
+
210
+ switch (intent.action) {
211
+ case 'new-scenario': {
212
+ const flowFile = join(apisDir, ...relPath.split('/')) + '.json';
213
+ const scenariosFile = join(apisDir, ...relPath.split('/')) + '.scenarios.json';
214
+ lines.push(`Relevant skill: emberflow-basics (the scenario shape).`);
215
+ lines.push('');
216
+ lines.push(
217
+ `Task: add a new scenario to the flow "${intent.flowId}". The flow definition lives at ${flowFile} and its scenarios live at ${scenariosFile}.`,
218
+ );
219
+ lines.push('');
220
+ lines.push(`User instruction (verbatim): ${intent.instruction}`);
221
+ lines.push('');
222
+ lines.push(
223
+ `Give the scenario an "expect" ({status?, body?, executedNodes?}) so it doubles as a test, not just a named input — at minimum assert status and the branch's terminal node id in executedNodes; add a body subset to expect wherever the response is deterministic.`,
224
+ );
225
+ lines.push('');
226
+ lines.push(mocksLine(scenariosFile));
227
+ lines.push('');
228
+ lines.push(
229
+ `Only edit ${flowFile} and ${scenariosFile}. Keep both files valid JSON, and validate that the flow's wiring (node connections and references) remains correct.`,
230
+ );
231
+ lines.push('');
232
+ lines.push(
233
+ `Verify with \`node ${EMBERFLOW_BIN} test ${intent.flowId}\` — don't stop at just running the scenario; the test command asserts the expect and exits 0/1, so a passing test is proof the scenario actually checks what it claims.`,
234
+ );
235
+ lines.push('');
236
+ lines.push(doctorLine(intent.flowId));
237
+ break;
238
+ }
239
+ case 'edit-node': {
240
+ const flowFile = join(apisDir, ...relPath.split('/')) + '.json';
241
+ lines.push(`Relevant skill: emberflow-basics (node + config shape).`);
242
+ lines.push('');
243
+ lines.push(
244
+ `Task: edit node "${intent.nodeId}" in the flow "${intent.flowId}". The flow definition lives at ${flowFile}.`,
245
+ );
246
+ lines.push('');
247
+ lines.push(`User instruction (verbatim): ${intent.instruction}`);
248
+ lines.push('');
249
+ lines.push(
250
+ `Only edit ${flowFile}. Keep the file valid JSON, and validate that the flow's wiring (node connections and references) remains correct.`,
251
+ );
252
+ lines.push('');
253
+ lines.push(doctorLine(intent.flowId));
254
+ break;
255
+ }
256
+ case 'edit-flow': {
257
+ const flowFile = join(apisDir, ...relPath.split('/')) + '.json';
258
+ if (intent.scaffold) {
259
+ // Studio "create" path: the op is a freshly-scaffolded Input → Response
260
+ // shell whose name is a PLACEHOLDER derived from the create prompt
261
+ // (e.g. "Similar Api Call Will"). Naming + building are the whole job.
262
+ lines.push(`Relevant skills: emberflow-new-workflow (building it out), emberflow-review-workflow (self-check).`);
263
+ lines.push('');
264
+ lines.push(`This operation was just scaffolded as an empty Input → Response shell. Its current id/name ("${intent.flowId}") is an auto-generated PLACEHOLDER from the create prompt, not a real name. The flow definition lives at ${flowFile}.`);
265
+ lines.push('');
266
+ lines.push(`User goal (verbatim): ${intent.instruction}`);
267
+ lines.push('');
268
+ lines.push('Do it in this order:');
269
+ lines.push(
270
+ `1. NAME IT FIRST. Pick a clear, descriptive kebab-case id + display name that say what the operation does, and rename the shell before building: node ${EMBERFLOW_BIN} rename ${intent.flowId} <new-id> --name "<Display Name>". This updates the id, the display name, and the http route together. (Keep the id in the same folder unless the goal implies otherwise.)`,
271
+ );
272
+ lines.push(
273
+ `2. BUILD IT OUT by editing the renamed file — add the logic nodes the goal needs between Input and the Response terminus. If it needs a node type that isn't registered yet, ALSO author it: add its implementation via registry.register(definition, impl) inside registerNodes(registry) in the project's emberflow.config.(mjs|js|ts) (or a nodes module it imports). Keep every referenced node type registered. ${traceKindLine}`,
274
+ );
275
+ lines.push(
276
+ `3. RUN + VERIFY: node ${EMBERFLOW_BIN} run <new-id> — read the nodeStates and iterate until it produces the right result. Self-check with emberflow-review-workflow.`,
277
+ );
278
+ lines.push('');
279
+ lines.push(doctorLine('<new-id>'));
280
+ break;
281
+ }
282
+ lines.push(`Relevant skills: emberflow-basics (the existing operation's shape), emberflow-review-workflow (self-check). This is about an EXISTING operation — do NOT treat it as building a new one.`);
283
+ lines.push('');
284
+ lines.push(`Task: the user's message below concerns the flow "${intent.flowId}". The flow definition lives at ${flowFile}.`);
285
+ lines.push('');
286
+ lines.push(`User instruction (verbatim): ${intent.instruction}`);
287
+ lines.push('');
288
+ lines.push(
289
+ `FIRST decide what the message is. If it is a QUESTION or a request to explain/locate something ("where is…", "why does…", "what happens when…"), ANSWER it and change NOTHING — no file writes, no create/save/delete, no doctor --fix; use the INSPECTION ORDER above (CLI before file reads), lead with the direct answer in 1-3 sentences, then supporting detail with file:line references, and stop there. Only when the message asks for a CHANGE do the edit instructions below apply. If it genuinely could be either, say what you would change and ask one clarifying question instead of editing.`,
290
+ );
291
+ lines.push('');
292
+ lines.push(
293
+ `After making changes, self-check your work using emberflow-review-workflow.`,
294
+ );
295
+ lines.push('');
296
+ lines.push(
297
+ `Edit ${flowFile} to make the change, building the operation out with as many nodes as the goal genuinely needs. If it needs a node type that isn't registered yet, ALSO author that node: add its implementation via registry.register(definition, impl) inside registerNodes(registry) in the project's emberflow.config.(mjs|js|ts) (or a nodes module that file imports). Keep every file you touch valid, and validate that the flow's wiring (node connections and references) stays correct — every referenced node type must be registered. ${traceKindLine} Before you consider it done, RUN it (node <bin> run <id>), read the nodeStates, and iterate until it produces the right result.`,
298
+ );
299
+ lines.push('');
300
+ lines.push(doctorLine(intent.flowId));
301
+ break;
302
+ }
303
+ case 'new-operation': {
304
+ const targetDir = join(apisDir, ...relPath.split('/'));
305
+ lines.push(`Relevant skills: emberflow-new-workflow, emberflow-review-workflow.`);
306
+ lines.push('');
307
+ lines.push(
308
+ `Task: create a brand-new operation that achieves the user's goal below. The operation does not exist yet — you are creating it from scratch inside ${targetDir}.`,
309
+ );
310
+ lines.push('');
311
+ lines.push(`User goal (verbatim): ${intent.instruction}`);
312
+ lines.push('');
313
+ lines.push(
314
+ `Choose a clear, descriptive kebab-case filename for the new operation and create it at ${targetDir}/<name>.json. Set the file's in-file "id" field equal to its path relative to the apis directory (for example, if you create ${targetDir}/charge.json, its "id" must be "${relPath}/charge" — do not invent a different id).`,
315
+ );
316
+ lines.push('');
317
+ lines.push(
318
+ `Decide the operation's shape from the goal — don't assume it's an HTTP endpoint. An operation always starts with an "Input" entry node and runs whatever logic nodes the goal needs; the trigger + terminus depend on what it is:`,
319
+ );
320
+ lines.push(
321
+ ` • An HTTP endpoint (something is called over the web): add an "http" trigger — { method, path } that fit the goal (you choose the mechanics) — Input receives the request as { params, query, body, headers }, and the flow ends in a "Response" node emitting { status, body }.`,
322
+ );
323
+ lines.push(
324
+ ` • An internal sub-flow (called by other operations via a Subflow node, or run in the studio): OMIT "http" and end in "Result" node(s).`,
325
+ );
326
+ lines.push(
327
+ `Pick whichever fits the goal, and build it out with as many nodes as the goal genuinely needs.`,
328
+ );
329
+ lines.push('');
330
+ lines.push('Do it in this order:');
331
+ lines.push(
332
+ `1. NAME + CREATE THE SHELL FIRST. Decide a clear, descriptive kebab-case id under "${relPath}" (e.g. ${relPath}/<descriptive-slug>), a human display name, and — for an HTTP endpoint — the method (GET to read, POST to create, PATCH/PUT to update, DELETE to remove) and the route path. Then create the shell with the CLI:`,
333
+ );
334
+ lines.push(` node ${EMBERFLOW_BIN} create <id> --method <METHOD> --path </route> --name "<Display Name>" (HTTP endpoint)`);
335
+ lines.push(` node ${EMBERFLOW_BIN} create <id> --name "<Display Name>" (internal sub-flow — no method/path; ends in Result)`);
336
+ lines.push(` This writes a valid shell (Input → Response/Result + the trigger). Do this BEFORE building any logic.`);
337
+ lines.push(
338
+ `2. BUILD IT OUT by editing the created file at ${targetDir}/<id>.json — add the logic nodes the goal needs between Input and the terminus. If the goal needs a node type that isn't registered yet, ALSO author it: add its implementation via registry.register(definition, impl) inside registerNodes(registry) in the project's emberflow.config.(mjs|js|ts) (or a nodes module that file imports). Don't modify files unrelated to this operation and its nodes. ${traceKindLine}`,
339
+ );
340
+ lines.push(
341
+ `3. RUN + VERIFY: node ${EMBERFLOW_BIN} run <id> — read the nodeStates, and iterate until it produces the right result.`,
342
+ );
343
+ lines.push('');
344
+ lines.push(doctorLine('<id>'));
345
+ lines.push('');
346
+ lines.push(
347
+ `ENVIRONMENTS + SECRETS INTAKE: if the operation touches infrastructure (a database, an external API, an LLM), check whether emberflow.environments.json covers what it needs. Where it doesn't, do NOT invent values: reference clearly-named secretRef keys (ctx.secrets.<KEY>) and vars (ctx.vars.<NAME>) in the node implementations, list those secret key NAMES (values never) under the relevant environment's "secrets" list in emberflow.environments.json, author the sidecar mocks so the operation is fully runnable in mock mode regardless, and END your final message by asking the user the specific open questions — which environments this should run against, which is production-like, and telling them to enter the secret values for the keys you named via the studio's Manage Environment dialog (never in this chat).`,
348
+ );
349
+ break;
350
+ }
351
+ case 'setup-auth': {
352
+ lines.push(`Relevant skill: emberflow-basics (the auth block shape). See the AUTH guidance above for the block's shape and CLI commands — this task is that guidance applied to one environment.`);
353
+ lines.push('');
354
+ lines.push(
355
+ `Task: get a working "auth" block for environment "${intent.environment}" from the description below (curl command, prose, or both).`,
356
+ );
357
+ lines.push('');
358
+ lines.push(`User instruction (verbatim): ${intent.instruction}`);
359
+ lines.push('');
360
+ lines.push(
361
+ `Inspect the target API's login from the instruction, then compose the EnvAuth JSON: attach (as: "cookie"|"header", name, secretRef, and prefix for schemes like "Bearer " or "Basic ") plus an optional login (request + capture). Use prefix for Bearer/Basic-style headers; use bodyRef when the login request body itself must carry a credential (a secret holding the request body JSON, not inline values). Apply it with: set-environment-auth ${intent.environment} --json '<EnvAuth JSON>'.`,
362
+ );
363
+ lines.push('');
364
+ lines.push(
365
+ `NEVER read or edit emberflow.environments.json directly — set-environment-auth is the only way to change this environment's auth. NEVER ask for or handle credential values — the user enters secret values themselves in the studio's Manage Environment dialog, never through you. If applying the auth block requires a secret that isn't set yet (a bodyRef for the login body, or the attach.secretRef for a static key with no login), name EXACTLY which key the user must fill in in the Manage Environment dialog, then STOP — do not guess, invent, or ask the user to paste a value into chat.`,
366
+ );
367
+ lines.push('');
368
+ lines.push(
369
+ `If the secrets the auth block needs are already present, verify the setup with: login-environment ${intent.environment}. If capture fails, read the error, adjust the auth JSON (e.g. the capture path/cookie name), re-apply with set-environment-auth, and retry — iterate until login-environment succeeds or you hit a missing-secret stop condition above.`,
370
+ );
371
+ lines.push('');
372
+ lines.push(
373
+ `Finish by running list-environments and reporting this environment's auth state (configured/authenticated) from its output.`,
374
+ );
375
+ break;
376
+ }
377
+ case 'setup-environments': {
378
+ lines.push(`Relevant skill: emberflow-basics (environments + auth). See the AUTH guidance above for the auth block's shape.`);
379
+ lines.push('');
380
+ lines.push(`Task: the user wants to set up the project's environments; their description follows.`);
381
+ lines.push('');
382
+ lines.push(`User instruction (verbatim): ${intent.instruction}`);
383
+ lines.push('');
384
+ lines.push(
385
+ `Read emberflow.environments.json at the project root if it exists; create it (alongside emberflow.config.*) if it does not. This file is STRUCTURE ONLY and safe for you to edit: create or extend "defaultEnvironment" plus one entry under "environments" per environment the user described, using kebab/lower-case names (e.g. "dev", "staging", "prod"). Each environment's "secrets" is a LIST of key NAMES (e.g. "secrets": ["API_KEY"]) — never a value map. Secret VALUES live in emberflow.secrets.json (chmod 0600), which you never read or write; the user fills those in via the Manage Environment dialog.`,
386
+ );
387
+ lines.push('');
388
+ lines.push(
389
+ `Work from the user's description ALONE. This is a config-writing task, not an investigation: do NOT explore the project's source code, node implementations, other repositories, deployment providers, or any MCP/external service to infer URLs, hostnames or settings the user didn't state. Where the user's description leaves a value unknown, write a clearly-named placeholder var (e.g. "baseUrl": "https://CHANGE-ME.example.com") and list it as an open item in your final message instead of hunting for it.`,
390
+ );
391
+ lines.push('');
392
+ lines.push(
393
+ `Mark production-like environments "protected": true — this forces studio safe mode for runs against them and blocks serving them by default, so mistakes there are costly and Emberflow guards them harder. Put base URLs and other non-secret config under each environment's "vars".`,
394
+ );
395
+ lines.push('');
396
+ lines.push(
397
+ `NEVER invent or write secret or credential VALUES anywhere — not in emberflow.environments.json (which holds only key NAMES), not in emberflow.secrets.json (the 0600 values file you never touch), and not in your output. Where auth or API keys are involved, scaffold at most the secret key NAMES in an environment's "secrets" list and an "auth" block containing secretRef NAMES (no values) — and in your final message, tell the user to add the actual secret values via the studio's per-environment Manage Environment dialog, or by running login-environment <env> for auth that captures a credential through a login flow.`,
398
+ );
399
+ lines.push('');
400
+ lines.push(`emberflow.environments.json is gitignored — mention this in your final message so the user isn't surprised it doesn't show up in git status.`);
401
+ lines.push('');
402
+ lines.push(
403
+ `Finish by summarizing, for each environment you created or extended: its name, whether it's protected, and what (if anything) the user still needs to fill in (e.g. "dev: needs no secrets" / "prod: needs the API_KEY secret value in Manage Environment").`,
404
+ );
405
+ lines.push('');
406
+ lines.push(
407
+ `This panel is a conversation: when the user's description leaves a load-bearing choice genuinely open — which environment is the default, which are production-like (protected), what an environment's base URL is — END your final message with those specific questions (numbered, one line each) so the user can answer in their next message. Ask about real gaps only; don't quiz them on things you could reasonably default and have clearly listed as placeholders.`,
408
+ );
409
+ break;
410
+ }
411
+ case 'scout-infrastructure': {
412
+ const manifestPath = 'emberflow/infrastructure.json';
413
+ lines.push(`Relevant skill: emberflow-basics (the project model + file layout).`);
414
+ lines.push('');
415
+ lines.push(
416
+ `Task: scout this project's existing infrastructure and write a manifest describing it. Your JOB for THIS intent is to READ the project — this deliberately INVERTS the "do NOT explore the project's source code" rule that constrains setup-environments; for scout-infrastructure, exploring the codebase IS the work.`,
417
+ );
418
+ lines.push('');
419
+ if (intent.instruction.trim()) {
420
+ lines.push(`User instruction (verbatim): ${intent.instruction}`);
421
+ lines.push('');
422
+ }
423
+ lines.push(
424
+ `Investigate broadly — enumerate what the project already depends on and talks to:`,
425
+ );
426
+ lines.push(
427
+ ` • Dependencies: package.json + lockfiles, requirements.txt / pyproject.toml, go.mod, Gemfile, composer.json, etc.`,
428
+ );
429
+ lines.push(` • Config files: docker-compose.yml, Dockerfile, .env.example / .env.sample (NAMES only), prisma/schema.prisma, ORM configs, framework config.`);
430
+ lines.push(` • ORM schemas / migrations: the models and tables the project defines.`);
431
+ lines.push(` • Env-var REFERENCES in source (process.env.X, os.environ["X"], import.meta.env.X, ${'${VAR}'} in compose) — capture the NAMES.`);
432
+ lines.push(` • HTTP clients / SDK usage: fetch/axios/requests calls, vendor SDKs (stripe, sendgrid, aws-sdk, openai, twilio, …).`);
433
+ lines.push(` • Existing route/endpoint definitions: the app's own HTTP surface (Express routers, FastAPI routes, etc.).`);
434
+ lines.push('');
435
+ lines.push(
436
+ `Classify each thing you find into one of these manifest kinds: database, http-api, queue, cache, email, llm, auth, framework, storage, other. Pick the closest kind; use "other" only when nothing fits.`,
437
+ );
438
+ lines.push('');
439
+ lines.push(
440
+ `WRITE the manifest to ${manifestPath} (create the emberflow/ directory if it doesn't exist). Pretty-printed JSON, this exact shape:`,
441
+ );
442
+ lines.push(
443
+ ` { "version": 1, "scannedAt": "<current ISO timestamp>", "greenfield": <bool>, "summary": "<one sentence>", "items": [ { "id": "<kebab-slug>", "kind": "<kind>", "name": "<human name>", "evidence": [ { "file": "<path>", "note": "<what proves it>" } ], "suggestedSecretRefs": ["<ENV_VAR_NAME>"], "suggestedVars": [], "notes": "<optional>" } ] }`,
444
+ );
445
+ lines.push('');
446
+ lines.push(
447
+ `HARD RULE — NEVER copy a secret VALUE into the manifest. suggestedSecretRefs holds env-var NAMES ONLY (e.g. "DATABASE_URL", "STRIPE_SECRET_KEY") — never the value behind the name, even if you happen to see it in a .env file. Evidence points at FILES (with a short note), never at credential contents.`,
448
+ );
449
+ lines.push('');
450
+ lines.push(
451
+ `Each item's "evidence" should point at the real file(s) that prove it (e.g. "package.json" for a dependency, "prisma/schema.prisma" for a database) with a short note, so the studio can show provenance and other agents can jump straight to the source. Give each item a stable kebab-case "id" and a human-readable "name".`,
452
+ );
453
+ lines.push('');
454
+ lines.push(
455
+ `GREENFIELD: if this project has essentially no infrastructure yet (a bare scaffold — no databases, external APIs, or providers), set "greenfield": true, leave "items" empty, and write a one-line summary saying so. Don't invent items to fill space.`,
456
+ );
457
+ lines.push('');
458
+ lines.push(
459
+ `Reminder (stated twice on purpose): the manifest is COMMITTED and contains NO secret values — only names, files, and notes. Double-check every suggestedSecretRefs entry and every note before you finish: if any of them is an actual secret value rather than a NAME, remove it.`,
460
+ );
461
+ lines.push('');
462
+ lines.push(
463
+ `Finish with a short summary of what you found (grouped by kind) PLUS an "open questions" list: anything you couldn't confidently classify, or that looks like infrastructure but you weren't sure about — so the user can confirm or correct it.`,
464
+ );
465
+ break;
466
+ }
467
+ case 'cover-operation': {
468
+ const flowFile = join(apisDir, ...relPath.split('/')) + '.json';
469
+ const scenariosFile = join(apisDir, ...relPath.split('/')) + '.scenarios.json';
470
+ lines.push(`Relevant skill: emberflow-basics (the scenario shape).`);
471
+ lines.push('');
472
+ lines.push(
473
+ `Task: build a branch-covering scenario suite for the operation "${intent.flowId}". The flow definition lives at ${flowFile} and its scenarios live at ${scenariosFile} (create it if it doesn't exist yet).`,
474
+ );
475
+ lines.push('');
476
+ lines.push(`User instruction (verbatim): ${intent.instruction}`);
477
+ lines.push('');
478
+ lines.push(
479
+ `Read ${flowFile} and identify every Route/Conditional branch and every terminal Response/Result node the operation can reach. For each branch, write or extend a scenario in ${scenariosFile} that drives execution down that branch, with an "expect" that at minimum asserts status and executedNodes (the branch's terminal node id) — add a body subset to expect wherever the response is deterministic.`,
480
+ );
481
+ lines.push('');
482
+ lines.push(
483
+ `Use a real id from the project's data when you can obtain one (e.g. by reading a data file or calling a lookup operation); otherwise a clearly-fake but well-formed placeholder (e.g. "test-id-1").`,
484
+ );
485
+ lines.push('');
486
+ lines.push(mocksLine(scenariosFile));
487
+ lines.push('');
488
+ lines.push(
489
+ `NEVER weaken or delete an existing expect to make the suite pass. If an existing expectation looks wrong, leave it as-is, make your new scenarios pass on their own merits, and report the discrepancy in your final message instead of touching it.`,
490
+ );
491
+ lines.push('');
492
+ lines.push(
493
+ `Verify with: node ${EMBERFLOW_BIN} test ${intent.flowId} — iterate (adjust the new scenarios, re-run) until every new scenario passes. Finish by reporting the suite summary (passed/failed/skipped) from the final test run.`,
494
+ );
495
+ lines.push('');
496
+ lines.push(doctorLine(intent.flowId));
497
+ break;
498
+ }
499
+ case 'ask': {
500
+ lines.push('Relevant skill: emberflow-basics (how the project is structured) — read it only if answering needs it.');
501
+ lines.push('');
502
+ if (intent.flowId) {
503
+ const flowFile = join(apisDir, ...relPath.split('/')) + '.json';
504
+ lines.push(`This is a question about the operation "${intent.flowId}". Its definition lives at ${flowFile}.`);
505
+ } else {
506
+ lines.push('This is a question about this Emberflow project as a whole.');
507
+ }
508
+ lines.push('');
509
+ lines.push(`User question (verbatim): ${intent.instruction}`);
510
+ lines.push('');
511
+ lines.push(
512
+ 'This is a QUESTION, not an edit — ANSWER it and change NOTHING. Do NOT write, create, save, edit, delete, or rename any file or operation; do NOT run doctor --fix; do NOT switch serving mode. Running READ-ONLY CLI commands (list-workflows, get-workflow, get-node, node-schema, samples, list-environments, and mock/test runs) and reading files is exactly how you investigate — that is fine and encouraged. Follow the INSPECTION ORDER above: reach through the CLI top-down before opening source files.',
513
+ );
514
+ lines.push('');
515
+ lines.push(
516
+ 'Answer format: LEAD with the direct answer in 1-3 sentences, then supporting detail with file:line references where they help. Keep it tight — plain prose and short lists (the panel renders simple markdown: bold, inline code, lists). Do NOT restate the question or pad with summaries the user did not ask for.',
517
+ );
518
+ break;
519
+ }
520
+ default: {
521
+ const action = (intent as { action: string }).action;
522
+ throw new Error('Unknown intent action: ' + action);
523
+ }
524
+ }
525
+
526
+ return lines.join('\n');
527
+ }