@aion0/forge 0.5.26 → 0.5.27

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 (253) hide show
  1. package/.forge/worktrees/pipeline-4dd8dc2d/CLAUDE.md +86 -0
  2. package/.forge/worktrees/pipeline-4dd8dc2d/README.md +136 -0
  3. package/.forge/worktrees/pipeline-4dd8dc2d/RELEASE_NOTES.md +36 -0
  4. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/agents/route.ts +17 -0
  5. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/[...nextauth]/route.ts +3 -0
  6. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/verify/route.ts +46 -0
  7. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/route.ts +31 -0
  8. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/stream/route.ts +63 -0
  9. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/route.ts +28 -0
  10. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  11. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  12. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/route.ts +37 -0
  13. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/sync/route.ts +17 -0
  14. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-templates/route.ts +145 -0
  15. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/code/route.ts +299 -0
  16. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/[id]/route.ts +62 -0
  17. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/route.ts +40 -0
  18. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/detect-cli/route.ts +46 -0
  19. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/route.ts +176 -0
  20. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/sessions/route.ts +54 -0
  21. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/favorites/route.ts +26 -0
  22. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/route.ts +6 -0
  23. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/run/route.ts +19 -0
  24. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/git/route.ts +149 -0
  25. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/help/route.ts +84 -0
  26. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/issue-scanner/route.ts +116 -0
  27. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/logs/route.ts +100 -0
  28. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/mobile-chat/route.ts +115 -0
  29. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/monitor/route.ts +74 -0
  30. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notifications/route.ts +42 -0
  31. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notify/test/route.ts +33 -0
  32. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/online/route.ts +40 -0
  33. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/[id]/route.ts +41 -0
  34. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/route.ts +90 -0
  35. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/plugins/route.ts +75 -0
  36. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/[...path]/route.ts +64 -0
  37. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/route.ts +156 -0
  38. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-pipelines/route.ts +91 -0
  39. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-sessions/route.ts +61 -0
  40. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/projects/route.ts +26 -0
  41. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/chat/route.ts +64 -0
  42. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/messages/route.ts +9 -0
  43. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/route.ts +17 -0
  44. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/route.ts +20 -0
  45. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/settings/route.ts +64 -0
  46. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/local/route.ts +228 -0
  47. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/route.ts +182 -0
  48. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/smith-templates/route.ts +81 -0
  49. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/status/route.ts +12 -0
  50. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tabs/route.ts +25 -0
  51. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/route.ts +51 -0
  52. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/stream/route.ts +77 -0
  53. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/link/route.ts +37 -0
  54. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/route.ts +44 -0
  55. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/session/route.ts +14 -0
  56. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/telegram/route.ts +23 -0
  57. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/templates/route.ts +6 -0
  58. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-bell/route.ts +39 -0
  59. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-cwd/route.ts +19 -0
  60. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-state/route.ts +15 -0
  61. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tunnel/route.ts +26 -0
  62. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/upgrade/route.ts +43 -0
  63. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/usage/route.ts +20 -0
  64. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/version/route.ts +78 -0
  65. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/watchers/route.ts +33 -0
  66. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/agents/route.ts +35 -0
  67. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/memory/route.ts +23 -0
  68. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/smith/route.ts +22 -0
  69. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/stream/route.ts +31 -0
  70. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/route.ts +79 -0
  71. package/.forge/worktrees/pipeline-4dd8dc2d/app/global-error.tsx +21 -0
  72. package/.forge/worktrees/pipeline-4dd8dc2d/app/globals.css +52 -0
  73. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.ico +0 -0
  74. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.png +0 -0
  75. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.svg +106 -0
  76. package/.forge/worktrees/pipeline-4dd8dc2d/app/layout.tsx +17 -0
  77. package/.forge/worktrees/pipeline-4dd8dc2d/app/login/LoginForm.tsx +96 -0
  78. package/.forge/worktrees/pipeline-4dd8dc2d/app/login/page.tsx +10 -0
  79. package/.forge/worktrees/pipeline-4dd8dc2d/app/mobile/page.tsx +10 -0
  80. package/.forge/worktrees/pipeline-4dd8dc2d/app/page.tsx +22 -0
  81. package/.forge/worktrees/pipeline-4dd8dc2d/bin/forge-server.mjs +484 -0
  82. package/.forge/worktrees/pipeline-4dd8dc2d/check-forge-status.sh +71 -0
  83. package/.forge/worktrees/pipeline-4dd8dc2d/cli/mw.ts +579 -0
  84. package/.forge/worktrees/pipeline-4dd8dc2d/components/BrowserPanel.tsx +175 -0
  85. package/.forge/worktrees/pipeline-4dd8dc2d/components/ChatPanel.tsx +191 -0
  86. package/.forge/worktrees/pipeline-4dd8dc2d/components/ClaudeTerminal.tsx +267 -0
  87. package/.forge/worktrees/pipeline-4dd8dc2d/components/CodeViewer.tsx +787 -0
  88. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationEditor.tsx +411 -0
  89. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationGraphView.tsx +347 -0
  90. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationTerminalView.tsx +303 -0
  91. package/.forge/worktrees/pipeline-4dd8dc2d/components/Dashboard.tsx +807 -0
  92. package/.forge/worktrees/pipeline-4dd8dc2d/components/DashboardWrapper.tsx +9 -0
  93. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryFlowEditor.tsx +491 -0
  94. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryList.tsx +230 -0
  95. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryWorkspace.tsx +589 -0
  96. package/.forge/worktrees/pipeline-4dd8dc2d/components/DocTerminal.tsx +187 -0
  97. package/.forge/worktrees/pipeline-4dd8dc2d/components/DocsViewer.tsx +574 -0
  98. package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpDialog.tsx +169 -0
  99. package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpTerminal.tsx +141 -0
  100. package/.forge/worktrees/pipeline-4dd8dc2d/components/InlinePipelineView.tsx +111 -0
  101. package/.forge/worktrees/pipeline-4dd8dc2d/components/LogViewer.tsx +194 -0
  102. package/.forge/worktrees/pipeline-4dd8dc2d/components/MarkdownContent.tsx +73 -0
  103. package/.forge/worktrees/pipeline-4dd8dc2d/components/MobileView.tsx +385 -0
  104. package/.forge/worktrees/pipeline-4dd8dc2d/components/MonitorPanel.tsx +122 -0
  105. package/.forge/worktrees/pipeline-4dd8dc2d/components/NewSessionModal.tsx +93 -0
  106. package/.forge/worktrees/pipeline-4dd8dc2d/components/NewTaskModal.tsx +492 -0
  107. package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineEditor.tsx +570 -0
  108. package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineView.tsx +1018 -0
  109. package/.forge/worktrees/pipeline-4dd8dc2d/components/PluginsPanel.tsx +472 -0
  110. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectDetail.tsx +1618 -0
  111. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectList.tsx +108 -0
  112. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectManager.tsx +401 -0
  113. package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionList.tsx +74 -0
  114. package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionView.tsx +726 -0
  115. package/.forge/worktrees/pipeline-4dd8dc2d/components/SettingsModal.tsx +1647 -0
  116. package/.forge/worktrees/pipeline-4dd8dc2d/components/SkillsPanel.tsx +969 -0
  117. package/.forge/worktrees/pipeline-4dd8dc2d/components/StatusBar.tsx +99 -0
  118. package/.forge/worktrees/pipeline-4dd8dc2d/components/TabBar.tsx +46 -0
  119. package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskBoard.tsx +113 -0
  120. package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskDetail.tsx +372 -0
  121. package/.forge/worktrees/pipeline-4dd8dc2d/components/TerminalLauncher.tsx +398 -0
  122. package/.forge/worktrees/pipeline-4dd8dc2d/components/TunnelToggle.tsx +206 -0
  123. package/.forge/worktrees/pipeline-4dd8dc2d/components/UsagePanel.tsx +207 -0
  124. package/.forge/worktrees/pipeline-4dd8dc2d/components/WebTerminal.tsx +1743 -0
  125. package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceTree.tsx +221 -0
  126. package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceView.tsx +4048 -0
  127. package/.forge/worktrees/pipeline-4dd8dc2d/dev-test.sh +5 -0
  128. package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Memory_Layer_Design.docx +0 -0
  129. package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Strategy_Research_2026.docx +0 -0
  130. package/.forge/worktrees/pipeline-4dd8dc2d/docs/LOCAL-DEPLOY.md +144 -0
  131. package/.forge/worktrees/pipeline-4dd8dc2d/docs/roadmap-multi-agent-workflow.md +330 -0
  132. package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.png +0 -0
  133. package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.svg +106 -0
  134. package/.forge/worktrees/pipeline-4dd8dc2d/hooks/useSidebarResize.ts +52 -0
  135. package/.forge/worktrees/pipeline-4dd8dc2d/install.sh +29 -0
  136. package/.forge/worktrees/pipeline-4dd8dc2d/instrumentation.ts +35 -0
  137. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/claude-adapter.ts +104 -0
  138. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/generic-adapter.ts +64 -0
  139. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/index.ts +245 -0
  140. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/types.ts +70 -0
  141. package/.forge/worktrees/pipeline-4dd8dc2d/lib/artifacts.ts +106 -0
  142. package/.forge/worktrees/pipeline-4dd8dc2d/lib/auth.ts +62 -0
  143. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/docker.yaml +70 -0
  144. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/http.yaml +66 -0
  145. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/jenkins.yaml +92 -0
  146. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/llm-vision.yaml +85 -0
  147. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/playwright.yaml +111 -0
  148. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/shell-command.yaml +60 -0
  149. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/slack.yaml +48 -0
  150. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/webhook.yaml +56 -0
  151. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-process.ts +361 -0
  152. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-sessions.ts +266 -0
  153. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-templates.ts +227 -0
  154. package/.forge/worktrees/pipeline-4dd8dc2d/lib/cloudflared.ts +424 -0
  155. package/.forge/worktrees/pipeline-4dd8dc2d/lib/crypto.ts +67 -0
  156. package/.forge/worktrees/pipeline-4dd8dc2d/lib/delivery.ts +787 -0
  157. package/.forge/worktrees/pipeline-4dd8dc2d/lib/dirs.ts +99 -0
  158. package/.forge/worktrees/pipeline-4dd8dc2d/lib/flows.ts +86 -0
  159. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-mcp-server.ts +732 -0
  160. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-inbox.md +38 -0
  161. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-send.md +47 -0
  162. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-status.md +32 -0
  163. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-workspace-sync.md +37 -0
  164. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/00-overview.md +40 -0
  165. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/01-settings.md +194 -0
  166. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/02-telegram.md +41 -0
  167. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/03-tunnel.md +31 -0
  168. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/04-tasks.md +52 -0
  169. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/05-pipelines.md +460 -0
  170. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/06-skills.md +43 -0
  171. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/07-projects.md +73 -0
  172. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/08-rules.md +53 -0
  173. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/09-issue-autofix.md +55 -0
  174. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/10-troubleshooting.md +89 -0
  175. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/11-workspace.md +810 -0
  176. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/CLAUDE.md +62 -0
  177. package/.forge/worktrees/pipeline-4dd8dc2d/lib/init.ts +266 -0
  178. package/.forge/worktrees/pipeline-4dd8dc2d/lib/issue-scanner.ts +298 -0
  179. package/.forge/worktrees/pipeline-4dd8dc2d/lib/logger.ts +79 -0
  180. package/.forge/worktrees/pipeline-4dd8dc2d/lib/notifications.ts +75 -0
  181. package/.forge/worktrees/pipeline-4dd8dc2d/lib/notify.ts +108 -0
  182. package/.forge/worktrees/pipeline-4dd8dc2d/lib/password.ts +97 -0
  183. package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline-scheduler.ts +373 -0
  184. package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline.ts +1565 -0
  185. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/executor.ts +347 -0
  186. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/registry.ts +228 -0
  187. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/types.ts +103 -0
  188. package/.forge/worktrees/pipeline-4dd8dc2d/lib/project-sessions.ts +53 -0
  189. package/.forge/worktrees/pipeline-4dd8dc2d/lib/projects.ts +86 -0
  190. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-manager.ts +156 -0
  191. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-utils.ts +53 -0
  192. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-watcher.ts +345 -0
  193. package/.forge/worktrees/pipeline-4dd8dc2d/lib/settings.ts +195 -0
  194. package/.forge/worktrees/pipeline-4dd8dc2d/lib/skills.ts +458 -0
  195. package/.forge/worktrees/pipeline-4dd8dc2d/lib/task-manager.ts +951 -0
  196. package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-bot.ts +1477 -0
  197. package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-standalone.ts +83 -0
  198. package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-server.ts +70 -0
  199. package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-standalone.ts +438 -0
  200. package/.forge/worktrees/pipeline-4dd8dc2d/lib/usage-scanner.ts +249 -0
  201. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/state-machine.test.ts +388 -0
  202. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/workspace.test.ts +311 -0
  203. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-bus.ts +416 -0
  204. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-worker.ts +655 -0
  205. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/api-backend.ts +262 -0
  206. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/cli-backend.ts +491 -0
  207. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/index.ts +84 -0
  208. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/manager.ts +136 -0
  209. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/orchestrator.ts +3415 -0
  210. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/persistence.ts +309 -0
  211. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/presets.ts +649 -0
  212. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/requests.ts +287 -0
  213. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/session-monitor.ts +240 -0
  214. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/skill-installer.ts +275 -0
  215. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/smith-memory.ts +498 -0
  216. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/types.ts +241 -0
  217. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/watch-manager.ts +560 -0
  218. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace-standalone.ts +978 -0
  219. package/.forge/worktrees/pipeline-4dd8dc2d/middleware.ts +51 -0
  220. package/.forge/worktrees/pipeline-4dd8dc2d/next.config.ts +26 -0
  221. package/.forge/worktrees/pipeline-4dd8dc2d/package.json +74 -0
  222. package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-lock.yaml +3719 -0
  223. package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-workspace.yaml +1 -0
  224. package/.forge/worktrees/pipeline-4dd8dc2d/postcss.config.mjs +7 -0
  225. package/.forge/worktrees/pipeline-4dd8dc2d/publish.sh +133 -0
  226. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/README.md +66 -0
  227. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/results/.gitignore +2 -0
  228. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/run.ts +635 -0
  229. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/task.md +26 -0
  230. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  231. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  232. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/task.md +48 -0
  233. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  234. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  235. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  236. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  237. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/verify-usage.ts +178 -0
  238. package/.forge/worktrees/pipeline-4dd8dc2d/src/config/index.ts +129 -0
  239. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/db/database.ts +259 -0
  240. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/memory/strategy.ts +32 -0
  241. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/chat.ts +65 -0
  242. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/registry.ts +60 -0
  243. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/session/manager.ts +190 -0
  244. package/.forge/worktrees/pipeline-4dd8dc2d/src/types/index.ts +129 -0
  245. package/.forge/worktrees/pipeline-4dd8dc2d/start.sh +32 -0
  246. package/.forge/worktrees/pipeline-4dd8dc2d/templates/smith-lead.json +45 -0
  247. package/.forge/worktrees/pipeline-4dd8dc2d/tsconfig.json +42 -0
  248. package/RELEASE_NOTES.md +11 -28
  249. package/app/api/terminal-bell/route.ts +6 -2
  250. package/components/WebTerminal.tsx +36 -2
  251. package/lib/terminal-standalone.ts +19 -2
  252. package/next-env.d.ts +1 -1
  253. package/package.json +1 -1
@@ -0,0 +1,570 @@
1
+ 'use client';
2
+
3
+ import { useState, useCallback, useRef, useEffect } from 'react';
4
+ import {
5
+ ReactFlow,
6
+ Background,
7
+ Controls,
8
+ addEdge,
9
+ useNodesState,
10
+ useEdgesState,
11
+ Handle,
12
+ Position,
13
+ type Node,
14
+ type Edge,
15
+ type Connection,
16
+ type NodeProps,
17
+ MarkerType,
18
+ } from '@xyflow/react';
19
+ import '@xyflow/react/dist/style.css';
20
+
21
+ // ─── Custom Node ──────────────────────────────────────────
22
+
23
+ interface NodeData {
24
+ label: string;
25
+ project: string;
26
+ prompt: string;
27
+ outputs: { name: string; extract: string }[];
28
+ onEdit: (id: string) => void;
29
+ onDelete: (id: string) => void;
30
+ [key: string]: unknown;
31
+ }
32
+
33
+ function PipelineNode({ id, data }: NodeProps<Node<NodeData>>) {
34
+ return (
35
+ <div className="bg-[#1e1e3a] border border-[#3a3a5a] rounded-lg shadow-lg min-w-[180px]">
36
+ <Handle type="target" position={Position.Top} className="!bg-[var(--accent)] !w-3 !h-3" />
37
+
38
+ <div className="px-3 py-2 border-b border-[#3a3a5a] flex items-center gap-2">
39
+ <span className={`text-[8px] px-1 py-0.5 rounded font-medium ${
40
+ (data as any).mode === 'shell' ? 'bg-yellow-500/20 text-yellow-400' :
41
+ (data as any).mode === 'plugin' ? 'bg-green-500/20 text-green-400' :
42
+ 'bg-purple-500/20 text-purple-400'
43
+ }`}>{(data as any).mode === 'plugin' ? `🔌 ${(data as any).plugin || 'plugin'}` : (data as any).mode === 'shell' ? 'shell' : ((data as any).agent || 'default')}</span>
44
+ <span className="text-xs font-semibold text-white">{data.label}</span>
45
+ <div className="ml-auto flex gap-1">
46
+ <button onClick={() => data.onEdit(id)} className="text-[9px] text-[var(--accent)] hover:text-white">edit</button>
47
+ <button onClick={() => data.onDelete(id)} className="text-[9px] text-red-400 hover:text-red-300">x</button>
48
+ </div>
49
+ </div>
50
+
51
+ <div className="px-3 py-1.5 space-y-0.5">
52
+ {data.project && <div className="text-[9px] text-[var(--accent)]">{data.project}</div>}
53
+ <div className="text-[9px] text-gray-400 truncate max-w-[200px]">{data.prompt.slice(0, 60) || 'No prompt'}{data.prompt.length > 60 ? '...' : ''}</div>
54
+ {data.outputs.length > 0 && (
55
+ <div className="text-[8px] text-green-400">
56
+ outputs: {data.outputs.map(o => o.name).join(', ')}
57
+ </div>
58
+ )}
59
+ </div>
60
+
61
+ <Handle type="source" position={Position.Bottom} className="!bg-[var(--accent)] !w-3 !h-3" />
62
+ </div>
63
+ );
64
+ }
65
+
66
+ const nodeTypes = { pipeline: PipelineNode };
67
+
68
+ // ─── Node Edit Modal ──────────────────────────────────────
69
+
70
+ function NodeEditModal({ node, projects, agents, onSave, onClose }: {
71
+ node: { id: string; project: string; prompt: string; agent?: string; mode?: string; plugin?: string; pluginAction?: string; pluginParams?: Record<string, any>; pluginWait?: boolean; outputs: { name: string; extract: string }[] };
72
+ projects: { name: string; root: string }[];
73
+ agents: { id: string; name: string }[];
74
+ onSave: (data: any) => void;
75
+ onClose: () => void;
76
+ }) {
77
+ const [id, setId] = useState(node.id);
78
+ const [project, setProject] = useState(node.project);
79
+ const [prompt, setPrompt] = useState(node.prompt);
80
+ const [agent, setAgent] = useState(node.agent || '');
81
+ const [mode, setMode] = useState(node.mode || 'claude');
82
+ // Plugin fields
83
+ const [pluginId, setPluginId] = useState(node.plugin || '');
84
+ const [pluginAction, setPluginAction] = useState(node.pluginAction || '');
85
+ const [pluginParams, setPluginParams] = useState(JSON.stringify(node.pluginParams || {}, null, 2));
86
+ const [pluginWait, setPluginWait] = useState(node.pluginWait || false);
87
+ const [availablePlugins, setAvailablePlugins] = useState<{ id: string; name: string; icon: string; installed: boolean; actions?: Record<string, any> }[]>([]);
88
+ const [selectedPluginDef, setSelectedPluginDef] = useState<any>(null);
89
+
90
+ // Fetch installed plugins
91
+ useEffect(() => {
92
+ fetch('/api/plugins?installed=true')
93
+ .then(r => r.json())
94
+ .then(d => {
95
+ const plugins = (d.plugins || []).map((p: any) => ({
96
+ id: p.id, name: p.definition?.name || p.id, icon: p.definition?.icon || '🔌',
97
+ installed: true, actions: p.definition?.actions || {},
98
+ params: p.definition?.params || {},
99
+ }));
100
+ setAvailablePlugins(plugins);
101
+ if (pluginId) {
102
+ const sel = plugins.find((p: any) => p.id === pluginId);
103
+ if (sel) setSelectedPluginDef(sel);
104
+ }
105
+ })
106
+ .catch(() => {});
107
+ }, []);
108
+ const [outputs, setOutputs] = useState(node.outputs);
109
+
110
+ return (
111
+ <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onClick={onClose}>
112
+ <div className="bg-[#1e1e3a] border border-[#3a3a5a] rounded-lg shadow-xl w-[450px] max-h-[80vh] overflow-y-auto" onClick={e => e.stopPropagation()}>
113
+ <div className="px-4 py-3 border-b border-[#3a3a5a]">
114
+ <h3 className="text-sm font-semibold text-white">Edit Node</h3>
115
+ </div>
116
+ <div className="p-4 space-y-3">
117
+ <div>
118
+ <label className="text-[10px] text-gray-400 block mb-1">Node ID</label>
119
+ <input
120
+ value={id}
121
+ onChange={e => setId(e.target.value.replace(/\s+/g, '_'))}
122
+ className="w-full text-xs bg-[#12122a] border border-[#3a3a5a] rounded px-2 py-1.5 text-white focus:outline-none focus:border-[var(--accent)] font-mono"
123
+ />
124
+ </div>
125
+ <div>
126
+ <label className="text-[10px] text-gray-400 block mb-1">Project</label>
127
+ <select
128
+ value={project}
129
+ onChange={e => setProject(e.target.value)}
130
+ className="w-full text-xs bg-[#12122a] border border-[#3a3a5a] rounded px-2 py-1.5 text-white"
131
+ >
132
+ <option value="">Select project...</option>
133
+ {[...new Set(projects.map(p => p.root))].map(root => (
134
+ <optgroup key={root} label={root.split('/').pop() || root}>
135
+ {projects.filter(p => p.root === root).map((p, i) => (
136
+ <option key={`${p.name}-${i}`} value={p.name}>{p.name}</option>
137
+ ))}
138
+ </optgroup>
139
+ ))}
140
+ </select>
141
+ </div>
142
+ <div className="flex gap-2">
143
+ <div className="flex-1">
144
+ <label className="text-[10px] text-gray-400 block mb-1">Mode</label>
145
+ <select
146
+ value={mode}
147
+ onChange={e => { setMode(e.target.value); if (e.target.value !== 'plugin') { setPluginId(''); setSelectedPluginDef(null); } }}
148
+ className="w-full text-xs bg-[#12122a] border border-[#3a3a5a] rounded px-2 py-1.5 text-white"
149
+ >
150
+ <option value="claude">Agent</option>
151
+ <option value="shell">Shell</option>
152
+ <option value="plugin">Plugin</option>
153
+ </select>
154
+ </div>
155
+ {mode !== 'shell' && (
156
+ <div className="flex-1">
157
+ <label className="text-[10px] text-gray-400 block mb-1">Agent</label>
158
+ <select
159
+ value={agent}
160
+ onChange={e => setAgent(e.target.value)}
161
+ className="w-full text-xs bg-[#12122a] border border-[#3a3a5a] rounded px-2 py-1.5 text-white"
162
+ >
163
+ <option value="">Default</option>
164
+ {agents.map(a => (
165
+ <option key={a.id} value={a.id}>{a.name}</option>
166
+ ))}
167
+ </select>
168
+ </div>
169
+ )}
170
+ </div>
171
+ {/* Plugin config */}
172
+ {mode === 'plugin' && (
173
+ <div className="space-y-2 p-2 bg-[#12122a] rounded border border-[#3a3a5a]">
174
+ <div>
175
+ <label className="text-[10px] text-gray-400 block mb-1">Plugin</label>
176
+ <select
177
+ value={pluginId}
178
+ onChange={e => {
179
+ setPluginId(e.target.value);
180
+ const sel = availablePlugins.find(p => p.id === e.target.value);
181
+ setSelectedPluginDef(sel || null);
182
+ setPluginAction('');
183
+ }}
184
+ className="w-full text-xs bg-[#1e1e3a] border border-[#3a3a5a] rounded px-2 py-1.5 text-white"
185
+ >
186
+ <option value="">Select plugin...</option>
187
+ {availablePlugins.map(p => (
188
+ <option key={p.id} value={p.id}>{p.icon} {p.name}</option>
189
+ ))}
190
+ </select>
191
+ {availablePlugins.length === 0 && (
192
+ <div className="text-[9px] text-yellow-400 mt-1">No plugins installed. Install from Settings → Plugins.</div>
193
+ )}
194
+ </div>
195
+ {selectedPluginDef && (
196
+ <>
197
+ <div>
198
+ <label className="text-[10px] text-gray-400 block mb-1">Action</label>
199
+ <select
200
+ value={pluginAction}
201
+ onChange={e => setPluginAction(e.target.value)}
202
+ className="w-full text-xs bg-[#1e1e3a] border border-[#3a3a5a] rounded px-2 py-1.5 text-white"
203
+ >
204
+ <option value="">Default</option>
205
+ {Object.keys(selectedPluginDef.actions || {}).map((a: string) => (
206
+ <option key={a} value={a}>{a}</option>
207
+ ))}
208
+ </select>
209
+ </div>
210
+ <div>
211
+ <label className="text-[10px] text-gray-400 block mb-1">Parameters (JSON)</label>
212
+ <textarea
213
+ value={pluginParams}
214
+ onChange={e => setPluginParams(e.target.value)}
215
+ rows={4}
216
+ className="w-full text-xs bg-[#1e1e3a] border border-[#3a3a5a] rounded px-2 py-1.5 text-white font-mono resize-y"
217
+ placeholder='{ "key": "value" }'
218
+ />
219
+ {selectedPluginDef.params && Object.keys(selectedPluginDef.params).length > 0 && (
220
+ <div className="text-[8px] text-gray-500 mt-0.5">
221
+ Available: {Object.entries(selectedPluginDef.params).map(([k, v]: [string, any]) => `${k}${v.required ? '*' : ''}`).join(', ')}
222
+ </div>
223
+ )}
224
+ </div>
225
+ <div className="flex items-center gap-2">
226
+ <input type="checkbox" id="pluginWait" checked={pluginWait} onChange={e => setPluginWait(e.target.checked)} className="accent-[var(--accent)]" />
227
+ <label htmlFor="pluginWait" className="text-[10px] text-gray-400">Wait for completion (poll)</label>
228
+ </div>
229
+ </>
230
+ )}
231
+ </div>
232
+ )}
233
+
234
+ <div>
235
+ <label className="text-[10px] text-gray-400 block mb-1">{mode === 'plugin' ? 'Notes (optional)' : 'Prompt'}</label>
236
+ <textarea
237
+ value={prompt}
238
+ onChange={e => setPrompt(e.target.value)}
239
+ rows={6}
240
+ className="w-full text-xs bg-[#12122a] border border-[#3a3a5a] rounded px-2 py-1.5 text-white focus:outline-none focus:border-[var(--accent)] font-mono resize-y"
241
+ placeholder="Use {{nodes.xxx.outputs.yyy}} to reference upstream outputs"
242
+ />
243
+ </div>
244
+ <div>
245
+ <label className="text-[10px] text-gray-400 block mb-1">Outputs</label>
246
+ {outputs.map((o, i) => (
247
+ <div key={i} className="flex gap-2 mb-1">
248
+ <input
249
+ value={o.name}
250
+ onChange={e => { const n = [...outputs]; n[i] = { ...n[i], name: e.target.value }; setOutputs(n); }}
251
+ placeholder="output name"
252
+ className="flex-1 text-xs bg-[#12122a] border border-[#3a3a5a] rounded px-2 py-1 text-white font-mono"
253
+ />
254
+ <select
255
+ value={o.extract}
256
+ onChange={e => { const n = [...outputs]; n[i] = { ...n[i], extract: e.target.value }; setOutputs(n); }}
257
+ className="text-xs bg-[#12122a] border border-[#3a3a5a] rounded px-2 py-1 text-white"
258
+ >
259
+ <option value="result">result</option>
260
+ <option value="git_diff">git_diff</option>
261
+ </select>
262
+ <button onClick={() => setOutputs(outputs.filter((_, j) => j !== i))} className="text-red-400 text-xs">x</button>
263
+ </div>
264
+ ))}
265
+ <button
266
+ onClick={() => setOutputs([...outputs, { name: '', extract: 'result' }])}
267
+ className="text-[9px] text-[var(--accent)] hover:text-white"
268
+ >
269
+ + Add output
270
+ </button>
271
+ </div>
272
+ </div>
273
+ <div className="px-4 py-3 border-t border-[#3a3a5a] flex gap-2 justify-end">
274
+ <button onClick={onClose} className="text-xs px-3 py-1 text-gray-400 hover:text-white">Cancel</button>
275
+ <button
276
+ onClick={() => {
277
+ let parsedParams: Record<string, any> = {};
278
+ try { parsedParams = JSON.parse(pluginParams || '{}'); } catch {}
279
+ onSave({
280
+ id, project, prompt, agent: agent || undefined, mode,
281
+ plugin: mode === 'plugin' ? pluginId : undefined,
282
+ pluginAction: mode === 'plugin' ? pluginAction || undefined : undefined,
283
+ pluginParams: mode === 'plugin' ? parsedParams : undefined,
284
+ pluginWait: mode === 'plugin' ? pluginWait : undefined,
285
+ outputs: outputs.filter(o => o.name),
286
+ });
287
+ }}
288
+ className="text-xs px-3 py-1 bg-[var(--accent)] text-white rounded hover:opacity-90"
289
+ >
290
+ Save
291
+ </button>
292
+ </div>
293
+ </div>
294
+ </div>
295
+ );
296
+ }
297
+
298
+ // ─── Main Editor ──────────────────────────────────────────
299
+
300
+ export default function PipelineEditor({ onSave, onClose, initialYaml }: {
301
+ onSave: (yaml: string) => void;
302
+ onClose: () => void;
303
+ initialYaml?: string;
304
+ }) {
305
+ const [nodes, setNodes, onNodesChange] = useNodesState<Node<NodeData>>([]);
306
+ const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
307
+ const [editingNode, setEditingNode] = useState<{ id: string; project: string; prompt: string; agent?: string; mode?: string; plugin?: string; pluginAction?: string; pluginParams?: Record<string, any>; pluginWait?: boolean; outputs: { name: string; extract: string }[] } | null>(null);
308
+ const [workflowName, setWorkflowName] = useState('');
309
+ const [availableAgents, setAvailableAgents] = useState<{ id: string; name: string }[]>([]);
310
+ const [workflowDesc, setWorkflowDesc] = useState('');
311
+ const [varsProject, setVarsProject] = useState('');
312
+ const [projects, setProjects] = useState<{ name: string; root: string }[]>([]);
313
+ const nextNodeId = useRef(1);
314
+
315
+ useEffect(() => {
316
+ fetch('/api/projects').then(r => r.json())
317
+ .then((p: { name: string; root: string }[]) => { if (Array.isArray(p)) setProjects(p); })
318
+ .catch(() => {});
319
+ fetch('/api/agents').then(r => r.json())
320
+ .then(data => setAvailableAgents((data.agents || []).filter((a: any) => a.enabled)))
321
+ .catch(() => {});
322
+ }, []);
323
+
324
+ // Load initial YAML if provided
325
+ useEffect(() => {
326
+ if (!initialYaml) return;
327
+ try {
328
+ const parsed = require('yaml').parse(initialYaml);
329
+ if (parsed.name) setWorkflowName(parsed.name);
330
+ if (parsed.description) setWorkflowDesc(parsed.description);
331
+ if (parsed.vars?.project) setVarsProject(parsed.vars.project);
332
+
333
+ const nodeEntries = Object.entries(parsed.nodes || {});
334
+ const newNodes: Node<NodeData>[] = [];
335
+ const newEdges: Edge[] = [];
336
+
337
+ nodeEntries.forEach(([id, def]: [string, any], idx) => {
338
+ newNodes.push({
339
+ id,
340
+ type: 'pipeline',
341
+ position: { x: 250, y: idx * 150 + 50 },
342
+ data: {
343
+ label: id,
344
+ project: def.project || '',
345
+ prompt: def.prompt || '',
346
+ agent: def.agent,
347
+ mode: def.mode || (def.plugin ? 'plugin' : undefined),
348
+ plugin: def.plugin,
349
+ pluginAction: def.plugin_action,
350
+ pluginParams: def.params,
351
+ pluginWait: def.wait,
352
+ outputs: (def.outputs || []).map((o: any) => ({ name: o.name, extract: o.extract || 'result' })),
353
+ onEdit: (nid: string) => handleEditNode(nid),
354
+ onDelete: (nid: string) => handleDeleteNode(nid),
355
+ },
356
+ });
357
+
358
+ for (const dep of (def.depends_on || [])) {
359
+ newEdges.push({
360
+ id: `${dep}-${id}`,
361
+ source: dep,
362
+ target: id,
363
+ markerEnd: { type: MarkerType.ArrowClosed },
364
+ style: { stroke: '#7c5bf0' },
365
+ });
366
+ }
367
+ });
368
+
369
+ setNodes(newNodes);
370
+ setEdges(newEdges);
371
+ nextNodeId.current = nodeEntries.length + 1;
372
+ } catch {}
373
+ }, [initialYaml]);
374
+
375
+ const onConnect = useCallback((params: Connection) => {
376
+ setEdges(eds => addEdge({
377
+ ...params,
378
+ markerEnd: { type: MarkerType.ArrowClosed },
379
+ style: { stroke: '#7c5bf0' },
380
+ }, eds));
381
+ }, [setEdges]);
382
+
383
+ const handleAddNode = useCallback(() => {
384
+ const id = `step_${nextNodeId.current++}`;
385
+ const newNode: Node<NodeData> = {
386
+ id,
387
+ type: 'pipeline',
388
+ position: { x: 250, y: nodes.length * 150 + 50 },
389
+ data: {
390
+ label: id,
391
+ project: varsProject ? '{{vars.project}}' : '',
392
+ prompt: '',
393
+ outputs: [],
394
+ onEdit: (nid: string) => handleEditNode(nid),
395
+ onDelete: (nid: string) => handleDeleteNode(nid),
396
+ },
397
+ };
398
+ setNodes(nds => [...nds, newNode]);
399
+ }, [nodes.length, varsProject, setNodes]);
400
+
401
+ const handleEditNode = useCallback((id: string) => {
402
+ setNodes(nds => {
403
+ const node = nds.find(n => n.id === id);
404
+ if (node) {
405
+ setEditingNode({
406
+ id: node.id,
407
+ project: node.data.project,
408
+ prompt: node.data.prompt,
409
+ agent: (node.data as any).agent,
410
+ mode: (node.data as any).mode,
411
+ plugin: (node.data as any).plugin,
412
+ pluginAction: (node.data as any).pluginAction,
413
+ pluginParams: (node.data as any).pluginParams,
414
+ pluginWait: (node.data as any).pluginWait,
415
+ outputs: node.data.outputs,
416
+ });
417
+ }
418
+ return nds;
419
+ });
420
+ }, [setNodes]);
421
+
422
+ const handleDeleteNode = useCallback((id: string) => {
423
+ setNodes(nds => nds.filter(n => n.id !== id));
424
+ setEdges(eds => eds.filter(e => e.source !== id && e.target !== id));
425
+ }, [setNodes, setEdges]);
426
+
427
+ const handleSaveNode = useCallback((data: { id: string; project: string; prompt: string; agent?: string; mode?: string; plugin?: string; pluginAction?: string; pluginParams?: Record<string, any>; pluginWait?: boolean; outputs: { name: string; extract: string }[] }) => {
428
+ setNodes(nds => nds.map(n => {
429
+ if (n.id === editingNode?.id) {
430
+ return {
431
+ ...n,
432
+ id: data.id,
433
+ data: {
434
+ ...n.data,
435
+ label: data.id,
436
+ project: data.project,
437
+ prompt: data.prompt,
438
+ agent: data.agent,
439
+ mode: data.mode,
440
+ plugin: data.plugin,
441
+ pluginAction: data.pluginAction,
442
+ pluginParams: data.pluginParams,
443
+ pluginWait: data.pluginWait,
444
+ outputs: data.outputs,
445
+ },
446
+ };
447
+ }
448
+ return n;
449
+ }));
450
+ // Update edges if id changed
451
+ if (editingNode && data.id !== editingNode.id) {
452
+ setEdges(eds => eds.map(e => ({
453
+ ...e,
454
+ id: e.id.replace(editingNode.id, data.id),
455
+ source: e.source === editingNode.id ? data.id : e.source,
456
+ target: e.target === editingNode.id ? data.id : e.target,
457
+ })));
458
+ }
459
+ setEditingNode(null);
460
+ }, [editingNode, setNodes, setEdges]);
461
+
462
+ // Generate YAML from current state
463
+ const generateYaml = useCallback(() => {
464
+ const workflow: any = {
465
+ name: workflowName,
466
+ description: workflowDesc || undefined,
467
+ vars: varsProject ? { project: varsProject } : undefined,
468
+ nodes: {} as any,
469
+ };
470
+
471
+ for (const node of nodes) {
472
+ const deps = edges.filter(e => e.target === node.id).map(e => e.source);
473
+ const nodeDef: any = {
474
+ project: node.data.project,
475
+ prompt: node.data.prompt,
476
+ };
477
+ if ((node.data as any).mode === 'shell') nodeDef.mode = 'shell';
478
+ if ((node.data as any).mode === 'plugin') {
479
+ nodeDef.mode = 'plugin';
480
+ nodeDef.plugin = (node.data as any).plugin;
481
+ if ((node.data as any).pluginAction) nodeDef.plugin_action = (node.data as any).pluginAction;
482
+ if ((node.data as any).pluginParams && Object.keys((node.data as any).pluginParams).length > 0) nodeDef.params = (node.data as any).pluginParams;
483
+ if ((node.data as any).pluginWait) nodeDef.wait = true;
484
+ }
485
+ if ((node.data as any).agent) nodeDef.agent = (node.data as any).agent;
486
+ if (deps.length > 0) nodeDef.depends_on = deps;
487
+ if (node.data.outputs.length > 0) nodeDef.outputs = node.data.outputs;
488
+ workflow.nodes[node.id] = nodeDef;
489
+ }
490
+
491
+ const YAML = require('yaml');
492
+ return YAML.stringify(workflow);
493
+ }, [nodes, edges, workflowName, workflowDesc, varsProject]);
494
+
495
+ return (
496
+ <div className="fixed inset-0 z-50 flex flex-col bg-[#0a0a1a]">
497
+ {/* Top bar */}
498
+ <div className="h-10 border-b border-[#3a3a5a] flex items-center px-4 gap-3 shrink-0">
499
+ <span className="text-xs font-semibold text-white">Pipeline Editor</span>
500
+ <input
501
+ value={workflowName}
502
+ onChange={e => setWorkflowName(e.target.value)}
503
+ className="text-xs bg-[#12122a] border border-[#3a3a5a] rounded px-2 py-0.5 text-white font-mono w-40"
504
+ placeholder="Workflow name"
505
+ />
506
+ <input
507
+ value={workflowDesc}
508
+ onChange={e => setWorkflowDesc(e.target.value)}
509
+ className="text-xs bg-[#12122a] border border-[#3a3a5a] rounded px-2 py-0.5 text-gray-400 flex-1"
510
+ placeholder="Description (optional)"
511
+ />
512
+ <input
513
+ value={varsProject}
514
+ onChange={e => setVarsProject(e.target.value)}
515
+ className="text-xs bg-[#12122a] border border-[#3a3a5a] rounded px-2 py-0.5 text-white font-mono w-32"
516
+ placeholder="Default project"
517
+ />
518
+ <button
519
+ onClick={handleAddNode}
520
+ className="text-xs px-3 py-1 bg-[var(--accent)] text-white rounded hover:opacity-90"
521
+ >
522
+ + Node
523
+ </button>
524
+ <button
525
+ onClick={() => onSave(generateYaml())}
526
+ className="text-xs px-3 py-1 bg-green-600 text-white rounded hover:opacity-90"
527
+ >
528
+ Save
529
+ </button>
530
+ <button
531
+ onClick={() => {
532
+ if (confirm('Discard unsaved changes?')) onClose();
533
+ }}
534
+ className="text-xs px-3 py-1 text-gray-400 hover:text-white"
535
+ >
536
+ Cancel
537
+ </button>
538
+ </div>
539
+
540
+ {/* Flow canvas */}
541
+ <div className="flex-1">
542
+ <ReactFlow
543
+ nodes={nodes}
544
+ edges={edges}
545
+ onNodesChange={onNodesChange}
546
+ onEdgesChange={onEdgesChange}
547
+ onConnect={onConnect}
548
+ nodeTypes={nodeTypes}
549
+ fitView
550
+ deleteKeyCode="Delete"
551
+ style={{ background: '#0a0a1a' }}
552
+ >
553
+ <Background color="#1a1a3a" gap={20} />
554
+ <Controls />
555
+ </ReactFlow>
556
+ </div>
557
+
558
+ {/* Edit modal */}
559
+ {editingNode && (
560
+ <NodeEditModal
561
+ node={editingNode}
562
+ projects={projects}
563
+ agents={availableAgents}
564
+ onSave={handleSaveNode}
565
+ onClose={() => setEditingNode(null)}
566
+ />
567
+ )}
568
+ </div>
569
+ );
570
+ }