@aion0/forge 0.5.24 → 0.5.26

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 (2743) hide show
  1. package/.forge/memory/graph.json +1 -0
  2. package/.forge/memory/knowledge.json +18 -0
  3. package/.forge/memory/meta.json +7 -0
  4. package/.forge/worktrees/pipeline-0a33c50d/CLAUDE.md +86 -0
  5. package/.forge/worktrees/pipeline-0a33c50d/README.md +136 -0
  6. package/.forge/worktrees/pipeline-0a33c50d/RELEASE_NOTES.md +11 -0
  7. package/.forge/worktrees/pipeline-0a33c50d/app/api/agents/route.ts +17 -0
  8. package/.forge/worktrees/pipeline-0a33c50d/app/api/auth/[...nextauth]/route.ts +3 -0
  9. package/.forge/worktrees/pipeline-0a33c50d/app/api/auth/verify/route.ts +46 -0
  10. package/.forge/worktrees/pipeline-0a33c50d/app/api/claude/[id]/route.ts +31 -0
  11. package/.forge/worktrees/pipeline-0a33c50d/app/api/claude/[id]/stream/route.ts +63 -0
  12. package/.forge/worktrees/pipeline-0a33c50d/app/api/claude/route.ts +28 -0
  13. package/.forge/worktrees/pipeline-0a33c50d/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  14. package/.forge/worktrees/pipeline-0a33c50d/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  15. package/.forge/worktrees/pipeline-0a33c50d/app/api/claude-sessions/[projectName]/route.ts +37 -0
  16. package/.forge/worktrees/pipeline-0a33c50d/app/api/claude-sessions/sync/route.ts +17 -0
  17. package/.forge/worktrees/pipeline-0a33c50d/app/api/claude-templates/route.ts +145 -0
  18. package/.forge/worktrees/pipeline-0a33c50d/app/api/code/route.ts +299 -0
  19. package/.forge/worktrees/pipeline-0a33c50d/app/api/delivery/[id]/route.ts +62 -0
  20. package/.forge/worktrees/pipeline-0a33c50d/app/api/delivery/route.ts +40 -0
  21. package/.forge/worktrees/pipeline-0a33c50d/app/api/detect-cli/route.ts +46 -0
  22. package/.forge/worktrees/pipeline-0a33c50d/app/api/docs/route.ts +176 -0
  23. package/.forge/worktrees/pipeline-0a33c50d/app/api/docs/sessions/route.ts +54 -0
  24. package/.forge/worktrees/pipeline-0a33c50d/app/api/favorites/route.ts +26 -0
  25. package/.forge/worktrees/pipeline-0a33c50d/app/api/flows/route.ts +6 -0
  26. package/.forge/worktrees/pipeline-0a33c50d/app/api/flows/run/route.ts +19 -0
  27. package/.forge/worktrees/pipeline-0a33c50d/app/api/git/route.ts +149 -0
  28. package/.forge/worktrees/pipeline-0a33c50d/app/api/help/route.ts +84 -0
  29. package/.forge/worktrees/pipeline-0a33c50d/app/api/issue-scanner/route.ts +116 -0
  30. package/.forge/worktrees/pipeline-0a33c50d/app/api/logs/route.ts +100 -0
  31. package/.forge/worktrees/pipeline-0a33c50d/app/api/mobile-chat/route.ts +115 -0
  32. package/.forge/worktrees/pipeline-0a33c50d/app/api/monitor/route.ts +74 -0
  33. package/.forge/worktrees/pipeline-0a33c50d/app/api/notifications/route.ts +42 -0
  34. package/.forge/worktrees/pipeline-0a33c50d/app/api/notify/test/route.ts +33 -0
  35. package/.forge/worktrees/pipeline-0a33c50d/app/api/online/route.ts +40 -0
  36. package/.forge/worktrees/pipeline-0a33c50d/app/api/pipelines/[id]/route.ts +41 -0
  37. package/.forge/worktrees/pipeline-0a33c50d/app/api/pipelines/route.ts +90 -0
  38. package/.forge/worktrees/pipeline-0a33c50d/app/api/plugins/route.ts +75 -0
  39. package/.forge/worktrees/pipeline-0a33c50d/app/api/preview/[...path]/route.ts +64 -0
  40. package/.forge/worktrees/pipeline-0a33c50d/app/api/preview/route.ts +156 -0
  41. package/.forge/worktrees/pipeline-0a33c50d/app/api/project-pipelines/route.ts +91 -0
  42. package/.forge/worktrees/pipeline-0a33c50d/app/api/project-sessions/route.ts +61 -0
  43. package/.forge/worktrees/pipeline-0a33c50d/app/api/projects/route.ts +26 -0
  44. package/.forge/worktrees/pipeline-0a33c50d/app/api/sessions/[id]/chat/route.ts +64 -0
  45. package/.forge/worktrees/pipeline-0a33c50d/app/api/sessions/[id]/messages/route.ts +9 -0
  46. package/.forge/worktrees/pipeline-0a33c50d/app/api/sessions/[id]/route.ts +17 -0
  47. package/.forge/worktrees/pipeline-0a33c50d/app/api/sessions/route.ts +20 -0
  48. package/.forge/worktrees/pipeline-0a33c50d/app/api/settings/route.ts +64 -0
  49. package/.forge/worktrees/pipeline-0a33c50d/app/api/skills/local/route.ts +228 -0
  50. package/.forge/worktrees/pipeline-0a33c50d/app/api/skills/route.ts +182 -0
  51. package/.forge/worktrees/pipeline-0a33c50d/app/api/smith-templates/route.ts +81 -0
  52. package/.forge/worktrees/pipeline-0a33c50d/app/api/status/route.ts +12 -0
  53. package/.forge/worktrees/pipeline-0a33c50d/app/api/tabs/route.ts +25 -0
  54. package/.forge/worktrees/pipeline-0a33c50d/app/api/tasks/[id]/route.ts +51 -0
  55. package/.forge/worktrees/pipeline-0a33c50d/app/api/tasks/[id]/stream/route.ts +77 -0
  56. package/.forge/worktrees/pipeline-0a33c50d/app/api/tasks/link/route.ts +37 -0
  57. package/.forge/worktrees/pipeline-0a33c50d/app/api/tasks/route.ts +44 -0
  58. package/.forge/worktrees/pipeline-0a33c50d/app/api/tasks/session/route.ts +14 -0
  59. package/.forge/worktrees/pipeline-0a33c50d/app/api/telegram/route.ts +23 -0
  60. package/.forge/worktrees/pipeline-0a33c50d/app/api/templates/route.ts +6 -0
  61. package/.forge/worktrees/pipeline-0a33c50d/app/api/terminal-bell/route.ts +35 -0
  62. package/.forge/worktrees/pipeline-0a33c50d/app/api/terminal-cwd/route.ts +19 -0
  63. package/.forge/worktrees/pipeline-0a33c50d/app/api/terminal-state/route.ts +15 -0
  64. package/.forge/worktrees/pipeline-0a33c50d/app/api/tunnel/route.ts +26 -0
  65. package/.forge/worktrees/pipeline-0a33c50d/app/api/upgrade/route.ts +43 -0
  66. package/.forge/worktrees/pipeline-0a33c50d/app/api/usage/route.ts +20 -0
  67. package/.forge/worktrees/pipeline-0a33c50d/app/api/version/route.ts +78 -0
  68. package/.forge/worktrees/pipeline-0a33c50d/app/api/watchers/route.ts +33 -0
  69. package/.forge/worktrees/pipeline-0a33c50d/app/api/workspace/[id]/agents/route.ts +35 -0
  70. package/.forge/worktrees/pipeline-0a33c50d/app/api/workspace/[id]/memory/route.ts +23 -0
  71. package/.forge/worktrees/pipeline-0a33c50d/app/api/workspace/[id]/smith/route.ts +22 -0
  72. package/.forge/worktrees/pipeline-0a33c50d/app/api/workspace/[id]/stream/route.ts +28 -0
  73. package/.forge/worktrees/pipeline-0a33c50d/app/api/workspace/route.ts +100 -0
  74. package/.forge/worktrees/pipeline-0a33c50d/app/global-error.tsx +21 -0
  75. package/.forge/worktrees/pipeline-0a33c50d/app/globals.css +52 -0
  76. package/.forge/worktrees/pipeline-0a33c50d/app/icon.ico +0 -0
  77. package/.forge/worktrees/pipeline-0a33c50d/app/icon.png +0 -0
  78. package/.forge/worktrees/pipeline-0a33c50d/app/icon.svg +106 -0
  79. package/.forge/worktrees/pipeline-0a33c50d/app/layout.tsx +17 -0
  80. package/.forge/worktrees/pipeline-0a33c50d/app/login/LoginForm.tsx +96 -0
  81. package/.forge/worktrees/pipeline-0a33c50d/app/login/page.tsx +10 -0
  82. package/.forge/worktrees/pipeline-0a33c50d/app/mobile/page.tsx +9 -0
  83. package/.forge/worktrees/pipeline-0a33c50d/app/page.tsx +21 -0
  84. package/.forge/worktrees/pipeline-0a33c50d/bin/forge-server.mjs +484 -0
  85. package/.forge/worktrees/pipeline-0a33c50d/check-forge-status.sh +71 -0
  86. package/.forge/worktrees/pipeline-0a33c50d/cli/mw.ts +579 -0
  87. package/.forge/worktrees/pipeline-0a33c50d/components/BrowserPanel.tsx +175 -0
  88. package/.forge/worktrees/pipeline-0a33c50d/components/ChatPanel.tsx +191 -0
  89. package/.forge/worktrees/pipeline-0a33c50d/components/ClaudeTerminal.tsx +267 -0
  90. package/.forge/worktrees/pipeline-0a33c50d/components/CodeViewer.tsx +787 -0
  91. package/.forge/worktrees/pipeline-0a33c50d/components/ConversationEditor.tsx +411 -0
  92. package/.forge/worktrees/pipeline-0a33c50d/components/ConversationGraphView.tsx +347 -0
  93. package/.forge/worktrees/pipeline-0a33c50d/components/ConversationTerminalView.tsx +303 -0
  94. package/.forge/worktrees/pipeline-0a33c50d/components/Dashboard.tsx +807 -0
  95. package/.forge/worktrees/pipeline-0a33c50d/components/DashboardWrapper.tsx +9 -0
  96. package/.forge/worktrees/pipeline-0a33c50d/components/DeliveryFlowEditor.tsx +491 -0
  97. package/.forge/worktrees/pipeline-0a33c50d/components/DeliveryList.tsx +230 -0
  98. package/.forge/worktrees/pipeline-0a33c50d/components/DeliveryWorkspace.tsx +589 -0
  99. package/.forge/worktrees/pipeline-0a33c50d/components/DocTerminal.tsx +187 -0
  100. package/.forge/worktrees/pipeline-0a33c50d/components/DocsViewer.tsx +574 -0
  101. package/.forge/worktrees/pipeline-0a33c50d/components/HelpDialog.tsx +169 -0
  102. package/.forge/worktrees/pipeline-0a33c50d/components/HelpTerminal.tsx +141 -0
  103. package/.forge/worktrees/pipeline-0a33c50d/components/InlinePipelineView.tsx +111 -0
  104. package/.forge/worktrees/pipeline-0a33c50d/components/LogViewer.tsx +194 -0
  105. package/.forge/worktrees/pipeline-0a33c50d/components/MarkdownContent.tsx +73 -0
  106. package/.forge/worktrees/pipeline-0a33c50d/components/MobileView.tsx +385 -0
  107. package/.forge/worktrees/pipeline-0a33c50d/components/MonitorPanel.tsx +122 -0
  108. package/.forge/worktrees/pipeline-0a33c50d/components/NewSessionModal.tsx +93 -0
  109. package/.forge/worktrees/pipeline-0a33c50d/components/NewTaskModal.tsx +492 -0
  110. package/.forge/worktrees/pipeline-0a33c50d/components/PipelineEditor.tsx +570 -0
  111. package/.forge/worktrees/pipeline-0a33c50d/components/PipelineView.tsx +1018 -0
  112. package/.forge/worktrees/pipeline-0a33c50d/components/PluginsPanel.tsx +472 -0
  113. package/.forge/worktrees/pipeline-0a33c50d/components/ProjectDetail.tsx +1618 -0
  114. package/.forge/worktrees/pipeline-0a33c50d/components/ProjectList.tsx +108 -0
  115. package/.forge/worktrees/pipeline-0a33c50d/components/ProjectManager.tsx +401 -0
  116. package/.forge/worktrees/pipeline-0a33c50d/components/SessionList.tsx +74 -0
  117. package/.forge/worktrees/pipeline-0a33c50d/components/SessionView.tsx +726 -0
  118. package/.forge/worktrees/pipeline-0a33c50d/components/SettingsModal.tsx +1647 -0
  119. package/.forge/worktrees/pipeline-0a33c50d/components/SkillsPanel.tsx +969 -0
  120. package/.forge/worktrees/pipeline-0a33c50d/components/StatusBar.tsx +99 -0
  121. package/.forge/worktrees/pipeline-0a33c50d/components/TabBar.tsx +46 -0
  122. package/.forge/worktrees/pipeline-0a33c50d/components/TaskBoard.tsx +113 -0
  123. package/.forge/worktrees/pipeline-0a33c50d/components/TaskDetail.tsx +372 -0
  124. package/.forge/worktrees/pipeline-0a33c50d/components/TerminalLauncher.tsx +398 -0
  125. package/.forge/worktrees/pipeline-0a33c50d/components/TunnelToggle.tsx +206 -0
  126. package/.forge/worktrees/pipeline-0a33c50d/components/UsagePanel.tsx +207 -0
  127. package/.forge/worktrees/pipeline-0a33c50d/components/WebTerminal.tsx +1683 -0
  128. package/.forge/worktrees/pipeline-0a33c50d/components/WorkspaceTree.tsx +221 -0
  129. package/.forge/worktrees/pipeline-0a33c50d/components/WorkspaceView.tsx +4048 -0
  130. package/.forge/worktrees/pipeline-0a33c50d/dev-test.sh +5 -0
  131. package/.forge/worktrees/pipeline-0a33c50d/docs/Forge_Memory_Layer_Design.docx +0 -0
  132. package/.forge/worktrees/pipeline-0a33c50d/docs/Forge_Strategy_Research_2026.docx +0 -0
  133. package/.forge/worktrees/pipeline-0a33c50d/docs/LOCAL-DEPLOY.md +144 -0
  134. package/.forge/worktrees/pipeline-0a33c50d/docs/roadmap-multi-agent-workflow.md +330 -0
  135. package/.forge/worktrees/pipeline-0a33c50d/forge-logo.png +0 -0
  136. package/.forge/worktrees/pipeline-0a33c50d/forge-logo.svg +106 -0
  137. package/.forge/worktrees/pipeline-0a33c50d/hooks/useSidebarResize.ts +52 -0
  138. package/.forge/worktrees/pipeline-0a33c50d/install.sh +29 -0
  139. package/.forge/worktrees/pipeline-0a33c50d/instrumentation.ts +35 -0
  140. package/.forge/worktrees/pipeline-0a33c50d/lib/agents/claude-adapter.ts +104 -0
  141. package/.forge/worktrees/pipeline-0a33c50d/lib/agents/generic-adapter.ts +64 -0
  142. package/.forge/worktrees/pipeline-0a33c50d/lib/agents/index.ts +245 -0
  143. package/.forge/worktrees/pipeline-0a33c50d/lib/agents/types.ts +70 -0
  144. package/.forge/worktrees/pipeline-0a33c50d/lib/artifacts.ts +106 -0
  145. package/.forge/worktrees/pipeline-0a33c50d/lib/auth.ts +62 -0
  146. package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/docker.yaml +70 -0
  147. package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/http.yaml +66 -0
  148. package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/jenkins.yaml +92 -0
  149. package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/llm-vision.yaml +85 -0
  150. package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/playwright.yaml +111 -0
  151. package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/shell-command.yaml +60 -0
  152. package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/slack.yaml +48 -0
  153. package/.forge/worktrees/pipeline-0a33c50d/lib/builtin-plugins/webhook.yaml +56 -0
  154. package/.forge/worktrees/pipeline-0a33c50d/lib/claude-process.ts +352 -0
  155. package/.forge/worktrees/pipeline-0a33c50d/lib/claude-sessions.ts +266 -0
  156. package/.forge/worktrees/pipeline-0a33c50d/lib/claude-templates.ts +227 -0
  157. package/.forge/worktrees/pipeline-0a33c50d/lib/cloudflared.ts +424 -0
  158. package/.forge/worktrees/pipeline-0a33c50d/lib/crypto.ts +67 -0
  159. package/.forge/worktrees/pipeline-0a33c50d/lib/delivery.ts +787 -0
  160. package/.forge/worktrees/pipeline-0a33c50d/lib/dirs.ts +99 -0
  161. package/.forge/worktrees/pipeline-0a33c50d/lib/flows.ts +86 -0
  162. package/.forge/worktrees/pipeline-0a33c50d/lib/forge-mcp-server.ts +717 -0
  163. package/.forge/worktrees/pipeline-0a33c50d/lib/forge-skills/forge-inbox.md +38 -0
  164. package/.forge/worktrees/pipeline-0a33c50d/lib/forge-skills/forge-send.md +47 -0
  165. package/.forge/worktrees/pipeline-0a33c50d/lib/forge-skills/forge-status.md +32 -0
  166. package/.forge/worktrees/pipeline-0a33c50d/lib/forge-skills/forge-workspace-sync.md +37 -0
  167. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/00-overview.md +40 -0
  168. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/01-settings.md +194 -0
  169. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/02-telegram.md +41 -0
  170. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/03-tunnel.md +31 -0
  171. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/04-tasks.md +52 -0
  172. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/05-pipelines.md +460 -0
  173. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/06-skills.md +43 -0
  174. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/07-projects.md +73 -0
  175. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/08-rules.md +53 -0
  176. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/09-issue-autofix.md +55 -0
  177. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/10-troubleshooting.md +89 -0
  178. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/11-workspace.md +810 -0
  179. package/.forge/worktrees/pipeline-0a33c50d/lib/help-docs/CLAUDE.md +62 -0
  180. package/.forge/worktrees/pipeline-0a33c50d/lib/init.ts +266 -0
  181. package/.forge/worktrees/pipeline-0a33c50d/lib/issue-scanner.ts +298 -0
  182. package/.forge/worktrees/pipeline-0a33c50d/lib/logger.ts +79 -0
  183. package/.forge/worktrees/pipeline-0a33c50d/lib/notifications.ts +75 -0
  184. package/.forge/worktrees/pipeline-0a33c50d/lib/notify.ts +108 -0
  185. package/.forge/worktrees/pipeline-0a33c50d/lib/password.ts +97 -0
  186. package/.forge/worktrees/pipeline-0a33c50d/lib/pipeline-scheduler.ts +373 -0
  187. package/.forge/worktrees/pipeline-0a33c50d/lib/pipeline.ts +1438 -0
  188. package/.forge/worktrees/pipeline-0a33c50d/lib/plugins/executor.ts +347 -0
  189. package/.forge/worktrees/pipeline-0a33c50d/lib/plugins/registry.ts +228 -0
  190. package/.forge/worktrees/pipeline-0a33c50d/lib/plugins/types.ts +103 -0
  191. package/.forge/worktrees/pipeline-0a33c50d/lib/project-sessions.ts +53 -0
  192. package/.forge/worktrees/pipeline-0a33c50d/lib/projects.ts +86 -0
  193. package/.forge/worktrees/pipeline-0a33c50d/lib/session-manager.ts +156 -0
  194. package/.forge/worktrees/pipeline-0a33c50d/lib/session-utils.ts +53 -0
  195. package/.forge/worktrees/pipeline-0a33c50d/lib/session-watcher.ts +345 -0
  196. package/.forge/worktrees/pipeline-0a33c50d/lib/settings.ts +195 -0
  197. package/.forge/worktrees/pipeline-0a33c50d/lib/skills.ts +458 -0
  198. package/.forge/worktrees/pipeline-0a33c50d/lib/task-manager.ts +949 -0
  199. package/.forge/worktrees/pipeline-0a33c50d/lib/telegram-bot.ts +1477 -0
  200. package/.forge/worktrees/pipeline-0a33c50d/lib/telegram-standalone.ts +83 -0
  201. package/.forge/worktrees/pipeline-0a33c50d/lib/terminal-server.ts +70 -0
  202. package/.forge/worktrees/pipeline-0a33c50d/lib/terminal-standalone.ts +421 -0
  203. package/.forge/worktrees/pipeline-0a33c50d/lib/usage-scanner.ts +249 -0
  204. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/__tests__/state-machine.test.ts +388 -0
  205. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/__tests__/workspace.test.ts +311 -0
  206. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/agent-bus.ts +416 -0
  207. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/agent-worker.ts +655 -0
  208. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/backends/api-backend.ts +262 -0
  209. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/backends/cli-backend.ts +491 -0
  210. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/index.ts +82 -0
  211. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/manager.ts +136 -0
  212. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/orchestrator.ts +3400 -0
  213. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/persistence.ts +309 -0
  214. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/presets.ts +649 -0
  215. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/requests.ts +287 -0
  216. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/session-monitor.ts +240 -0
  217. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/skill-installer.ts +275 -0
  218. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/smith-memory.ts +498 -0
  219. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/types.ts +241 -0
  220. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace/watch-manager.ts +560 -0
  221. package/.forge/worktrees/pipeline-0a33c50d/lib/workspace-standalone.ts +911 -0
  222. package/.forge/worktrees/pipeline-0a33c50d/middleware.ts +51 -0
  223. package/.forge/worktrees/pipeline-0a33c50d/next.config.ts +26 -0
  224. package/.forge/worktrees/pipeline-0a33c50d/package.json +74 -0
  225. package/.forge/worktrees/pipeline-0a33c50d/pnpm-lock.yaml +3719 -0
  226. package/.forge/worktrees/pipeline-0a33c50d/pnpm-workspace.yaml +1 -0
  227. package/.forge/worktrees/pipeline-0a33c50d/postcss.config.mjs +7 -0
  228. package/.forge/worktrees/pipeline-0a33c50d/publish.sh +133 -0
  229. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/README.md +66 -0
  230. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/results/.gitignore +2 -0
  231. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/run.ts +635 -0
  232. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/01-text-utils/task.md +26 -0
  233. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  234. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  235. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/02-pagination/task.md +48 -0
  236. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  237. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  238. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  239. package/.forge/worktrees/pipeline-0a33c50d/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  240. package/.forge/worktrees/pipeline-0a33c50d/scripts/verify-usage.ts +178 -0
  241. package/.forge/worktrees/pipeline-0a33c50d/src/config/index.ts +129 -0
  242. package/.forge/worktrees/pipeline-0a33c50d/src/core/db/database.ts +259 -0
  243. package/.forge/worktrees/pipeline-0a33c50d/src/core/memory/strategy.ts +32 -0
  244. package/.forge/worktrees/pipeline-0a33c50d/src/core/providers/chat.ts +65 -0
  245. package/.forge/worktrees/pipeline-0a33c50d/src/core/providers/registry.ts +60 -0
  246. package/.forge/worktrees/pipeline-0a33c50d/src/core/session/manager.ts +190 -0
  247. package/.forge/worktrees/pipeline-0a33c50d/src/types/index.ts +129 -0
  248. package/.forge/worktrees/pipeline-0a33c50d/start.sh +31 -0
  249. package/.forge/worktrees/pipeline-0a33c50d/templates/smith-lead.json +45 -0
  250. package/.forge/worktrees/pipeline-0a33c50d/tsconfig.json +42 -0
  251. package/.forge/worktrees/pipeline-2ba01c10/CLAUDE.md +86 -0
  252. package/.forge/worktrees/pipeline-2ba01c10/README.md +136 -0
  253. package/.forge/worktrees/pipeline-2ba01c10/RELEASE_NOTES.md +11 -0
  254. package/.forge/worktrees/pipeline-2ba01c10/app/api/agents/route.ts +17 -0
  255. package/.forge/worktrees/pipeline-2ba01c10/app/api/auth/[...nextauth]/route.ts +3 -0
  256. package/.forge/worktrees/pipeline-2ba01c10/app/api/auth/verify/route.ts +46 -0
  257. package/.forge/worktrees/pipeline-2ba01c10/app/api/claude/[id]/route.ts +31 -0
  258. package/.forge/worktrees/pipeline-2ba01c10/app/api/claude/[id]/stream/route.ts +63 -0
  259. package/.forge/worktrees/pipeline-2ba01c10/app/api/claude/route.ts +28 -0
  260. package/.forge/worktrees/pipeline-2ba01c10/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  261. package/.forge/worktrees/pipeline-2ba01c10/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  262. package/.forge/worktrees/pipeline-2ba01c10/app/api/claude-sessions/[projectName]/route.ts +37 -0
  263. package/.forge/worktrees/pipeline-2ba01c10/app/api/claude-sessions/sync/route.ts +17 -0
  264. package/.forge/worktrees/pipeline-2ba01c10/app/api/claude-templates/route.ts +145 -0
  265. package/.forge/worktrees/pipeline-2ba01c10/app/api/code/route.ts +299 -0
  266. package/.forge/worktrees/pipeline-2ba01c10/app/api/delivery/[id]/route.ts +62 -0
  267. package/.forge/worktrees/pipeline-2ba01c10/app/api/delivery/route.ts +40 -0
  268. package/.forge/worktrees/pipeline-2ba01c10/app/api/detect-cli/route.ts +46 -0
  269. package/.forge/worktrees/pipeline-2ba01c10/app/api/docs/route.ts +176 -0
  270. package/.forge/worktrees/pipeline-2ba01c10/app/api/docs/sessions/route.ts +54 -0
  271. package/.forge/worktrees/pipeline-2ba01c10/app/api/favorites/route.ts +26 -0
  272. package/.forge/worktrees/pipeline-2ba01c10/app/api/flows/route.ts +6 -0
  273. package/.forge/worktrees/pipeline-2ba01c10/app/api/flows/run/route.ts +19 -0
  274. package/.forge/worktrees/pipeline-2ba01c10/app/api/git/route.ts +149 -0
  275. package/.forge/worktrees/pipeline-2ba01c10/app/api/help/route.ts +84 -0
  276. package/.forge/worktrees/pipeline-2ba01c10/app/api/issue-scanner/route.ts +116 -0
  277. package/.forge/worktrees/pipeline-2ba01c10/app/api/logs/route.ts +100 -0
  278. package/.forge/worktrees/pipeline-2ba01c10/app/api/mobile-chat/route.ts +115 -0
  279. package/.forge/worktrees/pipeline-2ba01c10/app/api/monitor/route.ts +74 -0
  280. package/.forge/worktrees/pipeline-2ba01c10/app/api/notifications/route.ts +42 -0
  281. package/.forge/worktrees/pipeline-2ba01c10/app/api/notify/test/route.ts +33 -0
  282. package/.forge/worktrees/pipeline-2ba01c10/app/api/online/route.ts +40 -0
  283. package/.forge/worktrees/pipeline-2ba01c10/app/api/pipelines/[id]/route.ts +41 -0
  284. package/.forge/worktrees/pipeline-2ba01c10/app/api/pipelines/route.ts +90 -0
  285. package/.forge/worktrees/pipeline-2ba01c10/app/api/plugins/route.ts +75 -0
  286. package/.forge/worktrees/pipeline-2ba01c10/app/api/preview/[...path]/route.ts +64 -0
  287. package/.forge/worktrees/pipeline-2ba01c10/app/api/preview/route.ts +156 -0
  288. package/.forge/worktrees/pipeline-2ba01c10/app/api/project-pipelines/route.ts +91 -0
  289. package/.forge/worktrees/pipeline-2ba01c10/app/api/project-sessions/route.ts +61 -0
  290. package/.forge/worktrees/pipeline-2ba01c10/app/api/projects/route.ts +26 -0
  291. package/.forge/worktrees/pipeline-2ba01c10/app/api/sessions/[id]/chat/route.ts +64 -0
  292. package/.forge/worktrees/pipeline-2ba01c10/app/api/sessions/[id]/messages/route.ts +9 -0
  293. package/.forge/worktrees/pipeline-2ba01c10/app/api/sessions/[id]/route.ts +17 -0
  294. package/.forge/worktrees/pipeline-2ba01c10/app/api/sessions/route.ts +20 -0
  295. package/.forge/worktrees/pipeline-2ba01c10/app/api/settings/route.ts +64 -0
  296. package/.forge/worktrees/pipeline-2ba01c10/app/api/skills/local/route.ts +228 -0
  297. package/.forge/worktrees/pipeline-2ba01c10/app/api/skills/route.ts +182 -0
  298. package/.forge/worktrees/pipeline-2ba01c10/app/api/smith-templates/route.ts +81 -0
  299. package/.forge/worktrees/pipeline-2ba01c10/app/api/status/route.ts +12 -0
  300. package/.forge/worktrees/pipeline-2ba01c10/app/api/tabs/route.ts +25 -0
  301. package/.forge/worktrees/pipeline-2ba01c10/app/api/tasks/[id]/route.ts +51 -0
  302. package/.forge/worktrees/pipeline-2ba01c10/app/api/tasks/[id]/stream/route.ts +77 -0
  303. package/.forge/worktrees/pipeline-2ba01c10/app/api/tasks/link/route.ts +37 -0
  304. package/.forge/worktrees/pipeline-2ba01c10/app/api/tasks/route.ts +44 -0
  305. package/.forge/worktrees/pipeline-2ba01c10/app/api/tasks/session/route.ts +14 -0
  306. package/.forge/worktrees/pipeline-2ba01c10/app/api/telegram/route.ts +23 -0
  307. package/.forge/worktrees/pipeline-2ba01c10/app/api/templates/route.ts +6 -0
  308. package/.forge/worktrees/pipeline-2ba01c10/app/api/terminal-bell/route.ts +35 -0
  309. package/.forge/worktrees/pipeline-2ba01c10/app/api/terminal-cwd/route.ts +19 -0
  310. package/.forge/worktrees/pipeline-2ba01c10/app/api/terminal-state/route.ts +15 -0
  311. package/.forge/worktrees/pipeline-2ba01c10/app/api/tunnel/route.ts +26 -0
  312. package/.forge/worktrees/pipeline-2ba01c10/app/api/upgrade/route.ts +43 -0
  313. package/.forge/worktrees/pipeline-2ba01c10/app/api/usage/route.ts +20 -0
  314. package/.forge/worktrees/pipeline-2ba01c10/app/api/version/route.ts +78 -0
  315. package/.forge/worktrees/pipeline-2ba01c10/app/api/watchers/route.ts +33 -0
  316. package/.forge/worktrees/pipeline-2ba01c10/app/api/workspace/[id]/agents/route.ts +35 -0
  317. package/.forge/worktrees/pipeline-2ba01c10/app/api/workspace/[id]/memory/route.ts +23 -0
  318. package/.forge/worktrees/pipeline-2ba01c10/app/api/workspace/[id]/smith/route.ts +22 -0
  319. package/.forge/worktrees/pipeline-2ba01c10/app/api/workspace/[id]/stream/route.ts +28 -0
  320. package/.forge/worktrees/pipeline-2ba01c10/app/api/workspace/route.ts +100 -0
  321. package/.forge/worktrees/pipeline-2ba01c10/app/global-error.tsx +21 -0
  322. package/.forge/worktrees/pipeline-2ba01c10/app/globals.css +52 -0
  323. package/.forge/worktrees/pipeline-2ba01c10/app/icon.ico +0 -0
  324. package/.forge/worktrees/pipeline-2ba01c10/app/icon.png +0 -0
  325. package/.forge/worktrees/pipeline-2ba01c10/app/icon.svg +106 -0
  326. package/.forge/worktrees/pipeline-2ba01c10/app/layout.tsx +17 -0
  327. package/.forge/worktrees/pipeline-2ba01c10/app/login/LoginForm.tsx +96 -0
  328. package/.forge/worktrees/pipeline-2ba01c10/app/login/page.tsx +10 -0
  329. package/.forge/worktrees/pipeline-2ba01c10/app/mobile/page.tsx +9 -0
  330. package/.forge/worktrees/pipeline-2ba01c10/app/page.tsx +21 -0
  331. package/.forge/worktrees/pipeline-2ba01c10/bin/forge-server.mjs +484 -0
  332. package/.forge/worktrees/pipeline-2ba01c10/check-forge-status.sh +71 -0
  333. package/.forge/worktrees/pipeline-2ba01c10/cli/mw.ts +579 -0
  334. package/.forge/worktrees/pipeline-2ba01c10/components/BrowserPanel.tsx +175 -0
  335. package/.forge/worktrees/pipeline-2ba01c10/components/ChatPanel.tsx +191 -0
  336. package/.forge/worktrees/pipeline-2ba01c10/components/ClaudeTerminal.tsx +267 -0
  337. package/.forge/worktrees/pipeline-2ba01c10/components/CodeViewer.tsx +787 -0
  338. package/.forge/worktrees/pipeline-2ba01c10/components/ConversationEditor.tsx +411 -0
  339. package/.forge/worktrees/pipeline-2ba01c10/components/ConversationGraphView.tsx +347 -0
  340. package/.forge/worktrees/pipeline-2ba01c10/components/ConversationTerminalView.tsx +303 -0
  341. package/.forge/worktrees/pipeline-2ba01c10/components/Dashboard.tsx +807 -0
  342. package/.forge/worktrees/pipeline-2ba01c10/components/DashboardWrapper.tsx +9 -0
  343. package/.forge/worktrees/pipeline-2ba01c10/components/DeliveryFlowEditor.tsx +491 -0
  344. package/.forge/worktrees/pipeline-2ba01c10/components/DeliveryList.tsx +230 -0
  345. package/.forge/worktrees/pipeline-2ba01c10/components/DeliveryWorkspace.tsx +589 -0
  346. package/.forge/worktrees/pipeline-2ba01c10/components/DocTerminal.tsx +187 -0
  347. package/.forge/worktrees/pipeline-2ba01c10/components/DocsViewer.tsx +574 -0
  348. package/.forge/worktrees/pipeline-2ba01c10/components/HelpDialog.tsx +169 -0
  349. package/.forge/worktrees/pipeline-2ba01c10/components/HelpTerminal.tsx +141 -0
  350. package/.forge/worktrees/pipeline-2ba01c10/components/InlinePipelineView.tsx +111 -0
  351. package/.forge/worktrees/pipeline-2ba01c10/components/LogViewer.tsx +194 -0
  352. package/.forge/worktrees/pipeline-2ba01c10/components/MarkdownContent.tsx +73 -0
  353. package/.forge/worktrees/pipeline-2ba01c10/components/MobileView.tsx +385 -0
  354. package/.forge/worktrees/pipeline-2ba01c10/components/MonitorPanel.tsx +122 -0
  355. package/.forge/worktrees/pipeline-2ba01c10/components/NewSessionModal.tsx +93 -0
  356. package/.forge/worktrees/pipeline-2ba01c10/components/NewTaskModal.tsx +492 -0
  357. package/.forge/worktrees/pipeline-2ba01c10/components/PipelineEditor.tsx +570 -0
  358. package/.forge/worktrees/pipeline-2ba01c10/components/PipelineView.tsx +1018 -0
  359. package/.forge/worktrees/pipeline-2ba01c10/components/PluginsPanel.tsx +472 -0
  360. package/.forge/worktrees/pipeline-2ba01c10/components/ProjectDetail.tsx +1618 -0
  361. package/.forge/worktrees/pipeline-2ba01c10/components/ProjectList.tsx +108 -0
  362. package/.forge/worktrees/pipeline-2ba01c10/components/ProjectManager.tsx +401 -0
  363. package/.forge/worktrees/pipeline-2ba01c10/components/SessionList.tsx +74 -0
  364. package/.forge/worktrees/pipeline-2ba01c10/components/SessionView.tsx +726 -0
  365. package/.forge/worktrees/pipeline-2ba01c10/components/SettingsModal.tsx +1647 -0
  366. package/.forge/worktrees/pipeline-2ba01c10/components/SkillsPanel.tsx +969 -0
  367. package/.forge/worktrees/pipeline-2ba01c10/components/StatusBar.tsx +99 -0
  368. package/.forge/worktrees/pipeline-2ba01c10/components/TabBar.tsx +46 -0
  369. package/.forge/worktrees/pipeline-2ba01c10/components/TaskBoard.tsx +113 -0
  370. package/.forge/worktrees/pipeline-2ba01c10/components/TaskDetail.tsx +372 -0
  371. package/.forge/worktrees/pipeline-2ba01c10/components/TerminalLauncher.tsx +398 -0
  372. package/.forge/worktrees/pipeline-2ba01c10/components/TunnelToggle.tsx +206 -0
  373. package/.forge/worktrees/pipeline-2ba01c10/components/UsagePanel.tsx +207 -0
  374. package/.forge/worktrees/pipeline-2ba01c10/components/WebTerminal.tsx +1683 -0
  375. package/.forge/worktrees/pipeline-2ba01c10/components/WorkspaceTree.tsx +221 -0
  376. package/.forge/worktrees/pipeline-2ba01c10/components/WorkspaceView.tsx +4048 -0
  377. package/.forge/worktrees/pipeline-2ba01c10/dev-test.sh +5 -0
  378. package/.forge/worktrees/pipeline-2ba01c10/docs/Forge_Memory_Layer_Design.docx +0 -0
  379. package/.forge/worktrees/pipeline-2ba01c10/docs/Forge_Strategy_Research_2026.docx +0 -0
  380. package/.forge/worktrees/pipeline-2ba01c10/docs/LOCAL-DEPLOY.md +144 -0
  381. package/.forge/worktrees/pipeline-2ba01c10/docs/roadmap-multi-agent-workflow.md +330 -0
  382. package/.forge/worktrees/pipeline-2ba01c10/forge-logo.png +0 -0
  383. package/.forge/worktrees/pipeline-2ba01c10/forge-logo.svg +106 -0
  384. package/.forge/worktrees/pipeline-2ba01c10/hooks/useSidebarResize.ts +52 -0
  385. package/.forge/worktrees/pipeline-2ba01c10/install.sh +29 -0
  386. package/.forge/worktrees/pipeline-2ba01c10/instrumentation.ts +35 -0
  387. package/.forge/worktrees/pipeline-2ba01c10/lib/agents/claude-adapter.ts +104 -0
  388. package/.forge/worktrees/pipeline-2ba01c10/lib/agents/generic-adapter.ts +64 -0
  389. package/.forge/worktrees/pipeline-2ba01c10/lib/agents/index.ts +245 -0
  390. package/.forge/worktrees/pipeline-2ba01c10/lib/agents/types.ts +70 -0
  391. package/.forge/worktrees/pipeline-2ba01c10/lib/artifacts.ts +106 -0
  392. package/.forge/worktrees/pipeline-2ba01c10/lib/auth.ts +62 -0
  393. package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/docker.yaml +70 -0
  394. package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/http.yaml +66 -0
  395. package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/jenkins.yaml +92 -0
  396. package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/llm-vision.yaml +85 -0
  397. package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/playwright.yaml +111 -0
  398. package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/shell-command.yaml +60 -0
  399. package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/slack.yaml +48 -0
  400. package/.forge/worktrees/pipeline-2ba01c10/lib/builtin-plugins/webhook.yaml +56 -0
  401. package/.forge/worktrees/pipeline-2ba01c10/lib/claude-process.ts +352 -0
  402. package/.forge/worktrees/pipeline-2ba01c10/lib/claude-sessions.ts +266 -0
  403. package/.forge/worktrees/pipeline-2ba01c10/lib/claude-templates.ts +227 -0
  404. package/.forge/worktrees/pipeline-2ba01c10/lib/cloudflared.ts +424 -0
  405. package/.forge/worktrees/pipeline-2ba01c10/lib/crypto.ts +67 -0
  406. package/.forge/worktrees/pipeline-2ba01c10/lib/delivery.ts +787 -0
  407. package/.forge/worktrees/pipeline-2ba01c10/lib/dirs.ts +99 -0
  408. package/.forge/worktrees/pipeline-2ba01c10/lib/flows.ts +86 -0
  409. package/.forge/worktrees/pipeline-2ba01c10/lib/forge-mcp-server.ts +717 -0
  410. package/.forge/worktrees/pipeline-2ba01c10/lib/forge-skills/forge-inbox.md +38 -0
  411. package/.forge/worktrees/pipeline-2ba01c10/lib/forge-skills/forge-send.md +47 -0
  412. package/.forge/worktrees/pipeline-2ba01c10/lib/forge-skills/forge-status.md +32 -0
  413. package/.forge/worktrees/pipeline-2ba01c10/lib/forge-skills/forge-workspace-sync.md +37 -0
  414. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/00-overview.md +40 -0
  415. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/01-settings.md +194 -0
  416. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/02-telegram.md +41 -0
  417. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/03-tunnel.md +31 -0
  418. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/04-tasks.md +52 -0
  419. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/05-pipelines.md +460 -0
  420. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/06-skills.md +43 -0
  421. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/07-projects.md +73 -0
  422. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/08-rules.md +53 -0
  423. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/09-issue-autofix.md +55 -0
  424. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/10-troubleshooting.md +89 -0
  425. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/11-workspace.md +810 -0
  426. package/.forge/worktrees/pipeline-2ba01c10/lib/help-docs/CLAUDE.md +62 -0
  427. package/.forge/worktrees/pipeline-2ba01c10/lib/init.ts +266 -0
  428. package/.forge/worktrees/pipeline-2ba01c10/lib/issue-scanner.ts +298 -0
  429. package/.forge/worktrees/pipeline-2ba01c10/lib/logger.ts +79 -0
  430. package/.forge/worktrees/pipeline-2ba01c10/lib/notifications.ts +75 -0
  431. package/.forge/worktrees/pipeline-2ba01c10/lib/notify.ts +108 -0
  432. package/.forge/worktrees/pipeline-2ba01c10/lib/password.ts +97 -0
  433. package/.forge/worktrees/pipeline-2ba01c10/lib/pipeline-scheduler.ts +373 -0
  434. package/.forge/worktrees/pipeline-2ba01c10/lib/pipeline.ts +1438 -0
  435. package/.forge/worktrees/pipeline-2ba01c10/lib/plugins/executor.ts +347 -0
  436. package/.forge/worktrees/pipeline-2ba01c10/lib/plugins/registry.ts +228 -0
  437. package/.forge/worktrees/pipeline-2ba01c10/lib/plugins/types.ts +103 -0
  438. package/.forge/worktrees/pipeline-2ba01c10/lib/project-sessions.ts +53 -0
  439. package/.forge/worktrees/pipeline-2ba01c10/lib/projects.ts +86 -0
  440. package/.forge/worktrees/pipeline-2ba01c10/lib/session-manager.ts +156 -0
  441. package/.forge/worktrees/pipeline-2ba01c10/lib/session-utils.ts +53 -0
  442. package/.forge/worktrees/pipeline-2ba01c10/lib/session-watcher.ts +345 -0
  443. package/.forge/worktrees/pipeline-2ba01c10/lib/settings.ts +195 -0
  444. package/.forge/worktrees/pipeline-2ba01c10/lib/skills.ts +458 -0
  445. package/.forge/worktrees/pipeline-2ba01c10/lib/task-manager.ts +949 -0
  446. package/.forge/worktrees/pipeline-2ba01c10/lib/telegram-bot.ts +1477 -0
  447. package/.forge/worktrees/pipeline-2ba01c10/lib/telegram-standalone.ts +83 -0
  448. package/.forge/worktrees/pipeline-2ba01c10/lib/terminal-server.ts +70 -0
  449. package/.forge/worktrees/pipeline-2ba01c10/lib/terminal-standalone.ts +421 -0
  450. package/.forge/worktrees/pipeline-2ba01c10/lib/usage-scanner.ts +249 -0
  451. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/__tests__/state-machine.test.ts +388 -0
  452. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/__tests__/workspace.test.ts +311 -0
  453. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/agent-bus.ts +416 -0
  454. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/agent-worker.ts +655 -0
  455. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/backends/api-backend.ts +262 -0
  456. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/backends/cli-backend.ts +491 -0
  457. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/index.ts +82 -0
  458. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/manager.ts +136 -0
  459. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/orchestrator.ts +3400 -0
  460. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/persistence.ts +309 -0
  461. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/presets.ts +649 -0
  462. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/requests.ts +287 -0
  463. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/session-monitor.ts +240 -0
  464. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/skill-installer.ts +275 -0
  465. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/smith-memory.ts +498 -0
  466. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/types.ts +241 -0
  467. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace/watch-manager.ts +560 -0
  468. package/.forge/worktrees/pipeline-2ba01c10/lib/workspace-standalone.ts +911 -0
  469. package/.forge/worktrees/pipeline-2ba01c10/middleware.ts +51 -0
  470. package/.forge/worktrees/pipeline-2ba01c10/next.config.ts +26 -0
  471. package/.forge/worktrees/pipeline-2ba01c10/package.json +74 -0
  472. package/.forge/worktrees/pipeline-2ba01c10/pnpm-lock.yaml +3719 -0
  473. package/.forge/worktrees/pipeline-2ba01c10/pnpm-workspace.yaml +1 -0
  474. package/.forge/worktrees/pipeline-2ba01c10/postcss.config.mjs +7 -0
  475. package/.forge/worktrees/pipeline-2ba01c10/publish.sh +133 -0
  476. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/README.md +66 -0
  477. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/results/.gitignore +2 -0
  478. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/run.ts +635 -0
  479. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/01-text-utils/task.md +26 -0
  480. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  481. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  482. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/02-pagination/task.md +48 -0
  483. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  484. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  485. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  486. package/.forge/worktrees/pipeline-2ba01c10/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  487. package/.forge/worktrees/pipeline-2ba01c10/scripts/verify-usage.ts +178 -0
  488. package/.forge/worktrees/pipeline-2ba01c10/src/config/index.ts +129 -0
  489. package/.forge/worktrees/pipeline-2ba01c10/src/core/db/database.ts +259 -0
  490. package/.forge/worktrees/pipeline-2ba01c10/src/core/memory/strategy.ts +32 -0
  491. package/.forge/worktrees/pipeline-2ba01c10/src/core/providers/chat.ts +65 -0
  492. package/.forge/worktrees/pipeline-2ba01c10/src/core/providers/registry.ts +60 -0
  493. package/.forge/worktrees/pipeline-2ba01c10/src/core/session/manager.ts +190 -0
  494. package/.forge/worktrees/pipeline-2ba01c10/src/types/index.ts +129 -0
  495. package/.forge/worktrees/pipeline-2ba01c10/start.sh +31 -0
  496. package/.forge/worktrees/pipeline-2ba01c10/templates/smith-lead.json +45 -0
  497. package/.forge/worktrees/pipeline-2ba01c10/tsconfig.json +42 -0
  498. package/.forge/worktrees/pipeline-3156a8b3/CLAUDE.md +86 -0
  499. package/.forge/worktrees/pipeline-3156a8b3/README.md +136 -0
  500. package/.forge/worktrees/pipeline-3156a8b3/RELEASE_NOTES.md +11 -0
  501. package/.forge/worktrees/pipeline-3156a8b3/app/api/agents/route.ts +17 -0
  502. package/.forge/worktrees/pipeline-3156a8b3/app/api/auth/[...nextauth]/route.ts +3 -0
  503. package/.forge/worktrees/pipeline-3156a8b3/app/api/auth/verify/route.ts +46 -0
  504. package/.forge/worktrees/pipeline-3156a8b3/app/api/claude/[id]/route.ts +31 -0
  505. package/.forge/worktrees/pipeline-3156a8b3/app/api/claude/[id]/stream/route.ts +63 -0
  506. package/.forge/worktrees/pipeline-3156a8b3/app/api/claude/route.ts +28 -0
  507. package/.forge/worktrees/pipeline-3156a8b3/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  508. package/.forge/worktrees/pipeline-3156a8b3/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  509. package/.forge/worktrees/pipeline-3156a8b3/app/api/claude-sessions/[projectName]/route.ts +37 -0
  510. package/.forge/worktrees/pipeline-3156a8b3/app/api/claude-sessions/sync/route.ts +17 -0
  511. package/.forge/worktrees/pipeline-3156a8b3/app/api/claude-templates/route.ts +145 -0
  512. package/.forge/worktrees/pipeline-3156a8b3/app/api/code/route.ts +299 -0
  513. package/.forge/worktrees/pipeline-3156a8b3/app/api/delivery/[id]/route.ts +62 -0
  514. package/.forge/worktrees/pipeline-3156a8b3/app/api/delivery/route.ts +40 -0
  515. package/.forge/worktrees/pipeline-3156a8b3/app/api/detect-cli/route.ts +46 -0
  516. package/.forge/worktrees/pipeline-3156a8b3/app/api/docs/route.ts +176 -0
  517. package/.forge/worktrees/pipeline-3156a8b3/app/api/docs/sessions/route.ts +54 -0
  518. package/.forge/worktrees/pipeline-3156a8b3/app/api/favorites/route.ts +26 -0
  519. package/.forge/worktrees/pipeline-3156a8b3/app/api/flows/route.ts +6 -0
  520. package/.forge/worktrees/pipeline-3156a8b3/app/api/flows/run/route.ts +19 -0
  521. package/.forge/worktrees/pipeline-3156a8b3/app/api/git/route.ts +149 -0
  522. package/.forge/worktrees/pipeline-3156a8b3/app/api/help/route.ts +84 -0
  523. package/.forge/worktrees/pipeline-3156a8b3/app/api/issue-scanner/route.ts +116 -0
  524. package/.forge/worktrees/pipeline-3156a8b3/app/api/logs/route.ts +100 -0
  525. package/.forge/worktrees/pipeline-3156a8b3/app/api/mobile-chat/route.ts +115 -0
  526. package/.forge/worktrees/pipeline-3156a8b3/app/api/monitor/route.ts +74 -0
  527. package/.forge/worktrees/pipeline-3156a8b3/app/api/notifications/route.ts +42 -0
  528. package/.forge/worktrees/pipeline-3156a8b3/app/api/notify/test/route.ts +33 -0
  529. package/.forge/worktrees/pipeline-3156a8b3/app/api/online/route.ts +40 -0
  530. package/.forge/worktrees/pipeline-3156a8b3/app/api/pipelines/[id]/route.ts +41 -0
  531. package/.forge/worktrees/pipeline-3156a8b3/app/api/pipelines/route.ts +90 -0
  532. package/.forge/worktrees/pipeline-3156a8b3/app/api/plugins/route.ts +75 -0
  533. package/.forge/worktrees/pipeline-3156a8b3/app/api/preview/[...path]/route.ts +64 -0
  534. package/.forge/worktrees/pipeline-3156a8b3/app/api/preview/route.ts +156 -0
  535. package/.forge/worktrees/pipeline-3156a8b3/app/api/project-pipelines/route.ts +91 -0
  536. package/.forge/worktrees/pipeline-3156a8b3/app/api/project-sessions/route.ts +61 -0
  537. package/.forge/worktrees/pipeline-3156a8b3/app/api/projects/route.ts +26 -0
  538. package/.forge/worktrees/pipeline-3156a8b3/app/api/sessions/[id]/chat/route.ts +64 -0
  539. package/.forge/worktrees/pipeline-3156a8b3/app/api/sessions/[id]/messages/route.ts +9 -0
  540. package/.forge/worktrees/pipeline-3156a8b3/app/api/sessions/[id]/route.ts +17 -0
  541. package/.forge/worktrees/pipeline-3156a8b3/app/api/sessions/route.ts +20 -0
  542. package/.forge/worktrees/pipeline-3156a8b3/app/api/settings/route.ts +64 -0
  543. package/.forge/worktrees/pipeline-3156a8b3/app/api/skills/local/route.ts +228 -0
  544. package/.forge/worktrees/pipeline-3156a8b3/app/api/skills/route.ts +182 -0
  545. package/.forge/worktrees/pipeline-3156a8b3/app/api/smith-templates/route.ts +81 -0
  546. package/.forge/worktrees/pipeline-3156a8b3/app/api/status/route.ts +12 -0
  547. package/.forge/worktrees/pipeline-3156a8b3/app/api/tabs/route.ts +25 -0
  548. package/.forge/worktrees/pipeline-3156a8b3/app/api/tasks/[id]/route.ts +51 -0
  549. package/.forge/worktrees/pipeline-3156a8b3/app/api/tasks/[id]/stream/route.ts +77 -0
  550. package/.forge/worktrees/pipeline-3156a8b3/app/api/tasks/link/route.ts +37 -0
  551. package/.forge/worktrees/pipeline-3156a8b3/app/api/tasks/route.ts +44 -0
  552. package/.forge/worktrees/pipeline-3156a8b3/app/api/tasks/session/route.ts +14 -0
  553. package/.forge/worktrees/pipeline-3156a8b3/app/api/telegram/route.ts +23 -0
  554. package/.forge/worktrees/pipeline-3156a8b3/app/api/templates/route.ts +6 -0
  555. package/.forge/worktrees/pipeline-3156a8b3/app/api/terminal-bell/route.ts +35 -0
  556. package/.forge/worktrees/pipeline-3156a8b3/app/api/terminal-cwd/route.ts +19 -0
  557. package/.forge/worktrees/pipeline-3156a8b3/app/api/terminal-state/route.ts +15 -0
  558. package/.forge/worktrees/pipeline-3156a8b3/app/api/tunnel/route.ts +26 -0
  559. package/.forge/worktrees/pipeline-3156a8b3/app/api/upgrade/route.ts +43 -0
  560. package/.forge/worktrees/pipeline-3156a8b3/app/api/usage/route.ts +20 -0
  561. package/.forge/worktrees/pipeline-3156a8b3/app/api/version/route.ts +78 -0
  562. package/.forge/worktrees/pipeline-3156a8b3/app/api/watchers/route.ts +33 -0
  563. package/.forge/worktrees/pipeline-3156a8b3/app/api/workspace/[id]/agents/route.ts +35 -0
  564. package/.forge/worktrees/pipeline-3156a8b3/app/api/workspace/[id]/memory/route.ts +23 -0
  565. package/.forge/worktrees/pipeline-3156a8b3/app/api/workspace/[id]/smith/route.ts +22 -0
  566. package/.forge/worktrees/pipeline-3156a8b3/app/api/workspace/[id]/stream/route.ts +28 -0
  567. package/.forge/worktrees/pipeline-3156a8b3/app/api/workspace/route.ts +100 -0
  568. package/.forge/worktrees/pipeline-3156a8b3/app/global-error.tsx +21 -0
  569. package/.forge/worktrees/pipeline-3156a8b3/app/globals.css +52 -0
  570. package/.forge/worktrees/pipeline-3156a8b3/app/icon.ico +0 -0
  571. package/.forge/worktrees/pipeline-3156a8b3/app/icon.png +0 -0
  572. package/.forge/worktrees/pipeline-3156a8b3/app/icon.svg +106 -0
  573. package/.forge/worktrees/pipeline-3156a8b3/app/layout.tsx +17 -0
  574. package/.forge/worktrees/pipeline-3156a8b3/app/login/LoginForm.tsx +96 -0
  575. package/.forge/worktrees/pipeline-3156a8b3/app/login/page.tsx +10 -0
  576. package/.forge/worktrees/pipeline-3156a8b3/app/mobile/page.tsx +9 -0
  577. package/.forge/worktrees/pipeline-3156a8b3/app/page.tsx +21 -0
  578. package/.forge/worktrees/pipeline-3156a8b3/bin/forge-server.mjs +484 -0
  579. package/.forge/worktrees/pipeline-3156a8b3/check-forge-status.sh +71 -0
  580. package/.forge/worktrees/pipeline-3156a8b3/cli/mw.ts +579 -0
  581. package/.forge/worktrees/pipeline-3156a8b3/components/BrowserPanel.tsx +175 -0
  582. package/.forge/worktrees/pipeline-3156a8b3/components/ChatPanel.tsx +191 -0
  583. package/.forge/worktrees/pipeline-3156a8b3/components/ClaudeTerminal.tsx +267 -0
  584. package/.forge/worktrees/pipeline-3156a8b3/components/CodeViewer.tsx +787 -0
  585. package/.forge/worktrees/pipeline-3156a8b3/components/ConversationEditor.tsx +411 -0
  586. package/.forge/worktrees/pipeline-3156a8b3/components/ConversationGraphView.tsx +347 -0
  587. package/.forge/worktrees/pipeline-3156a8b3/components/ConversationTerminalView.tsx +303 -0
  588. package/.forge/worktrees/pipeline-3156a8b3/components/Dashboard.tsx +807 -0
  589. package/.forge/worktrees/pipeline-3156a8b3/components/DashboardWrapper.tsx +9 -0
  590. package/.forge/worktrees/pipeline-3156a8b3/components/DeliveryFlowEditor.tsx +491 -0
  591. package/.forge/worktrees/pipeline-3156a8b3/components/DeliveryList.tsx +230 -0
  592. package/.forge/worktrees/pipeline-3156a8b3/components/DeliveryWorkspace.tsx +589 -0
  593. package/.forge/worktrees/pipeline-3156a8b3/components/DocTerminal.tsx +187 -0
  594. package/.forge/worktrees/pipeline-3156a8b3/components/DocsViewer.tsx +574 -0
  595. package/.forge/worktrees/pipeline-3156a8b3/components/HelpDialog.tsx +169 -0
  596. package/.forge/worktrees/pipeline-3156a8b3/components/HelpTerminal.tsx +141 -0
  597. package/.forge/worktrees/pipeline-3156a8b3/components/InlinePipelineView.tsx +111 -0
  598. package/.forge/worktrees/pipeline-3156a8b3/components/LogViewer.tsx +194 -0
  599. package/.forge/worktrees/pipeline-3156a8b3/components/MarkdownContent.tsx +73 -0
  600. package/.forge/worktrees/pipeline-3156a8b3/components/MobileView.tsx +385 -0
  601. package/.forge/worktrees/pipeline-3156a8b3/components/MonitorPanel.tsx +122 -0
  602. package/.forge/worktrees/pipeline-3156a8b3/components/NewSessionModal.tsx +93 -0
  603. package/.forge/worktrees/pipeline-3156a8b3/components/NewTaskModal.tsx +492 -0
  604. package/.forge/worktrees/pipeline-3156a8b3/components/PipelineEditor.tsx +570 -0
  605. package/.forge/worktrees/pipeline-3156a8b3/components/PipelineView.tsx +1018 -0
  606. package/.forge/worktrees/pipeline-3156a8b3/components/PluginsPanel.tsx +472 -0
  607. package/.forge/worktrees/pipeline-3156a8b3/components/ProjectDetail.tsx +1618 -0
  608. package/.forge/worktrees/pipeline-3156a8b3/components/ProjectList.tsx +108 -0
  609. package/.forge/worktrees/pipeline-3156a8b3/components/ProjectManager.tsx +401 -0
  610. package/.forge/worktrees/pipeline-3156a8b3/components/SessionList.tsx +74 -0
  611. package/.forge/worktrees/pipeline-3156a8b3/components/SessionView.tsx +726 -0
  612. package/.forge/worktrees/pipeline-3156a8b3/components/SettingsModal.tsx +1647 -0
  613. package/.forge/worktrees/pipeline-3156a8b3/components/SkillsPanel.tsx +969 -0
  614. package/.forge/worktrees/pipeline-3156a8b3/components/StatusBar.tsx +99 -0
  615. package/.forge/worktrees/pipeline-3156a8b3/components/TabBar.tsx +46 -0
  616. package/.forge/worktrees/pipeline-3156a8b3/components/TaskBoard.tsx +113 -0
  617. package/.forge/worktrees/pipeline-3156a8b3/components/TaskDetail.tsx +372 -0
  618. package/.forge/worktrees/pipeline-3156a8b3/components/TerminalLauncher.tsx +398 -0
  619. package/.forge/worktrees/pipeline-3156a8b3/components/TunnelToggle.tsx +206 -0
  620. package/.forge/worktrees/pipeline-3156a8b3/components/UsagePanel.tsx +207 -0
  621. package/.forge/worktrees/pipeline-3156a8b3/components/WebTerminal.tsx +1683 -0
  622. package/.forge/worktrees/pipeline-3156a8b3/components/WorkspaceTree.tsx +221 -0
  623. package/.forge/worktrees/pipeline-3156a8b3/components/WorkspaceView.tsx +4048 -0
  624. package/.forge/worktrees/pipeline-3156a8b3/dev-test.sh +5 -0
  625. package/.forge/worktrees/pipeline-3156a8b3/docs/Forge_Memory_Layer_Design.docx +0 -0
  626. package/.forge/worktrees/pipeline-3156a8b3/docs/Forge_Strategy_Research_2026.docx +0 -0
  627. package/.forge/worktrees/pipeline-3156a8b3/docs/LOCAL-DEPLOY.md +144 -0
  628. package/.forge/worktrees/pipeline-3156a8b3/docs/roadmap-multi-agent-workflow.md +330 -0
  629. package/.forge/worktrees/pipeline-3156a8b3/forge-logo.png +0 -0
  630. package/.forge/worktrees/pipeline-3156a8b3/forge-logo.svg +106 -0
  631. package/.forge/worktrees/pipeline-3156a8b3/hooks/useSidebarResize.ts +52 -0
  632. package/.forge/worktrees/pipeline-3156a8b3/install.sh +29 -0
  633. package/.forge/worktrees/pipeline-3156a8b3/instrumentation.ts +35 -0
  634. package/.forge/worktrees/pipeline-3156a8b3/lib/agents/claude-adapter.ts +104 -0
  635. package/.forge/worktrees/pipeline-3156a8b3/lib/agents/generic-adapter.ts +64 -0
  636. package/.forge/worktrees/pipeline-3156a8b3/lib/agents/index.ts +245 -0
  637. package/.forge/worktrees/pipeline-3156a8b3/lib/agents/types.ts +70 -0
  638. package/.forge/worktrees/pipeline-3156a8b3/lib/artifacts.ts +106 -0
  639. package/.forge/worktrees/pipeline-3156a8b3/lib/auth.ts +62 -0
  640. package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/docker.yaml +70 -0
  641. package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/http.yaml +66 -0
  642. package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/jenkins.yaml +92 -0
  643. package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/llm-vision.yaml +85 -0
  644. package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/playwright.yaml +111 -0
  645. package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/shell-command.yaml +60 -0
  646. package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/slack.yaml +48 -0
  647. package/.forge/worktrees/pipeline-3156a8b3/lib/builtin-plugins/webhook.yaml +56 -0
  648. package/.forge/worktrees/pipeline-3156a8b3/lib/claude-process.ts +351 -0
  649. package/.forge/worktrees/pipeline-3156a8b3/lib/claude-sessions.ts +266 -0
  650. package/.forge/worktrees/pipeline-3156a8b3/lib/claude-templates.ts +227 -0
  651. package/.forge/worktrees/pipeline-3156a8b3/lib/cloudflared.ts +424 -0
  652. package/.forge/worktrees/pipeline-3156a8b3/lib/crypto.ts +67 -0
  653. package/.forge/worktrees/pipeline-3156a8b3/lib/delivery.ts +787 -0
  654. package/.forge/worktrees/pipeline-3156a8b3/lib/dirs.ts +99 -0
  655. package/.forge/worktrees/pipeline-3156a8b3/lib/flows.ts +86 -0
  656. package/.forge/worktrees/pipeline-3156a8b3/lib/forge-mcp-server.ts +717 -0
  657. package/.forge/worktrees/pipeline-3156a8b3/lib/forge-skills/forge-inbox.md +38 -0
  658. package/.forge/worktrees/pipeline-3156a8b3/lib/forge-skills/forge-send.md +47 -0
  659. package/.forge/worktrees/pipeline-3156a8b3/lib/forge-skills/forge-status.md +32 -0
  660. package/.forge/worktrees/pipeline-3156a8b3/lib/forge-skills/forge-workspace-sync.md +37 -0
  661. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/00-overview.md +40 -0
  662. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/01-settings.md +194 -0
  663. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/02-telegram.md +41 -0
  664. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/03-tunnel.md +31 -0
  665. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/04-tasks.md +52 -0
  666. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/05-pipelines.md +460 -0
  667. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/06-skills.md +43 -0
  668. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/07-projects.md +73 -0
  669. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/08-rules.md +53 -0
  670. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/09-issue-autofix.md +55 -0
  671. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/10-troubleshooting.md +89 -0
  672. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/11-workspace.md +810 -0
  673. package/.forge/worktrees/pipeline-3156a8b3/lib/help-docs/CLAUDE.md +62 -0
  674. package/.forge/worktrees/pipeline-3156a8b3/lib/init.ts +266 -0
  675. package/.forge/worktrees/pipeline-3156a8b3/lib/issue-scanner.ts +298 -0
  676. package/.forge/worktrees/pipeline-3156a8b3/lib/logger.ts +79 -0
  677. package/.forge/worktrees/pipeline-3156a8b3/lib/notifications.ts +75 -0
  678. package/.forge/worktrees/pipeline-3156a8b3/lib/notify.ts +108 -0
  679. package/.forge/worktrees/pipeline-3156a8b3/lib/password.ts +97 -0
  680. package/.forge/worktrees/pipeline-3156a8b3/lib/pipeline-scheduler.ts +373 -0
  681. package/.forge/worktrees/pipeline-3156a8b3/lib/pipeline.ts +1441 -0
  682. package/.forge/worktrees/pipeline-3156a8b3/lib/plugins/executor.ts +347 -0
  683. package/.forge/worktrees/pipeline-3156a8b3/lib/plugins/registry.ts +228 -0
  684. package/.forge/worktrees/pipeline-3156a8b3/lib/plugins/types.ts +103 -0
  685. package/.forge/worktrees/pipeline-3156a8b3/lib/project-sessions.ts +53 -0
  686. package/.forge/worktrees/pipeline-3156a8b3/lib/projects.ts +86 -0
  687. package/.forge/worktrees/pipeline-3156a8b3/lib/session-manager.ts +156 -0
  688. package/.forge/worktrees/pipeline-3156a8b3/lib/session-utils.ts +53 -0
  689. package/.forge/worktrees/pipeline-3156a8b3/lib/session-watcher.ts +345 -0
  690. package/.forge/worktrees/pipeline-3156a8b3/lib/settings.ts +195 -0
  691. package/.forge/worktrees/pipeline-3156a8b3/lib/skills.ts +458 -0
  692. package/.forge/worktrees/pipeline-3156a8b3/lib/task-manager.ts +949 -0
  693. package/.forge/worktrees/pipeline-3156a8b3/lib/telegram-bot.ts +1477 -0
  694. package/.forge/worktrees/pipeline-3156a8b3/lib/telegram-standalone.ts +83 -0
  695. package/.forge/worktrees/pipeline-3156a8b3/lib/terminal-server.ts +70 -0
  696. package/.forge/worktrees/pipeline-3156a8b3/lib/terminal-standalone.ts +421 -0
  697. package/.forge/worktrees/pipeline-3156a8b3/lib/usage-scanner.ts +249 -0
  698. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/__tests__/state-machine.test.ts +388 -0
  699. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/__tests__/workspace.test.ts +311 -0
  700. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/agent-bus.ts +416 -0
  701. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/agent-worker.ts +655 -0
  702. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/backends/api-backend.ts +262 -0
  703. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/backends/cli-backend.ts +491 -0
  704. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/index.ts +82 -0
  705. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/manager.ts +136 -0
  706. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/orchestrator.ts +3400 -0
  707. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/persistence.ts +309 -0
  708. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/presets.ts +649 -0
  709. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/requests.ts +287 -0
  710. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/session-monitor.ts +240 -0
  711. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/skill-installer.ts +275 -0
  712. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/smith-memory.ts +498 -0
  713. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/types.ts +241 -0
  714. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace/watch-manager.ts +560 -0
  715. package/.forge/worktrees/pipeline-3156a8b3/lib/workspace-standalone.ts +911 -0
  716. package/.forge/worktrees/pipeline-3156a8b3/middleware.ts +51 -0
  717. package/.forge/worktrees/pipeline-3156a8b3/next.config.ts +26 -0
  718. package/.forge/worktrees/pipeline-3156a8b3/package.json +74 -0
  719. package/.forge/worktrees/pipeline-3156a8b3/pnpm-lock.yaml +3719 -0
  720. package/.forge/worktrees/pipeline-3156a8b3/pnpm-workspace.yaml +1 -0
  721. package/.forge/worktrees/pipeline-3156a8b3/postcss.config.mjs +7 -0
  722. package/.forge/worktrees/pipeline-3156a8b3/publish.sh +133 -0
  723. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/README.md +66 -0
  724. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/results/.gitignore +2 -0
  725. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/run.ts +635 -0
  726. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/01-text-utils/task.md +26 -0
  727. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  728. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  729. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/02-pagination/task.md +48 -0
  730. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  731. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  732. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  733. package/.forge/worktrees/pipeline-3156a8b3/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  734. package/.forge/worktrees/pipeline-3156a8b3/scripts/verify-usage.ts +178 -0
  735. package/.forge/worktrees/pipeline-3156a8b3/src/config/index.ts +129 -0
  736. package/.forge/worktrees/pipeline-3156a8b3/src/core/db/database.ts +259 -0
  737. package/.forge/worktrees/pipeline-3156a8b3/src/core/memory/strategy.ts +32 -0
  738. package/.forge/worktrees/pipeline-3156a8b3/src/core/providers/chat.ts +65 -0
  739. package/.forge/worktrees/pipeline-3156a8b3/src/core/providers/registry.ts +60 -0
  740. package/.forge/worktrees/pipeline-3156a8b3/src/core/session/manager.ts +190 -0
  741. package/.forge/worktrees/pipeline-3156a8b3/src/types/index.ts +129 -0
  742. package/.forge/worktrees/pipeline-3156a8b3/start.sh +31 -0
  743. package/.forge/worktrees/pipeline-3156a8b3/templates/smith-lead.json +45 -0
  744. package/.forge/worktrees/pipeline-3156a8b3/tsconfig.json +42 -0
  745. package/.forge/worktrees/pipeline-316c6574/CLAUDE.md +86 -0
  746. package/.forge/worktrees/pipeline-316c6574/README.md +136 -0
  747. package/.forge/worktrees/pipeline-316c6574/RELEASE_NOTES.md +11 -0
  748. package/.forge/worktrees/pipeline-316c6574/app/api/agents/route.ts +17 -0
  749. package/.forge/worktrees/pipeline-316c6574/app/api/auth/[...nextauth]/route.ts +3 -0
  750. package/.forge/worktrees/pipeline-316c6574/app/api/auth/verify/route.ts +46 -0
  751. package/.forge/worktrees/pipeline-316c6574/app/api/claude/[id]/route.ts +31 -0
  752. package/.forge/worktrees/pipeline-316c6574/app/api/claude/[id]/stream/route.ts +63 -0
  753. package/.forge/worktrees/pipeline-316c6574/app/api/claude/route.ts +28 -0
  754. package/.forge/worktrees/pipeline-316c6574/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  755. package/.forge/worktrees/pipeline-316c6574/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  756. package/.forge/worktrees/pipeline-316c6574/app/api/claude-sessions/[projectName]/route.ts +37 -0
  757. package/.forge/worktrees/pipeline-316c6574/app/api/claude-sessions/sync/route.ts +17 -0
  758. package/.forge/worktrees/pipeline-316c6574/app/api/claude-templates/route.ts +145 -0
  759. package/.forge/worktrees/pipeline-316c6574/app/api/code/route.ts +299 -0
  760. package/.forge/worktrees/pipeline-316c6574/app/api/delivery/[id]/route.ts +62 -0
  761. package/.forge/worktrees/pipeline-316c6574/app/api/delivery/route.ts +40 -0
  762. package/.forge/worktrees/pipeline-316c6574/app/api/detect-cli/route.ts +46 -0
  763. package/.forge/worktrees/pipeline-316c6574/app/api/docs/route.ts +176 -0
  764. package/.forge/worktrees/pipeline-316c6574/app/api/docs/sessions/route.ts +54 -0
  765. package/.forge/worktrees/pipeline-316c6574/app/api/favorites/route.ts +26 -0
  766. package/.forge/worktrees/pipeline-316c6574/app/api/flows/route.ts +6 -0
  767. package/.forge/worktrees/pipeline-316c6574/app/api/flows/run/route.ts +19 -0
  768. package/.forge/worktrees/pipeline-316c6574/app/api/git/route.ts +149 -0
  769. package/.forge/worktrees/pipeline-316c6574/app/api/help/route.ts +84 -0
  770. package/.forge/worktrees/pipeline-316c6574/app/api/issue-scanner/route.ts +116 -0
  771. package/.forge/worktrees/pipeline-316c6574/app/api/logs/route.ts +100 -0
  772. package/.forge/worktrees/pipeline-316c6574/app/api/mobile-chat/route.ts +115 -0
  773. package/.forge/worktrees/pipeline-316c6574/app/api/monitor/route.ts +74 -0
  774. package/.forge/worktrees/pipeline-316c6574/app/api/notifications/route.ts +42 -0
  775. package/.forge/worktrees/pipeline-316c6574/app/api/notify/test/route.ts +33 -0
  776. package/.forge/worktrees/pipeline-316c6574/app/api/online/route.ts +40 -0
  777. package/.forge/worktrees/pipeline-316c6574/app/api/pipelines/[id]/route.ts +41 -0
  778. package/.forge/worktrees/pipeline-316c6574/app/api/pipelines/route.ts +90 -0
  779. package/.forge/worktrees/pipeline-316c6574/app/api/plugins/route.ts +75 -0
  780. package/.forge/worktrees/pipeline-316c6574/app/api/preview/[...path]/route.ts +64 -0
  781. package/.forge/worktrees/pipeline-316c6574/app/api/preview/route.ts +156 -0
  782. package/.forge/worktrees/pipeline-316c6574/app/api/project-pipelines/route.ts +91 -0
  783. package/.forge/worktrees/pipeline-316c6574/app/api/project-sessions/route.ts +61 -0
  784. package/.forge/worktrees/pipeline-316c6574/app/api/projects/route.ts +26 -0
  785. package/.forge/worktrees/pipeline-316c6574/app/api/sessions/[id]/chat/route.ts +64 -0
  786. package/.forge/worktrees/pipeline-316c6574/app/api/sessions/[id]/messages/route.ts +9 -0
  787. package/.forge/worktrees/pipeline-316c6574/app/api/sessions/[id]/route.ts +17 -0
  788. package/.forge/worktrees/pipeline-316c6574/app/api/sessions/route.ts +20 -0
  789. package/.forge/worktrees/pipeline-316c6574/app/api/settings/route.ts +64 -0
  790. package/.forge/worktrees/pipeline-316c6574/app/api/skills/local/route.ts +228 -0
  791. package/.forge/worktrees/pipeline-316c6574/app/api/skills/route.ts +182 -0
  792. package/.forge/worktrees/pipeline-316c6574/app/api/smith-templates/route.ts +81 -0
  793. package/.forge/worktrees/pipeline-316c6574/app/api/status/route.ts +12 -0
  794. package/.forge/worktrees/pipeline-316c6574/app/api/tabs/route.ts +25 -0
  795. package/.forge/worktrees/pipeline-316c6574/app/api/tasks/[id]/route.ts +51 -0
  796. package/.forge/worktrees/pipeline-316c6574/app/api/tasks/[id]/stream/route.ts +77 -0
  797. package/.forge/worktrees/pipeline-316c6574/app/api/tasks/link/route.ts +37 -0
  798. package/.forge/worktrees/pipeline-316c6574/app/api/tasks/route.ts +44 -0
  799. package/.forge/worktrees/pipeline-316c6574/app/api/tasks/session/route.ts +14 -0
  800. package/.forge/worktrees/pipeline-316c6574/app/api/telegram/route.ts +23 -0
  801. package/.forge/worktrees/pipeline-316c6574/app/api/templates/route.ts +6 -0
  802. package/.forge/worktrees/pipeline-316c6574/app/api/terminal-bell/route.ts +35 -0
  803. package/.forge/worktrees/pipeline-316c6574/app/api/terminal-cwd/route.ts +19 -0
  804. package/.forge/worktrees/pipeline-316c6574/app/api/terminal-state/route.ts +15 -0
  805. package/.forge/worktrees/pipeline-316c6574/app/api/tunnel/route.ts +26 -0
  806. package/.forge/worktrees/pipeline-316c6574/app/api/upgrade/route.ts +43 -0
  807. package/.forge/worktrees/pipeline-316c6574/app/api/usage/route.ts +20 -0
  808. package/.forge/worktrees/pipeline-316c6574/app/api/version/route.ts +78 -0
  809. package/.forge/worktrees/pipeline-316c6574/app/api/watchers/route.ts +33 -0
  810. package/.forge/worktrees/pipeline-316c6574/app/api/workspace/[id]/agents/route.ts +35 -0
  811. package/.forge/worktrees/pipeline-316c6574/app/api/workspace/[id]/memory/route.ts +23 -0
  812. package/.forge/worktrees/pipeline-316c6574/app/api/workspace/[id]/smith/route.ts +22 -0
  813. package/.forge/worktrees/pipeline-316c6574/app/api/workspace/[id]/stream/route.ts +28 -0
  814. package/.forge/worktrees/pipeline-316c6574/app/api/workspace/route.ts +100 -0
  815. package/.forge/worktrees/pipeline-316c6574/app/global-error.tsx +21 -0
  816. package/.forge/worktrees/pipeline-316c6574/app/globals.css +52 -0
  817. package/.forge/worktrees/pipeline-316c6574/app/icon.ico +0 -0
  818. package/.forge/worktrees/pipeline-316c6574/app/icon.png +0 -0
  819. package/.forge/worktrees/pipeline-316c6574/app/icon.svg +106 -0
  820. package/.forge/worktrees/pipeline-316c6574/app/layout.tsx +17 -0
  821. package/.forge/worktrees/pipeline-316c6574/app/login/LoginForm.tsx +96 -0
  822. package/.forge/worktrees/pipeline-316c6574/app/login/page.tsx +10 -0
  823. package/.forge/worktrees/pipeline-316c6574/app/mobile/page.tsx +9 -0
  824. package/.forge/worktrees/pipeline-316c6574/app/page.tsx +21 -0
  825. package/.forge/worktrees/pipeline-316c6574/bin/forge-server.mjs +484 -0
  826. package/.forge/worktrees/pipeline-316c6574/check-forge-status.sh +71 -0
  827. package/.forge/worktrees/pipeline-316c6574/cli/mw.ts +579 -0
  828. package/.forge/worktrees/pipeline-316c6574/components/BrowserPanel.tsx +175 -0
  829. package/.forge/worktrees/pipeline-316c6574/components/ChatPanel.tsx +191 -0
  830. package/.forge/worktrees/pipeline-316c6574/components/ClaudeTerminal.tsx +267 -0
  831. package/.forge/worktrees/pipeline-316c6574/components/CodeViewer.tsx +787 -0
  832. package/.forge/worktrees/pipeline-316c6574/components/ConversationEditor.tsx +411 -0
  833. package/.forge/worktrees/pipeline-316c6574/components/ConversationGraphView.tsx +347 -0
  834. package/.forge/worktrees/pipeline-316c6574/components/ConversationTerminalView.tsx +303 -0
  835. package/.forge/worktrees/pipeline-316c6574/components/Dashboard.tsx +807 -0
  836. package/.forge/worktrees/pipeline-316c6574/components/DashboardWrapper.tsx +9 -0
  837. package/.forge/worktrees/pipeline-316c6574/components/DeliveryFlowEditor.tsx +491 -0
  838. package/.forge/worktrees/pipeline-316c6574/components/DeliveryList.tsx +230 -0
  839. package/.forge/worktrees/pipeline-316c6574/components/DeliveryWorkspace.tsx +589 -0
  840. package/.forge/worktrees/pipeline-316c6574/components/DocTerminal.tsx +187 -0
  841. package/.forge/worktrees/pipeline-316c6574/components/DocsViewer.tsx +574 -0
  842. package/.forge/worktrees/pipeline-316c6574/components/HelpDialog.tsx +169 -0
  843. package/.forge/worktrees/pipeline-316c6574/components/HelpTerminal.tsx +141 -0
  844. package/.forge/worktrees/pipeline-316c6574/components/InlinePipelineView.tsx +111 -0
  845. package/.forge/worktrees/pipeline-316c6574/components/LogViewer.tsx +194 -0
  846. package/.forge/worktrees/pipeline-316c6574/components/MarkdownContent.tsx +73 -0
  847. package/.forge/worktrees/pipeline-316c6574/components/MobileView.tsx +385 -0
  848. package/.forge/worktrees/pipeline-316c6574/components/MonitorPanel.tsx +122 -0
  849. package/.forge/worktrees/pipeline-316c6574/components/NewSessionModal.tsx +93 -0
  850. package/.forge/worktrees/pipeline-316c6574/components/NewTaskModal.tsx +492 -0
  851. package/.forge/worktrees/pipeline-316c6574/components/PipelineEditor.tsx +570 -0
  852. package/.forge/worktrees/pipeline-316c6574/components/PipelineView.tsx +1018 -0
  853. package/.forge/worktrees/pipeline-316c6574/components/PluginsPanel.tsx +472 -0
  854. package/.forge/worktrees/pipeline-316c6574/components/ProjectDetail.tsx +1618 -0
  855. package/.forge/worktrees/pipeline-316c6574/components/ProjectList.tsx +108 -0
  856. package/.forge/worktrees/pipeline-316c6574/components/ProjectManager.tsx +401 -0
  857. package/.forge/worktrees/pipeline-316c6574/components/SessionList.tsx +74 -0
  858. package/.forge/worktrees/pipeline-316c6574/components/SessionView.tsx +726 -0
  859. package/.forge/worktrees/pipeline-316c6574/components/SettingsModal.tsx +1647 -0
  860. package/.forge/worktrees/pipeline-316c6574/components/SkillsPanel.tsx +969 -0
  861. package/.forge/worktrees/pipeline-316c6574/components/StatusBar.tsx +99 -0
  862. package/.forge/worktrees/pipeline-316c6574/components/TabBar.tsx +46 -0
  863. package/.forge/worktrees/pipeline-316c6574/components/TaskBoard.tsx +113 -0
  864. package/.forge/worktrees/pipeline-316c6574/components/TaskDetail.tsx +372 -0
  865. package/.forge/worktrees/pipeline-316c6574/components/TerminalLauncher.tsx +398 -0
  866. package/.forge/worktrees/pipeline-316c6574/components/TunnelToggle.tsx +206 -0
  867. package/.forge/worktrees/pipeline-316c6574/components/UsagePanel.tsx +207 -0
  868. package/.forge/worktrees/pipeline-316c6574/components/WebTerminal.tsx +1683 -0
  869. package/.forge/worktrees/pipeline-316c6574/components/WorkspaceTree.tsx +221 -0
  870. package/.forge/worktrees/pipeline-316c6574/components/WorkspaceView.tsx +4048 -0
  871. package/.forge/worktrees/pipeline-316c6574/dev-test.sh +5 -0
  872. package/.forge/worktrees/pipeline-316c6574/docs/Forge_Memory_Layer_Design.docx +0 -0
  873. package/.forge/worktrees/pipeline-316c6574/docs/Forge_Strategy_Research_2026.docx +0 -0
  874. package/.forge/worktrees/pipeline-316c6574/docs/LOCAL-DEPLOY.md +144 -0
  875. package/.forge/worktrees/pipeline-316c6574/docs/roadmap-multi-agent-workflow.md +330 -0
  876. package/.forge/worktrees/pipeline-316c6574/forge-logo.png +0 -0
  877. package/.forge/worktrees/pipeline-316c6574/forge-logo.svg +106 -0
  878. package/.forge/worktrees/pipeline-316c6574/hooks/useSidebarResize.ts +52 -0
  879. package/.forge/worktrees/pipeline-316c6574/install.sh +29 -0
  880. package/.forge/worktrees/pipeline-316c6574/instrumentation.ts +35 -0
  881. package/.forge/worktrees/pipeline-316c6574/lib/agents/claude-adapter.ts +104 -0
  882. package/.forge/worktrees/pipeline-316c6574/lib/agents/generic-adapter.ts +64 -0
  883. package/.forge/worktrees/pipeline-316c6574/lib/agents/index.ts +245 -0
  884. package/.forge/worktrees/pipeline-316c6574/lib/agents/types.ts +70 -0
  885. package/.forge/worktrees/pipeline-316c6574/lib/artifacts.ts +106 -0
  886. package/.forge/worktrees/pipeline-316c6574/lib/auth.ts +62 -0
  887. package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/docker.yaml +70 -0
  888. package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/http.yaml +66 -0
  889. package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/jenkins.yaml +92 -0
  890. package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/llm-vision.yaml +85 -0
  891. package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/playwright.yaml +111 -0
  892. package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/shell-command.yaml +60 -0
  893. package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/slack.yaml +48 -0
  894. package/.forge/worktrees/pipeline-316c6574/lib/builtin-plugins/webhook.yaml +56 -0
  895. package/.forge/worktrees/pipeline-316c6574/lib/claude-process.ts +361 -0
  896. package/.forge/worktrees/pipeline-316c6574/lib/claude-sessions.ts +266 -0
  897. package/.forge/worktrees/pipeline-316c6574/lib/claude-templates.ts +227 -0
  898. package/.forge/worktrees/pipeline-316c6574/lib/cloudflared.ts +424 -0
  899. package/.forge/worktrees/pipeline-316c6574/lib/crypto.ts +67 -0
  900. package/.forge/worktrees/pipeline-316c6574/lib/delivery.ts +787 -0
  901. package/.forge/worktrees/pipeline-316c6574/lib/dirs.ts +99 -0
  902. package/.forge/worktrees/pipeline-316c6574/lib/flows.ts +86 -0
  903. package/.forge/worktrees/pipeline-316c6574/lib/forge-mcp-server.ts +717 -0
  904. package/.forge/worktrees/pipeline-316c6574/lib/forge-skills/forge-inbox.md +38 -0
  905. package/.forge/worktrees/pipeline-316c6574/lib/forge-skills/forge-send.md +47 -0
  906. package/.forge/worktrees/pipeline-316c6574/lib/forge-skills/forge-status.md +32 -0
  907. package/.forge/worktrees/pipeline-316c6574/lib/forge-skills/forge-workspace-sync.md +37 -0
  908. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/00-overview.md +40 -0
  909. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/01-settings.md +194 -0
  910. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/02-telegram.md +41 -0
  911. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/03-tunnel.md +31 -0
  912. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/04-tasks.md +52 -0
  913. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/05-pipelines.md +460 -0
  914. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/06-skills.md +43 -0
  915. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/07-projects.md +73 -0
  916. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/08-rules.md +53 -0
  917. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/09-issue-autofix.md +55 -0
  918. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/10-troubleshooting.md +89 -0
  919. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/11-workspace.md +810 -0
  920. package/.forge/worktrees/pipeline-316c6574/lib/help-docs/CLAUDE.md +62 -0
  921. package/.forge/worktrees/pipeline-316c6574/lib/init.ts +266 -0
  922. package/.forge/worktrees/pipeline-316c6574/lib/issue-scanner.ts +298 -0
  923. package/.forge/worktrees/pipeline-316c6574/lib/logger.ts +79 -0
  924. package/.forge/worktrees/pipeline-316c6574/lib/notifications.ts +75 -0
  925. package/.forge/worktrees/pipeline-316c6574/lib/notify.ts +108 -0
  926. package/.forge/worktrees/pipeline-316c6574/lib/password.ts +97 -0
  927. package/.forge/worktrees/pipeline-316c6574/lib/pipeline-scheduler.ts +373 -0
  928. package/.forge/worktrees/pipeline-316c6574/lib/pipeline.ts +1441 -0
  929. package/.forge/worktrees/pipeline-316c6574/lib/plugins/executor.ts +347 -0
  930. package/.forge/worktrees/pipeline-316c6574/lib/plugins/registry.ts +228 -0
  931. package/.forge/worktrees/pipeline-316c6574/lib/plugins/types.ts +103 -0
  932. package/.forge/worktrees/pipeline-316c6574/lib/project-sessions.ts +53 -0
  933. package/.forge/worktrees/pipeline-316c6574/lib/projects.ts +86 -0
  934. package/.forge/worktrees/pipeline-316c6574/lib/session-manager.ts +156 -0
  935. package/.forge/worktrees/pipeline-316c6574/lib/session-utils.ts +53 -0
  936. package/.forge/worktrees/pipeline-316c6574/lib/session-watcher.ts +345 -0
  937. package/.forge/worktrees/pipeline-316c6574/lib/settings.ts +195 -0
  938. package/.forge/worktrees/pipeline-316c6574/lib/skills.ts +458 -0
  939. package/.forge/worktrees/pipeline-316c6574/lib/task-manager.ts +949 -0
  940. package/.forge/worktrees/pipeline-316c6574/lib/telegram-bot.ts +1477 -0
  941. package/.forge/worktrees/pipeline-316c6574/lib/telegram-standalone.ts +83 -0
  942. package/.forge/worktrees/pipeline-316c6574/lib/terminal-server.ts +70 -0
  943. package/.forge/worktrees/pipeline-316c6574/lib/terminal-standalone.ts +421 -0
  944. package/.forge/worktrees/pipeline-316c6574/lib/usage-scanner.ts +249 -0
  945. package/.forge/worktrees/pipeline-316c6574/lib/workspace/__tests__/state-machine.test.ts +388 -0
  946. package/.forge/worktrees/pipeline-316c6574/lib/workspace/__tests__/workspace.test.ts +311 -0
  947. package/.forge/worktrees/pipeline-316c6574/lib/workspace/agent-bus.ts +416 -0
  948. package/.forge/worktrees/pipeline-316c6574/lib/workspace/agent-worker.ts +655 -0
  949. package/.forge/worktrees/pipeline-316c6574/lib/workspace/backends/api-backend.ts +262 -0
  950. package/.forge/worktrees/pipeline-316c6574/lib/workspace/backends/cli-backend.ts +491 -0
  951. package/.forge/worktrees/pipeline-316c6574/lib/workspace/index.ts +82 -0
  952. package/.forge/worktrees/pipeline-316c6574/lib/workspace/manager.ts +136 -0
  953. package/.forge/worktrees/pipeline-316c6574/lib/workspace/orchestrator.ts +3400 -0
  954. package/.forge/worktrees/pipeline-316c6574/lib/workspace/persistence.ts +309 -0
  955. package/.forge/worktrees/pipeline-316c6574/lib/workspace/presets.ts +649 -0
  956. package/.forge/worktrees/pipeline-316c6574/lib/workspace/requests.ts +287 -0
  957. package/.forge/worktrees/pipeline-316c6574/lib/workspace/session-monitor.ts +240 -0
  958. package/.forge/worktrees/pipeline-316c6574/lib/workspace/skill-installer.ts +275 -0
  959. package/.forge/worktrees/pipeline-316c6574/lib/workspace/smith-memory.ts +498 -0
  960. package/.forge/worktrees/pipeline-316c6574/lib/workspace/types.ts +241 -0
  961. package/.forge/worktrees/pipeline-316c6574/lib/workspace/watch-manager.ts +560 -0
  962. package/.forge/worktrees/pipeline-316c6574/lib/workspace-standalone.ts +911 -0
  963. package/.forge/worktrees/pipeline-316c6574/middleware.ts +51 -0
  964. package/.forge/worktrees/pipeline-316c6574/next.config.ts +26 -0
  965. package/.forge/worktrees/pipeline-316c6574/package.json +74 -0
  966. package/.forge/worktrees/pipeline-316c6574/pnpm-lock.yaml +3719 -0
  967. package/.forge/worktrees/pipeline-316c6574/pnpm-workspace.yaml +1 -0
  968. package/.forge/worktrees/pipeline-316c6574/postcss.config.mjs +7 -0
  969. package/.forge/worktrees/pipeline-316c6574/publish.sh +133 -0
  970. package/.forge/worktrees/pipeline-316c6574/scripts/bench/README.md +66 -0
  971. package/.forge/worktrees/pipeline-316c6574/scripts/bench/results/.gitignore +2 -0
  972. package/.forge/worktrees/pipeline-316c6574/scripts/bench/run.ts +635 -0
  973. package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/01-text-utils/task.md +26 -0
  974. package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  975. package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  976. package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/02-pagination/task.md +48 -0
  977. package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  978. package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  979. package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  980. package/.forge/worktrees/pipeline-316c6574/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  981. package/.forge/worktrees/pipeline-316c6574/scripts/verify-usage.ts +178 -0
  982. package/.forge/worktrees/pipeline-316c6574/src/config/index.ts +129 -0
  983. package/.forge/worktrees/pipeline-316c6574/src/core/db/database.ts +259 -0
  984. package/.forge/worktrees/pipeline-316c6574/src/core/memory/strategy.ts +32 -0
  985. package/.forge/worktrees/pipeline-316c6574/src/core/providers/chat.ts +65 -0
  986. package/.forge/worktrees/pipeline-316c6574/src/core/providers/registry.ts +60 -0
  987. package/.forge/worktrees/pipeline-316c6574/src/core/session/manager.ts +190 -0
  988. package/.forge/worktrees/pipeline-316c6574/src/types/index.ts +129 -0
  989. package/.forge/worktrees/pipeline-316c6574/start.sh +31 -0
  990. package/.forge/worktrees/pipeline-316c6574/templates/smith-lead.json +45 -0
  991. package/.forge/worktrees/pipeline-316c6574/tsconfig.json +42 -0
  992. package/.forge/worktrees/pipeline-44a94121/CLAUDE.md +86 -0
  993. package/.forge/worktrees/pipeline-44a94121/README.md +136 -0
  994. package/.forge/worktrees/pipeline-44a94121/RELEASE_NOTES.md +11 -0
  995. package/.forge/worktrees/pipeline-44a94121/app/api/agents/route.ts +17 -0
  996. package/.forge/worktrees/pipeline-44a94121/app/api/auth/[...nextauth]/route.ts +3 -0
  997. package/.forge/worktrees/pipeline-44a94121/app/api/auth/verify/route.ts +46 -0
  998. package/.forge/worktrees/pipeline-44a94121/app/api/claude/[id]/route.ts +31 -0
  999. package/.forge/worktrees/pipeline-44a94121/app/api/claude/[id]/stream/route.ts +63 -0
  1000. package/.forge/worktrees/pipeline-44a94121/app/api/claude/route.ts +28 -0
  1001. package/.forge/worktrees/pipeline-44a94121/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  1002. package/.forge/worktrees/pipeline-44a94121/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  1003. package/.forge/worktrees/pipeline-44a94121/app/api/claude-sessions/[projectName]/route.ts +37 -0
  1004. package/.forge/worktrees/pipeline-44a94121/app/api/claude-sessions/sync/route.ts +17 -0
  1005. package/.forge/worktrees/pipeline-44a94121/app/api/claude-templates/route.ts +145 -0
  1006. package/.forge/worktrees/pipeline-44a94121/app/api/code/route.ts +299 -0
  1007. package/.forge/worktrees/pipeline-44a94121/app/api/delivery/[id]/route.ts +62 -0
  1008. package/.forge/worktrees/pipeline-44a94121/app/api/delivery/route.ts +40 -0
  1009. package/.forge/worktrees/pipeline-44a94121/app/api/detect-cli/route.ts +46 -0
  1010. package/.forge/worktrees/pipeline-44a94121/app/api/docs/route.ts +176 -0
  1011. package/.forge/worktrees/pipeline-44a94121/app/api/docs/sessions/route.ts +54 -0
  1012. package/.forge/worktrees/pipeline-44a94121/app/api/favorites/route.ts +26 -0
  1013. package/.forge/worktrees/pipeline-44a94121/app/api/flows/route.ts +6 -0
  1014. package/.forge/worktrees/pipeline-44a94121/app/api/flows/run/route.ts +19 -0
  1015. package/.forge/worktrees/pipeline-44a94121/app/api/git/route.ts +149 -0
  1016. package/.forge/worktrees/pipeline-44a94121/app/api/help/route.ts +84 -0
  1017. package/.forge/worktrees/pipeline-44a94121/app/api/issue-scanner/route.ts +116 -0
  1018. package/.forge/worktrees/pipeline-44a94121/app/api/logs/route.ts +100 -0
  1019. package/.forge/worktrees/pipeline-44a94121/app/api/mobile-chat/route.ts +115 -0
  1020. package/.forge/worktrees/pipeline-44a94121/app/api/monitor/route.ts +74 -0
  1021. package/.forge/worktrees/pipeline-44a94121/app/api/notifications/route.ts +42 -0
  1022. package/.forge/worktrees/pipeline-44a94121/app/api/notify/test/route.ts +33 -0
  1023. package/.forge/worktrees/pipeline-44a94121/app/api/online/route.ts +40 -0
  1024. package/.forge/worktrees/pipeline-44a94121/app/api/pipelines/[id]/route.ts +41 -0
  1025. package/.forge/worktrees/pipeline-44a94121/app/api/pipelines/route.ts +90 -0
  1026. package/.forge/worktrees/pipeline-44a94121/app/api/plugins/route.ts +75 -0
  1027. package/.forge/worktrees/pipeline-44a94121/app/api/preview/[...path]/route.ts +64 -0
  1028. package/.forge/worktrees/pipeline-44a94121/app/api/preview/route.ts +156 -0
  1029. package/.forge/worktrees/pipeline-44a94121/app/api/project-pipelines/route.ts +91 -0
  1030. package/.forge/worktrees/pipeline-44a94121/app/api/project-sessions/route.ts +61 -0
  1031. package/.forge/worktrees/pipeline-44a94121/app/api/projects/route.ts +26 -0
  1032. package/.forge/worktrees/pipeline-44a94121/app/api/sessions/[id]/chat/route.ts +64 -0
  1033. package/.forge/worktrees/pipeline-44a94121/app/api/sessions/[id]/messages/route.ts +9 -0
  1034. package/.forge/worktrees/pipeline-44a94121/app/api/sessions/[id]/route.ts +17 -0
  1035. package/.forge/worktrees/pipeline-44a94121/app/api/sessions/route.ts +20 -0
  1036. package/.forge/worktrees/pipeline-44a94121/app/api/settings/route.ts +64 -0
  1037. package/.forge/worktrees/pipeline-44a94121/app/api/skills/local/route.ts +228 -0
  1038. package/.forge/worktrees/pipeline-44a94121/app/api/skills/route.ts +182 -0
  1039. package/.forge/worktrees/pipeline-44a94121/app/api/smith-templates/route.ts +81 -0
  1040. package/.forge/worktrees/pipeline-44a94121/app/api/status/route.ts +12 -0
  1041. package/.forge/worktrees/pipeline-44a94121/app/api/tabs/route.ts +25 -0
  1042. package/.forge/worktrees/pipeline-44a94121/app/api/tasks/[id]/route.ts +51 -0
  1043. package/.forge/worktrees/pipeline-44a94121/app/api/tasks/[id]/stream/route.ts +77 -0
  1044. package/.forge/worktrees/pipeline-44a94121/app/api/tasks/link/route.ts +37 -0
  1045. package/.forge/worktrees/pipeline-44a94121/app/api/tasks/route.ts +44 -0
  1046. package/.forge/worktrees/pipeline-44a94121/app/api/tasks/session/route.ts +14 -0
  1047. package/.forge/worktrees/pipeline-44a94121/app/api/telegram/route.ts +23 -0
  1048. package/.forge/worktrees/pipeline-44a94121/app/api/templates/route.ts +6 -0
  1049. package/.forge/worktrees/pipeline-44a94121/app/api/terminal-bell/route.ts +35 -0
  1050. package/.forge/worktrees/pipeline-44a94121/app/api/terminal-cwd/route.ts +19 -0
  1051. package/.forge/worktrees/pipeline-44a94121/app/api/terminal-state/route.ts +15 -0
  1052. package/.forge/worktrees/pipeline-44a94121/app/api/tunnel/route.ts +26 -0
  1053. package/.forge/worktrees/pipeline-44a94121/app/api/upgrade/route.ts +43 -0
  1054. package/.forge/worktrees/pipeline-44a94121/app/api/usage/route.ts +20 -0
  1055. package/.forge/worktrees/pipeline-44a94121/app/api/version/route.ts +78 -0
  1056. package/.forge/worktrees/pipeline-44a94121/app/api/watchers/route.ts +33 -0
  1057. package/.forge/worktrees/pipeline-44a94121/app/api/workspace/[id]/agents/route.ts +35 -0
  1058. package/.forge/worktrees/pipeline-44a94121/app/api/workspace/[id]/memory/route.ts +23 -0
  1059. package/.forge/worktrees/pipeline-44a94121/app/api/workspace/[id]/smith/route.ts +22 -0
  1060. package/.forge/worktrees/pipeline-44a94121/app/api/workspace/[id]/stream/route.ts +28 -0
  1061. package/.forge/worktrees/pipeline-44a94121/app/api/workspace/route.ts +100 -0
  1062. package/.forge/worktrees/pipeline-44a94121/app/global-error.tsx +21 -0
  1063. package/.forge/worktrees/pipeline-44a94121/app/globals.css +52 -0
  1064. package/.forge/worktrees/pipeline-44a94121/app/icon.ico +0 -0
  1065. package/.forge/worktrees/pipeline-44a94121/app/icon.png +0 -0
  1066. package/.forge/worktrees/pipeline-44a94121/app/icon.svg +106 -0
  1067. package/.forge/worktrees/pipeline-44a94121/app/layout.tsx +17 -0
  1068. package/.forge/worktrees/pipeline-44a94121/app/login/LoginForm.tsx +96 -0
  1069. package/.forge/worktrees/pipeline-44a94121/app/login/page.tsx +10 -0
  1070. package/.forge/worktrees/pipeline-44a94121/app/mobile/page.tsx +9 -0
  1071. package/.forge/worktrees/pipeline-44a94121/app/page.tsx +21 -0
  1072. package/.forge/worktrees/pipeline-44a94121/bin/forge-server.mjs +484 -0
  1073. package/.forge/worktrees/pipeline-44a94121/check-forge-status.sh +71 -0
  1074. package/.forge/worktrees/pipeline-44a94121/cli/mw.ts +579 -0
  1075. package/.forge/worktrees/pipeline-44a94121/components/BrowserPanel.tsx +175 -0
  1076. package/.forge/worktrees/pipeline-44a94121/components/ChatPanel.tsx +191 -0
  1077. package/.forge/worktrees/pipeline-44a94121/components/ClaudeTerminal.tsx +267 -0
  1078. package/.forge/worktrees/pipeline-44a94121/components/CodeViewer.tsx +787 -0
  1079. package/.forge/worktrees/pipeline-44a94121/components/ConversationEditor.tsx +411 -0
  1080. package/.forge/worktrees/pipeline-44a94121/components/ConversationGraphView.tsx +347 -0
  1081. package/.forge/worktrees/pipeline-44a94121/components/ConversationTerminalView.tsx +303 -0
  1082. package/.forge/worktrees/pipeline-44a94121/components/Dashboard.tsx +807 -0
  1083. package/.forge/worktrees/pipeline-44a94121/components/DashboardWrapper.tsx +9 -0
  1084. package/.forge/worktrees/pipeline-44a94121/components/DeliveryFlowEditor.tsx +491 -0
  1085. package/.forge/worktrees/pipeline-44a94121/components/DeliveryList.tsx +230 -0
  1086. package/.forge/worktrees/pipeline-44a94121/components/DeliveryWorkspace.tsx +589 -0
  1087. package/.forge/worktrees/pipeline-44a94121/components/DocTerminal.tsx +187 -0
  1088. package/.forge/worktrees/pipeline-44a94121/components/DocsViewer.tsx +574 -0
  1089. package/.forge/worktrees/pipeline-44a94121/components/HelpDialog.tsx +169 -0
  1090. package/.forge/worktrees/pipeline-44a94121/components/HelpTerminal.tsx +141 -0
  1091. package/.forge/worktrees/pipeline-44a94121/components/InlinePipelineView.tsx +111 -0
  1092. package/.forge/worktrees/pipeline-44a94121/components/LogViewer.tsx +194 -0
  1093. package/.forge/worktrees/pipeline-44a94121/components/MarkdownContent.tsx +73 -0
  1094. package/.forge/worktrees/pipeline-44a94121/components/MobileView.tsx +385 -0
  1095. package/.forge/worktrees/pipeline-44a94121/components/MonitorPanel.tsx +122 -0
  1096. package/.forge/worktrees/pipeline-44a94121/components/NewSessionModal.tsx +93 -0
  1097. package/.forge/worktrees/pipeline-44a94121/components/NewTaskModal.tsx +492 -0
  1098. package/.forge/worktrees/pipeline-44a94121/components/PipelineEditor.tsx +570 -0
  1099. package/.forge/worktrees/pipeline-44a94121/components/PipelineView.tsx +1018 -0
  1100. package/.forge/worktrees/pipeline-44a94121/components/PluginsPanel.tsx +472 -0
  1101. package/.forge/worktrees/pipeline-44a94121/components/ProjectDetail.tsx +1618 -0
  1102. package/.forge/worktrees/pipeline-44a94121/components/ProjectList.tsx +108 -0
  1103. package/.forge/worktrees/pipeline-44a94121/components/ProjectManager.tsx +401 -0
  1104. package/.forge/worktrees/pipeline-44a94121/components/SessionList.tsx +74 -0
  1105. package/.forge/worktrees/pipeline-44a94121/components/SessionView.tsx +726 -0
  1106. package/.forge/worktrees/pipeline-44a94121/components/SettingsModal.tsx +1647 -0
  1107. package/.forge/worktrees/pipeline-44a94121/components/SkillsPanel.tsx +969 -0
  1108. package/.forge/worktrees/pipeline-44a94121/components/StatusBar.tsx +99 -0
  1109. package/.forge/worktrees/pipeline-44a94121/components/TabBar.tsx +46 -0
  1110. package/.forge/worktrees/pipeline-44a94121/components/TaskBoard.tsx +113 -0
  1111. package/.forge/worktrees/pipeline-44a94121/components/TaskDetail.tsx +372 -0
  1112. package/.forge/worktrees/pipeline-44a94121/components/TerminalLauncher.tsx +398 -0
  1113. package/.forge/worktrees/pipeline-44a94121/components/TunnelToggle.tsx +206 -0
  1114. package/.forge/worktrees/pipeline-44a94121/components/UsagePanel.tsx +207 -0
  1115. package/.forge/worktrees/pipeline-44a94121/components/WebTerminal.tsx +1683 -0
  1116. package/.forge/worktrees/pipeline-44a94121/components/WorkspaceTree.tsx +221 -0
  1117. package/.forge/worktrees/pipeline-44a94121/components/WorkspaceView.tsx +4048 -0
  1118. package/.forge/worktrees/pipeline-44a94121/dev-test.sh +5 -0
  1119. package/.forge/worktrees/pipeline-44a94121/docs/Forge_Memory_Layer_Design.docx +0 -0
  1120. package/.forge/worktrees/pipeline-44a94121/docs/Forge_Strategy_Research_2026.docx +0 -0
  1121. package/.forge/worktrees/pipeline-44a94121/docs/LOCAL-DEPLOY.md +144 -0
  1122. package/.forge/worktrees/pipeline-44a94121/docs/roadmap-multi-agent-workflow.md +330 -0
  1123. package/.forge/worktrees/pipeline-44a94121/forge-logo.png +0 -0
  1124. package/.forge/worktrees/pipeline-44a94121/forge-logo.svg +106 -0
  1125. package/.forge/worktrees/pipeline-44a94121/hooks/useSidebarResize.ts +52 -0
  1126. package/.forge/worktrees/pipeline-44a94121/install.sh +29 -0
  1127. package/.forge/worktrees/pipeline-44a94121/instrumentation.ts +35 -0
  1128. package/.forge/worktrees/pipeline-44a94121/lib/agents/claude-adapter.ts +104 -0
  1129. package/.forge/worktrees/pipeline-44a94121/lib/agents/generic-adapter.ts +64 -0
  1130. package/.forge/worktrees/pipeline-44a94121/lib/agents/index.ts +245 -0
  1131. package/.forge/worktrees/pipeline-44a94121/lib/agents/types.ts +70 -0
  1132. package/.forge/worktrees/pipeline-44a94121/lib/artifacts.ts +106 -0
  1133. package/.forge/worktrees/pipeline-44a94121/lib/auth.ts +62 -0
  1134. package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/docker.yaml +70 -0
  1135. package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/http.yaml +66 -0
  1136. package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/jenkins.yaml +92 -0
  1137. package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/llm-vision.yaml +85 -0
  1138. package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/playwright.yaml +111 -0
  1139. package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/shell-command.yaml +60 -0
  1140. package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/slack.yaml +48 -0
  1141. package/.forge/worktrees/pipeline-44a94121/lib/builtin-plugins/webhook.yaml +56 -0
  1142. package/.forge/worktrees/pipeline-44a94121/lib/claude-process.ts +351 -0
  1143. package/.forge/worktrees/pipeline-44a94121/lib/claude-sessions.ts +266 -0
  1144. package/.forge/worktrees/pipeline-44a94121/lib/claude-templates.ts +227 -0
  1145. package/.forge/worktrees/pipeline-44a94121/lib/cloudflared.ts +424 -0
  1146. package/.forge/worktrees/pipeline-44a94121/lib/crypto.ts +67 -0
  1147. package/.forge/worktrees/pipeline-44a94121/lib/delivery.ts +787 -0
  1148. package/.forge/worktrees/pipeline-44a94121/lib/dirs.ts +99 -0
  1149. package/.forge/worktrees/pipeline-44a94121/lib/flows.ts +86 -0
  1150. package/.forge/worktrees/pipeline-44a94121/lib/forge-mcp-server.ts +717 -0
  1151. package/.forge/worktrees/pipeline-44a94121/lib/forge-skills/forge-inbox.md +38 -0
  1152. package/.forge/worktrees/pipeline-44a94121/lib/forge-skills/forge-send.md +47 -0
  1153. package/.forge/worktrees/pipeline-44a94121/lib/forge-skills/forge-status.md +32 -0
  1154. package/.forge/worktrees/pipeline-44a94121/lib/forge-skills/forge-workspace-sync.md +37 -0
  1155. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/00-overview.md +40 -0
  1156. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/01-settings.md +194 -0
  1157. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/02-telegram.md +41 -0
  1158. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/03-tunnel.md +31 -0
  1159. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/04-tasks.md +52 -0
  1160. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/05-pipelines.md +460 -0
  1161. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/06-skills.md +43 -0
  1162. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/07-projects.md +73 -0
  1163. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/08-rules.md +53 -0
  1164. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/09-issue-autofix.md +55 -0
  1165. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/10-troubleshooting.md +89 -0
  1166. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/11-workspace.md +810 -0
  1167. package/.forge/worktrees/pipeline-44a94121/lib/help-docs/CLAUDE.md +62 -0
  1168. package/.forge/worktrees/pipeline-44a94121/lib/init.ts +266 -0
  1169. package/.forge/worktrees/pipeline-44a94121/lib/issue-scanner.ts +298 -0
  1170. package/.forge/worktrees/pipeline-44a94121/lib/logger.ts +79 -0
  1171. package/.forge/worktrees/pipeline-44a94121/lib/notifications.ts +75 -0
  1172. package/.forge/worktrees/pipeline-44a94121/lib/notify.ts +108 -0
  1173. package/.forge/worktrees/pipeline-44a94121/lib/password.ts +97 -0
  1174. package/.forge/worktrees/pipeline-44a94121/lib/pipeline-scheduler.ts +373 -0
  1175. package/.forge/worktrees/pipeline-44a94121/lib/pipeline.ts +1441 -0
  1176. package/.forge/worktrees/pipeline-44a94121/lib/plugins/executor.ts +347 -0
  1177. package/.forge/worktrees/pipeline-44a94121/lib/plugins/registry.ts +228 -0
  1178. package/.forge/worktrees/pipeline-44a94121/lib/plugins/types.ts +103 -0
  1179. package/.forge/worktrees/pipeline-44a94121/lib/project-sessions.ts +53 -0
  1180. package/.forge/worktrees/pipeline-44a94121/lib/projects.ts +86 -0
  1181. package/.forge/worktrees/pipeline-44a94121/lib/session-manager.ts +156 -0
  1182. package/.forge/worktrees/pipeline-44a94121/lib/session-utils.ts +53 -0
  1183. package/.forge/worktrees/pipeline-44a94121/lib/session-watcher.ts +345 -0
  1184. package/.forge/worktrees/pipeline-44a94121/lib/settings.ts +195 -0
  1185. package/.forge/worktrees/pipeline-44a94121/lib/skills.ts +458 -0
  1186. package/.forge/worktrees/pipeline-44a94121/lib/task-manager.ts +949 -0
  1187. package/.forge/worktrees/pipeline-44a94121/lib/telegram-bot.ts +1477 -0
  1188. package/.forge/worktrees/pipeline-44a94121/lib/telegram-standalone.ts +83 -0
  1189. package/.forge/worktrees/pipeline-44a94121/lib/terminal-server.ts +70 -0
  1190. package/.forge/worktrees/pipeline-44a94121/lib/terminal-standalone.ts +421 -0
  1191. package/.forge/worktrees/pipeline-44a94121/lib/usage-scanner.ts +249 -0
  1192. package/.forge/worktrees/pipeline-44a94121/lib/workspace/__tests__/state-machine.test.ts +388 -0
  1193. package/.forge/worktrees/pipeline-44a94121/lib/workspace/__tests__/workspace.test.ts +311 -0
  1194. package/.forge/worktrees/pipeline-44a94121/lib/workspace/agent-bus.ts +416 -0
  1195. package/.forge/worktrees/pipeline-44a94121/lib/workspace/agent-worker.ts +655 -0
  1196. package/.forge/worktrees/pipeline-44a94121/lib/workspace/backends/api-backend.ts +262 -0
  1197. package/.forge/worktrees/pipeline-44a94121/lib/workspace/backends/cli-backend.ts +491 -0
  1198. package/.forge/worktrees/pipeline-44a94121/lib/workspace/index.ts +82 -0
  1199. package/.forge/worktrees/pipeline-44a94121/lib/workspace/manager.ts +136 -0
  1200. package/.forge/worktrees/pipeline-44a94121/lib/workspace/orchestrator.ts +3400 -0
  1201. package/.forge/worktrees/pipeline-44a94121/lib/workspace/persistence.ts +309 -0
  1202. package/.forge/worktrees/pipeline-44a94121/lib/workspace/presets.ts +649 -0
  1203. package/.forge/worktrees/pipeline-44a94121/lib/workspace/requests.ts +287 -0
  1204. package/.forge/worktrees/pipeline-44a94121/lib/workspace/session-monitor.ts +240 -0
  1205. package/.forge/worktrees/pipeline-44a94121/lib/workspace/skill-installer.ts +275 -0
  1206. package/.forge/worktrees/pipeline-44a94121/lib/workspace/smith-memory.ts +498 -0
  1207. package/.forge/worktrees/pipeline-44a94121/lib/workspace/types.ts +241 -0
  1208. package/.forge/worktrees/pipeline-44a94121/lib/workspace/watch-manager.ts +560 -0
  1209. package/.forge/worktrees/pipeline-44a94121/lib/workspace-standalone.ts +911 -0
  1210. package/.forge/worktrees/pipeline-44a94121/middleware.ts +51 -0
  1211. package/.forge/worktrees/pipeline-44a94121/next.config.ts +26 -0
  1212. package/.forge/worktrees/pipeline-44a94121/package.json +74 -0
  1213. package/.forge/worktrees/pipeline-44a94121/pnpm-lock.yaml +3719 -0
  1214. package/.forge/worktrees/pipeline-44a94121/pnpm-workspace.yaml +1 -0
  1215. package/.forge/worktrees/pipeline-44a94121/postcss.config.mjs +7 -0
  1216. package/.forge/worktrees/pipeline-44a94121/publish.sh +133 -0
  1217. package/.forge/worktrees/pipeline-44a94121/scripts/bench/README.md +66 -0
  1218. package/.forge/worktrees/pipeline-44a94121/scripts/bench/results/.gitignore +2 -0
  1219. package/.forge/worktrees/pipeline-44a94121/scripts/bench/run.ts +635 -0
  1220. package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/01-text-utils/task.md +26 -0
  1221. package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  1222. package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  1223. package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/02-pagination/task.md +48 -0
  1224. package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  1225. package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  1226. package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  1227. package/.forge/worktrees/pipeline-44a94121/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  1228. package/.forge/worktrees/pipeline-44a94121/scripts/verify-usage.ts +178 -0
  1229. package/.forge/worktrees/pipeline-44a94121/src/config/index.ts +129 -0
  1230. package/.forge/worktrees/pipeline-44a94121/src/core/db/database.ts +259 -0
  1231. package/.forge/worktrees/pipeline-44a94121/src/core/memory/strategy.ts +32 -0
  1232. package/.forge/worktrees/pipeline-44a94121/src/core/providers/chat.ts +65 -0
  1233. package/.forge/worktrees/pipeline-44a94121/src/core/providers/registry.ts +60 -0
  1234. package/.forge/worktrees/pipeline-44a94121/src/core/session/manager.ts +190 -0
  1235. package/.forge/worktrees/pipeline-44a94121/src/types/index.ts +129 -0
  1236. package/.forge/worktrees/pipeline-44a94121/start.sh +31 -0
  1237. package/.forge/worktrees/pipeline-44a94121/templates/smith-lead.json +45 -0
  1238. package/.forge/worktrees/pipeline-44a94121/tsconfig.json +42 -0
  1239. package/.forge/worktrees/pipeline-d1757a50/CLAUDE.md +86 -0
  1240. package/.forge/worktrees/pipeline-d1757a50/README.md +136 -0
  1241. package/.forge/worktrees/pipeline-d1757a50/RELEASE_NOTES.md +11 -0
  1242. package/.forge/worktrees/pipeline-d1757a50/app/api/agents/route.ts +17 -0
  1243. package/.forge/worktrees/pipeline-d1757a50/app/api/auth/[...nextauth]/route.ts +3 -0
  1244. package/.forge/worktrees/pipeline-d1757a50/app/api/auth/verify/route.ts +46 -0
  1245. package/.forge/worktrees/pipeline-d1757a50/app/api/claude/[id]/route.ts +31 -0
  1246. package/.forge/worktrees/pipeline-d1757a50/app/api/claude/[id]/stream/route.ts +63 -0
  1247. package/.forge/worktrees/pipeline-d1757a50/app/api/claude/route.ts +28 -0
  1248. package/.forge/worktrees/pipeline-d1757a50/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  1249. package/.forge/worktrees/pipeline-d1757a50/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  1250. package/.forge/worktrees/pipeline-d1757a50/app/api/claude-sessions/[projectName]/route.ts +37 -0
  1251. package/.forge/worktrees/pipeline-d1757a50/app/api/claude-sessions/sync/route.ts +17 -0
  1252. package/.forge/worktrees/pipeline-d1757a50/app/api/claude-templates/route.ts +145 -0
  1253. package/.forge/worktrees/pipeline-d1757a50/app/api/code/route.ts +299 -0
  1254. package/.forge/worktrees/pipeline-d1757a50/app/api/delivery/[id]/route.ts +62 -0
  1255. package/.forge/worktrees/pipeline-d1757a50/app/api/delivery/route.ts +40 -0
  1256. package/.forge/worktrees/pipeline-d1757a50/app/api/detect-cli/route.ts +46 -0
  1257. package/.forge/worktrees/pipeline-d1757a50/app/api/docs/route.ts +176 -0
  1258. package/.forge/worktrees/pipeline-d1757a50/app/api/docs/sessions/route.ts +54 -0
  1259. package/.forge/worktrees/pipeline-d1757a50/app/api/favorites/route.ts +26 -0
  1260. package/.forge/worktrees/pipeline-d1757a50/app/api/flows/route.ts +6 -0
  1261. package/.forge/worktrees/pipeline-d1757a50/app/api/flows/run/route.ts +19 -0
  1262. package/.forge/worktrees/pipeline-d1757a50/app/api/git/route.ts +149 -0
  1263. package/.forge/worktrees/pipeline-d1757a50/app/api/help/route.ts +84 -0
  1264. package/.forge/worktrees/pipeline-d1757a50/app/api/issue-scanner/route.ts +116 -0
  1265. package/.forge/worktrees/pipeline-d1757a50/app/api/logs/route.ts +100 -0
  1266. package/.forge/worktrees/pipeline-d1757a50/app/api/mobile-chat/route.ts +115 -0
  1267. package/.forge/worktrees/pipeline-d1757a50/app/api/monitor/route.ts +74 -0
  1268. package/.forge/worktrees/pipeline-d1757a50/app/api/notifications/route.ts +42 -0
  1269. package/.forge/worktrees/pipeline-d1757a50/app/api/notify/test/route.ts +33 -0
  1270. package/.forge/worktrees/pipeline-d1757a50/app/api/online/route.ts +40 -0
  1271. package/.forge/worktrees/pipeline-d1757a50/app/api/pipelines/[id]/route.ts +41 -0
  1272. package/.forge/worktrees/pipeline-d1757a50/app/api/pipelines/route.ts +90 -0
  1273. package/.forge/worktrees/pipeline-d1757a50/app/api/plugins/route.ts +75 -0
  1274. package/.forge/worktrees/pipeline-d1757a50/app/api/preview/[...path]/route.ts +64 -0
  1275. package/.forge/worktrees/pipeline-d1757a50/app/api/preview/route.ts +156 -0
  1276. package/.forge/worktrees/pipeline-d1757a50/app/api/project-pipelines/route.ts +91 -0
  1277. package/.forge/worktrees/pipeline-d1757a50/app/api/project-sessions/route.ts +61 -0
  1278. package/.forge/worktrees/pipeline-d1757a50/app/api/projects/route.ts +26 -0
  1279. package/.forge/worktrees/pipeline-d1757a50/app/api/sessions/[id]/chat/route.ts +64 -0
  1280. package/.forge/worktrees/pipeline-d1757a50/app/api/sessions/[id]/messages/route.ts +9 -0
  1281. package/.forge/worktrees/pipeline-d1757a50/app/api/sessions/[id]/route.ts +17 -0
  1282. package/.forge/worktrees/pipeline-d1757a50/app/api/sessions/route.ts +20 -0
  1283. package/.forge/worktrees/pipeline-d1757a50/app/api/settings/route.ts +64 -0
  1284. package/.forge/worktrees/pipeline-d1757a50/app/api/skills/local/route.ts +228 -0
  1285. package/.forge/worktrees/pipeline-d1757a50/app/api/skills/route.ts +182 -0
  1286. package/.forge/worktrees/pipeline-d1757a50/app/api/smith-templates/route.ts +81 -0
  1287. package/.forge/worktrees/pipeline-d1757a50/app/api/status/route.ts +12 -0
  1288. package/.forge/worktrees/pipeline-d1757a50/app/api/tabs/route.ts +25 -0
  1289. package/.forge/worktrees/pipeline-d1757a50/app/api/tasks/[id]/route.ts +51 -0
  1290. package/.forge/worktrees/pipeline-d1757a50/app/api/tasks/[id]/stream/route.ts +77 -0
  1291. package/.forge/worktrees/pipeline-d1757a50/app/api/tasks/link/route.ts +37 -0
  1292. package/.forge/worktrees/pipeline-d1757a50/app/api/tasks/route.ts +44 -0
  1293. package/.forge/worktrees/pipeline-d1757a50/app/api/tasks/session/route.ts +14 -0
  1294. package/.forge/worktrees/pipeline-d1757a50/app/api/telegram/route.ts +23 -0
  1295. package/.forge/worktrees/pipeline-d1757a50/app/api/templates/route.ts +6 -0
  1296. package/.forge/worktrees/pipeline-d1757a50/app/api/terminal-bell/route.ts +35 -0
  1297. package/.forge/worktrees/pipeline-d1757a50/app/api/terminal-cwd/route.ts +19 -0
  1298. package/.forge/worktrees/pipeline-d1757a50/app/api/terminal-state/route.ts +15 -0
  1299. package/.forge/worktrees/pipeline-d1757a50/app/api/tunnel/route.ts +26 -0
  1300. package/.forge/worktrees/pipeline-d1757a50/app/api/upgrade/route.ts +43 -0
  1301. package/.forge/worktrees/pipeline-d1757a50/app/api/usage/route.ts +20 -0
  1302. package/.forge/worktrees/pipeline-d1757a50/app/api/version/route.ts +78 -0
  1303. package/.forge/worktrees/pipeline-d1757a50/app/api/watchers/route.ts +33 -0
  1304. package/.forge/worktrees/pipeline-d1757a50/app/api/workspace/[id]/agents/route.ts +35 -0
  1305. package/.forge/worktrees/pipeline-d1757a50/app/api/workspace/[id]/memory/route.ts +23 -0
  1306. package/.forge/worktrees/pipeline-d1757a50/app/api/workspace/[id]/smith/route.ts +22 -0
  1307. package/.forge/worktrees/pipeline-d1757a50/app/api/workspace/[id]/stream/route.ts +28 -0
  1308. package/.forge/worktrees/pipeline-d1757a50/app/api/workspace/route.ts +100 -0
  1309. package/.forge/worktrees/pipeline-d1757a50/app/global-error.tsx +21 -0
  1310. package/.forge/worktrees/pipeline-d1757a50/app/globals.css +52 -0
  1311. package/.forge/worktrees/pipeline-d1757a50/app/icon.ico +0 -0
  1312. package/.forge/worktrees/pipeline-d1757a50/app/icon.png +0 -0
  1313. package/.forge/worktrees/pipeline-d1757a50/app/icon.svg +106 -0
  1314. package/.forge/worktrees/pipeline-d1757a50/app/layout.tsx +17 -0
  1315. package/.forge/worktrees/pipeline-d1757a50/app/login/LoginForm.tsx +96 -0
  1316. package/.forge/worktrees/pipeline-d1757a50/app/login/page.tsx +10 -0
  1317. package/.forge/worktrees/pipeline-d1757a50/app/mobile/page.tsx +9 -0
  1318. package/.forge/worktrees/pipeline-d1757a50/app/page.tsx +21 -0
  1319. package/.forge/worktrees/pipeline-d1757a50/bin/forge-server.mjs +484 -0
  1320. package/.forge/worktrees/pipeline-d1757a50/check-forge-status.sh +71 -0
  1321. package/.forge/worktrees/pipeline-d1757a50/cli/mw.ts +579 -0
  1322. package/.forge/worktrees/pipeline-d1757a50/components/BrowserPanel.tsx +175 -0
  1323. package/.forge/worktrees/pipeline-d1757a50/components/ChatPanel.tsx +191 -0
  1324. package/.forge/worktrees/pipeline-d1757a50/components/ClaudeTerminal.tsx +267 -0
  1325. package/.forge/worktrees/pipeline-d1757a50/components/CodeViewer.tsx +787 -0
  1326. package/.forge/worktrees/pipeline-d1757a50/components/ConversationEditor.tsx +411 -0
  1327. package/.forge/worktrees/pipeline-d1757a50/components/ConversationGraphView.tsx +347 -0
  1328. package/.forge/worktrees/pipeline-d1757a50/components/ConversationTerminalView.tsx +303 -0
  1329. package/.forge/worktrees/pipeline-d1757a50/components/Dashboard.tsx +807 -0
  1330. package/.forge/worktrees/pipeline-d1757a50/components/DashboardWrapper.tsx +9 -0
  1331. package/.forge/worktrees/pipeline-d1757a50/components/DeliveryFlowEditor.tsx +491 -0
  1332. package/.forge/worktrees/pipeline-d1757a50/components/DeliveryList.tsx +230 -0
  1333. package/.forge/worktrees/pipeline-d1757a50/components/DeliveryWorkspace.tsx +589 -0
  1334. package/.forge/worktrees/pipeline-d1757a50/components/DocTerminal.tsx +187 -0
  1335. package/.forge/worktrees/pipeline-d1757a50/components/DocsViewer.tsx +574 -0
  1336. package/.forge/worktrees/pipeline-d1757a50/components/HelpDialog.tsx +169 -0
  1337. package/.forge/worktrees/pipeline-d1757a50/components/HelpTerminal.tsx +141 -0
  1338. package/.forge/worktrees/pipeline-d1757a50/components/InlinePipelineView.tsx +111 -0
  1339. package/.forge/worktrees/pipeline-d1757a50/components/LogViewer.tsx +194 -0
  1340. package/.forge/worktrees/pipeline-d1757a50/components/MarkdownContent.tsx +73 -0
  1341. package/.forge/worktrees/pipeline-d1757a50/components/MobileView.tsx +385 -0
  1342. package/.forge/worktrees/pipeline-d1757a50/components/MonitorPanel.tsx +122 -0
  1343. package/.forge/worktrees/pipeline-d1757a50/components/NewSessionModal.tsx +93 -0
  1344. package/.forge/worktrees/pipeline-d1757a50/components/NewTaskModal.tsx +492 -0
  1345. package/.forge/worktrees/pipeline-d1757a50/components/PipelineEditor.tsx +570 -0
  1346. package/.forge/worktrees/pipeline-d1757a50/components/PipelineView.tsx +1018 -0
  1347. package/.forge/worktrees/pipeline-d1757a50/components/PluginsPanel.tsx +472 -0
  1348. package/.forge/worktrees/pipeline-d1757a50/components/ProjectDetail.tsx +1618 -0
  1349. package/.forge/worktrees/pipeline-d1757a50/components/ProjectList.tsx +108 -0
  1350. package/.forge/worktrees/pipeline-d1757a50/components/ProjectManager.tsx +401 -0
  1351. package/.forge/worktrees/pipeline-d1757a50/components/SessionList.tsx +74 -0
  1352. package/.forge/worktrees/pipeline-d1757a50/components/SessionView.tsx +726 -0
  1353. package/.forge/worktrees/pipeline-d1757a50/components/SettingsModal.tsx +1647 -0
  1354. package/.forge/worktrees/pipeline-d1757a50/components/SkillsPanel.tsx +969 -0
  1355. package/.forge/worktrees/pipeline-d1757a50/components/StatusBar.tsx +99 -0
  1356. package/.forge/worktrees/pipeline-d1757a50/components/TabBar.tsx +46 -0
  1357. package/.forge/worktrees/pipeline-d1757a50/components/TaskBoard.tsx +113 -0
  1358. package/.forge/worktrees/pipeline-d1757a50/components/TaskDetail.tsx +372 -0
  1359. package/.forge/worktrees/pipeline-d1757a50/components/TerminalLauncher.tsx +398 -0
  1360. package/.forge/worktrees/pipeline-d1757a50/components/TunnelToggle.tsx +206 -0
  1361. package/.forge/worktrees/pipeline-d1757a50/components/UsagePanel.tsx +207 -0
  1362. package/.forge/worktrees/pipeline-d1757a50/components/WebTerminal.tsx +1683 -0
  1363. package/.forge/worktrees/pipeline-d1757a50/components/WorkspaceTree.tsx +221 -0
  1364. package/.forge/worktrees/pipeline-d1757a50/components/WorkspaceView.tsx +4048 -0
  1365. package/.forge/worktrees/pipeline-d1757a50/dev-test.sh +5 -0
  1366. package/.forge/worktrees/pipeline-d1757a50/docs/Forge_Memory_Layer_Design.docx +0 -0
  1367. package/.forge/worktrees/pipeline-d1757a50/docs/Forge_Strategy_Research_2026.docx +0 -0
  1368. package/.forge/worktrees/pipeline-d1757a50/docs/LOCAL-DEPLOY.md +144 -0
  1369. package/.forge/worktrees/pipeline-d1757a50/docs/roadmap-multi-agent-workflow.md +330 -0
  1370. package/.forge/worktrees/pipeline-d1757a50/forge-logo.png +0 -0
  1371. package/.forge/worktrees/pipeline-d1757a50/forge-logo.svg +106 -0
  1372. package/.forge/worktrees/pipeline-d1757a50/hooks/useSidebarResize.ts +52 -0
  1373. package/.forge/worktrees/pipeline-d1757a50/install.sh +29 -0
  1374. package/.forge/worktrees/pipeline-d1757a50/instrumentation.ts +35 -0
  1375. package/.forge/worktrees/pipeline-d1757a50/lib/agents/claude-adapter.ts +104 -0
  1376. package/.forge/worktrees/pipeline-d1757a50/lib/agents/generic-adapter.ts +64 -0
  1377. package/.forge/worktrees/pipeline-d1757a50/lib/agents/index.ts +245 -0
  1378. package/.forge/worktrees/pipeline-d1757a50/lib/agents/types.ts +70 -0
  1379. package/.forge/worktrees/pipeline-d1757a50/lib/artifacts.ts +106 -0
  1380. package/.forge/worktrees/pipeline-d1757a50/lib/auth.ts +62 -0
  1381. package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/docker.yaml +70 -0
  1382. package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/http.yaml +66 -0
  1383. package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/jenkins.yaml +92 -0
  1384. package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/llm-vision.yaml +85 -0
  1385. package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/playwright.yaml +111 -0
  1386. package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/shell-command.yaml +60 -0
  1387. package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/slack.yaml +48 -0
  1388. package/.forge/worktrees/pipeline-d1757a50/lib/builtin-plugins/webhook.yaml +56 -0
  1389. package/.forge/worktrees/pipeline-d1757a50/lib/claude-process.ts +361 -0
  1390. package/.forge/worktrees/pipeline-d1757a50/lib/claude-sessions.ts +266 -0
  1391. package/.forge/worktrees/pipeline-d1757a50/lib/claude-templates.ts +227 -0
  1392. package/.forge/worktrees/pipeline-d1757a50/lib/cloudflared.ts +424 -0
  1393. package/.forge/worktrees/pipeline-d1757a50/lib/crypto.ts +67 -0
  1394. package/.forge/worktrees/pipeline-d1757a50/lib/delivery.ts +787 -0
  1395. package/.forge/worktrees/pipeline-d1757a50/lib/dirs.ts +99 -0
  1396. package/.forge/worktrees/pipeline-d1757a50/lib/flows.ts +86 -0
  1397. package/.forge/worktrees/pipeline-d1757a50/lib/forge-mcp-server.ts +717 -0
  1398. package/.forge/worktrees/pipeline-d1757a50/lib/forge-skills/forge-inbox.md +38 -0
  1399. package/.forge/worktrees/pipeline-d1757a50/lib/forge-skills/forge-send.md +47 -0
  1400. package/.forge/worktrees/pipeline-d1757a50/lib/forge-skills/forge-status.md +32 -0
  1401. package/.forge/worktrees/pipeline-d1757a50/lib/forge-skills/forge-workspace-sync.md +37 -0
  1402. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/00-overview.md +40 -0
  1403. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/01-settings.md +194 -0
  1404. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/02-telegram.md +41 -0
  1405. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/03-tunnel.md +31 -0
  1406. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/04-tasks.md +52 -0
  1407. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/05-pipelines.md +460 -0
  1408. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/06-skills.md +43 -0
  1409. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/07-projects.md +73 -0
  1410. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/08-rules.md +53 -0
  1411. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/09-issue-autofix.md +55 -0
  1412. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/10-troubleshooting.md +89 -0
  1413. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/11-workspace.md +810 -0
  1414. package/.forge/worktrees/pipeline-d1757a50/lib/help-docs/CLAUDE.md +62 -0
  1415. package/.forge/worktrees/pipeline-d1757a50/lib/init.ts +266 -0
  1416. package/.forge/worktrees/pipeline-d1757a50/lib/issue-scanner.ts +298 -0
  1417. package/.forge/worktrees/pipeline-d1757a50/lib/logger.ts +79 -0
  1418. package/.forge/worktrees/pipeline-d1757a50/lib/notifications.ts +75 -0
  1419. package/.forge/worktrees/pipeline-d1757a50/lib/notify.ts +108 -0
  1420. package/.forge/worktrees/pipeline-d1757a50/lib/password.ts +97 -0
  1421. package/.forge/worktrees/pipeline-d1757a50/lib/pipeline-scheduler.ts +373 -0
  1422. package/.forge/worktrees/pipeline-d1757a50/lib/pipeline.ts +1441 -0
  1423. package/.forge/worktrees/pipeline-d1757a50/lib/plugins/executor.ts +347 -0
  1424. package/.forge/worktrees/pipeline-d1757a50/lib/plugins/registry.ts +228 -0
  1425. package/.forge/worktrees/pipeline-d1757a50/lib/plugins/types.ts +103 -0
  1426. package/.forge/worktrees/pipeline-d1757a50/lib/project-sessions.ts +53 -0
  1427. package/.forge/worktrees/pipeline-d1757a50/lib/projects.ts +86 -0
  1428. package/.forge/worktrees/pipeline-d1757a50/lib/session-manager.ts +156 -0
  1429. package/.forge/worktrees/pipeline-d1757a50/lib/session-utils.ts +53 -0
  1430. package/.forge/worktrees/pipeline-d1757a50/lib/session-watcher.ts +345 -0
  1431. package/.forge/worktrees/pipeline-d1757a50/lib/settings.ts +195 -0
  1432. package/.forge/worktrees/pipeline-d1757a50/lib/skills.ts +458 -0
  1433. package/.forge/worktrees/pipeline-d1757a50/lib/task-manager.ts +949 -0
  1434. package/.forge/worktrees/pipeline-d1757a50/lib/telegram-bot.ts +1477 -0
  1435. package/.forge/worktrees/pipeline-d1757a50/lib/telegram-standalone.ts +83 -0
  1436. package/.forge/worktrees/pipeline-d1757a50/lib/terminal-server.ts +70 -0
  1437. package/.forge/worktrees/pipeline-d1757a50/lib/terminal-standalone.ts +421 -0
  1438. package/.forge/worktrees/pipeline-d1757a50/lib/usage-scanner.ts +249 -0
  1439. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/__tests__/state-machine.test.ts +388 -0
  1440. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/__tests__/workspace.test.ts +311 -0
  1441. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/agent-bus.ts +416 -0
  1442. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/agent-worker.ts +655 -0
  1443. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/backends/api-backend.ts +262 -0
  1444. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/backends/cli-backend.ts +491 -0
  1445. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/index.ts +82 -0
  1446. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/manager.ts +136 -0
  1447. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/orchestrator.ts +3400 -0
  1448. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/persistence.ts +309 -0
  1449. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/presets.ts +649 -0
  1450. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/requests.ts +287 -0
  1451. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/session-monitor.ts +240 -0
  1452. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/skill-installer.ts +275 -0
  1453. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/smith-memory.ts +498 -0
  1454. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/types.ts +241 -0
  1455. package/.forge/worktrees/pipeline-d1757a50/lib/workspace/watch-manager.ts +560 -0
  1456. package/.forge/worktrees/pipeline-d1757a50/lib/workspace-standalone.ts +911 -0
  1457. package/.forge/worktrees/pipeline-d1757a50/middleware.ts +51 -0
  1458. package/.forge/worktrees/pipeline-d1757a50/next.config.ts +26 -0
  1459. package/.forge/worktrees/pipeline-d1757a50/package.json +74 -0
  1460. package/.forge/worktrees/pipeline-d1757a50/pnpm-lock.yaml +3719 -0
  1461. package/.forge/worktrees/pipeline-d1757a50/pnpm-workspace.yaml +1 -0
  1462. package/.forge/worktrees/pipeline-d1757a50/postcss.config.mjs +7 -0
  1463. package/.forge/worktrees/pipeline-d1757a50/publish.sh +133 -0
  1464. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/README.md +66 -0
  1465. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/results/.gitignore +2 -0
  1466. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/run.ts +635 -0
  1467. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/01-text-utils/task.md +26 -0
  1468. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  1469. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  1470. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/02-pagination/task.md +48 -0
  1471. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  1472. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  1473. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  1474. package/.forge/worktrees/pipeline-d1757a50/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  1475. package/.forge/worktrees/pipeline-d1757a50/scripts/verify-usage.ts +178 -0
  1476. package/.forge/worktrees/pipeline-d1757a50/src/config/index.ts +129 -0
  1477. package/.forge/worktrees/pipeline-d1757a50/src/core/db/database.ts +259 -0
  1478. package/.forge/worktrees/pipeline-d1757a50/src/core/memory/strategy.ts +32 -0
  1479. package/.forge/worktrees/pipeline-d1757a50/src/core/providers/chat.ts +65 -0
  1480. package/.forge/worktrees/pipeline-d1757a50/src/core/providers/registry.ts +60 -0
  1481. package/.forge/worktrees/pipeline-d1757a50/src/core/session/manager.ts +190 -0
  1482. package/.forge/worktrees/pipeline-d1757a50/src/types/index.ts +129 -0
  1483. package/.forge/worktrees/pipeline-d1757a50/start.sh +31 -0
  1484. package/.forge/worktrees/pipeline-d1757a50/templates/smith-lead.json +45 -0
  1485. package/.forge/worktrees/pipeline-d1757a50/tsconfig.json +42 -0
  1486. package/.forge/worktrees/pipeline-d59c2fe2/CLAUDE.md +86 -0
  1487. package/.forge/worktrees/pipeline-d59c2fe2/README.md +136 -0
  1488. package/.forge/worktrees/pipeline-d59c2fe2/RELEASE_NOTES.md +11 -0
  1489. package/.forge/worktrees/pipeline-d59c2fe2/app/api/agents/route.ts +17 -0
  1490. package/.forge/worktrees/pipeline-d59c2fe2/app/api/auth/[...nextauth]/route.ts +3 -0
  1491. package/.forge/worktrees/pipeline-d59c2fe2/app/api/auth/verify/route.ts +46 -0
  1492. package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude/[id]/route.ts +31 -0
  1493. package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude/[id]/stream/route.ts +63 -0
  1494. package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude/route.ts +28 -0
  1495. package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  1496. package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  1497. package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude-sessions/[projectName]/route.ts +37 -0
  1498. package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude-sessions/sync/route.ts +17 -0
  1499. package/.forge/worktrees/pipeline-d59c2fe2/app/api/claude-templates/route.ts +145 -0
  1500. package/.forge/worktrees/pipeline-d59c2fe2/app/api/code/route.ts +299 -0
  1501. package/.forge/worktrees/pipeline-d59c2fe2/app/api/delivery/[id]/route.ts +62 -0
  1502. package/.forge/worktrees/pipeline-d59c2fe2/app/api/delivery/route.ts +40 -0
  1503. package/.forge/worktrees/pipeline-d59c2fe2/app/api/detect-cli/route.ts +46 -0
  1504. package/.forge/worktrees/pipeline-d59c2fe2/app/api/docs/route.ts +176 -0
  1505. package/.forge/worktrees/pipeline-d59c2fe2/app/api/docs/sessions/route.ts +54 -0
  1506. package/.forge/worktrees/pipeline-d59c2fe2/app/api/favorites/route.ts +26 -0
  1507. package/.forge/worktrees/pipeline-d59c2fe2/app/api/flows/route.ts +6 -0
  1508. package/.forge/worktrees/pipeline-d59c2fe2/app/api/flows/run/route.ts +19 -0
  1509. package/.forge/worktrees/pipeline-d59c2fe2/app/api/git/route.ts +149 -0
  1510. package/.forge/worktrees/pipeline-d59c2fe2/app/api/help/route.ts +84 -0
  1511. package/.forge/worktrees/pipeline-d59c2fe2/app/api/issue-scanner/route.ts +116 -0
  1512. package/.forge/worktrees/pipeline-d59c2fe2/app/api/logs/route.ts +100 -0
  1513. package/.forge/worktrees/pipeline-d59c2fe2/app/api/mobile-chat/route.ts +115 -0
  1514. package/.forge/worktrees/pipeline-d59c2fe2/app/api/monitor/route.ts +74 -0
  1515. package/.forge/worktrees/pipeline-d59c2fe2/app/api/notifications/route.ts +42 -0
  1516. package/.forge/worktrees/pipeline-d59c2fe2/app/api/notify/test/route.ts +33 -0
  1517. package/.forge/worktrees/pipeline-d59c2fe2/app/api/online/route.ts +40 -0
  1518. package/.forge/worktrees/pipeline-d59c2fe2/app/api/pipelines/[id]/route.ts +41 -0
  1519. package/.forge/worktrees/pipeline-d59c2fe2/app/api/pipelines/route.ts +90 -0
  1520. package/.forge/worktrees/pipeline-d59c2fe2/app/api/plugins/route.ts +75 -0
  1521. package/.forge/worktrees/pipeline-d59c2fe2/app/api/preview/[...path]/route.ts +64 -0
  1522. package/.forge/worktrees/pipeline-d59c2fe2/app/api/preview/route.ts +156 -0
  1523. package/.forge/worktrees/pipeline-d59c2fe2/app/api/project-pipelines/route.ts +91 -0
  1524. package/.forge/worktrees/pipeline-d59c2fe2/app/api/project-sessions/route.ts +61 -0
  1525. package/.forge/worktrees/pipeline-d59c2fe2/app/api/projects/route.ts +26 -0
  1526. package/.forge/worktrees/pipeline-d59c2fe2/app/api/sessions/[id]/chat/route.ts +64 -0
  1527. package/.forge/worktrees/pipeline-d59c2fe2/app/api/sessions/[id]/messages/route.ts +9 -0
  1528. package/.forge/worktrees/pipeline-d59c2fe2/app/api/sessions/[id]/route.ts +17 -0
  1529. package/.forge/worktrees/pipeline-d59c2fe2/app/api/sessions/route.ts +20 -0
  1530. package/.forge/worktrees/pipeline-d59c2fe2/app/api/settings/route.ts +64 -0
  1531. package/.forge/worktrees/pipeline-d59c2fe2/app/api/skills/local/route.ts +228 -0
  1532. package/.forge/worktrees/pipeline-d59c2fe2/app/api/skills/route.ts +182 -0
  1533. package/.forge/worktrees/pipeline-d59c2fe2/app/api/smith-templates/route.ts +81 -0
  1534. package/.forge/worktrees/pipeline-d59c2fe2/app/api/status/route.ts +12 -0
  1535. package/.forge/worktrees/pipeline-d59c2fe2/app/api/tabs/route.ts +25 -0
  1536. package/.forge/worktrees/pipeline-d59c2fe2/app/api/tasks/[id]/route.ts +51 -0
  1537. package/.forge/worktrees/pipeline-d59c2fe2/app/api/tasks/[id]/stream/route.ts +77 -0
  1538. package/.forge/worktrees/pipeline-d59c2fe2/app/api/tasks/link/route.ts +37 -0
  1539. package/.forge/worktrees/pipeline-d59c2fe2/app/api/tasks/route.ts +44 -0
  1540. package/.forge/worktrees/pipeline-d59c2fe2/app/api/tasks/session/route.ts +14 -0
  1541. package/.forge/worktrees/pipeline-d59c2fe2/app/api/telegram/route.ts +23 -0
  1542. package/.forge/worktrees/pipeline-d59c2fe2/app/api/templates/route.ts +6 -0
  1543. package/.forge/worktrees/pipeline-d59c2fe2/app/api/terminal-bell/route.ts +35 -0
  1544. package/.forge/worktrees/pipeline-d59c2fe2/app/api/terminal-cwd/route.ts +19 -0
  1545. package/.forge/worktrees/pipeline-d59c2fe2/app/api/terminal-state/route.ts +15 -0
  1546. package/.forge/worktrees/pipeline-d59c2fe2/app/api/tunnel/route.ts +26 -0
  1547. package/.forge/worktrees/pipeline-d59c2fe2/app/api/upgrade/route.ts +43 -0
  1548. package/.forge/worktrees/pipeline-d59c2fe2/app/api/usage/route.ts +20 -0
  1549. package/.forge/worktrees/pipeline-d59c2fe2/app/api/version/route.ts +78 -0
  1550. package/.forge/worktrees/pipeline-d59c2fe2/app/api/watchers/route.ts +33 -0
  1551. package/.forge/worktrees/pipeline-d59c2fe2/app/api/workspace/[id]/agents/route.ts +35 -0
  1552. package/.forge/worktrees/pipeline-d59c2fe2/app/api/workspace/[id]/memory/route.ts +23 -0
  1553. package/.forge/worktrees/pipeline-d59c2fe2/app/api/workspace/[id]/smith/route.ts +22 -0
  1554. package/.forge/worktrees/pipeline-d59c2fe2/app/api/workspace/[id]/stream/route.ts +28 -0
  1555. package/.forge/worktrees/pipeline-d59c2fe2/app/api/workspace/route.ts +100 -0
  1556. package/.forge/worktrees/pipeline-d59c2fe2/app/global-error.tsx +21 -0
  1557. package/.forge/worktrees/pipeline-d59c2fe2/app/globals.css +52 -0
  1558. package/.forge/worktrees/pipeline-d59c2fe2/app/icon.ico +0 -0
  1559. package/.forge/worktrees/pipeline-d59c2fe2/app/icon.png +0 -0
  1560. package/.forge/worktrees/pipeline-d59c2fe2/app/icon.svg +106 -0
  1561. package/.forge/worktrees/pipeline-d59c2fe2/app/layout.tsx +17 -0
  1562. package/.forge/worktrees/pipeline-d59c2fe2/app/login/LoginForm.tsx +96 -0
  1563. package/.forge/worktrees/pipeline-d59c2fe2/app/login/page.tsx +10 -0
  1564. package/.forge/worktrees/pipeline-d59c2fe2/app/mobile/page.tsx +9 -0
  1565. package/.forge/worktrees/pipeline-d59c2fe2/app/page.tsx +21 -0
  1566. package/.forge/worktrees/pipeline-d59c2fe2/bin/forge-server.mjs +484 -0
  1567. package/.forge/worktrees/pipeline-d59c2fe2/check-forge-status.sh +71 -0
  1568. package/.forge/worktrees/pipeline-d59c2fe2/cli/mw.ts +579 -0
  1569. package/.forge/worktrees/pipeline-d59c2fe2/components/BrowserPanel.tsx +175 -0
  1570. package/.forge/worktrees/pipeline-d59c2fe2/components/ChatPanel.tsx +191 -0
  1571. package/.forge/worktrees/pipeline-d59c2fe2/components/ClaudeTerminal.tsx +267 -0
  1572. package/.forge/worktrees/pipeline-d59c2fe2/components/CodeViewer.tsx +787 -0
  1573. package/.forge/worktrees/pipeline-d59c2fe2/components/ConversationEditor.tsx +411 -0
  1574. package/.forge/worktrees/pipeline-d59c2fe2/components/ConversationGraphView.tsx +347 -0
  1575. package/.forge/worktrees/pipeline-d59c2fe2/components/ConversationTerminalView.tsx +303 -0
  1576. package/.forge/worktrees/pipeline-d59c2fe2/components/Dashboard.tsx +807 -0
  1577. package/.forge/worktrees/pipeline-d59c2fe2/components/DashboardWrapper.tsx +9 -0
  1578. package/.forge/worktrees/pipeline-d59c2fe2/components/DeliveryFlowEditor.tsx +491 -0
  1579. package/.forge/worktrees/pipeline-d59c2fe2/components/DeliveryList.tsx +230 -0
  1580. package/.forge/worktrees/pipeline-d59c2fe2/components/DeliveryWorkspace.tsx +589 -0
  1581. package/.forge/worktrees/pipeline-d59c2fe2/components/DocTerminal.tsx +187 -0
  1582. package/.forge/worktrees/pipeline-d59c2fe2/components/DocsViewer.tsx +574 -0
  1583. package/.forge/worktrees/pipeline-d59c2fe2/components/HelpDialog.tsx +169 -0
  1584. package/.forge/worktrees/pipeline-d59c2fe2/components/HelpTerminal.tsx +141 -0
  1585. package/.forge/worktrees/pipeline-d59c2fe2/components/InlinePipelineView.tsx +111 -0
  1586. package/.forge/worktrees/pipeline-d59c2fe2/components/LogViewer.tsx +194 -0
  1587. package/.forge/worktrees/pipeline-d59c2fe2/components/MarkdownContent.tsx +73 -0
  1588. package/.forge/worktrees/pipeline-d59c2fe2/components/MobileView.tsx +385 -0
  1589. package/.forge/worktrees/pipeline-d59c2fe2/components/MonitorPanel.tsx +122 -0
  1590. package/.forge/worktrees/pipeline-d59c2fe2/components/NewSessionModal.tsx +93 -0
  1591. package/.forge/worktrees/pipeline-d59c2fe2/components/NewTaskModal.tsx +492 -0
  1592. package/.forge/worktrees/pipeline-d59c2fe2/components/PipelineEditor.tsx +570 -0
  1593. package/.forge/worktrees/pipeline-d59c2fe2/components/PipelineView.tsx +1018 -0
  1594. package/.forge/worktrees/pipeline-d59c2fe2/components/PluginsPanel.tsx +472 -0
  1595. package/.forge/worktrees/pipeline-d59c2fe2/components/ProjectDetail.tsx +1618 -0
  1596. package/.forge/worktrees/pipeline-d59c2fe2/components/ProjectList.tsx +108 -0
  1597. package/.forge/worktrees/pipeline-d59c2fe2/components/ProjectManager.tsx +401 -0
  1598. package/.forge/worktrees/pipeline-d59c2fe2/components/SessionList.tsx +74 -0
  1599. package/.forge/worktrees/pipeline-d59c2fe2/components/SessionView.tsx +726 -0
  1600. package/.forge/worktrees/pipeline-d59c2fe2/components/SettingsModal.tsx +1647 -0
  1601. package/.forge/worktrees/pipeline-d59c2fe2/components/SkillsPanel.tsx +969 -0
  1602. package/.forge/worktrees/pipeline-d59c2fe2/components/StatusBar.tsx +99 -0
  1603. package/.forge/worktrees/pipeline-d59c2fe2/components/TabBar.tsx +46 -0
  1604. package/.forge/worktrees/pipeline-d59c2fe2/components/TaskBoard.tsx +113 -0
  1605. package/.forge/worktrees/pipeline-d59c2fe2/components/TaskDetail.tsx +372 -0
  1606. package/.forge/worktrees/pipeline-d59c2fe2/components/TerminalLauncher.tsx +398 -0
  1607. package/.forge/worktrees/pipeline-d59c2fe2/components/TunnelToggle.tsx +206 -0
  1608. package/.forge/worktrees/pipeline-d59c2fe2/components/UsagePanel.tsx +207 -0
  1609. package/.forge/worktrees/pipeline-d59c2fe2/components/WebTerminal.tsx +1683 -0
  1610. package/.forge/worktrees/pipeline-d59c2fe2/components/WorkspaceTree.tsx +221 -0
  1611. package/.forge/worktrees/pipeline-d59c2fe2/components/WorkspaceView.tsx +4048 -0
  1612. package/.forge/worktrees/pipeline-d59c2fe2/dev-test.sh +5 -0
  1613. package/.forge/worktrees/pipeline-d59c2fe2/docs/Forge_Memory_Layer_Design.docx +0 -0
  1614. package/.forge/worktrees/pipeline-d59c2fe2/docs/Forge_Strategy_Research_2026.docx +0 -0
  1615. package/.forge/worktrees/pipeline-d59c2fe2/docs/LOCAL-DEPLOY.md +144 -0
  1616. package/.forge/worktrees/pipeline-d59c2fe2/docs/roadmap-multi-agent-workflow.md +330 -0
  1617. package/.forge/worktrees/pipeline-d59c2fe2/forge-logo.png +0 -0
  1618. package/.forge/worktrees/pipeline-d59c2fe2/forge-logo.svg +106 -0
  1619. package/.forge/worktrees/pipeline-d59c2fe2/hooks/useSidebarResize.ts +52 -0
  1620. package/.forge/worktrees/pipeline-d59c2fe2/install.sh +29 -0
  1621. package/.forge/worktrees/pipeline-d59c2fe2/instrumentation.ts +35 -0
  1622. package/.forge/worktrees/pipeline-d59c2fe2/lib/agents/claude-adapter.ts +104 -0
  1623. package/.forge/worktrees/pipeline-d59c2fe2/lib/agents/generic-adapter.ts +64 -0
  1624. package/.forge/worktrees/pipeline-d59c2fe2/lib/agents/index.ts +245 -0
  1625. package/.forge/worktrees/pipeline-d59c2fe2/lib/agents/types.ts +70 -0
  1626. package/.forge/worktrees/pipeline-d59c2fe2/lib/artifacts.ts +106 -0
  1627. package/.forge/worktrees/pipeline-d59c2fe2/lib/auth.ts +62 -0
  1628. package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/docker.yaml +70 -0
  1629. package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/http.yaml +66 -0
  1630. package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/jenkins.yaml +92 -0
  1631. package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/llm-vision.yaml +85 -0
  1632. package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/playwright.yaml +111 -0
  1633. package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/shell-command.yaml +60 -0
  1634. package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/slack.yaml +48 -0
  1635. package/.forge/worktrees/pipeline-d59c2fe2/lib/builtin-plugins/webhook.yaml +56 -0
  1636. package/.forge/worktrees/pipeline-d59c2fe2/lib/claude-process.ts +361 -0
  1637. package/.forge/worktrees/pipeline-d59c2fe2/lib/claude-sessions.ts +266 -0
  1638. package/.forge/worktrees/pipeline-d59c2fe2/lib/claude-templates.ts +227 -0
  1639. package/.forge/worktrees/pipeline-d59c2fe2/lib/cloudflared.ts +424 -0
  1640. package/.forge/worktrees/pipeline-d59c2fe2/lib/crypto.ts +67 -0
  1641. package/.forge/worktrees/pipeline-d59c2fe2/lib/delivery.ts +787 -0
  1642. package/.forge/worktrees/pipeline-d59c2fe2/lib/dirs.ts +99 -0
  1643. package/.forge/worktrees/pipeline-d59c2fe2/lib/flows.ts +86 -0
  1644. package/.forge/worktrees/pipeline-d59c2fe2/lib/forge-mcp-server.ts +717 -0
  1645. package/.forge/worktrees/pipeline-d59c2fe2/lib/forge-skills/forge-inbox.md +38 -0
  1646. package/.forge/worktrees/pipeline-d59c2fe2/lib/forge-skills/forge-send.md +47 -0
  1647. package/.forge/worktrees/pipeline-d59c2fe2/lib/forge-skills/forge-status.md +32 -0
  1648. package/.forge/worktrees/pipeline-d59c2fe2/lib/forge-skills/forge-workspace-sync.md +37 -0
  1649. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/00-overview.md +40 -0
  1650. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/01-settings.md +194 -0
  1651. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/02-telegram.md +41 -0
  1652. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/03-tunnel.md +31 -0
  1653. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/04-tasks.md +52 -0
  1654. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/05-pipelines.md +460 -0
  1655. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/06-skills.md +43 -0
  1656. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/07-projects.md +73 -0
  1657. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/08-rules.md +53 -0
  1658. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/09-issue-autofix.md +55 -0
  1659. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/10-troubleshooting.md +89 -0
  1660. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/11-workspace.md +810 -0
  1661. package/.forge/worktrees/pipeline-d59c2fe2/lib/help-docs/CLAUDE.md +62 -0
  1662. package/.forge/worktrees/pipeline-d59c2fe2/lib/init.ts +266 -0
  1663. package/.forge/worktrees/pipeline-d59c2fe2/lib/issue-scanner.ts +298 -0
  1664. package/.forge/worktrees/pipeline-d59c2fe2/lib/logger.ts +79 -0
  1665. package/.forge/worktrees/pipeline-d59c2fe2/lib/notifications.ts +75 -0
  1666. package/.forge/worktrees/pipeline-d59c2fe2/lib/notify.ts +108 -0
  1667. package/.forge/worktrees/pipeline-d59c2fe2/lib/password.ts +97 -0
  1668. package/.forge/worktrees/pipeline-d59c2fe2/lib/pipeline-scheduler.ts +373 -0
  1669. package/.forge/worktrees/pipeline-d59c2fe2/lib/pipeline.ts +1441 -0
  1670. package/.forge/worktrees/pipeline-d59c2fe2/lib/plugins/executor.ts +347 -0
  1671. package/.forge/worktrees/pipeline-d59c2fe2/lib/plugins/registry.ts +228 -0
  1672. package/.forge/worktrees/pipeline-d59c2fe2/lib/plugins/types.ts +103 -0
  1673. package/.forge/worktrees/pipeline-d59c2fe2/lib/project-sessions.ts +53 -0
  1674. package/.forge/worktrees/pipeline-d59c2fe2/lib/projects.ts +86 -0
  1675. package/.forge/worktrees/pipeline-d59c2fe2/lib/session-manager.ts +156 -0
  1676. package/.forge/worktrees/pipeline-d59c2fe2/lib/session-utils.ts +53 -0
  1677. package/.forge/worktrees/pipeline-d59c2fe2/lib/session-watcher.ts +345 -0
  1678. package/.forge/worktrees/pipeline-d59c2fe2/lib/settings.ts +195 -0
  1679. package/.forge/worktrees/pipeline-d59c2fe2/lib/skills.ts +458 -0
  1680. package/.forge/worktrees/pipeline-d59c2fe2/lib/task-manager.ts +949 -0
  1681. package/.forge/worktrees/pipeline-d59c2fe2/lib/telegram-bot.ts +1477 -0
  1682. package/.forge/worktrees/pipeline-d59c2fe2/lib/telegram-standalone.ts +83 -0
  1683. package/.forge/worktrees/pipeline-d59c2fe2/lib/terminal-server.ts +70 -0
  1684. package/.forge/worktrees/pipeline-d59c2fe2/lib/terminal-standalone.ts +421 -0
  1685. package/.forge/worktrees/pipeline-d59c2fe2/lib/usage-scanner.ts +249 -0
  1686. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/__tests__/state-machine.test.ts +388 -0
  1687. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/__tests__/workspace.test.ts +311 -0
  1688. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/agent-bus.ts +416 -0
  1689. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/agent-worker.ts +655 -0
  1690. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/backends/api-backend.ts +262 -0
  1691. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/backends/cli-backend.ts +491 -0
  1692. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/index.ts +82 -0
  1693. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/manager.ts +136 -0
  1694. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/orchestrator.ts +3400 -0
  1695. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/persistence.ts +309 -0
  1696. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/presets.ts +649 -0
  1697. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/requests.ts +287 -0
  1698. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/session-monitor.ts +240 -0
  1699. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/skill-installer.ts +275 -0
  1700. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/smith-memory.ts +498 -0
  1701. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/types.ts +241 -0
  1702. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace/watch-manager.ts +560 -0
  1703. package/.forge/worktrees/pipeline-d59c2fe2/lib/workspace-standalone.ts +911 -0
  1704. package/.forge/worktrees/pipeline-d59c2fe2/middleware.ts +51 -0
  1705. package/.forge/worktrees/pipeline-d59c2fe2/next.config.ts +26 -0
  1706. package/.forge/worktrees/pipeline-d59c2fe2/package.json +74 -0
  1707. package/.forge/worktrees/pipeline-d59c2fe2/pnpm-lock.yaml +3719 -0
  1708. package/.forge/worktrees/pipeline-d59c2fe2/pnpm-workspace.yaml +1 -0
  1709. package/.forge/worktrees/pipeline-d59c2fe2/postcss.config.mjs +7 -0
  1710. package/.forge/worktrees/pipeline-d59c2fe2/publish.sh +133 -0
  1711. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/README.md +66 -0
  1712. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/results/.gitignore +2 -0
  1713. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/run.ts +635 -0
  1714. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/01-text-utils/task.md +26 -0
  1715. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  1716. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  1717. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/02-pagination/task.md +48 -0
  1718. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  1719. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  1720. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  1721. package/.forge/worktrees/pipeline-d59c2fe2/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  1722. package/.forge/worktrees/pipeline-d59c2fe2/scripts/verify-usage.ts +178 -0
  1723. package/.forge/worktrees/pipeline-d59c2fe2/src/config/index.ts +129 -0
  1724. package/.forge/worktrees/pipeline-d59c2fe2/src/core/db/database.ts +259 -0
  1725. package/.forge/worktrees/pipeline-d59c2fe2/src/core/memory/strategy.ts +32 -0
  1726. package/.forge/worktrees/pipeline-d59c2fe2/src/core/providers/chat.ts +65 -0
  1727. package/.forge/worktrees/pipeline-d59c2fe2/src/core/providers/registry.ts +60 -0
  1728. package/.forge/worktrees/pipeline-d59c2fe2/src/core/session/manager.ts +190 -0
  1729. package/.forge/worktrees/pipeline-d59c2fe2/src/types/index.ts +129 -0
  1730. package/.forge/worktrees/pipeline-d59c2fe2/start.sh +31 -0
  1731. package/.forge/worktrees/pipeline-d59c2fe2/templates/smith-lead.json +45 -0
  1732. package/.forge/worktrees/pipeline-d59c2fe2/tsconfig.json +42 -0
  1733. package/.forge/worktrees/pipeline-d6a6ef23/CLAUDE.md +86 -0
  1734. package/.forge/worktrees/pipeline-d6a6ef23/README.md +136 -0
  1735. package/.forge/worktrees/pipeline-d6a6ef23/RELEASE_NOTES.md +11 -0
  1736. package/.forge/worktrees/pipeline-d6a6ef23/app/api/agents/route.ts +17 -0
  1737. package/.forge/worktrees/pipeline-d6a6ef23/app/api/auth/[...nextauth]/route.ts +3 -0
  1738. package/.forge/worktrees/pipeline-d6a6ef23/app/api/auth/verify/route.ts +46 -0
  1739. package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude/[id]/route.ts +31 -0
  1740. package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude/[id]/stream/route.ts +63 -0
  1741. package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude/route.ts +28 -0
  1742. package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  1743. package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  1744. package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude-sessions/[projectName]/route.ts +37 -0
  1745. package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude-sessions/sync/route.ts +17 -0
  1746. package/.forge/worktrees/pipeline-d6a6ef23/app/api/claude-templates/route.ts +145 -0
  1747. package/.forge/worktrees/pipeline-d6a6ef23/app/api/code/route.ts +299 -0
  1748. package/.forge/worktrees/pipeline-d6a6ef23/app/api/delivery/[id]/route.ts +62 -0
  1749. package/.forge/worktrees/pipeline-d6a6ef23/app/api/delivery/route.ts +40 -0
  1750. package/.forge/worktrees/pipeline-d6a6ef23/app/api/detect-cli/route.ts +46 -0
  1751. package/.forge/worktrees/pipeline-d6a6ef23/app/api/docs/route.ts +176 -0
  1752. package/.forge/worktrees/pipeline-d6a6ef23/app/api/docs/sessions/route.ts +54 -0
  1753. package/.forge/worktrees/pipeline-d6a6ef23/app/api/favorites/route.ts +26 -0
  1754. package/.forge/worktrees/pipeline-d6a6ef23/app/api/flows/route.ts +6 -0
  1755. package/.forge/worktrees/pipeline-d6a6ef23/app/api/flows/run/route.ts +19 -0
  1756. package/.forge/worktrees/pipeline-d6a6ef23/app/api/git/route.ts +149 -0
  1757. package/.forge/worktrees/pipeline-d6a6ef23/app/api/help/route.ts +84 -0
  1758. package/.forge/worktrees/pipeline-d6a6ef23/app/api/issue-scanner/route.ts +116 -0
  1759. package/.forge/worktrees/pipeline-d6a6ef23/app/api/logs/route.ts +100 -0
  1760. package/.forge/worktrees/pipeline-d6a6ef23/app/api/mobile-chat/route.ts +115 -0
  1761. package/.forge/worktrees/pipeline-d6a6ef23/app/api/monitor/route.ts +74 -0
  1762. package/.forge/worktrees/pipeline-d6a6ef23/app/api/notifications/route.ts +42 -0
  1763. package/.forge/worktrees/pipeline-d6a6ef23/app/api/notify/test/route.ts +33 -0
  1764. package/.forge/worktrees/pipeline-d6a6ef23/app/api/online/route.ts +40 -0
  1765. package/.forge/worktrees/pipeline-d6a6ef23/app/api/pipelines/[id]/route.ts +41 -0
  1766. package/.forge/worktrees/pipeline-d6a6ef23/app/api/pipelines/route.ts +90 -0
  1767. package/.forge/worktrees/pipeline-d6a6ef23/app/api/plugins/route.ts +75 -0
  1768. package/.forge/worktrees/pipeline-d6a6ef23/app/api/preview/[...path]/route.ts +64 -0
  1769. package/.forge/worktrees/pipeline-d6a6ef23/app/api/preview/route.ts +156 -0
  1770. package/.forge/worktrees/pipeline-d6a6ef23/app/api/project-pipelines/route.ts +91 -0
  1771. package/.forge/worktrees/pipeline-d6a6ef23/app/api/project-sessions/route.ts +61 -0
  1772. package/.forge/worktrees/pipeline-d6a6ef23/app/api/projects/route.ts +26 -0
  1773. package/.forge/worktrees/pipeline-d6a6ef23/app/api/sessions/[id]/chat/route.ts +64 -0
  1774. package/.forge/worktrees/pipeline-d6a6ef23/app/api/sessions/[id]/messages/route.ts +9 -0
  1775. package/.forge/worktrees/pipeline-d6a6ef23/app/api/sessions/[id]/route.ts +17 -0
  1776. package/.forge/worktrees/pipeline-d6a6ef23/app/api/sessions/route.ts +20 -0
  1777. package/.forge/worktrees/pipeline-d6a6ef23/app/api/settings/route.ts +64 -0
  1778. package/.forge/worktrees/pipeline-d6a6ef23/app/api/skills/local/route.ts +228 -0
  1779. package/.forge/worktrees/pipeline-d6a6ef23/app/api/skills/route.ts +182 -0
  1780. package/.forge/worktrees/pipeline-d6a6ef23/app/api/smith-templates/route.ts +81 -0
  1781. package/.forge/worktrees/pipeline-d6a6ef23/app/api/status/route.ts +12 -0
  1782. package/.forge/worktrees/pipeline-d6a6ef23/app/api/tabs/route.ts +25 -0
  1783. package/.forge/worktrees/pipeline-d6a6ef23/app/api/tasks/[id]/route.ts +51 -0
  1784. package/.forge/worktrees/pipeline-d6a6ef23/app/api/tasks/[id]/stream/route.ts +77 -0
  1785. package/.forge/worktrees/pipeline-d6a6ef23/app/api/tasks/link/route.ts +37 -0
  1786. package/.forge/worktrees/pipeline-d6a6ef23/app/api/tasks/route.ts +44 -0
  1787. package/.forge/worktrees/pipeline-d6a6ef23/app/api/tasks/session/route.ts +14 -0
  1788. package/.forge/worktrees/pipeline-d6a6ef23/app/api/telegram/route.ts +23 -0
  1789. package/.forge/worktrees/pipeline-d6a6ef23/app/api/templates/route.ts +6 -0
  1790. package/.forge/worktrees/pipeline-d6a6ef23/app/api/terminal-bell/route.ts +35 -0
  1791. package/.forge/worktrees/pipeline-d6a6ef23/app/api/terminal-cwd/route.ts +19 -0
  1792. package/.forge/worktrees/pipeline-d6a6ef23/app/api/terminal-state/route.ts +15 -0
  1793. package/.forge/worktrees/pipeline-d6a6ef23/app/api/tunnel/route.ts +26 -0
  1794. package/.forge/worktrees/pipeline-d6a6ef23/app/api/upgrade/route.ts +43 -0
  1795. package/.forge/worktrees/pipeline-d6a6ef23/app/api/usage/route.ts +20 -0
  1796. package/.forge/worktrees/pipeline-d6a6ef23/app/api/version/route.ts +78 -0
  1797. package/.forge/worktrees/pipeline-d6a6ef23/app/api/watchers/route.ts +33 -0
  1798. package/.forge/worktrees/pipeline-d6a6ef23/app/api/workspace/[id]/agents/route.ts +35 -0
  1799. package/.forge/worktrees/pipeline-d6a6ef23/app/api/workspace/[id]/memory/route.ts +23 -0
  1800. package/.forge/worktrees/pipeline-d6a6ef23/app/api/workspace/[id]/smith/route.ts +22 -0
  1801. package/.forge/worktrees/pipeline-d6a6ef23/app/api/workspace/[id]/stream/route.ts +28 -0
  1802. package/.forge/worktrees/pipeline-d6a6ef23/app/api/workspace/route.ts +100 -0
  1803. package/.forge/worktrees/pipeline-d6a6ef23/app/global-error.tsx +21 -0
  1804. package/.forge/worktrees/pipeline-d6a6ef23/app/globals.css +52 -0
  1805. package/.forge/worktrees/pipeline-d6a6ef23/app/icon.ico +0 -0
  1806. package/.forge/worktrees/pipeline-d6a6ef23/app/icon.png +0 -0
  1807. package/.forge/worktrees/pipeline-d6a6ef23/app/icon.svg +106 -0
  1808. package/.forge/worktrees/pipeline-d6a6ef23/app/layout.tsx +17 -0
  1809. package/.forge/worktrees/pipeline-d6a6ef23/app/login/LoginForm.tsx +96 -0
  1810. package/.forge/worktrees/pipeline-d6a6ef23/app/login/page.tsx +10 -0
  1811. package/.forge/worktrees/pipeline-d6a6ef23/app/mobile/page.tsx +9 -0
  1812. package/.forge/worktrees/pipeline-d6a6ef23/app/page.tsx +21 -0
  1813. package/.forge/worktrees/pipeline-d6a6ef23/bin/forge-server.mjs +484 -0
  1814. package/.forge/worktrees/pipeline-d6a6ef23/check-forge-status.sh +71 -0
  1815. package/.forge/worktrees/pipeline-d6a6ef23/cli/mw.ts +579 -0
  1816. package/.forge/worktrees/pipeline-d6a6ef23/components/BrowserPanel.tsx +175 -0
  1817. package/.forge/worktrees/pipeline-d6a6ef23/components/ChatPanel.tsx +191 -0
  1818. package/.forge/worktrees/pipeline-d6a6ef23/components/ClaudeTerminal.tsx +267 -0
  1819. package/.forge/worktrees/pipeline-d6a6ef23/components/CodeViewer.tsx +787 -0
  1820. package/.forge/worktrees/pipeline-d6a6ef23/components/ConversationEditor.tsx +411 -0
  1821. package/.forge/worktrees/pipeline-d6a6ef23/components/ConversationGraphView.tsx +347 -0
  1822. package/.forge/worktrees/pipeline-d6a6ef23/components/ConversationTerminalView.tsx +303 -0
  1823. package/.forge/worktrees/pipeline-d6a6ef23/components/Dashboard.tsx +807 -0
  1824. package/.forge/worktrees/pipeline-d6a6ef23/components/DashboardWrapper.tsx +9 -0
  1825. package/.forge/worktrees/pipeline-d6a6ef23/components/DeliveryFlowEditor.tsx +491 -0
  1826. package/.forge/worktrees/pipeline-d6a6ef23/components/DeliveryList.tsx +230 -0
  1827. package/.forge/worktrees/pipeline-d6a6ef23/components/DeliveryWorkspace.tsx +589 -0
  1828. package/.forge/worktrees/pipeline-d6a6ef23/components/DocTerminal.tsx +187 -0
  1829. package/.forge/worktrees/pipeline-d6a6ef23/components/DocsViewer.tsx +574 -0
  1830. package/.forge/worktrees/pipeline-d6a6ef23/components/HelpDialog.tsx +169 -0
  1831. package/.forge/worktrees/pipeline-d6a6ef23/components/HelpTerminal.tsx +141 -0
  1832. package/.forge/worktrees/pipeline-d6a6ef23/components/InlinePipelineView.tsx +111 -0
  1833. package/.forge/worktrees/pipeline-d6a6ef23/components/LogViewer.tsx +194 -0
  1834. package/.forge/worktrees/pipeline-d6a6ef23/components/MarkdownContent.tsx +73 -0
  1835. package/.forge/worktrees/pipeline-d6a6ef23/components/MobileView.tsx +385 -0
  1836. package/.forge/worktrees/pipeline-d6a6ef23/components/MonitorPanel.tsx +122 -0
  1837. package/.forge/worktrees/pipeline-d6a6ef23/components/NewSessionModal.tsx +93 -0
  1838. package/.forge/worktrees/pipeline-d6a6ef23/components/NewTaskModal.tsx +492 -0
  1839. package/.forge/worktrees/pipeline-d6a6ef23/components/PipelineEditor.tsx +570 -0
  1840. package/.forge/worktrees/pipeline-d6a6ef23/components/PipelineView.tsx +1018 -0
  1841. package/.forge/worktrees/pipeline-d6a6ef23/components/PluginsPanel.tsx +472 -0
  1842. package/.forge/worktrees/pipeline-d6a6ef23/components/ProjectDetail.tsx +1618 -0
  1843. package/.forge/worktrees/pipeline-d6a6ef23/components/ProjectList.tsx +108 -0
  1844. package/.forge/worktrees/pipeline-d6a6ef23/components/ProjectManager.tsx +401 -0
  1845. package/.forge/worktrees/pipeline-d6a6ef23/components/SessionList.tsx +74 -0
  1846. package/.forge/worktrees/pipeline-d6a6ef23/components/SessionView.tsx +726 -0
  1847. package/.forge/worktrees/pipeline-d6a6ef23/components/SettingsModal.tsx +1647 -0
  1848. package/.forge/worktrees/pipeline-d6a6ef23/components/SkillsPanel.tsx +969 -0
  1849. package/.forge/worktrees/pipeline-d6a6ef23/components/StatusBar.tsx +99 -0
  1850. package/.forge/worktrees/pipeline-d6a6ef23/components/TabBar.tsx +46 -0
  1851. package/.forge/worktrees/pipeline-d6a6ef23/components/TaskBoard.tsx +113 -0
  1852. package/.forge/worktrees/pipeline-d6a6ef23/components/TaskDetail.tsx +372 -0
  1853. package/.forge/worktrees/pipeline-d6a6ef23/components/TerminalLauncher.tsx +398 -0
  1854. package/.forge/worktrees/pipeline-d6a6ef23/components/TunnelToggle.tsx +206 -0
  1855. package/.forge/worktrees/pipeline-d6a6ef23/components/UsagePanel.tsx +207 -0
  1856. package/.forge/worktrees/pipeline-d6a6ef23/components/WebTerminal.tsx +1683 -0
  1857. package/.forge/worktrees/pipeline-d6a6ef23/components/WorkspaceTree.tsx +221 -0
  1858. package/.forge/worktrees/pipeline-d6a6ef23/components/WorkspaceView.tsx +4048 -0
  1859. package/.forge/worktrees/pipeline-d6a6ef23/dev-test.sh +5 -0
  1860. package/.forge/worktrees/pipeline-d6a6ef23/docs/Forge_Memory_Layer_Design.docx +0 -0
  1861. package/.forge/worktrees/pipeline-d6a6ef23/docs/Forge_Strategy_Research_2026.docx +0 -0
  1862. package/.forge/worktrees/pipeline-d6a6ef23/docs/LOCAL-DEPLOY.md +144 -0
  1863. package/.forge/worktrees/pipeline-d6a6ef23/docs/roadmap-multi-agent-workflow.md +330 -0
  1864. package/.forge/worktrees/pipeline-d6a6ef23/forge-logo.png +0 -0
  1865. package/.forge/worktrees/pipeline-d6a6ef23/forge-logo.svg +106 -0
  1866. package/.forge/worktrees/pipeline-d6a6ef23/hooks/useSidebarResize.ts +52 -0
  1867. package/.forge/worktrees/pipeline-d6a6ef23/install.sh +29 -0
  1868. package/.forge/worktrees/pipeline-d6a6ef23/instrumentation.ts +35 -0
  1869. package/.forge/worktrees/pipeline-d6a6ef23/lib/agents/claude-adapter.ts +104 -0
  1870. package/.forge/worktrees/pipeline-d6a6ef23/lib/agents/generic-adapter.ts +64 -0
  1871. package/.forge/worktrees/pipeline-d6a6ef23/lib/agents/index.ts +245 -0
  1872. package/.forge/worktrees/pipeline-d6a6ef23/lib/agents/types.ts +70 -0
  1873. package/.forge/worktrees/pipeline-d6a6ef23/lib/artifacts.ts +106 -0
  1874. package/.forge/worktrees/pipeline-d6a6ef23/lib/auth.ts +62 -0
  1875. package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/docker.yaml +70 -0
  1876. package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/http.yaml +66 -0
  1877. package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/jenkins.yaml +92 -0
  1878. package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/llm-vision.yaml +85 -0
  1879. package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/playwright.yaml +111 -0
  1880. package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/shell-command.yaml +60 -0
  1881. package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/slack.yaml +48 -0
  1882. package/.forge/worktrees/pipeline-d6a6ef23/lib/builtin-plugins/webhook.yaml +56 -0
  1883. package/.forge/worktrees/pipeline-d6a6ef23/lib/claude-process.ts +352 -0
  1884. package/.forge/worktrees/pipeline-d6a6ef23/lib/claude-sessions.ts +266 -0
  1885. package/.forge/worktrees/pipeline-d6a6ef23/lib/claude-templates.ts +227 -0
  1886. package/.forge/worktrees/pipeline-d6a6ef23/lib/cloudflared.ts +424 -0
  1887. package/.forge/worktrees/pipeline-d6a6ef23/lib/crypto.ts +67 -0
  1888. package/.forge/worktrees/pipeline-d6a6ef23/lib/delivery.ts +787 -0
  1889. package/.forge/worktrees/pipeline-d6a6ef23/lib/dirs.ts +99 -0
  1890. package/.forge/worktrees/pipeline-d6a6ef23/lib/flows.ts +86 -0
  1891. package/.forge/worktrees/pipeline-d6a6ef23/lib/forge-mcp-server.ts +717 -0
  1892. package/.forge/worktrees/pipeline-d6a6ef23/lib/forge-skills/forge-inbox.md +38 -0
  1893. package/.forge/worktrees/pipeline-d6a6ef23/lib/forge-skills/forge-send.md +47 -0
  1894. package/.forge/worktrees/pipeline-d6a6ef23/lib/forge-skills/forge-status.md +32 -0
  1895. package/.forge/worktrees/pipeline-d6a6ef23/lib/forge-skills/forge-workspace-sync.md +37 -0
  1896. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/00-overview.md +40 -0
  1897. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/01-settings.md +194 -0
  1898. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/02-telegram.md +41 -0
  1899. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/03-tunnel.md +31 -0
  1900. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/04-tasks.md +52 -0
  1901. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/05-pipelines.md +460 -0
  1902. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/06-skills.md +43 -0
  1903. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/07-projects.md +73 -0
  1904. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/08-rules.md +53 -0
  1905. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/09-issue-autofix.md +55 -0
  1906. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/10-troubleshooting.md +89 -0
  1907. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/11-workspace.md +810 -0
  1908. package/.forge/worktrees/pipeline-d6a6ef23/lib/help-docs/CLAUDE.md +62 -0
  1909. package/.forge/worktrees/pipeline-d6a6ef23/lib/init.ts +266 -0
  1910. package/.forge/worktrees/pipeline-d6a6ef23/lib/issue-scanner.ts +298 -0
  1911. package/.forge/worktrees/pipeline-d6a6ef23/lib/logger.ts +79 -0
  1912. package/.forge/worktrees/pipeline-d6a6ef23/lib/notifications.ts +75 -0
  1913. package/.forge/worktrees/pipeline-d6a6ef23/lib/notify.ts +108 -0
  1914. package/.forge/worktrees/pipeline-d6a6ef23/lib/password.ts +97 -0
  1915. package/.forge/worktrees/pipeline-d6a6ef23/lib/pipeline-scheduler.ts +373 -0
  1916. package/.forge/worktrees/pipeline-d6a6ef23/lib/pipeline.ts +1438 -0
  1917. package/.forge/worktrees/pipeline-d6a6ef23/lib/plugins/executor.ts +347 -0
  1918. package/.forge/worktrees/pipeline-d6a6ef23/lib/plugins/registry.ts +228 -0
  1919. package/.forge/worktrees/pipeline-d6a6ef23/lib/plugins/types.ts +103 -0
  1920. package/.forge/worktrees/pipeline-d6a6ef23/lib/project-sessions.ts +53 -0
  1921. package/.forge/worktrees/pipeline-d6a6ef23/lib/projects.ts +86 -0
  1922. package/.forge/worktrees/pipeline-d6a6ef23/lib/session-manager.ts +156 -0
  1923. package/.forge/worktrees/pipeline-d6a6ef23/lib/session-utils.ts +53 -0
  1924. package/.forge/worktrees/pipeline-d6a6ef23/lib/session-watcher.ts +345 -0
  1925. package/.forge/worktrees/pipeline-d6a6ef23/lib/settings.ts +195 -0
  1926. package/.forge/worktrees/pipeline-d6a6ef23/lib/skills.ts +458 -0
  1927. package/.forge/worktrees/pipeline-d6a6ef23/lib/task-manager.ts +949 -0
  1928. package/.forge/worktrees/pipeline-d6a6ef23/lib/telegram-bot.ts +1477 -0
  1929. package/.forge/worktrees/pipeline-d6a6ef23/lib/telegram-standalone.ts +83 -0
  1930. package/.forge/worktrees/pipeline-d6a6ef23/lib/terminal-server.ts +70 -0
  1931. package/.forge/worktrees/pipeline-d6a6ef23/lib/terminal-standalone.ts +421 -0
  1932. package/.forge/worktrees/pipeline-d6a6ef23/lib/usage-scanner.ts +249 -0
  1933. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/__tests__/state-machine.test.ts +388 -0
  1934. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/__tests__/workspace.test.ts +311 -0
  1935. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/agent-bus.ts +416 -0
  1936. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/agent-worker.ts +655 -0
  1937. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/backends/api-backend.ts +262 -0
  1938. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/backends/cli-backend.ts +491 -0
  1939. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/index.ts +82 -0
  1940. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/manager.ts +136 -0
  1941. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/orchestrator.ts +3400 -0
  1942. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/persistence.ts +309 -0
  1943. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/presets.ts +649 -0
  1944. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/requests.ts +287 -0
  1945. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/session-monitor.ts +240 -0
  1946. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/skill-installer.ts +275 -0
  1947. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/smith-memory.ts +498 -0
  1948. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/types.ts +241 -0
  1949. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace/watch-manager.ts +560 -0
  1950. package/.forge/worktrees/pipeline-d6a6ef23/lib/workspace-standalone.ts +911 -0
  1951. package/.forge/worktrees/pipeline-d6a6ef23/middleware.ts +51 -0
  1952. package/.forge/worktrees/pipeline-d6a6ef23/next.config.ts +26 -0
  1953. package/.forge/worktrees/pipeline-d6a6ef23/package.json +74 -0
  1954. package/.forge/worktrees/pipeline-d6a6ef23/pnpm-lock.yaml +3719 -0
  1955. package/.forge/worktrees/pipeline-d6a6ef23/pnpm-workspace.yaml +1 -0
  1956. package/.forge/worktrees/pipeline-d6a6ef23/postcss.config.mjs +7 -0
  1957. package/.forge/worktrees/pipeline-d6a6ef23/publish.sh +133 -0
  1958. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/README.md +66 -0
  1959. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/results/.gitignore +2 -0
  1960. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/run.ts +635 -0
  1961. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/01-text-utils/task.md +26 -0
  1962. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  1963. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  1964. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/02-pagination/task.md +48 -0
  1965. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  1966. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  1967. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  1968. package/.forge/worktrees/pipeline-d6a6ef23/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  1969. package/.forge/worktrees/pipeline-d6a6ef23/scripts/verify-usage.ts +178 -0
  1970. package/.forge/worktrees/pipeline-d6a6ef23/src/config/index.ts +129 -0
  1971. package/.forge/worktrees/pipeline-d6a6ef23/src/core/db/database.ts +259 -0
  1972. package/.forge/worktrees/pipeline-d6a6ef23/src/core/memory/strategy.ts +32 -0
  1973. package/.forge/worktrees/pipeline-d6a6ef23/src/core/providers/chat.ts +65 -0
  1974. package/.forge/worktrees/pipeline-d6a6ef23/src/core/providers/registry.ts +60 -0
  1975. package/.forge/worktrees/pipeline-d6a6ef23/src/core/session/manager.ts +190 -0
  1976. package/.forge/worktrees/pipeline-d6a6ef23/src/types/index.ts +129 -0
  1977. package/.forge/worktrees/pipeline-d6a6ef23/start.sh +31 -0
  1978. package/.forge/worktrees/pipeline-d6a6ef23/templates/smith-lead.json +45 -0
  1979. package/.forge/worktrees/pipeline-d6a6ef23/tsconfig.json +42 -0
  1980. package/.forge/worktrees/pipeline-e7f78b7a/CLAUDE.md +86 -0
  1981. package/.forge/worktrees/pipeline-e7f78b7a/README.md +136 -0
  1982. package/.forge/worktrees/pipeline-e7f78b7a/RELEASE_NOTES.md +11 -0
  1983. package/.forge/worktrees/pipeline-e7f78b7a/app/api/agents/route.ts +17 -0
  1984. package/.forge/worktrees/pipeline-e7f78b7a/app/api/auth/[...nextauth]/route.ts +3 -0
  1985. package/.forge/worktrees/pipeline-e7f78b7a/app/api/auth/verify/route.ts +46 -0
  1986. package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude/[id]/route.ts +31 -0
  1987. package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude/[id]/stream/route.ts +63 -0
  1988. package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude/route.ts +28 -0
  1989. package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  1990. package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  1991. package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude-sessions/[projectName]/route.ts +37 -0
  1992. package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude-sessions/sync/route.ts +17 -0
  1993. package/.forge/worktrees/pipeline-e7f78b7a/app/api/claude-templates/route.ts +145 -0
  1994. package/.forge/worktrees/pipeline-e7f78b7a/app/api/code/route.ts +299 -0
  1995. package/.forge/worktrees/pipeline-e7f78b7a/app/api/delivery/[id]/route.ts +62 -0
  1996. package/.forge/worktrees/pipeline-e7f78b7a/app/api/delivery/route.ts +40 -0
  1997. package/.forge/worktrees/pipeline-e7f78b7a/app/api/detect-cli/route.ts +46 -0
  1998. package/.forge/worktrees/pipeline-e7f78b7a/app/api/docs/route.ts +176 -0
  1999. package/.forge/worktrees/pipeline-e7f78b7a/app/api/docs/sessions/route.ts +54 -0
  2000. package/.forge/worktrees/pipeline-e7f78b7a/app/api/favorites/route.ts +26 -0
  2001. package/.forge/worktrees/pipeline-e7f78b7a/app/api/flows/route.ts +6 -0
  2002. package/.forge/worktrees/pipeline-e7f78b7a/app/api/flows/run/route.ts +19 -0
  2003. package/.forge/worktrees/pipeline-e7f78b7a/app/api/git/route.ts +149 -0
  2004. package/.forge/worktrees/pipeline-e7f78b7a/app/api/help/route.ts +84 -0
  2005. package/.forge/worktrees/pipeline-e7f78b7a/app/api/issue-scanner/route.ts +116 -0
  2006. package/.forge/worktrees/pipeline-e7f78b7a/app/api/logs/route.ts +100 -0
  2007. package/.forge/worktrees/pipeline-e7f78b7a/app/api/mobile-chat/route.ts +115 -0
  2008. package/.forge/worktrees/pipeline-e7f78b7a/app/api/monitor/route.ts +74 -0
  2009. package/.forge/worktrees/pipeline-e7f78b7a/app/api/notifications/route.ts +42 -0
  2010. package/.forge/worktrees/pipeline-e7f78b7a/app/api/notify/test/route.ts +33 -0
  2011. package/.forge/worktrees/pipeline-e7f78b7a/app/api/online/route.ts +40 -0
  2012. package/.forge/worktrees/pipeline-e7f78b7a/app/api/pipelines/[id]/route.ts +41 -0
  2013. package/.forge/worktrees/pipeline-e7f78b7a/app/api/pipelines/route.ts +90 -0
  2014. package/.forge/worktrees/pipeline-e7f78b7a/app/api/plugins/route.ts +75 -0
  2015. package/.forge/worktrees/pipeline-e7f78b7a/app/api/preview/[...path]/route.ts +64 -0
  2016. package/.forge/worktrees/pipeline-e7f78b7a/app/api/preview/route.ts +156 -0
  2017. package/.forge/worktrees/pipeline-e7f78b7a/app/api/project-pipelines/route.ts +91 -0
  2018. package/.forge/worktrees/pipeline-e7f78b7a/app/api/project-sessions/route.ts +61 -0
  2019. package/.forge/worktrees/pipeline-e7f78b7a/app/api/projects/route.ts +26 -0
  2020. package/.forge/worktrees/pipeline-e7f78b7a/app/api/sessions/[id]/chat/route.ts +64 -0
  2021. package/.forge/worktrees/pipeline-e7f78b7a/app/api/sessions/[id]/messages/route.ts +9 -0
  2022. package/.forge/worktrees/pipeline-e7f78b7a/app/api/sessions/[id]/route.ts +17 -0
  2023. package/.forge/worktrees/pipeline-e7f78b7a/app/api/sessions/route.ts +20 -0
  2024. package/.forge/worktrees/pipeline-e7f78b7a/app/api/settings/route.ts +64 -0
  2025. package/.forge/worktrees/pipeline-e7f78b7a/app/api/skills/local/route.ts +228 -0
  2026. package/.forge/worktrees/pipeline-e7f78b7a/app/api/skills/route.ts +182 -0
  2027. package/.forge/worktrees/pipeline-e7f78b7a/app/api/smith-templates/route.ts +81 -0
  2028. package/.forge/worktrees/pipeline-e7f78b7a/app/api/status/route.ts +12 -0
  2029. package/.forge/worktrees/pipeline-e7f78b7a/app/api/tabs/route.ts +25 -0
  2030. package/.forge/worktrees/pipeline-e7f78b7a/app/api/tasks/[id]/route.ts +51 -0
  2031. package/.forge/worktrees/pipeline-e7f78b7a/app/api/tasks/[id]/stream/route.ts +77 -0
  2032. package/.forge/worktrees/pipeline-e7f78b7a/app/api/tasks/link/route.ts +37 -0
  2033. package/.forge/worktrees/pipeline-e7f78b7a/app/api/tasks/route.ts +44 -0
  2034. package/.forge/worktrees/pipeline-e7f78b7a/app/api/tasks/session/route.ts +14 -0
  2035. package/.forge/worktrees/pipeline-e7f78b7a/app/api/telegram/route.ts +23 -0
  2036. package/.forge/worktrees/pipeline-e7f78b7a/app/api/templates/route.ts +6 -0
  2037. package/.forge/worktrees/pipeline-e7f78b7a/app/api/terminal-bell/route.ts +35 -0
  2038. package/.forge/worktrees/pipeline-e7f78b7a/app/api/terminal-cwd/route.ts +19 -0
  2039. package/.forge/worktrees/pipeline-e7f78b7a/app/api/terminal-state/route.ts +15 -0
  2040. package/.forge/worktrees/pipeline-e7f78b7a/app/api/tunnel/route.ts +26 -0
  2041. package/.forge/worktrees/pipeline-e7f78b7a/app/api/upgrade/route.ts +43 -0
  2042. package/.forge/worktrees/pipeline-e7f78b7a/app/api/usage/route.ts +20 -0
  2043. package/.forge/worktrees/pipeline-e7f78b7a/app/api/version/route.ts +78 -0
  2044. package/.forge/worktrees/pipeline-e7f78b7a/app/api/watchers/route.ts +33 -0
  2045. package/.forge/worktrees/pipeline-e7f78b7a/app/api/workspace/[id]/agents/route.ts +35 -0
  2046. package/.forge/worktrees/pipeline-e7f78b7a/app/api/workspace/[id]/memory/route.ts +23 -0
  2047. package/.forge/worktrees/pipeline-e7f78b7a/app/api/workspace/[id]/smith/route.ts +22 -0
  2048. package/.forge/worktrees/pipeline-e7f78b7a/app/api/workspace/[id]/stream/route.ts +28 -0
  2049. package/.forge/worktrees/pipeline-e7f78b7a/app/api/workspace/route.ts +100 -0
  2050. package/.forge/worktrees/pipeline-e7f78b7a/app/global-error.tsx +21 -0
  2051. package/.forge/worktrees/pipeline-e7f78b7a/app/globals.css +52 -0
  2052. package/.forge/worktrees/pipeline-e7f78b7a/app/icon.ico +0 -0
  2053. package/.forge/worktrees/pipeline-e7f78b7a/app/icon.png +0 -0
  2054. package/.forge/worktrees/pipeline-e7f78b7a/app/icon.svg +106 -0
  2055. package/.forge/worktrees/pipeline-e7f78b7a/app/layout.tsx +17 -0
  2056. package/.forge/worktrees/pipeline-e7f78b7a/app/login/LoginForm.tsx +96 -0
  2057. package/.forge/worktrees/pipeline-e7f78b7a/app/login/page.tsx +10 -0
  2058. package/.forge/worktrees/pipeline-e7f78b7a/app/mobile/page.tsx +9 -0
  2059. package/.forge/worktrees/pipeline-e7f78b7a/app/page.tsx +21 -0
  2060. package/.forge/worktrees/pipeline-e7f78b7a/bin/forge-server.mjs +484 -0
  2061. package/.forge/worktrees/pipeline-e7f78b7a/check-forge-status.sh +71 -0
  2062. package/.forge/worktrees/pipeline-e7f78b7a/cli/mw.ts +579 -0
  2063. package/.forge/worktrees/pipeline-e7f78b7a/components/BrowserPanel.tsx +175 -0
  2064. package/.forge/worktrees/pipeline-e7f78b7a/components/ChatPanel.tsx +191 -0
  2065. package/.forge/worktrees/pipeline-e7f78b7a/components/ClaudeTerminal.tsx +267 -0
  2066. package/.forge/worktrees/pipeline-e7f78b7a/components/CodeViewer.tsx +787 -0
  2067. package/.forge/worktrees/pipeline-e7f78b7a/components/ConversationEditor.tsx +411 -0
  2068. package/.forge/worktrees/pipeline-e7f78b7a/components/ConversationGraphView.tsx +347 -0
  2069. package/.forge/worktrees/pipeline-e7f78b7a/components/ConversationTerminalView.tsx +303 -0
  2070. package/.forge/worktrees/pipeline-e7f78b7a/components/Dashboard.tsx +807 -0
  2071. package/.forge/worktrees/pipeline-e7f78b7a/components/DashboardWrapper.tsx +9 -0
  2072. package/.forge/worktrees/pipeline-e7f78b7a/components/DeliveryFlowEditor.tsx +491 -0
  2073. package/.forge/worktrees/pipeline-e7f78b7a/components/DeliveryList.tsx +230 -0
  2074. package/.forge/worktrees/pipeline-e7f78b7a/components/DeliveryWorkspace.tsx +589 -0
  2075. package/.forge/worktrees/pipeline-e7f78b7a/components/DocTerminal.tsx +187 -0
  2076. package/.forge/worktrees/pipeline-e7f78b7a/components/DocsViewer.tsx +574 -0
  2077. package/.forge/worktrees/pipeline-e7f78b7a/components/HelpDialog.tsx +169 -0
  2078. package/.forge/worktrees/pipeline-e7f78b7a/components/HelpTerminal.tsx +141 -0
  2079. package/.forge/worktrees/pipeline-e7f78b7a/components/InlinePipelineView.tsx +111 -0
  2080. package/.forge/worktrees/pipeline-e7f78b7a/components/LogViewer.tsx +194 -0
  2081. package/.forge/worktrees/pipeline-e7f78b7a/components/MarkdownContent.tsx +73 -0
  2082. package/.forge/worktrees/pipeline-e7f78b7a/components/MobileView.tsx +385 -0
  2083. package/.forge/worktrees/pipeline-e7f78b7a/components/MonitorPanel.tsx +122 -0
  2084. package/.forge/worktrees/pipeline-e7f78b7a/components/NewSessionModal.tsx +93 -0
  2085. package/.forge/worktrees/pipeline-e7f78b7a/components/NewTaskModal.tsx +492 -0
  2086. package/.forge/worktrees/pipeline-e7f78b7a/components/PipelineEditor.tsx +570 -0
  2087. package/.forge/worktrees/pipeline-e7f78b7a/components/PipelineView.tsx +1018 -0
  2088. package/.forge/worktrees/pipeline-e7f78b7a/components/PluginsPanel.tsx +472 -0
  2089. package/.forge/worktrees/pipeline-e7f78b7a/components/ProjectDetail.tsx +1618 -0
  2090. package/.forge/worktrees/pipeline-e7f78b7a/components/ProjectList.tsx +108 -0
  2091. package/.forge/worktrees/pipeline-e7f78b7a/components/ProjectManager.tsx +401 -0
  2092. package/.forge/worktrees/pipeline-e7f78b7a/components/SessionList.tsx +74 -0
  2093. package/.forge/worktrees/pipeline-e7f78b7a/components/SessionView.tsx +726 -0
  2094. package/.forge/worktrees/pipeline-e7f78b7a/components/SettingsModal.tsx +1647 -0
  2095. package/.forge/worktrees/pipeline-e7f78b7a/components/SkillsPanel.tsx +969 -0
  2096. package/.forge/worktrees/pipeline-e7f78b7a/components/StatusBar.tsx +99 -0
  2097. package/.forge/worktrees/pipeline-e7f78b7a/components/TabBar.tsx +46 -0
  2098. package/.forge/worktrees/pipeline-e7f78b7a/components/TaskBoard.tsx +113 -0
  2099. package/.forge/worktrees/pipeline-e7f78b7a/components/TaskDetail.tsx +372 -0
  2100. package/.forge/worktrees/pipeline-e7f78b7a/components/TerminalLauncher.tsx +398 -0
  2101. package/.forge/worktrees/pipeline-e7f78b7a/components/TunnelToggle.tsx +206 -0
  2102. package/.forge/worktrees/pipeline-e7f78b7a/components/UsagePanel.tsx +207 -0
  2103. package/.forge/worktrees/pipeline-e7f78b7a/components/WebTerminal.tsx +1683 -0
  2104. package/.forge/worktrees/pipeline-e7f78b7a/components/WorkspaceTree.tsx +221 -0
  2105. package/.forge/worktrees/pipeline-e7f78b7a/components/WorkspaceView.tsx +4048 -0
  2106. package/.forge/worktrees/pipeline-e7f78b7a/dev-test.sh +5 -0
  2107. package/.forge/worktrees/pipeline-e7f78b7a/docs/Forge_Memory_Layer_Design.docx +0 -0
  2108. package/.forge/worktrees/pipeline-e7f78b7a/docs/Forge_Strategy_Research_2026.docx +0 -0
  2109. package/.forge/worktrees/pipeline-e7f78b7a/docs/LOCAL-DEPLOY.md +144 -0
  2110. package/.forge/worktrees/pipeline-e7f78b7a/docs/roadmap-multi-agent-workflow.md +330 -0
  2111. package/.forge/worktrees/pipeline-e7f78b7a/forge-logo.png +0 -0
  2112. package/.forge/worktrees/pipeline-e7f78b7a/forge-logo.svg +106 -0
  2113. package/.forge/worktrees/pipeline-e7f78b7a/hooks/useSidebarResize.ts +52 -0
  2114. package/.forge/worktrees/pipeline-e7f78b7a/install.sh +29 -0
  2115. package/.forge/worktrees/pipeline-e7f78b7a/instrumentation.ts +35 -0
  2116. package/.forge/worktrees/pipeline-e7f78b7a/lib/agents/claude-adapter.ts +104 -0
  2117. package/.forge/worktrees/pipeline-e7f78b7a/lib/agents/generic-adapter.ts +64 -0
  2118. package/.forge/worktrees/pipeline-e7f78b7a/lib/agents/index.ts +245 -0
  2119. package/.forge/worktrees/pipeline-e7f78b7a/lib/agents/types.ts +70 -0
  2120. package/.forge/worktrees/pipeline-e7f78b7a/lib/artifacts.ts +106 -0
  2121. package/.forge/worktrees/pipeline-e7f78b7a/lib/auth.ts +62 -0
  2122. package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/docker.yaml +70 -0
  2123. package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/http.yaml +66 -0
  2124. package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/jenkins.yaml +92 -0
  2125. package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/llm-vision.yaml +85 -0
  2126. package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/playwright.yaml +111 -0
  2127. package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/shell-command.yaml +60 -0
  2128. package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/slack.yaml +48 -0
  2129. package/.forge/worktrees/pipeline-e7f78b7a/lib/builtin-plugins/webhook.yaml +56 -0
  2130. package/.forge/worktrees/pipeline-e7f78b7a/lib/claude-process.ts +352 -0
  2131. package/.forge/worktrees/pipeline-e7f78b7a/lib/claude-sessions.ts +266 -0
  2132. package/.forge/worktrees/pipeline-e7f78b7a/lib/claude-templates.ts +227 -0
  2133. package/.forge/worktrees/pipeline-e7f78b7a/lib/cloudflared.ts +424 -0
  2134. package/.forge/worktrees/pipeline-e7f78b7a/lib/crypto.ts +67 -0
  2135. package/.forge/worktrees/pipeline-e7f78b7a/lib/delivery.ts +787 -0
  2136. package/.forge/worktrees/pipeline-e7f78b7a/lib/dirs.ts +99 -0
  2137. package/.forge/worktrees/pipeline-e7f78b7a/lib/flows.ts +86 -0
  2138. package/.forge/worktrees/pipeline-e7f78b7a/lib/forge-mcp-server.ts +717 -0
  2139. package/.forge/worktrees/pipeline-e7f78b7a/lib/forge-skills/forge-inbox.md +38 -0
  2140. package/.forge/worktrees/pipeline-e7f78b7a/lib/forge-skills/forge-send.md +47 -0
  2141. package/.forge/worktrees/pipeline-e7f78b7a/lib/forge-skills/forge-status.md +32 -0
  2142. package/.forge/worktrees/pipeline-e7f78b7a/lib/forge-skills/forge-workspace-sync.md +37 -0
  2143. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/00-overview.md +40 -0
  2144. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/01-settings.md +194 -0
  2145. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/02-telegram.md +41 -0
  2146. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/03-tunnel.md +31 -0
  2147. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/04-tasks.md +52 -0
  2148. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/05-pipelines.md +460 -0
  2149. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/06-skills.md +43 -0
  2150. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/07-projects.md +73 -0
  2151. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/08-rules.md +53 -0
  2152. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/09-issue-autofix.md +55 -0
  2153. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/10-troubleshooting.md +89 -0
  2154. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/11-workspace.md +810 -0
  2155. package/.forge/worktrees/pipeline-e7f78b7a/lib/help-docs/CLAUDE.md +62 -0
  2156. package/.forge/worktrees/pipeline-e7f78b7a/lib/init.ts +266 -0
  2157. package/.forge/worktrees/pipeline-e7f78b7a/lib/issue-scanner.ts +298 -0
  2158. package/.forge/worktrees/pipeline-e7f78b7a/lib/logger.ts +79 -0
  2159. package/.forge/worktrees/pipeline-e7f78b7a/lib/notifications.ts +75 -0
  2160. package/.forge/worktrees/pipeline-e7f78b7a/lib/notify.ts +108 -0
  2161. package/.forge/worktrees/pipeline-e7f78b7a/lib/password.ts +97 -0
  2162. package/.forge/worktrees/pipeline-e7f78b7a/lib/pipeline-scheduler.ts +373 -0
  2163. package/.forge/worktrees/pipeline-e7f78b7a/lib/pipeline.ts +1441 -0
  2164. package/.forge/worktrees/pipeline-e7f78b7a/lib/plugins/executor.ts +347 -0
  2165. package/.forge/worktrees/pipeline-e7f78b7a/lib/plugins/registry.ts +228 -0
  2166. package/.forge/worktrees/pipeline-e7f78b7a/lib/plugins/types.ts +103 -0
  2167. package/.forge/worktrees/pipeline-e7f78b7a/lib/project-sessions.ts +53 -0
  2168. package/.forge/worktrees/pipeline-e7f78b7a/lib/projects.ts +86 -0
  2169. package/.forge/worktrees/pipeline-e7f78b7a/lib/session-manager.ts +156 -0
  2170. package/.forge/worktrees/pipeline-e7f78b7a/lib/session-utils.ts +53 -0
  2171. package/.forge/worktrees/pipeline-e7f78b7a/lib/session-watcher.ts +345 -0
  2172. package/.forge/worktrees/pipeline-e7f78b7a/lib/settings.ts +195 -0
  2173. package/.forge/worktrees/pipeline-e7f78b7a/lib/skills.ts +458 -0
  2174. package/.forge/worktrees/pipeline-e7f78b7a/lib/task-manager.ts +949 -0
  2175. package/.forge/worktrees/pipeline-e7f78b7a/lib/telegram-bot.ts +1477 -0
  2176. package/.forge/worktrees/pipeline-e7f78b7a/lib/telegram-standalone.ts +83 -0
  2177. package/.forge/worktrees/pipeline-e7f78b7a/lib/terminal-server.ts +70 -0
  2178. package/.forge/worktrees/pipeline-e7f78b7a/lib/terminal-standalone.ts +421 -0
  2179. package/.forge/worktrees/pipeline-e7f78b7a/lib/usage-scanner.ts +249 -0
  2180. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/__tests__/state-machine.test.ts +388 -0
  2181. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/__tests__/workspace.test.ts +311 -0
  2182. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/agent-bus.ts +416 -0
  2183. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/agent-worker.ts +655 -0
  2184. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/backends/api-backend.ts +262 -0
  2185. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/backends/cli-backend.ts +491 -0
  2186. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/index.ts +82 -0
  2187. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/manager.ts +136 -0
  2188. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/orchestrator.ts +3400 -0
  2189. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/persistence.ts +309 -0
  2190. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/presets.ts +649 -0
  2191. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/requests.ts +287 -0
  2192. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/session-monitor.ts +240 -0
  2193. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/skill-installer.ts +275 -0
  2194. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/smith-memory.ts +498 -0
  2195. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/types.ts +241 -0
  2196. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace/watch-manager.ts +560 -0
  2197. package/.forge/worktrees/pipeline-e7f78b7a/lib/workspace-standalone.ts +911 -0
  2198. package/.forge/worktrees/pipeline-e7f78b7a/middleware.ts +51 -0
  2199. package/.forge/worktrees/pipeline-e7f78b7a/next.config.ts +26 -0
  2200. package/.forge/worktrees/pipeline-e7f78b7a/package.json +74 -0
  2201. package/.forge/worktrees/pipeline-e7f78b7a/pnpm-lock.yaml +3719 -0
  2202. package/.forge/worktrees/pipeline-e7f78b7a/pnpm-workspace.yaml +1 -0
  2203. package/.forge/worktrees/pipeline-e7f78b7a/postcss.config.mjs +7 -0
  2204. package/.forge/worktrees/pipeline-e7f78b7a/publish.sh +133 -0
  2205. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/README.md +66 -0
  2206. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/results/.gitignore +2 -0
  2207. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/run.ts +635 -0
  2208. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/01-text-utils/task.md +26 -0
  2209. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  2210. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  2211. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/02-pagination/task.md +48 -0
  2212. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  2213. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  2214. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  2215. package/.forge/worktrees/pipeline-e7f78b7a/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  2216. package/.forge/worktrees/pipeline-e7f78b7a/scripts/verify-usage.ts +178 -0
  2217. package/.forge/worktrees/pipeline-e7f78b7a/src/config/index.ts +129 -0
  2218. package/.forge/worktrees/pipeline-e7f78b7a/src/core/db/database.ts +259 -0
  2219. package/.forge/worktrees/pipeline-e7f78b7a/src/core/memory/strategy.ts +32 -0
  2220. package/.forge/worktrees/pipeline-e7f78b7a/src/core/providers/chat.ts +65 -0
  2221. package/.forge/worktrees/pipeline-e7f78b7a/src/core/providers/registry.ts +60 -0
  2222. package/.forge/worktrees/pipeline-e7f78b7a/src/core/session/manager.ts +190 -0
  2223. package/.forge/worktrees/pipeline-e7f78b7a/src/types/index.ts +129 -0
  2224. package/.forge/worktrees/pipeline-e7f78b7a/start.sh +31 -0
  2225. package/.forge/worktrees/pipeline-e7f78b7a/templates/smith-lead.json +45 -0
  2226. package/.forge/worktrees/pipeline-e7f78b7a/tsconfig.json +42 -0
  2227. package/.forge/worktrees/pipeline-e97c13c7/CLAUDE.md +86 -0
  2228. package/.forge/worktrees/pipeline-e97c13c7/README.md +136 -0
  2229. package/.forge/worktrees/pipeline-e97c13c7/RELEASE_NOTES.md +11 -0
  2230. package/.forge/worktrees/pipeline-e97c13c7/app/api/agents/route.ts +17 -0
  2231. package/.forge/worktrees/pipeline-e97c13c7/app/api/auth/[...nextauth]/route.ts +3 -0
  2232. package/.forge/worktrees/pipeline-e97c13c7/app/api/auth/verify/route.ts +46 -0
  2233. package/.forge/worktrees/pipeline-e97c13c7/app/api/claude/[id]/route.ts +31 -0
  2234. package/.forge/worktrees/pipeline-e97c13c7/app/api/claude/[id]/stream/route.ts +63 -0
  2235. package/.forge/worktrees/pipeline-e97c13c7/app/api/claude/route.ts +28 -0
  2236. package/.forge/worktrees/pipeline-e97c13c7/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  2237. package/.forge/worktrees/pipeline-e97c13c7/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  2238. package/.forge/worktrees/pipeline-e97c13c7/app/api/claude-sessions/[projectName]/route.ts +37 -0
  2239. package/.forge/worktrees/pipeline-e97c13c7/app/api/claude-sessions/sync/route.ts +17 -0
  2240. package/.forge/worktrees/pipeline-e97c13c7/app/api/claude-templates/route.ts +145 -0
  2241. package/.forge/worktrees/pipeline-e97c13c7/app/api/code/route.ts +299 -0
  2242. package/.forge/worktrees/pipeline-e97c13c7/app/api/delivery/[id]/route.ts +62 -0
  2243. package/.forge/worktrees/pipeline-e97c13c7/app/api/delivery/route.ts +40 -0
  2244. package/.forge/worktrees/pipeline-e97c13c7/app/api/detect-cli/route.ts +46 -0
  2245. package/.forge/worktrees/pipeline-e97c13c7/app/api/docs/route.ts +176 -0
  2246. package/.forge/worktrees/pipeline-e97c13c7/app/api/docs/sessions/route.ts +54 -0
  2247. package/.forge/worktrees/pipeline-e97c13c7/app/api/favorites/route.ts +26 -0
  2248. package/.forge/worktrees/pipeline-e97c13c7/app/api/flows/route.ts +6 -0
  2249. package/.forge/worktrees/pipeline-e97c13c7/app/api/flows/run/route.ts +19 -0
  2250. package/.forge/worktrees/pipeline-e97c13c7/app/api/git/route.ts +149 -0
  2251. package/.forge/worktrees/pipeline-e97c13c7/app/api/help/route.ts +84 -0
  2252. package/.forge/worktrees/pipeline-e97c13c7/app/api/issue-scanner/route.ts +116 -0
  2253. package/.forge/worktrees/pipeline-e97c13c7/app/api/logs/route.ts +100 -0
  2254. package/.forge/worktrees/pipeline-e97c13c7/app/api/mobile-chat/route.ts +115 -0
  2255. package/.forge/worktrees/pipeline-e97c13c7/app/api/monitor/route.ts +74 -0
  2256. package/.forge/worktrees/pipeline-e97c13c7/app/api/notifications/route.ts +42 -0
  2257. package/.forge/worktrees/pipeline-e97c13c7/app/api/notify/test/route.ts +33 -0
  2258. package/.forge/worktrees/pipeline-e97c13c7/app/api/online/route.ts +40 -0
  2259. package/.forge/worktrees/pipeline-e97c13c7/app/api/pipelines/[id]/route.ts +41 -0
  2260. package/.forge/worktrees/pipeline-e97c13c7/app/api/pipelines/route.ts +90 -0
  2261. package/.forge/worktrees/pipeline-e97c13c7/app/api/plugins/route.ts +75 -0
  2262. package/.forge/worktrees/pipeline-e97c13c7/app/api/preview/[...path]/route.ts +64 -0
  2263. package/.forge/worktrees/pipeline-e97c13c7/app/api/preview/route.ts +156 -0
  2264. package/.forge/worktrees/pipeline-e97c13c7/app/api/project-pipelines/route.ts +91 -0
  2265. package/.forge/worktrees/pipeline-e97c13c7/app/api/project-sessions/route.ts +61 -0
  2266. package/.forge/worktrees/pipeline-e97c13c7/app/api/projects/route.ts +26 -0
  2267. package/.forge/worktrees/pipeline-e97c13c7/app/api/sessions/[id]/chat/route.ts +64 -0
  2268. package/.forge/worktrees/pipeline-e97c13c7/app/api/sessions/[id]/messages/route.ts +9 -0
  2269. package/.forge/worktrees/pipeline-e97c13c7/app/api/sessions/[id]/route.ts +17 -0
  2270. package/.forge/worktrees/pipeline-e97c13c7/app/api/sessions/route.ts +20 -0
  2271. package/.forge/worktrees/pipeline-e97c13c7/app/api/settings/route.ts +64 -0
  2272. package/.forge/worktrees/pipeline-e97c13c7/app/api/skills/local/route.ts +228 -0
  2273. package/.forge/worktrees/pipeline-e97c13c7/app/api/skills/route.ts +182 -0
  2274. package/.forge/worktrees/pipeline-e97c13c7/app/api/smith-templates/route.ts +81 -0
  2275. package/.forge/worktrees/pipeline-e97c13c7/app/api/status/route.ts +12 -0
  2276. package/.forge/worktrees/pipeline-e97c13c7/app/api/tabs/route.ts +25 -0
  2277. package/.forge/worktrees/pipeline-e97c13c7/app/api/tasks/[id]/route.ts +51 -0
  2278. package/.forge/worktrees/pipeline-e97c13c7/app/api/tasks/[id]/stream/route.ts +77 -0
  2279. package/.forge/worktrees/pipeline-e97c13c7/app/api/tasks/link/route.ts +37 -0
  2280. package/.forge/worktrees/pipeline-e97c13c7/app/api/tasks/route.ts +44 -0
  2281. package/.forge/worktrees/pipeline-e97c13c7/app/api/tasks/session/route.ts +14 -0
  2282. package/.forge/worktrees/pipeline-e97c13c7/app/api/telegram/route.ts +23 -0
  2283. package/.forge/worktrees/pipeline-e97c13c7/app/api/templates/route.ts +6 -0
  2284. package/.forge/worktrees/pipeline-e97c13c7/app/api/terminal-bell/route.ts +35 -0
  2285. package/.forge/worktrees/pipeline-e97c13c7/app/api/terminal-cwd/route.ts +19 -0
  2286. package/.forge/worktrees/pipeline-e97c13c7/app/api/terminal-state/route.ts +15 -0
  2287. package/.forge/worktrees/pipeline-e97c13c7/app/api/tunnel/route.ts +26 -0
  2288. package/.forge/worktrees/pipeline-e97c13c7/app/api/upgrade/route.ts +43 -0
  2289. package/.forge/worktrees/pipeline-e97c13c7/app/api/usage/route.ts +20 -0
  2290. package/.forge/worktrees/pipeline-e97c13c7/app/api/version/route.ts +78 -0
  2291. package/.forge/worktrees/pipeline-e97c13c7/app/api/watchers/route.ts +33 -0
  2292. package/.forge/worktrees/pipeline-e97c13c7/app/api/workspace/[id]/agents/route.ts +35 -0
  2293. package/.forge/worktrees/pipeline-e97c13c7/app/api/workspace/[id]/memory/route.ts +23 -0
  2294. package/.forge/worktrees/pipeline-e97c13c7/app/api/workspace/[id]/smith/route.ts +22 -0
  2295. package/.forge/worktrees/pipeline-e97c13c7/app/api/workspace/[id]/stream/route.ts +28 -0
  2296. package/.forge/worktrees/pipeline-e97c13c7/app/api/workspace/route.ts +100 -0
  2297. package/.forge/worktrees/pipeline-e97c13c7/app/global-error.tsx +21 -0
  2298. package/.forge/worktrees/pipeline-e97c13c7/app/globals.css +52 -0
  2299. package/.forge/worktrees/pipeline-e97c13c7/app/icon.ico +0 -0
  2300. package/.forge/worktrees/pipeline-e97c13c7/app/icon.png +0 -0
  2301. package/.forge/worktrees/pipeline-e97c13c7/app/icon.svg +106 -0
  2302. package/.forge/worktrees/pipeline-e97c13c7/app/layout.tsx +17 -0
  2303. package/.forge/worktrees/pipeline-e97c13c7/app/login/LoginForm.tsx +96 -0
  2304. package/.forge/worktrees/pipeline-e97c13c7/app/login/page.tsx +10 -0
  2305. package/.forge/worktrees/pipeline-e97c13c7/app/mobile/page.tsx +9 -0
  2306. package/.forge/worktrees/pipeline-e97c13c7/app/page.tsx +21 -0
  2307. package/.forge/worktrees/pipeline-e97c13c7/bin/forge-server.mjs +484 -0
  2308. package/.forge/worktrees/pipeline-e97c13c7/check-forge-status.sh +71 -0
  2309. package/.forge/worktrees/pipeline-e97c13c7/cli/mw.ts +579 -0
  2310. package/.forge/worktrees/pipeline-e97c13c7/components/BrowserPanel.tsx +175 -0
  2311. package/.forge/worktrees/pipeline-e97c13c7/components/ChatPanel.tsx +191 -0
  2312. package/.forge/worktrees/pipeline-e97c13c7/components/ClaudeTerminal.tsx +267 -0
  2313. package/.forge/worktrees/pipeline-e97c13c7/components/CodeViewer.tsx +787 -0
  2314. package/.forge/worktrees/pipeline-e97c13c7/components/ConversationEditor.tsx +411 -0
  2315. package/.forge/worktrees/pipeline-e97c13c7/components/ConversationGraphView.tsx +347 -0
  2316. package/.forge/worktrees/pipeline-e97c13c7/components/ConversationTerminalView.tsx +303 -0
  2317. package/.forge/worktrees/pipeline-e97c13c7/components/Dashboard.tsx +807 -0
  2318. package/.forge/worktrees/pipeline-e97c13c7/components/DashboardWrapper.tsx +9 -0
  2319. package/.forge/worktrees/pipeline-e97c13c7/components/DeliveryFlowEditor.tsx +491 -0
  2320. package/.forge/worktrees/pipeline-e97c13c7/components/DeliveryList.tsx +230 -0
  2321. package/.forge/worktrees/pipeline-e97c13c7/components/DeliveryWorkspace.tsx +589 -0
  2322. package/.forge/worktrees/pipeline-e97c13c7/components/DocTerminal.tsx +187 -0
  2323. package/.forge/worktrees/pipeline-e97c13c7/components/DocsViewer.tsx +574 -0
  2324. package/.forge/worktrees/pipeline-e97c13c7/components/HelpDialog.tsx +169 -0
  2325. package/.forge/worktrees/pipeline-e97c13c7/components/HelpTerminal.tsx +141 -0
  2326. package/.forge/worktrees/pipeline-e97c13c7/components/InlinePipelineView.tsx +111 -0
  2327. package/.forge/worktrees/pipeline-e97c13c7/components/LogViewer.tsx +194 -0
  2328. package/.forge/worktrees/pipeline-e97c13c7/components/MarkdownContent.tsx +73 -0
  2329. package/.forge/worktrees/pipeline-e97c13c7/components/MobileView.tsx +385 -0
  2330. package/.forge/worktrees/pipeline-e97c13c7/components/MonitorPanel.tsx +122 -0
  2331. package/.forge/worktrees/pipeline-e97c13c7/components/NewSessionModal.tsx +93 -0
  2332. package/.forge/worktrees/pipeline-e97c13c7/components/NewTaskModal.tsx +492 -0
  2333. package/.forge/worktrees/pipeline-e97c13c7/components/PipelineEditor.tsx +570 -0
  2334. package/.forge/worktrees/pipeline-e97c13c7/components/PipelineView.tsx +1018 -0
  2335. package/.forge/worktrees/pipeline-e97c13c7/components/PluginsPanel.tsx +472 -0
  2336. package/.forge/worktrees/pipeline-e97c13c7/components/ProjectDetail.tsx +1618 -0
  2337. package/.forge/worktrees/pipeline-e97c13c7/components/ProjectList.tsx +108 -0
  2338. package/.forge/worktrees/pipeline-e97c13c7/components/ProjectManager.tsx +401 -0
  2339. package/.forge/worktrees/pipeline-e97c13c7/components/SessionList.tsx +74 -0
  2340. package/.forge/worktrees/pipeline-e97c13c7/components/SessionView.tsx +726 -0
  2341. package/.forge/worktrees/pipeline-e97c13c7/components/SettingsModal.tsx +1647 -0
  2342. package/.forge/worktrees/pipeline-e97c13c7/components/SkillsPanel.tsx +969 -0
  2343. package/.forge/worktrees/pipeline-e97c13c7/components/StatusBar.tsx +99 -0
  2344. package/.forge/worktrees/pipeline-e97c13c7/components/TabBar.tsx +46 -0
  2345. package/.forge/worktrees/pipeline-e97c13c7/components/TaskBoard.tsx +113 -0
  2346. package/.forge/worktrees/pipeline-e97c13c7/components/TaskDetail.tsx +372 -0
  2347. package/.forge/worktrees/pipeline-e97c13c7/components/TerminalLauncher.tsx +398 -0
  2348. package/.forge/worktrees/pipeline-e97c13c7/components/TunnelToggle.tsx +206 -0
  2349. package/.forge/worktrees/pipeline-e97c13c7/components/UsagePanel.tsx +207 -0
  2350. package/.forge/worktrees/pipeline-e97c13c7/components/WebTerminal.tsx +1683 -0
  2351. package/.forge/worktrees/pipeline-e97c13c7/components/WorkspaceTree.tsx +221 -0
  2352. package/.forge/worktrees/pipeline-e97c13c7/components/WorkspaceView.tsx +4048 -0
  2353. package/.forge/worktrees/pipeline-e97c13c7/dev-test.sh +5 -0
  2354. package/.forge/worktrees/pipeline-e97c13c7/docs/Forge_Memory_Layer_Design.docx +0 -0
  2355. package/.forge/worktrees/pipeline-e97c13c7/docs/Forge_Strategy_Research_2026.docx +0 -0
  2356. package/.forge/worktrees/pipeline-e97c13c7/docs/LOCAL-DEPLOY.md +144 -0
  2357. package/.forge/worktrees/pipeline-e97c13c7/docs/roadmap-multi-agent-workflow.md +330 -0
  2358. package/.forge/worktrees/pipeline-e97c13c7/forge-logo.png +0 -0
  2359. package/.forge/worktrees/pipeline-e97c13c7/forge-logo.svg +106 -0
  2360. package/.forge/worktrees/pipeline-e97c13c7/hooks/useSidebarResize.ts +52 -0
  2361. package/.forge/worktrees/pipeline-e97c13c7/install.sh +29 -0
  2362. package/.forge/worktrees/pipeline-e97c13c7/instrumentation.ts +35 -0
  2363. package/.forge/worktrees/pipeline-e97c13c7/lib/agents/claude-adapter.ts +104 -0
  2364. package/.forge/worktrees/pipeline-e97c13c7/lib/agents/generic-adapter.ts +64 -0
  2365. package/.forge/worktrees/pipeline-e97c13c7/lib/agents/index.ts +245 -0
  2366. package/.forge/worktrees/pipeline-e97c13c7/lib/agents/types.ts +70 -0
  2367. package/.forge/worktrees/pipeline-e97c13c7/lib/artifacts.ts +106 -0
  2368. package/.forge/worktrees/pipeline-e97c13c7/lib/auth.ts +62 -0
  2369. package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/docker.yaml +70 -0
  2370. package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/http.yaml +66 -0
  2371. package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/jenkins.yaml +92 -0
  2372. package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/llm-vision.yaml +85 -0
  2373. package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/playwright.yaml +111 -0
  2374. package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/shell-command.yaml +60 -0
  2375. package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/slack.yaml +48 -0
  2376. package/.forge/worktrees/pipeline-e97c13c7/lib/builtin-plugins/webhook.yaml +56 -0
  2377. package/.forge/worktrees/pipeline-e97c13c7/lib/claude-process.ts +352 -0
  2378. package/.forge/worktrees/pipeline-e97c13c7/lib/claude-sessions.ts +266 -0
  2379. package/.forge/worktrees/pipeline-e97c13c7/lib/claude-templates.ts +227 -0
  2380. package/.forge/worktrees/pipeline-e97c13c7/lib/cloudflared.ts +424 -0
  2381. package/.forge/worktrees/pipeline-e97c13c7/lib/crypto.ts +67 -0
  2382. package/.forge/worktrees/pipeline-e97c13c7/lib/delivery.ts +787 -0
  2383. package/.forge/worktrees/pipeline-e97c13c7/lib/dirs.ts +99 -0
  2384. package/.forge/worktrees/pipeline-e97c13c7/lib/flows.ts +86 -0
  2385. package/.forge/worktrees/pipeline-e97c13c7/lib/forge-mcp-server.ts +717 -0
  2386. package/.forge/worktrees/pipeline-e97c13c7/lib/forge-skills/forge-inbox.md +38 -0
  2387. package/.forge/worktrees/pipeline-e97c13c7/lib/forge-skills/forge-send.md +47 -0
  2388. package/.forge/worktrees/pipeline-e97c13c7/lib/forge-skills/forge-status.md +32 -0
  2389. package/.forge/worktrees/pipeline-e97c13c7/lib/forge-skills/forge-workspace-sync.md +37 -0
  2390. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/00-overview.md +40 -0
  2391. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/01-settings.md +194 -0
  2392. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/02-telegram.md +41 -0
  2393. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/03-tunnel.md +31 -0
  2394. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/04-tasks.md +52 -0
  2395. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/05-pipelines.md +460 -0
  2396. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/06-skills.md +43 -0
  2397. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/07-projects.md +73 -0
  2398. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/08-rules.md +53 -0
  2399. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/09-issue-autofix.md +55 -0
  2400. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/10-troubleshooting.md +89 -0
  2401. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/11-workspace.md +810 -0
  2402. package/.forge/worktrees/pipeline-e97c13c7/lib/help-docs/CLAUDE.md +62 -0
  2403. package/.forge/worktrees/pipeline-e97c13c7/lib/init.ts +266 -0
  2404. package/.forge/worktrees/pipeline-e97c13c7/lib/issue-scanner.ts +298 -0
  2405. package/.forge/worktrees/pipeline-e97c13c7/lib/logger.ts +79 -0
  2406. package/.forge/worktrees/pipeline-e97c13c7/lib/notifications.ts +75 -0
  2407. package/.forge/worktrees/pipeline-e97c13c7/lib/notify.ts +108 -0
  2408. package/.forge/worktrees/pipeline-e97c13c7/lib/password.ts +97 -0
  2409. package/.forge/worktrees/pipeline-e97c13c7/lib/pipeline-scheduler.ts +373 -0
  2410. package/.forge/worktrees/pipeline-e97c13c7/lib/pipeline.ts +1438 -0
  2411. package/.forge/worktrees/pipeline-e97c13c7/lib/plugins/executor.ts +347 -0
  2412. package/.forge/worktrees/pipeline-e97c13c7/lib/plugins/registry.ts +228 -0
  2413. package/.forge/worktrees/pipeline-e97c13c7/lib/plugins/types.ts +103 -0
  2414. package/.forge/worktrees/pipeline-e97c13c7/lib/project-sessions.ts +53 -0
  2415. package/.forge/worktrees/pipeline-e97c13c7/lib/projects.ts +86 -0
  2416. package/.forge/worktrees/pipeline-e97c13c7/lib/session-manager.ts +156 -0
  2417. package/.forge/worktrees/pipeline-e97c13c7/lib/session-utils.ts +53 -0
  2418. package/.forge/worktrees/pipeline-e97c13c7/lib/session-watcher.ts +345 -0
  2419. package/.forge/worktrees/pipeline-e97c13c7/lib/settings.ts +195 -0
  2420. package/.forge/worktrees/pipeline-e97c13c7/lib/skills.ts +458 -0
  2421. package/.forge/worktrees/pipeline-e97c13c7/lib/task-manager.ts +949 -0
  2422. package/.forge/worktrees/pipeline-e97c13c7/lib/telegram-bot.ts +1477 -0
  2423. package/.forge/worktrees/pipeline-e97c13c7/lib/telegram-standalone.ts +83 -0
  2424. package/.forge/worktrees/pipeline-e97c13c7/lib/terminal-server.ts +70 -0
  2425. package/.forge/worktrees/pipeline-e97c13c7/lib/terminal-standalone.ts +421 -0
  2426. package/.forge/worktrees/pipeline-e97c13c7/lib/usage-scanner.ts +249 -0
  2427. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/__tests__/state-machine.test.ts +388 -0
  2428. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/__tests__/workspace.test.ts +311 -0
  2429. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/agent-bus.ts +416 -0
  2430. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/agent-worker.ts +655 -0
  2431. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/backends/api-backend.ts +262 -0
  2432. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/backends/cli-backend.ts +491 -0
  2433. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/index.ts +82 -0
  2434. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/manager.ts +136 -0
  2435. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/orchestrator.ts +3400 -0
  2436. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/persistence.ts +309 -0
  2437. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/presets.ts +649 -0
  2438. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/requests.ts +287 -0
  2439. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/session-monitor.ts +240 -0
  2440. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/skill-installer.ts +275 -0
  2441. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/smith-memory.ts +498 -0
  2442. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/types.ts +241 -0
  2443. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace/watch-manager.ts +560 -0
  2444. package/.forge/worktrees/pipeline-e97c13c7/lib/workspace-standalone.ts +911 -0
  2445. package/.forge/worktrees/pipeline-e97c13c7/middleware.ts +51 -0
  2446. package/.forge/worktrees/pipeline-e97c13c7/next.config.ts +26 -0
  2447. package/.forge/worktrees/pipeline-e97c13c7/package.json +74 -0
  2448. package/.forge/worktrees/pipeline-e97c13c7/pnpm-lock.yaml +3719 -0
  2449. package/.forge/worktrees/pipeline-e97c13c7/pnpm-workspace.yaml +1 -0
  2450. package/.forge/worktrees/pipeline-e97c13c7/postcss.config.mjs +7 -0
  2451. package/.forge/worktrees/pipeline-e97c13c7/publish.sh +133 -0
  2452. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/README.md +66 -0
  2453. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/results/.gitignore +2 -0
  2454. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/run.ts +635 -0
  2455. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/01-text-utils/task.md +26 -0
  2456. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  2457. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  2458. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/02-pagination/task.md +48 -0
  2459. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  2460. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  2461. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  2462. package/.forge/worktrees/pipeline-e97c13c7/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  2463. package/.forge/worktrees/pipeline-e97c13c7/scripts/verify-usage.ts +178 -0
  2464. package/.forge/worktrees/pipeline-e97c13c7/src/config/index.ts +129 -0
  2465. package/.forge/worktrees/pipeline-e97c13c7/src/core/db/database.ts +259 -0
  2466. package/.forge/worktrees/pipeline-e97c13c7/src/core/memory/strategy.ts +32 -0
  2467. package/.forge/worktrees/pipeline-e97c13c7/src/core/providers/chat.ts +65 -0
  2468. package/.forge/worktrees/pipeline-e97c13c7/src/core/providers/registry.ts +60 -0
  2469. package/.forge/worktrees/pipeline-e97c13c7/src/core/session/manager.ts +190 -0
  2470. package/.forge/worktrees/pipeline-e97c13c7/src/types/index.ts +129 -0
  2471. package/.forge/worktrees/pipeline-e97c13c7/start.sh +31 -0
  2472. package/.forge/worktrees/pipeline-e97c13c7/templates/smith-lead.json +45 -0
  2473. package/.forge/worktrees/pipeline-e97c13c7/tsconfig.json +42 -0
  2474. package/.forge/worktrees/pipeline-ecd7cb0f/CLAUDE.md +86 -0
  2475. package/.forge/worktrees/pipeline-ecd7cb0f/README.md +136 -0
  2476. package/.forge/worktrees/pipeline-ecd7cb0f/RELEASE_NOTES.md +11 -0
  2477. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/agents/route.ts +17 -0
  2478. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/auth/[...nextauth]/route.ts +3 -0
  2479. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/auth/verify/route.ts +46 -0
  2480. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude/[id]/route.ts +31 -0
  2481. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude/[id]/stream/route.ts +63 -0
  2482. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude/route.ts +28 -0
  2483. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  2484. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  2485. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude-sessions/[projectName]/route.ts +37 -0
  2486. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude-sessions/sync/route.ts +17 -0
  2487. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/claude-templates/route.ts +145 -0
  2488. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/code/route.ts +299 -0
  2489. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/delivery/[id]/route.ts +62 -0
  2490. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/delivery/route.ts +40 -0
  2491. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/detect-cli/route.ts +46 -0
  2492. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/docs/route.ts +176 -0
  2493. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/docs/sessions/route.ts +54 -0
  2494. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/favorites/route.ts +26 -0
  2495. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/flows/route.ts +6 -0
  2496. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/flows/run/route.ts +19 -0
  2497. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/git/route.ts +149 -0
  2498. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/help/route.ts +84 -0
  2499. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/issue-scanner/route.ts +116 -0
  2500. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/logs/route.ts +100 -0
  2501. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/mobile-chat/route.ts +115 -0
  2502. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/monitor/route.ts +74 -0
  2503. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/notifications/route.ts +42 -0
  2504. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/notify/test/route.ts +33 -0
  2505. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/online/route.ts +40 -0
  2506. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/pipelines/[id]/route.ts +41 -0
  2507. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/pipelines/route.ts +90 -0
  2508. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/plugins/route.ts +75 -0
  2509. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/preview/[...path]/route.ts +64 -0
  2510. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/preview/route.ts +156 -0
  2511. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/project-pipelines/route.ts +91 -0
  2512. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/project-sessions/route.ts +61 -0
  2513. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/projects/route.ts +26 -0
  2514. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/sessions/[id]/chat/route.ts +64 -0
  2515. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/sessions/[id]/messages/route.ts +9 -0
  2516. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/sessions/[id]/route.ts +17 -0
  2517. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/sessions/route.ts +20 -0
  2518. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/settings/route.ts +64 -0
  2519. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/skills/local/route.ts +228 -0
  2520. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/skills/route.ts +182 -0
  2521. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/smith-templates/route.ts +81 -0
  2522. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/status/route.ts +12 -0
  2523. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tabs/route.ts +25 -0
  2524. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tasks/[id]/route.ts +51 -0
  2525. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tasks/[id]/stream/route.ts +77 -0
  2526. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tasks/link/route.ts +37 -0
  2527. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tasks/route.ts +44 -0
  2528. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tasks/session/route.ts +14 -0
  2529. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/telegram/route.ts +23 -0
  2530. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/templates/route.ts +6 -0
  2531. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/terminal-bell/route.ts +35 -0
  2532. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/terminal-cwd/route.ts +19 -0
  2533. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/terminal-state/route.ts +15 -0
  2534. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/tunnel/route.ts +26 -0
  2535. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/upgrade/route.ts +43 -0
  2536. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/usage/route.ts +20 -0
  2537. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/version/route.ts +78 -0
  2538. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/watchers/route.ts +33 -0
  2539. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/workspace/[id]/agents/route.ts +35 -0
  2540. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/workspace/[id]/memory/route.ts +23 -0
  2541. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/workspace/[id]/smith/route.ts +22 -0
  2542. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/workspace/[id]/stream/route.ts +28 -0
  2543. package/.forge/worktrees/pipeline-ecd7cb0f/app/api/workspace/route.ts +100 -0
  2544. package/.forge/worktrees/pipeline-ecd7cb0f/app/global-error.tsx +21 -0
  2545. package/.forge/worktrees/pipeline-ecd7cb0f/app/globals.css +52 -0
  2546. package/.forge/worktrees/pipeline-ecd7cb0f/app/icon.ico +0 -0
  2547. package/.forge/worktrees/pipeline-ecd7cb0f/app/icon.png +0 -0
  2548. package/.forge/worktrees/pipeline-ecd7cb0f/app/icon.svg +106 -0
  2549. package/.forge/worktrees/pipeline-ecd7cb0f/app/layout.tsx +17 -0
  2550. package/.forge/worktrees/pipeline-ecd7cb0f/app/login/LoginForm.tsx +96 -0
  2551. package/.forge/worktrees/pipeline-ecd7cb0f/app/login/page.tsx +10 -0
  2552. package/.forge/worktrees/pipeline-ecd7cb0f/app/mobile/page.tsx +9 -0
  2553. package/.forge/worktrees/pipeline-ecd7cb0f/app/page.tsx +21 -0
  2554. package/.forge/worktrees/pipeline-ecd7cb0f/bin/forge-server.mjs +484 -0
  2555. package/.forge/worktrees/pipeline-ecd7cb0f/check-forge-status.sh +71 -0
  2556. package/.forge/worktrees/pipeline-ecd7cb0f/cli/mw.ts +579 -0
  2557. package/.forge/worktrees/pipeline-ecd7cb0f/components/BrowserPanel.tsx +175 -0
  2558. package/.forge/worktrees/pipeline-ecd7cb0f/components/ChatPanel.tsx +191 -0
  2559. package/.forge/worktrees/pipeline-ecd7cb0f/components/ClaudeTerminal.tsx +267 -0
  2560. package/.forge/worktrees/pipeline-ecd7cb0f/components/CodeViewer.tsx +787 -0
  2561. package/.forge/worktrees/pipeline-ecd7cb0f/components/ConversationEditor.tsx +411 -0
  2562. package/.forge/worktrees/pipeline-ecd7cb0f/components/ConversationGraphView.tsx +347 -0
  2563. package/.forge/worktrees/pipeline-ecd7cb0f/components/ConversationTerminalView.tsx +303 -0
  2564. package/.forge/worktrees/pipeline-ecd7cb0f/components/Dashboard.tsx +807 -0
  2565. package/.forge/worktrees/pipeline-ecd7cb0f/components/DashboardWrapper.tsx +9 -0
  2566. package/.forge/worktrees/pipeline-ecd7cb0f/components/DeliveryFlowEditor.tsx +491 -0
  2567. package/.forge/worktrees/pipeline-ecd7cb0f/components/DeliveryList.tsx +230 -0
  2568. package/.forge/worktrees/pipeline-ecd7cb0f/components/DeliveryWorkspace.tsx +589 -0
  2569. package/.forge/worktrees/pipeline-ecd7cb0f/components/DocTerminal.tsx +187 -0
  2570. package/.forge/worktrees/pipeline-ecd7cb0f/components/DocsViewer.tsx +574 -0
  2571. package/.forge/worktrees/pipeline-ecd7cb0f/components/HelpDialog.tsx +169 -0
  2572. package/.forge/worktrees/pipeline-ecd7cb0f/components/HelpTerminal.tsx +141 -0
  2573. package/.forge/worktrees/pipeline-ecd7cb0f/components/InlinePipelineView.tsx +111 -0
  2574. package/.forge/worktrees/pipeline-ecd7cb0f/components/LogViewer.tsx +194 -0
  2575. package/.forge/worktrees/pipeline-ecd7cb0f/components/MarkdownContent.tsx +73 -0
  2576. package/.forge/worktrees/pipeline-ecd7cb0f/components/MobileView.tsx +385 -0
  2577. package/.forge/worktrees/pipeline-ecd7cb0f/components/MonitorPanel.tsx +122 -0
  2578. package/.forge/worktrees/pipeline-ecd7cb0f/components/NewSessionModal.tsx +93 -0
  2579. package/.forge/worktrees/pipeline-ecd7cb0f/components/NewTaskModal.tsx +492 -0
  2580. package/.forge/worktrees/pipeline-ecd7cb0f/components/PipelineEditor.tsx +570 -0
  2581. package/.forge/worktrees/pipeline-ecd7cb0f/components/PipelineView.tsx +1018 -0
  2582. package/.forge/worktrees/pipeline-ecd7cb0f/components/PluginsPanel.tsx +472 -0
  2583. package/.forge/worktrees/pipeline-ecd7cb0f/components/ProjectDetail.tsx +1618 -0
  2584. package/.forge/worktrees/pipeline-ecd7cb0f/components/ProjectList.tsx +108 -0
  2585. package/.forge/worktrees/pipeline-ecd7cb0f/components/ProjectManager.tsx +401 -0
  2586. package/.forge/worktrees/pipeline-ecd7cb0f/components/SessionList.tsx +74 -0
  2587. package/.forge/worktrees/pipeline-ecd7cb0f/components/SessionView.tsx +726 -0
  2588. package/.forge/worktrees/pipeline-ecd7cb0f/components/SettingsModal.tsx +1647 -0
  2589. package/.forge/worktrees/pipeline-ecd7cb0f/components/SkillsPanel.tsx +969 -0
  2590. package/.forge/worktrees/pipeline-ecd7cb0f/components/StatusBar.tsx +99 -0
  2591. package/.forge/worktrees/pipeline-ecd7cb0f/components/TabBar.tsx +46 -0
  2592. package/.forge/worktrees/pipeline-ecd7cb0f/components/TaskBoard.tsx +113 -0
  2593. package/.forge/worktrees/pipeline-ecd7cb0f/components/TaskDetail.tsx +372 -0
  2594. package/.forge/worktrees/pipeline-ecd7cb0f/components/TerminalLauncher.tsx +398 -0
  2595. package/.forge/worktrees/pipeline-ecd7cb0f/components/TunnelToggle.tsx +206 -0
  2596. package/.forge/worktrees/pipeline-ecd7cb0f/components/UsagePanel.tsx +207 -0
  2597. package/.forge/worktrees/pipeline-ecd7cb0f/components/WebTerminal.tsx +1683 -0
  2598. package/.forge/worktrees/pipeline-ecd7cb0f/components/WorkspaceTree.tsx +221 -0
  2599. package/.forge/worktrees/pipeline-ecd7cb0f/components/WorkspaceView.tsx +4048 -0
  2600. package/.forge/worktrees/pipeline-ecd7cb0f/dev-test.sh +5 -0
  2601. package/.forge/worktrees/pipeline-ecd7cb0f/docs/Forge_Memory_Layer_Design.docx +0 -0
  2602. package/.forge/worktrees/pipeline-ecd7cb0f/docs/Forge_Strategy_Research_2026.docx +0 -0
  2603. package/.forge/worktrees/pipeline-ecd7cb0f/docs/LOCAL-DEPLOY.md +144 -0
  2604. package/.forge/worktrees/pipeline-ecd7cb0f/docs/roadmap-multi-agent-workflow.md +330 -0
  2605. package/.forge/worktrees/pipeline-ecd7cb0f/forge-logo.png +0 -0
  2606. package/.forge/worktrees/pipeline-ecd7cb0f/forge-logo.svg +106 -0
  2607. package/.forge/worktrees/pipeline-ecd7cb0f/hooks/useSidebarResize.ts +52 -0
  2608. package/.forge/worktrees/pipeline-ecd7cb0f/install.sh +29 -0
  2609. package/.forge/worktrees/pipeline-ecd7cb0f/instrumentation.ts +35 -0
  2610. package/.forge/worktrees/pipeline-ecd7cb0f/lib/agents/claude-adapter.ts +104 -0
  2611. package/.forge/worktrees/pipeline-ecd7cb0f/lib/agents/generic-adapter.ts +64 -0
  2612. package/.forge/worktrees/pipeline-ecd7cb0f/lib/agents/index.ts +245 -0
  2613. package/.forge/worktrees/pipeline-ecd7cb0f/lib/agents/types.ts +70 -0
  2614. package/.forge/worktrees/pipeline-ecd7cb0f/lib/artifacts.ts +106 -0
  2615. package/.forge/worktrees/pipeline-ecd7cb0f/lib/auth.ts +62 -0
  2616. package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/docker.yaml +70 -0
  2617. package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/http.yaml +66 -0
  2618. package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/jenkins.yaml +92 -0
  2619. package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/llm-vision.yaml +85 -0
  2620. package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/playwright.yaml +111 -0
  2621. package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/shell-command.yaml +60 -0
  2622. package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/slack.yaml +48 -0
  2623. package/.forge/worktrees/pipeline-ecd7cb0f/lib/builtin-plugins/webhook.yaml +56 -0
  2624. package/.forge/worktrees/pipeline-ecd7cb0f/lib/claude-process.ts +352 -0
  2625. package/.forge/worktrees/pipeline-ecd7cb0f/lib/claude-sessions.ts +266 -0
  2626. package/.forge/worktrees/pipeline-ecd7cb0f/lib/claude-templates.ts +227 -0
  2627. package/.forge/worktrees/pipeline-ecd7cb0f/lib/cloudflared.ts +424 -0
  2628. package/.forge/worktrees/pipeline-ecd7cb0f/lib/crypto.ts +67 -0
  2629. package/.forge/worktrees/pipeline-ecd7cb0f/lib/delivery.ts +787 -0
  2630. package/.forge/worktrees/pipeline-ecd7cb0f/lib/dirs.ts +99 -0
  2631. package/.forge/worktrees/pipeline-ecd7cb0f/lib/flows.ts +86 -0
  2632. package/.forge/worktrees/pipeline-ecd7cb0f/lib/forge-mcp-server.ts +717 -0
  2633. package/.forge/worktrees/pipeline-ecd7cb0f/lib/forge-skills/forge-inbox.md +38 -0
  2634. package/.forge/worktrees/pipeline-ecd7cb0f/lib/forge-skills/forge-send.md +47 -0
  2635. package/.forge/worktrees/pipeline-ecd7cb0f/lib/forge-skills/forge-status.md +32 -0
  2636. package/.forge/worktrees/pipeline-ecd7cb0f/lib/forge-skills/forge-workspace-sync.md +37 -0
  2637. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/00-overview.md +40 -0
  2638. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/01-settings.md +194 -0
  2639. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/02-telegram.md +41 -0
  2640. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/03-tunnel.md +31 -0
  2641. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/04-tasks.md +52 -0
  2642. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/05-pipelines.md +460 -0
  2643. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/06-skills.md +43 -0
  2644. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/07-projects.md +73 -0
  2645. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/08-rules.md +53 -0
  2646. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/09-issue-autofix.md +55 -0
  2647. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/10-troubleshooting.md +89 -0
  2648. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/11-workspace.md +810 -0
  2649. package/.forge/worktrees/pipeline-ecd7cb0f/lib/help-docs/CLAUDE.md +62 -0
  2650. package/.forge/worktrees/pipeline-ecd7cb0f/lib/init.ts +266 -0
  2651. package/.forge/worktrees/pipeline-ecd7cb0f/lib/issue-scanner.ts +298 -0
  2652. package/.forge/worktrees/pipeline-ecd7cb0f/lib/logger.ts +79 -0
  2653. package/.forge/worktrees/pipeline-ecd7cb0f/lib/notifications.ts +75 -0
  2654. package/.forge/worktrees/pipeline-ecd7cb0f/lib/notify.ts +108 -0
  2655. package/.forge/worktrees/pipeline-ecd7cb0f/lib/password.ts +97 -0
  2656. package/.forge/worktrees/pipeline-ecd7cb0f/lib/pipeline-scheduler.ts +373 -0
  2657. package/.forge/worktrees/pipeline-ecd7cb0f/lib/pipeline.ts +1441 -0
  2658. package/.forge/worktrees/pipeline-ecd7cb0f/lib/plugins/executor.ts +347 -0
  2659. package/.forge/worktrees/pipeline-ecd7cb0f/lib/plugins/registry.ts +228 -0
  2660. package/.forge/worktrees/pipeline-ecd7cb0f/lib/plugins/types.ts +103 -0
  2661. package/.forge/worktrees/pipeline-ecd7cb0f/lib/project-sessions.ts +53 -0
  2662. package/.forge/worktrees/pipeline-ecd7cb0f/lib/projects.ts +86 -0
  2663. package/.forge/worktrees/pipeline-ecd7cb0f/lib/session-manager.ts +156 -0
  2664. package/.forge/worktrees/pipeline-ecd7cb0f/lib/session-utils.ts +53 -0
  2665. package/.forge/worktrees/pipeline-ecd7cb0f/lib/session-watcher.ts +345 -0
  2666. package/.forge/worktrees/pipeline-ecd7cb0f/lib/settings.ts +195 -0
  2667. package/.forge/worktrees/pipeline-ecd7cb0f/lib/skills.ts +458 -0
  2668. package/.forge/worktrees/pipeline-ecd7cb0f/lib/task-manager.ts +949 -0
  2669. package/.forge/worktrees/pipeline-ecd7cb0f/lib/telegram-bot.ts +1477 -0
  2670. package/.forge/worktrees/pipeline-ecd7cb0f/lib/telegram-standalone.ts +83 -0
  2671. package/.forge/worktrees/pipeline-ecd7cb0f/lib/terminal-server.ts +70 -0
  2672. package/.forge/worktrees/pipeline-ecd7cb0f/lib/terminal-standalone.ts +421 -0
  2673. package/.forge/worktrees/pipeline-ecd7cb0f/lib/usage-scanner.ts +249 -0
  2674. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/__tests__/state-machine.test.ts +388 -0
  2675. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/__tests__/workspace.test.ts +311 -0
  2676. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/agent-bus.ts +416 -0
  2677. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/agent-worker.ts +655 -0
  2678. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/backends/api-backend.ts +262 -0
  2679. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/backends/cli-backend.ts +491 -0
  2680. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/index.ts +82 -0
  2681. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/manager.ts +136 -0
  2682. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/orchestrator.ts +3400 -0
  2683. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/persistence.ts +309 -0
  2684. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/presets.ts +649 -0
  2685. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/requests.ts +287 -0
  2686. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/session-monitor.ts +240 -0
  2687. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/skill-installer.ts +275 -0
  2688. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/smith-memory.ts +498 -0
  2689. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/types.ts +241 -0
  2690. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace/watch-manager.ts +560 -0
  2691. package/.forge/worktrees/pipeline-ecd7cb0f/lib/workspace-standalone.ts +911 -0
  2692. package/.forge/worktrees/pipeline-ecd7cb0f/middleware.ts +51 -0
  2693. package/.forge/worktrees/pipeline-ecd7cb0f/next.config.ts +26 -0
  2694. package/.forge/worktrees/pipeline-ecd7cb0f/package.json +74 -0
  2695. package/.forge/worktrees/pipeline-ecd7cb0f/pnpm-lock.yaml +3719 -0
  2696. package/.forge/worktrees/pipeline-ecd7cb0f/pnpm-workspace.yaml +1 -0
  2697. package/.forge/worktrees/pipeline-ecd7cb0f/postcss.config.mjs +7 -0
  2698. package/.forge/worktrees/pipeline-ecd7cb0f/publish.sh +133 -0
  2699. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/README.md +66 -0
  2700. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/results/.gitignore +2 -0
  2701. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/run.ts +635 -0
  2702. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/01-text-utils/task.md +26 -0
  2703. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  2704. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  2705. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/02-pagination/task.md +48 -0
  2706. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  2707. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  2708. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  2709. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  2710. package/.forge/worktrees/pipeline-ecd7cb0f/scripts/verify-usage.ts +178 -0
  2711. package/.forge/worktrees/pipeline-ecd7cb0f/src/config/index.ts +129 -0
  2712. package/.forge/worktrees/pipeline-ecd7cb0f/src/core/db/database.ts +259 -0
  2713. package/.forge/worktrees/pipeline-ecd7cb0f/src/core/memory/strategy.ts +32 -0
  2714. package/.forge/worktrees/pipeline-ecd7cb0f/src/core/providers/chat.ts +65 -0
  2715. package/.forge/worktrees/pipeline-ecd7cb0f/src/core/providers/registry.ts +60 -0
  2716. package/.forge/worktrees/pipeline-ecd7cb0f/src/core/session/manager.ts +190 -0
  2717. package/.forge/worktrees/pipeline-ecd7cb0f/src/types/index.ts +129 -0
  2718. package/.forge/worktrees/pipeline-ecd7cb0f/start.sh +31 -0
  2719. package/.forge/worktrees/pipeline-ecd7cb0f/templates/smith-lead.json +45 -0
  2720. package/.forge/worktrees/pipeline-ecd7cb0f/tsconfig.json +42 -0
  2721. package/RELEASE_NOTES.md +26 -15
  2722. package/app/api/workspace/[id]/stream/route.ts +3 -0
  2723. package/app/api/workspace/route.ts +20 -41
  2724. package/app/mobile/page.tsx +2 -1
  2725. package/app/page.tsx +2 -1
  2726. package/components/WebTerminal.tsx +27 -1
  2727. package/docs/Forge_Memory_Layer_Design.docx +0 -0
  2728. package/docs/Forge_Strategy_Research_2026.docx +0 -0
  2729. package/lib/claude-process.ts +14 -5
  2730. package/lib/forge-mcp-server.ts +20 -5
  2731. package/lib/help-docs/05-pipelines.md +44 -2
  2732. package/lib/pipeline.ts +181 -24
  2733. package/lib/project-sessions.ts +1 -1
  2734. package/lib/task-manager.ts +5 -3
  2735. package/lib/terminal-standalone.ts +2 -2
  2736. package/lib/workspace/index.ts +3 -1
  2737. package/lib/workspace/manager.ts +1 -1
  2738. package/lib/workspace/orchestrator.ts +23 -8
  2739. package/lib/workspace-standalone.ts +67 -0
  2740. package/middleware.ts +2 -2
  2741. package/package.json +1 -1
  2742. package/pnpm-workspace.yaml +1 -0
  2743. package/start.sh +1 -0
@@ -0,0 +1,4048 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useCallback, useMemo, useRef, forwardRef, useImperativeHandle, lazy, Suspense } from 'react';
4
+ import { TerminalSessionPickerLazy, fetchAgentSessions, type PickerSelection } from './TerminalLauncher';
5
+ import {
6
+ ReactFlow, Background, Controls, Handle, Position, useReactFlow, ReactFlowProvider,
7
+ type Node, type NodeProps, MarkerType, type NodeChange,
8
+ applyNodeChanges,
9
+ } from '@xyflow/react';
10
+ import '@xyflow/react/dist/style.css';
11
+
12
+ // ─── Types (mirrors lib/workspace/types) ─────────────────
13
+
14
+ interface AgentConfig {
15
+ id: string; label: string; icon: string; role: string;
16
+ type?: 'agent' | 'input';
17
+ primary?: boolean;
18
+ content?: string;
19
+ entries?: { content: string; timestamp: number }[];
20
+ backend: 'api' | 'cli';
21
+ agentId?: string; provider?: string; model?: string;
22
+ dependsOn: string[];
23
+ workDir?: string;
24
+ outputs: string[];
25
+ steps: { id: string; label: string; prompt: string }[];
26
+ requiresApproval?: boolean;
27
+ persistentSession?: boolean;
28
+ skipPermissions?: boolean;
29
+ boundSessionId?: string;
30
+ watch?: { enabled: boolean; interval: number; targets: any[]; action?: 'log' | 'analyze' | 'approve' | 'send_message'; prompt?: string; sendTo?: string };
31
+ plugins?: string[]; // plugin IDs to auto-install when agent is created
32
+ }
33
+
34
+ interface AgentState {
35
+ smithStatus: 'down' | 'starting' | 'active';
36
+ taskStatus: 'idle' | 'running' | 'done' | 'failed';
37
+ currentStep?: number;
38
+ tmuxSession?: string;
39
+ artifacts: { type: string; path?: string; summary?: string }[];
40
+ error?: string; lastCheckpoint?: number;
41
+ daemonIteration?: number;
42
+ }
43
+
44
+ // ─── Constants ───────────────────────────────────────────
45
+
46
+ const COLORS = [
47
+ { border: '#22c55e', bg: '#0a1a0a', accent: '#4ade80' },
48
+ { border: '#3b82f6', bg: '#0a0f1a', accent: '#60a5fa' },
49
+ { border: '#a855f7', bg: '#100a1a', accent: '#c084fc' },
50
+ { border: '#f97316', bg: '#1a100a', accent: '#fb923c' },
51
+ { border: '#ec4899', bg: '#1a0a10', accent: '#f472b6' },
52
+ { border: '#06b6d4', bg: '#0a1a1a', accent: '#22d3ee' },
53
+ ];
54
+
55
+ // Smith status colors
56
+ const SMITH_STATUS: Record<string, { label: string; color: string; glow?: boolean }> = {
57
+ down: { label: 'down', color: '#30363d' },
58
+ starting: { label: 'starting', color: '#f0883e' }, // orange: ensurePersistentSession in progress
59
+ active: { label: 'active', color: '#3fb950', glow: true },
60
+ };
61
+
62
+ // Task status colors
63
+ const TASK_STATUS: Record<string, { label: string; color: string; glow?: boolean }> = {
64
+ idle: { label: 'idle', color: '#30363d' },
65
+ running: { label: 'running', color: '#3fb950', glow: true },
66
+ done: { label: 'done', color: '#58a6ff' },
67
+ failed: { label: 'failed', color: '#f85149' },
68
+ };
69
+
70
+ const PRESET_AGENTS: Omit<AgentConfig, 'id'>[] = [
71
+ {
72
+ label: 'Lead', icon: '👑', backend: 'cli', agentId: 'claude', dependsOn: [], outputs: ['docs/lead/'],
73
+ primary: true, persistentSession: true, plugins: ['playwright', 'shell-command'],
74
+ role: `Lead — primary coordinator (recommended for Primary smith). Context auto-includes Workspace Team (all agents, roles, status, missing roles).
75
+
76
+ SOP: Intake → HAS Architect? delegate via create_request : break down yourself → HAS Engineer? create_request(open) : implement in src/ → HAS QA? auto-notified : test yourself → HAS Reviewer? auto-notified : review yourself.
77
+
78
+ SOP: Monitor → get_status + list_requests → stuck/failed agents: send_message or take over → unclaimed requests: nudge Engineers.
79
+
80
+ SOP: Quality Gate → ALL requests done + review=approved + qa=passed → write docs/lead/delivery-summary.md.
81
+
82
+ Gap coverage: missing PM → you break requirements; missing Engineer → you code; missing QA → you test; missing Reviewer → you review. Every delegation uses create_request with acceptance_criteria.`,
83
+ steps: [
84
+ { id: 'intake', label: 'Intake & Analyze', prompt: 'Read Workspace Team in context. Identify present/missing roles and incoming requirements. Classify scope and plan delegation vs self-handling.' },
85
+ { id: 'delegate', label: 'Create Requests & Route', prompt: 'create_request for each task with acceptance_criteria. Route to Architect/Engineer or note for self-implementation. Verify with list_requests.' },
86
+ { id: 'cover-gaps', label: 'Cover Missing Roles', prompt: 'Implement/test/review for any missing role. update_response for each section you cover.' },
87
+ { id: 'monitor', label: 'Monitor & Unblock', prompt: 'get_status + list_requests. Unblock stuck agents via send_message or take over their work.' },
88
+ { id: 'gate', label: 'Quality Gate & Summary', prompt: 'Verify all requests done/approved/passed. Write docs/lead/delivery-summary.md.' },
89
+ ],
90
+ },
91
+ {
92
+ label: 'PM', icon: '📋', backend: 'cli', agentId: 'claude', dependsOn: [], outputs: ['docs/prd/'],
93
+ role: `Product Manager. Context auto-includes Workspace Team.
94
+
95
+ SOP: Read upstream input → list docs/prd/ for version history → identify NEW vs covered → create NEW versioned PRD (never overwrite).
96
+
97
+ PRD structure: version + date, summary, goals, user stories with testable acceptance_criteria, constraints, out of scope, open questions.
98
+
99
+ Version: patch (v1.0.1) = clarification, minor (v1.1) = new feature, major (v2.0) = scope overhaul.
100
+
101
+ Handoff: Do NOT create request docs or write code. Architect/Lead reads docs/prd/ downstream.`,
102
+ steps: [
103
+ { id: 'analyze', label: 'Analyze Requirements', prompt: 'Read Workspace Team. Read upstream input. List docs/prd/ for version history. Identify NEW vs already covered requirements. Decide version number.' },
104
+ { id: 'write-prd', label: 'Write PRD', prompt: 'Create NEW versioned file in docs/prd/. Include testable acceptance criteria for every user story. Never overwrite existing PRD files.' },
105
+ { id: 'self-review', label: 'Self-Review', prompt: 'Checklist: criteria testable by QA? Edge cases? Scope clear for Engineer? No duplication? Fix issues.' },
106
+ ],
107
+ },
108
+ {
109
+ label: 'Engineer', icon: '🔨', backend: 'cli', agentId: 'claude', dependsOn: [], outputs: ['src/', 'docs/architecture/'],
110
+ role: `Senior Software Engineer. Context auto-includes Workspace Team.
111
+
112
+ SOP: Find Work → list_requests(status: "open") → claim_request → get_request for details.
113
+ SOP: Implement → read acceptance_criteria → design (docs/architecture/) → code (src/) → self-test.
114
+ SOP: Report → update_response(section: "engineer", data: {files_changed, notes}) → auto-notifies QA/Reviewer.
115
+
116
+ IF claim fails (already taken) → pick next open request.
117
+ IF blocked by unclear requirement → send_message to upstream (Architect/PM/Lead) with specific question.
118
+ IF no open requests → check inbox for direct assignments.
119
+
120
+ Rules: always claim before starting, always update_response when done, follow existing conventions, architecture docs versioned (never overwrite).`,
121
+ steps: [
122
+ { id: 'claim', label: 'Find & Claim', prompt: 'Read Workspace Team. Check inbox. list_requests(status: "open"). claim_request on highest priority. If none, check inbox.' },
123
+ { id: 'design', label: 'Design', prompt: 'get_request for details. Read acceptance_criteria. Read existing code + docs/architecture/. Create new architecture doc if significant change.' },
124
+ { id: 'implement', label: 'Implement', prompt: 'Implement per design. Follow conventions. Track files changed. Run existing tests. Verify against each acceptance_criterion.' },
125
+ { id: 'report', label: 'Report Done', prompt: 'update_response(section: "engineer") with files_changed and notes. If blocked, send_message upstream.' },
126
+ ],
127
+ },
128
+ {
129
+ label: 'QA', icon: '🧪', backend: 'cli', agentId: 'claude', dependsOn: [], outputs: ['tests/', 'docs/qa/'],
130
+ plugins: ['playwright', 'shell-command'],
131
+ role: `QA Engineer. Context auto-includes Workspace Team.
132
+
133
+ SOP: Find Work → list_requests(status: "qa") → get_request → read acceptance_criteria + engineer's files_changed.
134
+ SOP: Test → map each criterion to test cases → write Playwright tests in tests/e2e/ → run via run_plugin or npx playwright.
135
+ SOP: Report → update_response(section: "qa", data: {result, test_files, findings}).
136
+
137
+ IF result=passed → auto-advances, no message needed.
138
+ IF result=failed → classify: CRITICAL/MAJOR → ONE send_message to Engineer. MINOR → report only, no message.
139
+
140
+ Rules: never fix bugs (report only), each test traces to acceptance_criterion, max 1 consolidated message, no messages during planning/writing steps.`,
141
+ steps: [
142
+ { id: 'find-work', label: 'Find Work', prompt: 'Read Workspace Team. Check inbox. list_requests(status: "qa"). get_request for acceptance_criteria and engineer notes.' },
143
+ { id: 'plan', label: 'Test Plan', prompt: 'Map each criterion to test cases (happy path + edge + error). Write docs/qa/test-plan. Skip already-tested unchanged features.' },
144
+ { id: 'write-tests', label: 'Write Tests', prompt: 'Write Playwright tests in tests/e2e/. Create config if missing. No messages in this step.' },
145
+ { id: 'execute', label: 'Execute & Report', prompt: 'Run tests. Record pass/fail per criterion. update_response(section: qa). If critical/major failures: ONE send_message to Engineer.' },
146
+ ],
147
+ },
148
+ {
149
+ label: 'Reviewer', icon: '🔍', backend: 'cli', agentId: 'claude', dependsOn: [], outputs: ['docs/review/'],
150
+ role: `Code Reviewer. Context auto-includes Workspace Team.
151
+
152
+ SOP: Find Work → list_requests(status: "review") → get_request → read request + engineer response + QA results.
153
+ SOP: Review each file in files_changed → check: criteria met? code quality? security (OWASP)? performance? → classify CRITICAL/MAJOR/MINOR.
154
+ SOP: Verdict → approved (all good) / changes_requested (issues) / rejected (security/data).
155
+ SOP: Report → update_response(section: "review", data: {result, findings}) → write docs/review/.
156
+
157
+ IF approved → auto-advances to done, no message.
158
+ IF changes_requested → ONE send_message to Engineer with top issues.
159
+ IF rejected → send_message to Engineer AND Lead.
160
+
161
+ Rules: never modify code, review only files_changed (not entire codebase), actionable feedback ("change X to Y because Z"), MINOR findings in report only.`,
162
+ steps: [
163
+ { id: 'find-work', label: 'Find Work', prompt: 'Read Workspace Team. Check inbox. list_requests(status: "review"). get_request for full context.' },
164
+ { id: 'review', label: 'Code Review', prompt: 'Review each file in files_changed: criteria met? quality? security? performance? Classify CRITICAL/MAJOR/MINOR.' },
165
+ { id: 'report', label: 'Verdict & Report', prompt: 'Decide verdict. update_response(section: review). Write docs/review/. If changes_requested/rejected: ONE message to Engineer (+ Lead if rejected).' },
166
+ ],
167
+ },
168
+ {
169
+ label: 'UI Designer', icon: '🎨', backend: 'cli', agentId: 'claude', dependsOn: [], outputs: ['docs/ui-spec.md', 'src/'],
170
+ plugins: ['playwright', 'shell-command'],
171
+ role: `UI/UX Designer — You design and implement user interfaces. You write real UI code, preview it visually, and iterate until the quality meets your standards.
172
+
173
+ Rules:
174
+ - You WRITE CODE, not just specs. Implement the UI yourself.
175
+ - After writing UI code, always preview your work: take a screenshot and review it visually.
176
+ - Iterate: if the screenshot doesn't look right, fix the code and screenshot again. Aim for 3-5 review cycles.
177
+ - Focus on user experience first, aesthetics second
178
+ - Design for the existing tech stack (check project's UI framework)
179
+ - Be specific: colors (hex), spacing (px/rem), typography, component hierarchy
180
+ - Consider responsive design, accessibility (WCAG), dark/light mode
181
+ - Include interaction states: hover, active, disabled, loading, error, empty
182
+ - Reference existing UI patterns in the codebase for consistency
183
+
184
+ Visual review workflow:
185
+ 1. Write/modify UI code
186
+ 2. Start dev server if not running (e.g., npm run dev)
187
+ 3. Take screenshot: run_plugin({ plugin: "<playwright-instance>", action: "screenshot", params: { url: "http://localhost:3000/page" } })
188
+ 4. Read the screenshot file to visually evaluate your work
189
+ 5. Grade yourself: layout correctness, visual polish, consistency with existing UI, responsiveness
190
+ 6. If not satisfied, fix and repeat from step 2
191
+ 7. When satisfied, document the final design in docs/ui-spec.md
192
+
193
+ If reference designs or mockups exist in the project (e.g., docs/designs/), study them before implementing.`,
194
+ steps: [
195
+ { id: 'audit', label: 'UI Audit', prompt: 'Analyze the existing UI: framework used (React/Vue/etc), component library, design tokens (colors, spacing, fonts), layout patterns. Take screenshots of existing pages to understand the current look and feel. Document the current design system.' },
196
+ { id: 'implement', label: 'Implement UI', prompt: 'Based on the PRD, implement the UI. Write real component code. Start the dev server, take screenshots of your work, and iterate until the visual quality is high. Aim for at least 3 review cycles — screenshot, evaluate, improve.' },
197
+ { id: 'polish', label: 'Polish & Document', prompt: 'Final polish pass: check all states (loading, empty, error, hover, disabled), responsive breakpoints, dark/light mode. Take final screenshots. Write docs/ui-spec.md documenting: component hierarchy, design decisions, interaction patterns, and accessibility notes.' },
198
+ ],
199
+ },
200
+ {
201
+ label: 'Design Evaluator', icon: '🔍', backend: 'cli', agentId: 'claude', dependsOn: [], outputs: ['docs/design-review.md'],
202
+ plugins: ['playwright', 'llm-vision'],
203
+ role: `Design Evaluator — You are a senior design critic. You evaluate UI implementations visually, not by reading code. You are deliberately skeptical and hold work to a high standard.
204
+
205
+ You evaluate on 4 dimensions (each scored 1-10):
206
+ 1. **Design Quality** — Visual coherence, distinct identity, not generic/template-like
207
+ 2. **Originality** — Evidence of intentional design decisions vs default AI patterns
208
+ 3. **Craft** — Typography, spacing, color harmony, alignment, pixel-level polish
209
+ 4. **Functionality** — Usability, interaction clarity, error states, responsiveness
210
+
211
+ Rules:
212
+ - NEVER modify code — only evaluate and report
213
+ - Always take screenshots and visually inspect before scoring
214
+ - Use run_plugin with Playwright to screenshot every relevant page/state
215
+ - If llm-vision instances are available, use them for cross-model evaluation
216
+ - Be specific: "the spacing between header and content is 8px, should be 16px for breathing room"
217
+ - A score of 7+ means "good enough to ship". Below 7 means "needs revision"
218
+ - Send feedback to UI Designer via send_message with specific, actionable items
219
+ - If overall score < 7, request changes. If >= 7, approve with minor suggestions.
220
+
221
+ Workflow:
222
+ 1. Receive notification that UI Designer has completed work
223
+ 2. Take screenshots of all relevant pages and states (normal, loading, error, empty, mobile)
224
+ 3. Evaluate each screenshot against the 4 dimensions
225
+ 4. Optionally send screenshots to llm-vision instances for additional opinions
226
+ 5. Write docs/design-review.md with scores, specific feedback, and verdict
227
+ 6. send_message to UI Designer: APPROVE or REQUEST_CHANGES with actionable feedback`,
228
+ steps: [
229
+ { id: 'screenshot', label: 'Visual Capture', prompt: 'Take screenshots of all pages and states the UI Designer worked on. Include: default view, loading state, error state, empty state, mobile viewport (375px), tablet viewport (768px). Save all screenshots to /tmp/ and list them.' },
230
+ { id: 'evaluate', label: 'Evaluate', prompt: 'Review each screenshot. Score each page on the 4 dimensions (Design Quality, Originality, Craft, Functionality). Be critical and specific. If llm-vision plugin instances are available, send key screenshots for additional evaluation and compare opinions.' },
231
+ { id: 'report', label: 'Report & Feedback', prompt: 'Write docs/design-review.md with: overall scores, per-page breakdown, specific issues with suggested fixes. Send verdict to UI Designer via send_message: APPROVE (score >= 7) or REQUEST_CHANGES (score < 7) with the top 3-5 actionable items.' },
232
+ ],
233
+ },
234
+ ];
235
+
236
+ // ─── API helpers ─────────────────────────────────────────
237
+
238
+ async function wsApi(workspaceId: string, action: string, body?: Record<string, any>) {
239
+ const res = await fetch(`/api/workspace/${workspaceId}/agents`, {
240
+ method: 'POST',
241
+ headers: { 'Content-Type': 'application/json' },
242
+ body: JSON.stringify({ action, ...body }),
243
+ });
244
+ const data = await res.json();
245
+ if (data.warning) {
246
+ alert(`Warning: ${data.warning}`);
247
+ }
248
+ if (!res.ok && data.error) {
249
+ alert(`Error: ${data.error}`);
250
+ }
251
+ return data;
252
+ }
253
+
254
+ async function ensureWorkspace(projectPath: string, projectName: string): Promise<string> {
255
+ // Find or create workspace
256
+ const res = await fetch(`/api/workspace?projectPath=${encodeURIComponent(projectPath)}`);
257
+ const existing = await res.json();
258
+ if (existing?.id) return existing.id;
259
+
260
+ const createRes = await fetch('/api/workspace', {
261
+ method: 'POST',
262
+ headers: { 'Content-Type': 'application/json' },
263
+ body: JSON.stringify({ projectPath, projectName }),
264
+ });
265
+ const created = await createRes.json();
266
+ return created.id;
267
+ }
268
+
269
+ // ─── SSE Hook ────────────────────────────────────────────
270
+
271
+ function useWorkspaceStream(workspaceId: string | null, onEvent?: (event: any) => void) {
272
+ const [agents, setAgents] = useState<AgentConfig[]>([]);
273
+ const [states, setStates] = useState<Record<string, AgentState>>({});
274
+ const [logPreview, setLogPreview] = useState<Record<string, string[]>>({});
275
+ const [busLog, setBusLog] = useState<any[]>([]);
276
+ const [daemonActive, setDaemonActive] = useState(false);
277
+ const onEventRef = useRef(onEvent);
278
+ onEventRef.current = onEvent;
279
+
280
+ useEffect(() => {
281
+ if (!workspaceId) return;
282
+
283
+ const es = new EventSource(`/api/workspace/${workspaceId}/stream`);
284
+
285
+ es.onmessage = (e) => {
286
+ try {
287
+ const event = JSON.parse(e.data);
288
+
289
+ if (event.type === 'init') {
290
+ setAgents(event.agents || []);
291
+ setStates(event.agentStates || {});
292
+ setBusLog(event.busLog || []);
293
+ if (event.daemonActive !== undefined) setDaemonActive(event.daemonActive);
294
+ return;
295
+ }
296
+
297
+ if (event.type === 'task_status') {
298
+ setStates(prev => ({
299
+ ...prev,
300
+ [event.agentId]: {
301
+ ...prev[event.agentId],
302
+ taskStatus: event.taskStatus,
303
+ error: event.error,
304
+ },
305
+ }));
306
+ }
307
+
308
+ if (event.type === 'smith_status') {
309
+ setStates(prev => ({
310
+ ...prev,
311
+ [event.agentId]: {
312
+ ...prev[event.agentId],
313
+ smithStatus: event.smithStatus,
314
+ },
315
+ }));
316
+ }
317
+
318
+ if (event.type === 'log') {
319
+ const entry = event.entry;
320
+ if (entry?.content) {
321
+ setLogPreview(prev => {
322
+ // Summary entries replace the preview entirely (cleaner display)
323
+ if (entry.subtype === 'step_summary' || entry.subtype === 'final_summary') {
324
+ const summaryLines = entry.content.split('\n').filter((l: string) => l.trim()).slice(0, 4);
325
+ return { ...prev, [event.agentId]: summaryLines };
326
+ }
327
+ // Regular logs: append, keep last 3
328
+ const lines = [...(prev[event.agentId] || []), entry.content].slice(-3);
329
+ return { ...prev, [event.agentId]: lines };
330
+ });
331
+ }
332
+ }
333
+
334
+ if (event.type === 'step') {
335
+ setStates(prev => ({
336
+ ...prev,
337
+ [event.agentId]: { ...prev[event.agentId], currentStep: event.stepIndex },
338
+ }));
339
+ }
340
+
341
+ if (event.type === 'error') {
342
+ setStates(prev => ({
343
+ ...prev,
344
+ [event.agentId]: { ...prev[event.agentId], taskStatus: 'failed', error: event.error },
345
+ }));
346
+ }
347
+
348
+ if (event.type === 'bus_message') {
349
+ setBusLog(prev => prev.some(m => m.id === event.message.id) ? prev : [...prev, event.message]);
350
+ }
351
+
352
+ if (event.type === 'bus_message_status') {
353
+ setBusLog(prev => prev.map(m =>
354
+ m.id === event.messageId ? { ...m, status: event.status } : m
355
+ ));
356
+ }
357
+
358
+ if (event.type === 'bus_log_updated') {
359
+ setBusLog(event.log || []);
360
+ }
361
+
362
+ // Server pushed updated agents list + states (after add/remove/update/reset)
363
+ if (event.type === 'agents_changed') {
364
+ const newAgents = event.agents || [];
365
+ setAgents(prev => {
366
+ // Guard: don't accept a smaller agents list unless it was an explicit removal
367
+ // (removal shrinks by exactly 1, not more)
368
+ if (newAgents.length > 0 && newAgents.length < prev.length - 1) {
369
+ console.warn(`[sse] agents_changed: ignoring shrink from ${prev.length} to ${newAgents.length}`);
370
+ return prev;
371
+ }
372
+ return newAgents;
373
+ });
374
+ if (event.agentStates) setStates(event.agentStates);
375
+ }
376
+
377
+ // Watch alerts — update agent state with last alert
378
+ if (event.type === 'watch_alert') {
379
+ setStates(prev => ({
380
+ ...prev,
381
+ [event.agentId]: {
382
+ ...prev[event.agentId],
383
+ lastWatchAlert: event.summary,
384
+ lastWatchTime: event.timestamp,
385
+ },
386
+ }));
387
+ }
388
+
389
+ // Forward special events to the component
390
+ if (event.type === 'user_input_request' || event.type === 'workspace_complete') {
391
+ onEventRef.current?.(event);
392
+ }
393
+ } catch {}
394
+ };
395
+
396
+ return () => es.close();
397
+ }, [workspaceId]);
398
+
399
+ return { agents, states, logPreview, busLog, setAgents, daemonActive, setDaemonActive };
400
+ }
401
+
402
+ // ─── Session Target Selector (for Watch) ─────────────────
403
+
404
+ function SessionTargetSelector({ target, agents, projectPath, onChange }: {
405
+ target: { type: string; path?: string; pattern?: string; cmd?: string };
406
+ agents: AgentConfig[];
407
+ projectPath?: string;
408
+ onChange: (updated: typeof target) => void;
409
+ }) {
410
+ const [sessions, setSessions] = useState<{ id: string; modified: string; label: string }[]>([]);
411
+
412
+ // Load sessions and mark fixed session
413
+ useEffect(() => {
414
+ if (!projectPath) return;
415
+ const pName = (projectPath || '').replace(/\/+$/, '').split('/').pop() || '';
416
+ Promise.all([
417
+ fetch(`/api/claude-sessions/${encodeURIComponent(pName)}`).then(r => r.json()).catch(() => []),
418
+ fetch(`/api/project-sessions?projectPath=${encodeURIComponent(projectPath)}`).then(r => r.json()).catch(() => ({})),
419
+ ]).then(([data, psData]) => {
420
+ const fixedId = psData?.fixedSessionId || '';
421
+ if (Array.isArray(data)) {
422
+ setSessions(data.map((s: any, i: number) => {
423
+ const sid = s.sessionId || s.id || '';
424
+ const isBound = sid === fixedId;
425
+ const label = isBound ? `${sid.slice(0, 8)} (fixed)` : i === 0 ? `${sid.slice(0, 8)} (latest)` : sid.slice(0, 8);
426
+ return { id: sid, modified: s.modified || '', label };
427
+ }));
428
+ }
429
+ });
430
+ }, [projectPath]);
431
+
432
+ return (
433
+ <>
434
+ <select value={target.path || ''} onChange={e => onChange({ ...target, path: e.target.value, cmd: '' })}
435
+ className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white w-24">
436
+ <option value="">Any agent</option>
437
+ {agents.map(a => <option key={a.id} value={a.id}>{a.icon} {a.label}</option>)}
438
+ </select>
439
+ <select value={target.cmd || ''} onChange={e => onChange({ ...target, cmd: e.target.value })}
440
+ className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white w-28">
441
+ <option value="">Latest session</option>
442
+ {sessions.map(s => (
443
+ <option key={s.id} value={s.id}>{s.label}{s.modified ? ` · ${new Date(s.modified).toLocaleDateString()}` : ''}</option>
444
+ ))}
445
+ </select>
446
+ <input value={target.pattern || ''} onChange={e => onChange({ ...target, pattern: e.target.value })}
447
+ placeholder="regex (optional)"
448
+ className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white w-24" />
449
+ </>
450
+ );
451
+ }
452
+
453
+ // ─── Watch Path Picker (file/directory browser) ─────────
454
+
455
+ function WatchPathPicker({ value, projectPath, onChange }: { value: string; projectPath: string; onChange: (v: string) => void }) {
456
+ const [showBrowser, setShowBrowser] = useState(false);
457
+ const [tree, setTree] = useState<any[]>([]);
458
+ const [search, setSearch] = useState('');
459
+ const [flatFiles, setFlatFiles] = useState<string[]>([]);
460
+
461
+ const loadTree = useCallback(() => {
462
+ if (!projectPath) return;
463
+ fetch(`/api/code?dir=${encodeURIComponent(projectPath)}`)
464
+ .then(r => r.json())
465
+ .then(data => {
466
+ setTree(data.tree || []);
467
+ // Build flat list for search
468
+ const files: string[] = [];
469
+ const walk = (nodes: any[], prefix = '') => {
470
+ for (const n of nodes || []) {
471
+ const path = prefix ? `${prefix}/${n.name}` : n.name;
472
+ files.push(n.type === 'dir' ? path + '/' : path);
473
+ if (n.children) walk(n.children, path);
474
+ }
475
+ };
476
+ walk(data.tree || []);
477
+ setFlatFiles(files);
478
+ })
479
+ .catch(() => {});
480
+ }, [projectPath]);
481
+
482
+ const filtered = search ? flatFiles.filter(f => f.toLowerCase().includes(search.toLowerCase())).slice(0, 30) : [];
483
+
484
+ return (
485
+ <div className="flex-1 flex items-center gap-1 relative">
486
+ <input
487
+ value={value}
488
+ onChange={e => onChange(e.target.value)}
489
+ placeholder="./ (project root)"
490
+ className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white flex-1"
491
+ />
492
+ <button onClick={() => { setShowBrowser(!showBrowser); if (!showBrowser) loadTree(); }}
493
+ className="text-[9px] px-1 py-0.5 rounded bg-[#30363d] text-gray-400 hover:text-white shrink-0">📂</button>
494
+
495
+ {showBrowser && (
496
+ <div className="absolute left-0 right-0 top-full mt-1 z-50 bg-[#0d1117] border border-[#30363d] rounded-lg shadow-xl max-h-60 overflow-hidden flex flex-col" style={{ minWidth: 250 }}>
497
+ <input
498
+ value={search}
499
+ onChange={e => setSearch(e.target.value)}
500
+ placeholder="Search files & dirs..."
501
+ autoFocus
502
+ className="text-[10px] bg-[#161b22] border-b border-[#30363d] px-2 py-1 text-white focus:outline-none"
503
+ />
504
+ <div className="overflow-y-auto flex-1">
505
+ {search ? (
506
+ // Search results
507
+ filtered.length > 0 ? filtered.map(f => (
508
+ <div key={f} onClick={() => { onChange(f); setShowBrowser(false); setSearch(''); }}
509
+ className="px-2 py-0.5 text-[9px] text-gray-300 hover:bg-[#161b22] cursor-pointer truncate font-mono">
510
+ {f.endsWith('/') ? `📁 ${f}` : `📄 ${f}`}
511
+ </div>
512
+ )) : <div className="px-2 py-1 text-[9px] text-gray-500">No matches</div>
513
+ ) : (
514
+ // Tree view (first 2 levels)
515
+ tree.map(n => <PathTreeNode key={n.name} node={n} prefix="" onSelect={p => { onChange(p); setShowBrowser(false); }} />)
516
+ )}
517
+ </div>
518
+ <div className="flex items-center justify-between px-2 py-0.5 border-t border-[#30363d] bg-[#161b22]">
519
+ <span className="text-[8px] text-gray-600">{flatFiles.length} items</span>
520
+ <button onClick={() => setShowBrowser(false)} className="text-[8px] text-gray-500 hover:text-white">Close</button>
521
+ </div>
522
+ </div>
523
+ )}
524
+ </div>
525
+ );
526
+ }
527
+
528
+ function PathTreeNode({ node, prefix, onSelect, depth = 0 }: { node: any; prefix: string; onSelect: (path: string) => void; depth?: number }) {
529
+ const [expanded, setExpanded] = useState(depth < 1);
530
+ const path = prefix ? `${prefix}/${node.name}` : node.name;
531
+ const isDir = node.type === 'dir';
532
+
533
+ if (!isDir && depth > 1) return null; // only show files at top 2 levels
534
+
535
+ return (
536
+ <div>
537
+ <div
538
+ onClick={() => isDir ? setExpanded(!expanded) : onSelect(path)}
539
+ className="flex items-center px-2 py-0.5 text-[9px] hover:bg-[#161b22] cursor-pointer"
540
+ style={{ paddingLeft: 8 + depth * 12 }}
541
+ >
542
+ <span className="text-gray-500 mr-1 w-3">{isDir ? (expanded ? '▼' : '▶') : ''}</span>
543
+ <span className={isDir ? 'text-[var(--accent)]' : 'text-gray-400'}>{isDir ? '📁' : '📄'} {node.name}</span>
544
+ {isDir && (
545
+ <button onClick={e => { e.stopPropagation(); onSelect(path + '/'); }}
546
+ className="ml-auto text-[8px] text-gray-600 hover:text-[var(--accent)]">select</button>
547
+ )}
548
+ </div>
549
+ {isDir && expanded && node.children && depth < 2 && (
550
+ node.children.map((c: any) => <PathTreeNode key={c.name} node={c} prefix={path} onSelect={onSelect} depth={depth + 1} />)
551
+ )}
552
+ </div>
553
+ );
554
+ }
555
+
556
+ // ─── Fixed Session Picker ────────────────────────────────
557
+
558
+ function FixedSessionPicker({ projectPath, value, onChange }: { projectPath?: string; value: string; onChange: (v: string) => void }) {
559
+ const [sessions, setSessions] = useState<{ id: string; modified: string; size: number }[]>([]);
560
+ const [copied, setCopied] = useState(false);
561
+
562
+ useEffect(() => {
563
+ if (!projectPath) return;
564
+ const pName = projectPath.replace(/\/+$/, '').split('/').pop() || '';
565
+ fetch(`/api/claude-sessions/${encodeURIComponent(pName)}`)
566
+ .then(r => r.json())
567
+ .then(data => { if (Array.isArray(data)) setSessions(data.map((s: any) => ({ id: s.sessionId || s.id || '', modified: s.modified || '', size: s.size || 0 }))); })
568
+ .catch(() => {});
569
+ }, [projectPath]);
570
+
571
+ const formatTime = (iso: string) => {
572
+ if (!iso) return '';
573
+ const diff = Date.now() - new Date(iso).getTime();
574
+ if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
575
+ if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
576
+ return new Date(iso).toLocaleDateString();
577
+ };
578
+ const formatSize = (b: number) => b < 1024 ? `${b}B` : b < 1048576 ? `${(b / 1024).toFixed(0)}KB` : `${(b / 1048576).toFixed(1)}MB`;
579
+
580
+ const copyId = () => {
581
+ if (!value) return;
582
+ navigator.clipboard.writeText(value).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1500); });
583
+ };
584
+
585
+ return (
586
+ <div className="flex flex-col gap-0.5">
587
+ <label className="text-[9px] text-gray-500">Bound Session {value ? '' : '(auto-detect on first start)'}</label>
588
+ <select value={value} onChange={e => onChange(e.target.value)}
589
+ className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-gray-400 font-mono focus:outline-none focus:border-[#58a6ff]">
590
+ <option value="">Auto-detect (latest session)</option>
591
+ {sessions.map(s => (
592
+ <option key={s.id} value={s.id}>
593
+ {s.id.slice(0, 8)} · {formatTime(s.modified)} · {formatSize(s.size)}
594
+ </option>
595
+ ))}
596
+ </select>
597
+ {value && (
598
+ <div className="flex items-center gap-1 mt-0.5">
599
+ <code className="text-[8px] text-gray-500 font-mono bg-[#0d1117] px-1.5 py-0.5 rounded border border-[#21262d] flex-1 overflow-hidden text-ellipsis select-all">{value}</code>
600
+ <button onClick={copyId} className="text-[8px] px-1.5 py-0.5 rounded bg-[#30363d] text-gray-400 hover:text-white shrink-0">{copied ? '✓' : 'Copy'}</button>
601
+ <button onClick={() => onChange('')} className="text-[8px] px-1.5 py-0.5 rounded text-gray-600 hover:text-red-400 shrink-0">Clear</button>
602
+ </div>
603
+ )}
604
+ </div>
605
+ );
606
+ }
607
+
608
+ // ─── Agent Config Modal ──────────────────────────────────
609
+
610
+ function AgentConfigModal({ initial, mode, existingAgents, projectPath, onConfirm, onCancel }: {
611
+ initial: Partial<AgentConfig>;
612
+ mode: 'add' | 'edit';
613
+ existingAgents: AgentConfig[];
614
+ projectPath?: string;
615
+ onConfirm: (cfg: Omit<AgentConfig, 'id'>) => void;
616
+ onCancel: () => void;
617
+ }) {
618
+ const [label, setLabel] = useState(initial.label || '');
619
+ const [icon, setIcon] = useState(initial.icon || '🤖');
620
+ const [role, setRole] = useState(initial.role || '');
621
+ const [backend, setBackend] = useState<'api' | 'cli'>(initial.backend === 'api' ? 'api' : 'cli');
622
+ const [agentId, setAgentId] = useState(initial.agentId || 'claude');
623
+ const [availableAgents, setAvailableAgents] = useState<{ id: string; name: string; isProfile?: boolean; backendType?: string; base?: string; cliType?: string }[]>([]);
624
+
625
+ const [pluginInstances, setPluginInstances] = useState<{ id: string; name: string; icon: string; source?: string }[]>([]);
626
+ const [pluginDefs, setPluginDefs] = useState<{ id: string; name: string; icon: string }[]>([]);
627
+
628
+ useEffect(() => {
629
+ fetch('/api/agents').then(r => r.json()).then(data => {
630
+ const list = (data.agents || data || []).map((a: any) => ({
631
+ id: a.id, name: a.name || a.id,
632
+ isProfile: a.isProfile || a.base,
633
+ base: a.base,
634
+ cliType: a.cliType,
635
+ backendType: a.backendType || 'cli',
636
+ }));
637
+ setAvailableAgents(list);
638
+ }).catch(() => {});
639
+ // Fetch saved smith templates
640
+ fetch('/api/smith-templates').then(r => r.json()).then(data => {
641
+ setSavedTemplates(data.templates || []);
642
+ }).catch(() => {});
643
+ // Fetch both: plugin definitions + installed instances
644
+ Promise.all([
645
+ fetch('/api/plugins').then(r => r.json()),
646
+ fetch('/api/plugins?installed=true').then(r => r.json()),
647
+ ]).then(([defData, instData]) => {
648
+ setPluginDefs((defData.plugins || []).map((p: any) => ({ id: p.id, name: p.name, icon: p.icon })));
649
+ setPluginInstances((instData.plugins || []).map((p: any) => ({
650
+ id: p.id,
651
+ name: p.instanceName || p.definition?.name || p.id,
652
+ icon: p.definition?.icon || '🔌',
653
+ source: p.source,
654
+ })));
655
+ }).catch(() => {});
656
+ }, []);
657
+ const [workDirVal, setWorkDirVal] = useState(initial.workDir || '');
658
+ const [outputs, setOutputs] = useState((initial.outputs || []).join(', '));
659
+ const [selectedDeps, setSelectedDeps] = useState<Set<string>>(new Set(initial.dependsOn || []));
660
+ const [stepsText, setStepsText] = useState(
661
+ (initial.steps || []).map(s => `${s.label}: ${s.prompt}`).join('\n') || ''
662
+ );
663
+ const [requiresApproval, setRequiresApproval] = useState(initial.requiresApproval || false);
664
+ const [isPrimary, setIsPrimary] = useState(initial.primary || false);
665
+ const hasPrimaryAlready = existingAgents.some(a => a.primary && a.id !== initial.id);
666
+ const [persistentSession, setPersistentSession] = useState(initial.persistentSession || initial.primary || false);
667
+ const [skipPermissions, setSkipPermissions] = useState(initial.skipPermissions !== false);
668
+ const [agentModel, setAgentModel] = useState(initial.model || '');
669
+ const [watchEnabled, setWatchEnabled] = useState(initial.watch?.enabled || false);
670
+ const [watchInterval, setWatchInterval] = useState(String(initial.watch?.interval || 60));
671
+ const [watchAction, setWatchAction] = useState<'log' | 'analyze' | 'approve' | 'send_message'>(initial.watch?.action || 'log');
672
+ const [watchPrompt, setWatchPrompt] = useState(initial.watch?.prompt || '');
673
+ const [watchSendTo, setWatchSendTo] = useState(initial.watch?.sendTo || '');
674
+ const [selectedPlugins, setSelectedPlugins] = useState<string[]>(initial.plugins || []);
675
+ const [recommendedTypes, setRecommendedTypes] = useState<string[]>([]);
676
+ const [savedTemplates, setSavedTemplates] = useState<{ id: string; name: string; icon: string; description?: string; config: any }[]>([]);
677
+ const [watchDebounce, setWatchDebounce] = useState(String(initial.watch?.targets?.[0]?.debounce ?? 10));
678
+ const [watchTargets, setWatchTargets] = useState<{ type: string; path?: string; cmd?: string; pattern?: string }[]>(
679
+ initial.watch?.targets || []
680
+ );
681
+ const [projectDirs, setProjectDirs] = useState<string[]>([]);
682
+
683
+ useEffect(() => {
684
+ if (!watchEnabled || !projectPath) return;
685
+ fetch(`/api/code?dir=${encodeURIComponent(projectPath)}`)
686
+ .then(r => r.json())
687
+ .then(data => {
688
+ // Collect directories with depth limit (max 2 levels for readability)
689
+ const dirs: string[] = [];
690
+ const walk = (nodes: any[], prefix = '', depth = 0) => {
691
+ for (const n of nodes || []) {
692
+ if (n.type === 'dir') {
693
+ const path = prefix ? `${prefix}/${n.name}` : n.name;
694
+ dirs.push(path);
695
+ if (n.children && depth < 2) walk(n.children, path, depth + 1);
696
+ }
697
+ }
698
+ };
699
+ walk(data.tree || []);
700
+ setProjectDirs(dirs);
701
+ })
702
+ .catch(() => {});
703
+ }, [watchEnabled, projectPath]);
704
+
705
+ const applyPreset = (p: Omit<AgentConfig, 'id'>) => {
706
+ setLabel(p.label); setIcon(p.icon); setRole(p.role);
707
+ setBackend(p.backend); setAgentId(p.agentId || 'claude');
708
+ setWorkDirVal(p.workDir || './');
709
+ setOutputs(p.outputs.join(', '));
710
+ setStepsText(p.steps.map(s => `${s.label}: ${s.prompt}`).join('\n'));
711
+ setRecommendedTypes(p.plugins || []);
712
+ setSelectedPlugins(p.plugins || []);
713
+ if (p.persistentSession !== undefined) setPersistentSession(!!p.persistentSession);
714
+ if (p.skipPermissions !== undefined) setSkipPermissions(p.skipPermissions !== false);
715
+ if (p.requiresApproval !== undefined) setRequiresApproval(!!p.requiresApproval);
716
+ if (p.model) setAgentModel(p.model);
717
+ if (p.watch) {
718
+ setWatchEnabled(!!p.watch.enabled);
719
+ setWatchInterval(String(p.watch.interval || 60));
720
+ setWatchAction(p.watch.action || 'log');
721
+ setWatchPrompt(p.watch.prompt || '');
722
+ setWatchSendTo(p.watch.sendTo || '');
723
+ setWatchTargets(p.watch.targets || []);
724
+ setWatchDebounce(String(p.watch.targets?.[0]?.debounce ?? 10));
725
+ }
726
+ };
727
+
728
+ const applySavedTemplate = (t: { config: any }) => {
729
+ const c = t.config;
730
+ applyPreset({
731
+ label: c.label || '', icon: c.icon || '🤖', role: c.role || '',
732
+ backend: c.backend || 'cli', agentId: c.agentId, dependsOn: [],
733
+ workDir: c.workDir || './', outputs: c.outputs || [],
734
+ steps: c.steps || [], plugins: c.plugins,
735
+ persistentSession: c.persistentSession, skipPermissions: c.skipPermissions,
736
+ requiresApproval: c.requiresApproval, model: c.model,
737
+ watch: c.watch,
738
+ } as any);
739
+ };
740
+
741
+ const handleImportFile = () => {
742
+ const input = document.createElement('input');
743
+ input.type = 'file';
744
+ input.accept = '.json';
745
+ input.onchange = async (e) => {
746
+ const file = (e.target as HTMLInputElement).files?.[0];
747
+ if (!file) return;
748
+ try {
749
+ const text = await file.text();
750
+ const data = JSON.parse(text);
751
+ // Support both raw config and template wrapper
752
+ const config = data.config || data;
753
+ applySavedTemplate({ config });
754
+ } catch {
755
+ alert('Invalid template file');
756
+ }
757
+ };
758
+ input.click();
759
+ };
760
+
761
+ const toggleDep = (id: string) => {
762
+ setSelectedDeps(prev => {
763
+ const next = new Set(prev);
764
+ if (next.has(id)) next.delete(id); else next.add(id);
765
+ return next;
766
+ });
767
+ };
768
+
769
+ const parseSteps = () => stepsText.split('\n').filter(Boolean).map((line, i) => {
770
+ const [lbl, ...rest] = line.split(':');
771
+ return { id: `step-${i}`, label: lbl.trim(), prompt: rest.join(':').trim() || lbl.trim() };
772
+ });
773
+
774
+ // Filter out self when editing
775
+ const otherAgents = existingAgents.filter(a => a.id !== initial.id);
776
+
777
+ return (
778
+ <div className="fixed inset-0 z-50 flex items-center justify-center" style={{ background: 'rgba(0,0,0,0.75)' }}
779
+ onClick={e => { if (e.target === e.currentTarget) onCancel(); }}>
780
+ <div className="w-[440px] max-h-[80vh] overflow-auto rounded-lg border border-[#30363d] p-4 shadow-xl" style={{ background: '#0d1117' }}>
781
+ <div className="flex items-center justify-between mb-3">
782
+ <span className="text-sm font-bold text-white">{mode === 'add' ? 'Add Agent' : 'Edit Agent'}</span>
783
+ <button onClick={onCancel} className="text-gray-500 hover:text-white text-xs">✕</button>
784
+ </div>
785
+
786
+ <div className="flex flex-col gap-2.5">
787
+ {/* Preset + saved templates (add mode only) */}
788
+ {mode === 'add' && (
789
+ <div className="flex flex-col gap-1">
790
+ <label className="text-[9px] text-gray-500 uppercase">Presets</label>
791
+ <div className="flex gap-1 flex-wrap">
792
+ {PRESET_AGENTS.map((p, i) => (
793
+ <button key={i} onClick={() => applyPreset(p)}
794
+ title={p.primary ? 'Recommended for Primary smith (runs at project root, coordinates others)' : p.label}
795
+ className={`text-[9px] px-2 py-1 rounded border transition-colors ${label === p.label ? 'border-[#58a6ff] text-[#58a6ff] bg-[#58a6ff]/10' : p.primary ? 'border-[#f0883e]/40 text-[#f0883e] hover:border-[#f0883e]' : 'border-[#30363d] text-gray-400 hover:text-white'}`}>
796
+ {p.icon} {p.label}{p.primary ? ' ★' : ''}
797
+ </button>
798
+ ))}
799
+ <button onClick={() => { setLabel(''); setIcon('🤖'); setRole(''); setStepsText(''); setOutputs(''); }}
800
+ className={`text-[9px] px-2 py-1 rounded border border-dashed ${!label ? 'border-[#58a6ff] text-[#58a6ff]' : 'border-[#30363d] text-gray-500 hover:text-white'}`}>
801
+ Custom
802
+ </button>
803
+ </div>
804
+ {savedTemplates.length > 0 && (<>
805
+ <label className="text-[9px] text-gray-500 uppercase mt-1">Saved Templates</label>
806
+ <div className="flex gap-1 flex-wrap">
807
+ {savedTemplates.map(t => (
808
+ <button key={t.id} onClick={() => applySavedTemplate(t)}
809
+ className={`text-[9px] px-2 py-1 rounded border transition-colors ${label === t.config?.label ? 'border-[#f0883e] text-[#f0883e] bg-[#f0883e]/10' : 'border-[#30363d] text-gray-400 hover:text-white'}`}
810
+ title={t.description || t.name}>
811
+ {t.icon} {t.name}
812
+ </button>
813
+ ))}
814
+ </div>
815
+ </>)}
816
+ <button onClick={handleImportFile}
817
+ className="text-[9px] px-2 py-1 rounded border border-dashed border-[#30363d] text-gray-500 hover:text-white hover:border-gray-400 self-start mt-0.5">
818
+ 📂 Import from file
819
+ </button>
820
+ </div>
821
+ )}
822
+
823
+ {/* Icon + Label */}
824
+ <div className="flex gap-2">
825
+ <div className="flex flex-col gap-1">
826
+ <label className="text-[9px] text-gray-500 uppercase">Icon</label>
827
+ <input value={icon} onChange={e => setIcon(e.target.value)} className="w-12 text-center text-sm bg-[#161b22] border border-[#30363d] rounded px-1 py-1 text-white focus:outline-none focus:border-[#58a6ff]" />
828
+ </div>
829
+ <div className="flex flex-col gap-1 flex-1">
830
+ <label className="text-[9px] text-gray-500 uppercase">Label</label>
831
+ <input value={label} onChange={e => setLabel(e.target.value)} placeholder="e.g. Engineer" className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff]" />
832
+ </div>
833
+ </div>
834
+
835
+ {/* Backend */}
836
+ <div className="flex flex-col gap-1">
837
+ <label className="text-[9px] text-gray-500 uppercase">Backend</label>
838
+ <div className="flex gap-1">
839
+ {(['cli', 'api'] as const).map(b => (
840
+ <button key={b} onClick={() => setBackend(b)}
841
+ className={`text-[9px] px-2 py-1 rounded border ${backend === b ? 'border-[#58a6ff] text-[#58a6ff] bg-[#58a6ff]/10' : 'border-[#30363d] text-gray-400 hover:text-white'}`}>
842
+ {b === 'cli' ? 'CLI (subscription)' : 'API (api key)'}
843
+ </button>
844
+ ))}
845
+ </div>
846
+ </div>
847
+
848
+ {/* Agent selection — dynamic from /api/agents */}
849
+ {backend === 'cli' && (
850
+ <div className="flex flex-col gap-1">
851
+ <label className="text-[9px] text-gray-500 uppercase">Agent / Profile</label>
852
+ <div className="flex gap-1 flex-wrap">
853
+ {(availableAgents.length > 0
854
+ ? availableAgents.filter(a => a.backendType !== 'api')
855
+ : [{ id: 'claude', name: 'claude' }, { id: 'codex', name: 'codex' }, { id: 'aider', name: 'aider' }]
856
+ ).map(a => (
857
+ <button key={a.id} onClick={() => setAgentId(a.id)}
858
+ className={`text-[9px] px-2 py-1 rounded border ${agentId === a.id ? 'border-[#58a6ff] text-[#58a6ff] bg-[#58a6ff]/10' : 'border-[#30363d] text-gray-400 hover:text-white'}`}>
859
+ {a.name}{a.isProfile ? ' ●' : ''}
860
+ </button>
861
+ ))}
862
+ </div>
863
+ </div>
864
+ )}
865
+ {backend === 'api' && (
866
+ <div className="flex flex-col gap-1">
867
+ <label className="text-[9px] text-gray-500 uppercase">API Profile</label>
868
+ <div className="flex gap-1 flex-wrap">
869
+ {availableAgents.filter(a => a.backendType === 'api').map(a => (
870
+ <button key={a.id} onClick={() => setAgentId(a.id)}
871
+ className={`text-[9px] px-2 py-1 rounded border ${agentId === a.id ? 'border-[#58a6ff] text-[#58a6ff] bg-[#58a6ff]/10' : 'border-[#30363d] text-gray-400 hover:text-white'}`}>
872
+ {a.name}
873
+ </button>
874
+ ))}
875
+ {availableAgents.filter(a => a.backendType === 'api').length === 0 && (
876
+ <span className="text-[9px] text-gray-600">No API profiles configured. Add in Settings.</span>
877
+ )}
878
+ </div>
879
+ </div>
880
+ )}
881
+
882
+ {/* Role */}
883
+ <div className="flex flex-col gap-1">
884
+ <label className="text-[9px] text-gray-500 uppercase">Role / System Prompt</label>
885
+ <textarea value={role} onChange={e => setRole(e.target.value)} rows={5}
886
+ placeholder="Describe this agent's role, responsibilities, available tools, and decision criteria. This will be synced to CLAUDE.md in the agent's working directory."
887
+ className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff] resize-y" />
888
+ </div>
889
+
890
+ {/* Plugin Instances grouped by plugin */}
891
+ <div className="flex flex-col gap-1">
892
+ <label className="text-[9px] text-gray-500 uppercase">Plugin Instances</label>
893
+ {(() => {
894
+ const withSource = pluginInstances.filter(i => i.source);
895
+ if (withSource.length === 0) return <span className="text-[8px] text-gray-600">No instances — create in Marketplace → Plugins</span>;
896
+ // Group by source plugin
897
+ const groups: Record<string, typeof withSource> = {};
898
+ for (const inst of withSource) {
899
+ const key = inst.source!;
900
+ if (!groups[key]) groups[key] = [];
901
+ groups[key].push(inst);
902
+ }
903
+ // Show recommended types that have no instances yet
904
+ const missingRecommended = recommendedTypes.filter(rt =>
905
+ !withSource.some(i => i.source === rt)
906
+ );
907
+
908
+ return <>
909
+ {Object.entries(groups).map(([sourceId, insts]) => {
910
+ const def = pluginDefs.find(d => d.id === sourceId);
911
+ const isRecommended = recommendedTypes.includes(sourceId);
912
+ return (
913
+ <div key={sourceId} className="flex items-start gap-2">
914
+ <span className={`text-[9px] shrink-0 w-20 pt-1 truncate ${isRecommended ? 'text-[#58a6ff]' : 'text-gray-500'}`} title={def?.name || sourceId}>
915
+ {def?.icon || '🔌'} {def?.name || sourceId}
916
+ {isRecommended && <span className="text-[7px] ml-0.5">★</span>}
917
+ </span>
918
+ <div className="flex flex-wrap gap-1 flex-1">
919
+ {insts.map(inst => {
920
+ const selected = selectedPlugins.includes(inst.id);
921
+ return (
922
+ <button key={inst.id}
923
+ onClick={() => setSelectedPlugins(prev => selected ? prev.filter(x => x !== inst.id) : [...prev, inst.id])}
924
+ className={`text-[9px] px-2 py-0.5 rounded border transition-colors ${
925
+ selected
926
+ ? 'border-green-500/40 text-green-400 bg-green-500/10'
927
+ : isRecommended
928
+ ? 'border-[#58a6ff]/30 text-[#58a6ff]/70 hover:text-[#58a6ff]'
929
+ : 'border-[#30363d] text-gray-500 hover:text-gray-300'
930
+ }`}>
931
+ {inst.name}
932
+ </button>
933
+ );
934
+ })}
935
+ </div>
936
+ </div>
937
+ );
938
+ })}
939
+ {missingRecommended.length > 0 && missingRecommended.map(rt => {
940
+ const def = pluginDefs.find(d => d.id === rt);
941
+ return (
942
+ <div key={rt} className="flex items-start gap-2">
943
+ <span className="text-[9px] text-[#58a6ff] shrink-0 w-20 pt-1 truncate">
944
+ {def?.icon || '🔌'} {def?.name || rt}<span className="text-[7px] ml-0.5">★</span>
945
+ </span>
946
+ <span className="text-[8px] text-[#58a6ff]/50 italic pt-1">No instances — create in Marketplace → Plugins</span>
947
+ </div>
948
+ );
949
+ })}
950
+ </>;
951
+
952
+ })()}
953
+ </div>
954
+
955
+ {/* Depends On — checkbox list of existing agents */}
956
+ {otherAgents.length > 0 && (
957
+ <div className="flex flex-col gap-1">
958
+ <label className="text-[9px] text-gray-500 uppercase">Depends On (upstream agents)</label>
959
+ <div className="flex flex-wrap gap-1.5">
960
+ {otherAgents.map(a => (
961
+ <button key={a.id} onClick={() => toggleDep(a.id)}
962
+ className={`text-[9px] px-2 py-1 rounded border flex items-center gap-1 ${
963
+ selectedDeps.has(a.id)
964
+ ? 'border-[#58a6ff] text-[#58a6ff] bg-[#58a6ff]/10'
965
+ : 'border-[#30363d] text-gray-400 hover:text-white'}`}>
966
+ <span>{selectedDeps.has(a.id) ? '☑' : '☐'}</span>
967
+ <span>{a.icon} {a.label}</span>
968
+ </button>
969
+ ))}
970
+ </div>
971
+ </div>
972
+ )}
973
+
974
+ {/* Work Dir + Outputs */}
975
+ <div className="flex gap-2">
976
+ <div className="flex flex-col gap-1 w-28">
977
+ <label className="text-[9px] text-gray-500 uppercase">Work Dir</label>
978
+ <input value={isPrimary ? './' : workDirVal} onChange={e => !isPrimary && setWorkDirVal(e.target.value)} placeholder={label ? `${label.toLowerCase().replace(/\s+/g, '-')}/` : 'engineer/'}
979
+ disabled={isPrimary}
980
+ className={`text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff] ${isPrimary ? 'opacity-50 cursor-not-allowed' : ''}`} />
981
+ <div className="text-[8px] text-gray-600 mt-0.5">
982
+ → {'{project}/'}{(workDirVal || (label ? `${label.toLowerCase().replace(/\s+/g, '-')}/` : '')).replace(/^\.?\//, '')}
983
+ </div>
984
+ </div>
985
+ <div className="flex flex-col gap-1 flex-1">
986
+ <label className="text-[9px] text-gray-500 uppercase">Outputs</label>
987
+ <input value={outputs} onChange={e => setOutputs(e.target.value)} placeholder="docs/prd.md, src/"
988
+ className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff]" />
989
+ </div>
990
+ </div>
991
+
992
+ {/* Primary Agent */}
993
+ <div className="flex items-center gap-2">
994
+ <input type="checkbox" id="primaryAgent" checked={isPrimary}
995
+ onChange={e => {
996
+ const v = e.target.checked;
997
+ setIsPrimary(v);
998
+ if (v) { setPersistentSession(true); setWorkDirVal('./'); }
999
+ }}
1000
+ disabled={hasPrimaryAlready && !isPrimary}
1001
+ className={`accent-[#f0883e] ${hasPrimaryAlready && !isPrimary ? 'opacity-50 cursor-not-allowed' : ''}`} />
1002
+ <label htmlFor="primaryAgent" className={`text-[9px] ${isPrimary ? 'text-[#f0883e] font-medium' : 'text-gray-400'}`}>
1003
+ Primary agent (terminal-only, root directory, fixed session)
1004
+ {hasPrimaryAlready && !isPrimary && <span className="text-gray-600 ml-1">— already set on another agent</span>}
1005
+ </label>
1006
+ </div>
1007
+
1008
+ {/* Requires Approval */}
1009
+ <div className="flex items-center gap-2">
1010
+ <input type="checkbox" id="requiresApproval" checked={requiresApproval} onChange={e => setRequiresApproval(e.target.checked)}
1011
+ className="accent-[#58a6ff]" />
1012
+ <label htmlFor="requiresApproval" className="text-[9px] text-gray-400">Require approval before processing inbox messages</label>
1013
+ </div>
1014
+
1015
+ {/* Persistent Session — only for claude-code based agents */}
1016
+ {(() => {
1017
+ // Check if selected agent supports terminal mode (claude-code or its profiles)
1018
+ const selectedAgent = availableAgents.find(a => a.id === agentId);
1019
+ const isClaude = selectedAgent?.cliType === 'claude-code' || selectedAgent?.base === 'claude' || !selectedAgent;
1020
+ const canTerminal = isClaude || isPrimary;
1021
+ return canTerminal ? (
1022
+ <>
1023
+ <div className="flex items-center gap-2">
1024
+ <input type="checkbox" id="persistentSession" checked={persistentSession} onChange={e => !isPrimary && setPersistentSession(e.target.checked)}
1025
+ disabled={isPrimary}
1026
+ className={`accent-[#3fb950] ${isPrimary ? 'opacity-50 cursor-not-allowed' : ''}`} />
1027
+ <label htmlFor="persistentSession" className={`text-[9px] text-gray-400 ${isPrimary ? 'opacity-50' : ''}`}>
1028
+ Terminal mode {isPrimary ? '(required for primary)' : '— run in terminal instead of headless'}
1029
+ </label>
1030
+ </div>
1031
+ {persistentSession && (
1032
+ <div className="flex flex-col gap-1.5 ml-4">
1033
+ <div className="flex items-center gap-2">
1034
+ <input type="checkbox" id="skipPermissions" checked={skipPermissions} onChange={e => setSkipPermissions(e.target.checked)}
1035
+ className="accent-[#f0883e]" />
1036
+ <label htmlFor="skipPermissions" className="text-[9px] text-gray-400">Skip permissions (auto-approve all tool calls)</label>
1037
+ </div>
1038
+ </div>
1039
+ )}
1040
+ </>
1041
+ ) : (
1042
+ <div className="text-[8px] text-gray-500 bg-gray-500/10 px-2 py-1 rounded">
1043
+ Headless mode only — {agentId} does not support terminal mode
1044
+ </div>
1045
+ );
1046
+ })()}
1047
+
1048
+ {/* Model override — only for claude-code agents */}
1049
+ {(() => {
1050
+ const sa = availableAgents.find(a => a.id === agentId);
1051
+ const ct = sa?.cliType || (agentId === 'claude' ? 'claude-code' : '');
1052
+ if (ct !== 'claude-code') return null;
1053
+ return (
1054
+ <div className="flex flex-col gap-0.5">
1055
+ <label className="text-[9px] text-gray-500 uppercase">Model</label>
1056
+ <input value={agentModel} onChange={e => setAgentModel(e.target.value)}
1057
+ placeholder="default (uses profile or system default)"
1058
+ list="workspace-model-list"
1059
+ className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff] font-mono" />
1060
+ <datalist id="workspace-model-list">
1061
+ <option value="claude-sonnet-4-6" />
1062
+ <option value="claude-opus-4-6" />
1063
+ <option value="claude-haiku-4-5-20251001" />
1064
+ </datalist>
1065
+ </div>
1066
+ );
1067
+ })()}
1068
+
1069
+ {/* Steps */}
1070
+ <div className="flex flex-col gap-1">
1071
+ <label className="text-[9px] text-gray-500 uppercase">Steps (one per line — Label: Prompt)</label>
1072
+ <textarea value={stepsText} onChange={e => setStepsText(e.target.value)} rows={4}
1073
+ placeholder="Analyze: Read docs and identify requirements&#10;Write: Write PRD to docs/prd.md&#10;Review: Review and improve"
1074
+ className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff] resize-none font-mono" />
1075
+ </div>
1076
+
1077
+ {/* Watch */}
1078
+ <div className="flex flex-col gap-1.5 border-t border-[#21262d] pt-2 mt-1">
1079
+ <div className="flex items-center gap-2">
1080
+ <label className="text-[9px] text-gray-500 uppercase">Watch</label>
1081
+ <input type="checkbox" checked={watchEnabled} onChange={e => setWatchEnabled(e.target.checked)}
1082
+ className="accent-[#58a6ff]" />
1083
+ <span className="text-[8px] text-gray-600">Autonomous periodic monitoring</span>
1084
+ </div>
1085
+ {watchEnabled && (<>
1086
+ <div className="flex gap-2">
1087
+ <div className="flex flex-col gap-0.5">
1088
+ <label className="text-[8px] text-gray-600">Interval (s)</label>
1089
+ <input value={watchInterval} onChange={e => setWatchInterval(e.target.value)} type="number" min="10"
1090
+ className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff] w-16" />
1091
+ </div>
1092
+ <div className="flex flex-col gap-0.5">
1093
+ <label className="text-[8px] text-gray-600">Debounce (s)</label>
1094
+ <input value={watchDebounce} onChange={e => setWatchDebounce(e.target.value)} type="number" min="0"
1095
+ className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff] w-16" />
1096
+ </div>
1097
+ <div className="flex flex-col gap-0.5 flex-1">
1098
+ <label className="text-[8px] text-gray-600">On Change</label>
1099
+ <select value={watchAction} onChange={e => setWatchAction(e.target.value as any)}
1100
+ className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff]">
1101
+ <option value="log">Log only</option>
1102
+ <option value="analyze">Auto analyze</option>
1103
+ <option value="approve">Require approval</option>
1104
+ <option value="send_message">Send to agent</option>
1105
+ </select>
1106
+ </div>
1107
+ {watchAction === 'send_message' && (
1108
+ <div className="flex flex-col gap-0.5 flex-1">
1109
+ <label className="text-[8px] text-gray-600">Send to</label>
1110
+ <select value={watchSendTo} onChange={e => setWatchSendTo(e.target.value)}
1111
+ className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff]">
1112
+ <option value="">Select agent...</option>
1113
+ {existingAgents.filter(a => a.id !== initial.id).map(a =>
1114
+ <option key={a.id} value={a.id}>{a.icon} {a.label}</option>
1115
+ )}
1116
+ </select>
1117
+ </div>
1118
+ )}
1119
+ </div>
1120
+ <div className="flex flex-col gap-1">
1121
+ <label className="text-[8px] text-gray-600">Targets</label>
1122
+ {watchTargets.map((t, i) => (
1123
+ <div key={i} className="flex items-center gap-1">
1124
+ <select value={t.type} onChange={e => {
1125
+ const next = [...watchTargets];
1126
+ next[i] = { type: e.target.value };
1127
+ setWatchTargets(next);
1128
+ }} className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white w-24">
1129
+ <option value="directory">Directory</option>
1130
+ <option value="git">Git</option>
1131
+ <option value="agent_output">Agent Output</option>
1132
+ <option value="agent_log">Agent Log</option>
1133
+ <option value="session">Session Output</option>
1134
+ <option value="command">Command</option>
1135
+ <option value="agent_status">Agent Status</option>
1136
+ </select>
1137
+ {t.type === 'directory' && (
1138
+ <WatchPathPicker
1139
+ value={t.path || ''}
1140
+ projectPath={projectPath || ''}
1141
+ onChange={v => {
1142
+ const next = [...watchTargets];
1143
+ next[i] = { ...t, path: v };
1144
+ setWatchTargets(next);
1145
+ }}
1146
+ />
1147
+ )}
1148
+ {t.type === 'agent_status' && (<>
1149
+ <select value={t.path || ''} onChange={e => {
1150
+ const next = [...watchTargets];
1151
+ next[i] = { ...t, path: e.target.value };
1152
+ setWatchTargets(next);
1153
+ }} className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white flex-1">
1154
+ <option value="">Select agent...</option>
1155
+ {existingAgents.filter(a => a.id !== initial.id).map(a =>
1156
+ <option key={a.id} value={a.id}>{a.icon} {a.label}</option>
1157
+ )}
1158
+ </select>
1159
+ <select value={t.pattern || ''} onChange={e => {
1160
+ const next = [...watchTargets];
1161
+ next[i] = { ...t, pattern: e.target.value };
1162
+ setWatchTargets(next);
1163
+ }} className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white w-20">
1164
+ <option value="">Any change</option>
1165
+ <option value="done">done</option>
1166
+ <option value="failed">failed</option>
1167
+ <option value="running">running</option>
1168
+ <option value="idle">idle</option>
1169
+ </select>
1170
+ </>)}
1171
+ {t.type === 'agent_output' && (
1172
+ <select value={t.path || ''} onChange={e => {
1173
+ const next = [...watchTargets];
1174
+ next[i] = { ...t, path: e.target.value };
1175
+ setWatchTargets(next);
1176
+ }} className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white flex-1">
1177
+ <option value="">Select agent...</option>
1178
+ {existingAgents.filter(a => a.id !== initial.id).map(a =>
1179
+ <option key={a.id} value={a.id}>{a.icon} {a.label}</option>
1180
+ )}
1181
+ </select>
1182
+ )}
1183
+ {t.type === 'agent_log' && (<>
1184
+ <select value={t.path || ''} onChange={e => {
1185
+ const next = [...watchTargets];
1186
+ next[i] = { ...t, path: e.target.value };
1187
+ setWatchTargets(next);
1188
+ }} className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white flex-1">
1189
+ <option value="">Select agent...</option>
1190
+ {existingAgents.filter(a => a.id !== initial.id).map(a =>
1191
+ <option key={a.id} value={a.id}>{a.icon} {a.label}</option>
1192
+ )}
1193
+ </select>
1194
+ <input value={t.pattern || ''} onChange={e => {
1195
+ const next = [...watchTargets];
1196
+ next[i] = { ...t, pattern: e.target.value };
1197
+ setWatchTargets(next);
1198
+ }} placeholder="keyword (optional)"
1199
+ className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white w-24" />
1200
+ </>)}
1201
+ {t.type === 'session' && (
1202
+ <SessionTargetSelector
1203
+ target={t}
1204
+ agents={existingAgents.filter(a => a.id !== initial.id)}
1205
+ projectPath={projectPath}
1206
+ onChange={(updated) => {
1207
+ const next = [...watchTargets];
1208
+ next[i] = updated;
1209
+ setWatchTargets(next);
1210
+ }}
1211
+ />
1212
+ )}
1213
+ {t.type === 'command' && (
1214
+ <input value={t.cmd || ''} onChange={e => {
1215
+ const next = [...watchTargets];
1216
+ next[i] = { ...t, cmd: e.target.value };
1217
+ setWatchTargets(next);
1218
+ }} placeholder="npm test"
1219
+ className="text-[10px] bg-[#161b22] border border-[#30363d] rounded px-1 py-0.5 text-white flex-1" />
1220
+ )}
1221
+ <button onClick={() => setWatchTargets(watchTargets.filter((_, j) => j !== i))}
1222
+ className="text-[9px] text-gray-500 hover:text-red-400">✕</button>
1223
+ </div>
1224
+ ))}
1225
+ <button onClick={() => setWatchTargets([...watchTargets, { type: 'directory' }])}
1226
+ className="text-[8px] text-gray-500 hover:text-[#58a6ff] self-start">+ Add target</button>
1227
+ </div>
1228
+ {watchAction === 'analyze' && (
1229
+ <div className="flex flex-col gap-0.5">
1230
+ <label className="text-[8px] text-gray-600">Analysis prompt (optional)</label>
1231
+ <input value={watchPrompt} onChange={e => setWatchPrompt(e.target.value)}
1232
+ placeholder="Analyze these changes and check for issues..."
1233
+ className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff]" />
1234
+ </div>
1235
+ )}
1236
+ {watchAction === 'send_message' && (
1237
+ <div className="flex flex-col gap-0.5">
1238
+ <label className="text-[8px] text-gray-600">Message context (sent with detected changes)</label>
1239
+ <input value={watchPrompt} onChange={e => setWatchPrompt(e.target.value)}
1240
+ placeholder="Review the following changes and report issues..."
1241
+ className="text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1 text-white focus:outline-none focus:border-[#58a6ff]" />
1242
+ </div>
1243
+ )}
1244
+ </>)}
1245
+ </div>
1246
+ </div>
1247
+
1248
+ <div className="flex justify-end gap-2 mt-4">
1249
+ {mode === 'edit' && (
1250
+ <button onClick={() => {
1251
+ const config = {
1252
+ label: label.trim(), icon: icon.trim() || '🤖', role: role.trim(),
1253
+ backend, agentId, workDir: workDirVal.trim() || './',
1254
+ outputs: outputs.split(',').map(s => s.trim()).filter(Boolean),
1255
+ steps: parseSteps(), plugins: selectedPlugins.length > 0 ? selectedPlugins : undefined,
1256
+ persistentSession: persistentSession || undefined, skipPermissions: persistentSession ? (skipPermissions ? undefined : false) : undefined,
1257
+ model: agentModel || undefined, requiresApproval: requiresApproval || undefined,
1258
+ watch: watchEnabled && watchTargets.length > 0 ? { enabled: true, interval: Math.max(10, parseInt(watchInterval) || 60), targets: watchTargets.map(t => ({ ...t, debounce: parseInt(watchDebounce) || 10 })), action: watchAction, prompt: watchPrompt || undefined, sendTo: watchSendTo || undefined } : undefined,
1259
+ };
1260
+ const blob = new Blob([JSON.stringify({ config, name: label.trim(), icon: icon.trim() || '🤖', exportedAt: Date.now() }, null, 2)], { type: 'application/json' });
1261
+ const url = URL.createObjectURL(blob);
1262
+ const a = document.createElement('a');
1263
+ a.href = url; a.download = `smith-${label.trim().toLowerCase().replace(/\s+/g, '-')}.json`; a.click();
1264
+ URL.revokeObjectURL(url);
1265
+ }} className="text-xs px-3 py-1.5 rounded border border-[#30363d] text-gray-400 hover:text-white mr-auto" title="Export config as file">
1266
+ 📤 Export
1267
+ </button>
1268
+ )}
1269
+ <button onClick={onCancel} className="text-xs px-3 py-1.5 rounded border border-[#30363d] text-gray-400 hover:text-white">Cancel</button>
1270
+ <button disabled={!label.trim()} onClick={() => {
1271
+ onConfirm({
1272
+ label: label.trim(), icon: icon.trim() || '🤖', role: role.trim(),
1273
+ backend, agentId, dependsOn: Array.from(selectedDeps),
1274
+ workDir: isPrimary ? './' : (workDirVal.trim() || label.trim().toLowerCase().replace(/\s+/g, '-') + '/'),
1275
+ outputs: outputs.split(',').map(s => s.trim()).filter(Boolean),
1276
+ steps: parseSteps(),
1277
+ primary: isPrimary || undefined,
1278
+ requiresApproval: requiresApproval || undefined,
1279
+ persistentSession: (() => {
1280
+ if (isPrimary) return true;
1281
+ // Non-terminal agents (codex, aider, etc.) force headless
1282
+ const sa = availableAgents.find(a => a.id === agentId);
1283
+ const isClaude = sa?.cliType === 'claude-code' || sa?.base === 'claude' || !sa;
1284
+ return (isClaude || isPrimary) ? (persistentSession || undefined) : false;
1285
+ })(),
1286
+ skipPermissions: persistentSession ? (skipPermissions ? undefined : false) : undefined,
1287
+ model: agentModel || undefined,
1288
+ watch: watchEnabled && watchTargets.length > 0 ? {
1289
+ enabled: true,
1290
+ interval: Math.max(10, parseInt(watchInterval) || 60),
1291
+ targets: watchTargets.map(t => ({ ...t, debounce: parseInt(watchDebounce) || 10 })),
1292
+ action: watchAction,
1293
+ prompt: watchPrompt || undefined,
1294
+ sendTo: watchSendTo || undefined,
1295
+ } : undefined,
1296
+ plugins: selectedPlugins.length > 0 ? selectedPlugins : undefined,
1297
+ } as any);
1298
+ }} className="text-xs px-3 py-1.5 rounded bg-[#238636] text-white hover:bg-[#2ea043] disabled:opacity-40">
1299
+ {mode === 'add' ? 'Add' : 'Save'}
1300
+ </button>
1301
+ </div>
1302
+ </div>
1303
+ </div>
1304
+ );
1305
+ }
1306
+
1307
+ // ─── Message Dialog ──────────────────────────────────────
1308
+
1309
+ function MessageDialog({ agentLabel, onSend, onCancel }: {
1310
+ agentLabel: string;
1311
+ onSend: (msg: string) => void;
1312
+ onCancel: () => void;
1313
+ }) {
1314
+ const [msg, setMsg] = useState('');
1315
+ return (
1316
+ <div className="fixed inset-0 z-50 flex items-center justify-center" style={{ background: 'rgba(0,0,0,0.75)' }}
1317
+ onClick={e => { if (e.target === e.currentTarget) onCancel(); }}>
1318
+ <div className="w-96 rounded-lg border border-[#30363d] p-4 shadow-xl" style={{ background: '#0d1117' }}>
1319
+ <div className="text-sm font-bold text-white mb-2">Message to {agentLabel}</div>
1320
+ <textarea value={msg} onChange={e => setMsg(e.target.value)} rows={3} autoFocus
1321
+ placeholder="Type your message..."
1322
+ className="w-full text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1.5 text-white focus:outline-none focus:border-[#58a6ff] resize-none" />
1323
+ <div className="flex justify-end gap-2 mt-3">
1324
+ <button onClick={onCancel} className="text-xs px-3 py-1.5 rounded border border-[#30363d] text-gray-400 hover:text-white">Cancel</button>
1325
+ <button onClick={() => { if (msg.trim()) onSend(msg.trim()); }}
1326
+ className="text-xs px-3 py-1.5 rounded bg-[#238636] text-white hover:bg-[#2ea043]">Send</button>
1327
+ </div>
1328
+ </div>
1329
+ </div>
1330
+ );
1331
+ }
1332
+
1333
+ // ─── Run Prompt Dialog ───────────────────────────────────
1334
+
1335
+ function RunPromptDialog({ agentLabel, onRun, onCancel }: {
1336
+ agentLabel: string;
1337
+ onRun: (input: string) => void;
1338
+ onCancel: () => void;
1339
+ }) {
1340
+ const [input, setInput] = useState('');
1341
+ return (
1342
+ <div className="fixed inset-0 z-50 flex items-center justify-center" style={{ background: 'rgba(0,0,0,0.75)' }}
1343
+ onClick={e => { if (e.target === e.currentTarget) onCancel(); }}>
1344
+ <div className="w-[460px] rounded-lg border border-[#30363d] p-4 shadow-xl" style={{ background: '#0d1117' }}>
1345
+ <div className="text-sm font-bold text-white mb-1">Run {agentLabel}</div>
1346
+ <div className="text-[9px] text-gray-500 mb-3">Describe the task or requirements. This will be the initial input for the agent.</div>
1347
+ <textarea value={input} onChange={e => setInput(e.target.value)} rows={5} autoFocus
1348
+ placeholder="e.g. Build a REST API for user management with login, registration, and profile endpoints. Use Express + TypeScript + PostgreSQL."
1349
+ className="w-full text-xs bg-[#161b22] border border-[#30363d] rounded px-2 py-1.5 text-white focus:outline-none focus:border-[#58a6ff] resize-none" />
1350
+ <div className="flex items-center justify-between mt-3">
1351
+ <span className="text-[8px] text-gray-600">Leave empty to run without specific input</span>
1352
+ <div className="flex gap-2">
1353
+ <button onClick={onCancel} className="text-xs px-3 py-1.5 rounded border border-[#30363d] text-gray-400 hover:text-white">Cancel</button>
1354
+ <button onClick={() => onRun(input.trim())}
1355
+ className="text-xs px-3 py-1.5 rounded bg-[#238636] text-white hover:bg-[#2ea043]">▶ Run</button>
1356
+ </div>
1357
+ </div>
1358
+ </div>
1359
+ </div>
1360
+ );
1361
+ }
1362
+
1363
+ // ─── Log Panel (overlay) ─────────────────────────────────
1364
+
1365
+ /** Format log content: extract readable text from JSON, format nicely */
1366
+ function LogContent({ content, subtype }: { content: string; subtype?: string }) {
1367
+ if (!content) return null;
1368
+ const MAX_LINES = 40;
1369
+ const MAX_CHARS = 4000;
1370
+
1371
+ let text = content;
1372
+
1373
+ // Try to parse JSON and extract human-readable content
1374
+ if (text.startsWith('{') || text.startsWith('[')) {
1375
+ try {
1376
+ const parsed = JSON.parse(text);
1377
+ if (typeof parsed === 'string') {
1378
+ text = parsed;
1379
+ } else if (parsed.content) {
1380
+ text = String(parsed.content);
1381
+ } else if (parsed.text) {
1382
+ text = String(parsed.text);
1383
+ } else if (parsed.result) {
1384
+ text = typeof parsed.result === 'string' ? parsed.result : JSON.stringify(parsed.result, null, 2);
1385
+ } else if (parsed.message?.content) {
1386
+ // Claude stream-json format
1387
+ const blocks = Array.isArray(parsed.message.content) ? parsed.message.content : [parsed.message.content];
1388
+ text = blocks.map((b: any) => {
1389
+ if (typeof b === 'string') return b;
1390
+ if (b.type === 'text') return b.text;
1391
+ if (b.type === 'tool_use') return `🔧 ${b.name}(${typeof b.input === 'string' ? b.input : JSON.stringify(b.input).slice(0, 100)})`;
1392
+ if (b.type === 'tool_result') return `→ ${typeof b.content === 'string' ? b.content.slice(0, 200) : JSON.stringify(b.content).slice(0, 200)}`;
1393
+ return JSON.stringify(b).slice(0, 100);
1394
+ }).join('\n');
1395
+ } else if (Array.isArray(parsed)) {
1396
+ text = parsed.map((item: any) => typeof item === 'string' ? item : JSON.stringify(item)).join('\n');
1397
+ } else {
1398
+ // Generic object — show key fields only
1399
+ const keys = Object.keys(parsed);
1400
+ if (keys.length <= 5) {
1401
+ text = keys.map(k => `${k}: ${typeof parsed[k] === 'string' ? parsed[k] : JSON.stringify(parsed[k]).slice(0, 80)}`).join('\n');
1402
+ } else {
1403
+ text = JSON.stringify(parsed, null, 2);
1404
+ }
1405
+ }
1406
+ } catch {
1407
+ // Not valid JSON, keep as-is
1408
+ }
1409
+ }
1410
+
1411
+ // Truncate
1412
+ const lines = text.split('\n');
1413
+ const truncatedLines = lines.length > MAX_LINES;
1414
+ const truncatedChars = text.length > MAX_CHARS;
1415
+ if (truncatedLines) text = lines.slice(0, MAX_LINES).join('\n');
1416
+ if (truncatedChars) text = text.slice(0, MAX_CHARS);
1417
+ const truncated = truncatedLines || truncatedChars;
1418
+
1419
+ return (
1420
+ <span className="break-all">
1421
+ <pre className="whitespace-pre-wrap text-[10px] leading-relaxed inline">{text}</pre>
1422
+ {truncated && <span className="text-gray-600 text-[9px]"> ...({lines.length} lines)</span>}
1423
+ </span>
1424
+ );
1425
+ }
1426
+
1427
+ function LogPanel({ agentId, agentLabel, workspaceId, onClose }: {
1428
+ agentId: string; agentLabel: string; workspaceId: string; onClose: () => void;
1429
+ }) {
1430
+ const [logs, setLogs] = useState<any[]>([]);
1431
+ const [filter, setFilter] = useState<'all' | 'messages' | 'summaries'>('all');
1432
+ const scrollRef = useRef<HTMLDivElement>(null);
1433
+
1434
+ useEffect(() => {
1435
+ // Read persistent logs from logs.jsonl (not in-memory state history)
1436
+ fetch(`/api/workspace/${workspaceId}/smith`, {
1437
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
1438
+ body: JSON.stringify({ action: 'logs', agentId }),
1439
+ }).then(r => r.json()).then(data => {
1440
+ if (data.logs?.length) setLogs(data.logs);
1441
+ }).catch(() => {});
1442
+ }, [workspaceId, agentId]);
1443
+
1444
+ useEffect(() => {
1445
+ scrollRef.current?.scrollTo(0, scrollRef.current.scrollHeight);
1446
+ }, [logs, filter]);
1447
+
1448
+ const filteredLogs = filter === 'all' ? logs :
1449
+ filter === 'messages' ? logs.filter((e: any) => e.subtype === 'bus_message' || e.subtype === 'revalidation_request' || e.subtype === 'user_message') :
1450
+ logs.filter((e: any) => e.subtype === 'step_summary' || e.subtype === 'final_summary');
1451
+
1452
+ const msgCount = logs.filter((e: any) => e.subtype === 'bus_message' || e.subtype === 'revalidation_request' || e.subtype === 'user_message').length;
1453
+
1454
+ return (
1455
+ <div className="fixed inset-0 z-50 flex items-center justify-center" style={{ background: 'rgba(0,0,0,0.85)' }}
1456
+ onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
1457
+ <div className="flex flex-col rounded-xl overflow-hidden shadow-2xl" style={{ width: '75vw', height: '65vh', border: '1px solid #30363d', background: '#0d1117' }}>
1458
+ <div className="flex items-center gap-2 px-4 py-2 border-b border-[#30363d] shrink-0">
1459
+ <span className="text-sm font-bold text-white">Logs: {agentLabel}</span>
1460
+ <span className="text-[9px] text-gray-500">{filteredLogs.length}/{logs.length}</span>
1461
+ {/* Filter tabs */}
1462
+ <div className="flex gap-1 ml-3">
1463
+ {([['all', 'All'], ['messages', `📨 Messages${msgCount > 0 ? ` (${msgCount})` : ''}`], ['summaries', '📊 Summaries']] as const).map(([key, label]) => (
1464
+ <button key={key} onClick={() => setFilter(key as any)}
1465
+ className={`text-[8px] px-2 py-0.5 rounded ${filter === key ? 'bg-[#21262d] text-white' : 'text-gray-500 hover:text-gray-300'}`}>
1466
+ {label}
1467
+ </button>
1468
+ ))}
1469
+ </div>
1470
+ <button onClick={async () => {
1471
+ await fetch(`/api/workspace/${workspaceId}/smith`, {
1472
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
1473
+ body: JSON.stringify({ action: 'clear_logs', agentId }),
1474
+ });
1475
+ setLogs([]);
1476
+ }} className="text-[8px] text-gray-500 hover:text-red-400 ml-auto mr-2">Clear</button>
1477
+ <button onClick={onClose} className="text-gray-500 hover:text-white text-sm">✕</button>
1478
+ </div>
1479
+ <div ref={scrollRef} className="flex-1 overflow-auto p-3 font-mono text-[11px] space-y-0.5">
1480
+ {filteredLogs.length === 0 && <div className="text-gray-600 text-center mt-8">{filter === 'all' ? 'No logs yet' : 'No matching entries'}</div>}
1481
+ {filteredLogs.map((entry, i) => {
1482
+ const isSummary = entry.subtype === 'step_summary' || entry.subtype === 'final_summary';
1483
+ const isBusMsg = entry.subtype === 'bus_message' || entry.subtype === 'revalidation_request' || entry.subtype === 'user_message';
1484
+ return (
1485
+ <div key={i} className={`${
1486
+ isSummary ? 'my-1 px-2 py-1.5 rounded border border-[#21262d] text-[#58a6ff] bg-[#161b22]' :
1487
+ isBusMsg ? 'my-0.5 px-2 py-1 rounded border border-[#f0883e30] text-[#f0883e] bg-[#f0883e08]' :
1488
+ 'flex gap-2 ' + (
1489
+ entry.type === 'system' ? 'text-gray-600' :
1490
+ entry.type === 'result' ? 'text-green-400' : 'text-gray-300'
1491
+ )
1492
+ }`}>
1493
+ {isSummary ? (
1494
+ <pre className="whitespace-pre-wrap text-[10px] leading-relaxed">{entry.content}</pre>
1495
+ ) : isBusMsg ? (
1496
+ <div className="text-[10px] flex items-center gap-2">
1497
+ <span>📨</span>
1498
+ <span className="text-[8px] text-gray-500">{entry.timestamp ? new Date(entry.timestamp).toLocaleTimeString() : ''}</span>
1499
+ <span>{entry.content}</span>
1500
+ </div>
1501
+ ) : (
1502
+ <>
1503
+ <span className="text-[8px] text-gray-600 shrink-0 w-16">{entry.timestamp ? new Date(entry.timestamp).toLocaleTimeString() : ''}</span>
1504
+ {entry.subtype === 'tool_use' && <span className="text-yellow-500 shrink-0">🔧 {entry.tool || 'tool'}</span>}
1505
+ {entry.subtype === 'tool_result' && <span className="text-cyan-500 shrink-0">→</span>}
1506
+ {entry.subtype === 'init' && <span className="text-blue-400 shrink-0">⚡</span>}
1507
+ {entry.subtype === 'daemon' && <span className="text-purple-400 shrink-0">👁</span>}
1508
+ {entry.subtype === 'watch_detected' && <span className="text-orange-400 shrink-0">🔍</span>}
1509
+ {entry.subtype === 'error' && <span className="text-red-400 shrink-0">❌</span>}
1510
+ {!entry.tool && entry.subtype === 'text' && <span className="text-gray-500 shrink-0">💬</span>}
1511
+ <LogContent content={entry.content} subtype={entry.subtype} />
1512
+ </>
1513
+ )}
1514
+ </div>
1515
+ );
1516
+ })}
1517
+ </div>
1518
+ </div>
1519
+ </div>
1520
+ );
1521
+ }
1522
+
1523
+ // ─── Memory Panel ────────────────────────────────────────
1524
+
1525
+ const TYPE_COLORS: Record<string, string> = {
1526
+ decision: 'text-yellow-400', bugfix: 'text-red-400', feature: 'text-green-400',
1527
+ refactor: 'text-cyan-400', discovery: 'text-purple-400', change: 'text-gray-400', session: 'text-blue-400',
1528
+ };
1529
+
1530
+ function MemoryPanel({ agentId, agentLabel, workspaceId, onClose }: {
1531
+ agentId: string; agentLabel: string; workspaceId: string; onClose: () => void;
1532
+ }) {
1533
+ const [data, setData] = useState<any>(null);
1534
+
1535
+ useEffect(() => {
1536
+ fetch(`/api/workspace/${workspaceId}/memory?agentId=${encodeURIComponent(agentId)}`)
1537
+ .then(r => r.json()).then(setData).catch(() => {});
1538
+ }, [workspaceId, agentId]);
1539
+
1540
+ const stats = data?.stats;
1541
+ const display: any[] = data?.display || [];
1542
+
1543
+ return (
1544
+ <div className="fixed inset-0 z-50 flex items-center justify-center" style={{ background: 'rgba(0,0,0,0.85)' }}
1545
+ onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
1546
+ <div className="flex flex-col rounded-xl overflow-hidden shadow-2xl" style={{ width: '70vw', height: '65vh', border: '1px solid #30363d', background: '#0d1117' }}>
1547
+ {/* Header */}
1548
+ <div className="flex items-center gap-2 px-4 py-2 border-b border-[#30363d] shrink-0">
1549
+ <span className="text-sm">🧠</span>
1550
+ <span className="text-sm font-bold text-white">Memory: {agentLabel}</span>
1551
+ {stats && (
1552
+ <span className="text-[9px] text-gray-500">
1553
+ {stats.totalObservations} observations, {stats.totalSessions} sessions
1554
+ {stats.lastUpdated && ` · last updated ${new Date(stats.lastUpdated).toLocaleString()}`}
1555
+ </span>
1556
+ )}
1557
+ <button onClick={onClose} className="text-gray-500 hover:text-white text-sm ml-auto">✕</button>
1558
+ </div>
1559
+
1560
+ {/* Stats bar */}
1561
+ {stats?.typeBreakdown && Object.keys(stats.typeBreakdown).length > 0 && (
1562
+ <div className="flex items-center gap-3 px-4 py-1.5 border-b border-[#21262d] text-[9px]">
1563
+ {Object.entries(stats.typeBreakdown).map(([type, count]) => (
1564
+ <span key={type} className={TYPE_COLORS[type] || 'text-gray-400'}>
1565
+ {type}: {count as number}
1566
+ </span>
1567
+ ))}
1568
+ </div>
1569
+ )}
1570
+
1571
+ {/* Entries */}
1572
+ <div className="flex-1 overflow-auto p-3 space-y-1.5">
1573
+ {display.length === 0 && (
1574
+ <div className="text-gray-600 text-center mt-8">No memory yet. Run this agent to build memory.</div>
1575
+ )}
1576
+ {display.map((entry: any) => (
1577
+ <div key={entry.id} className={`rounded px-3 py-2 ${entry.isCompact ? 'opacity-60' : ''}`}
1578
+ style={{ background: '#161b22', border: '1px solid #21262d' }}>
1579
+ <div className="flex items-center gap-2">
1580
+ <span className="text-[10px]">{entry.icon}</span>
1581
+ <span className={`text-[9px] font-medium ${TYPE_COLORS[entry.type] || 'text-gray-400'}`}>{entry.type}</span>
1582
+ <span className="text-[10px] text-white flex-1 truncate">{entry.title}</span>
1583
+ <span className="text-[8px] text-gray-600 shrink-0">
1584
+ {new Date(entry.timestamp).toLocaleString()}
1585
+ </span>
1586
+ </div>
1587
+ {!entry.isCompact && entry.subtitle && (
1588
+ <div className="text-[9px] text-gray-500 mt-1">{entry.subtitle}</div>
1589
+ )}
1590
+ {!entry.isCompact && entry.facts && entry.facts.length > 0 && (
1591
+ <div className="mt-1 space-y-0.5">
1592
+ {entry.facts.map((f: string, i: number) => (
1593
+ <div key={i} className="text-[8px] text-gray-500">• {f}</div>
1594
+ ))}
1595
+ </div>
1596
+ )}
1597
+ {entry.files && entry.files.length > 0 && (
1598
+ <div className="text-[8px] text-gray-600 mt-1">
1599
+ Files: {entry.files.join(', ')}
1600
+ </div>
1601
+ )}
1602
+ </div>
1603
+ ))}
1604
+ </div>
1605
+ </div>
1606
+ </div>
1607
+ );
1608
+ }
1609
+
1610
+ // ─── Bus Message Panel ───────────────────────────────────
1611
+
1612
+ // ─── Agent Inbox/Outbox Panel ────────────────────────────
1613
+
1614
+ function InboxPanel({ agentId, agentLabel, busLog, agents, workspaceId, onClose }: {
1615
+ agentId: string; agentLabel: string; busLog: any[]; agents: AgentConfig[]; workspaceId: string; onClose: () => void;
1616
+ }) {
1617
+ const labelMap = new Map(agents.map(a => [a.id, `${a.icon} ${a.label}`]));
1618
+ const getLabel = (id: string) => labelMap.get(id) || id;
1619
+ const [deletedIds, setDeletedIds] = useState<Set<string>>(new Set());
1620
+ const [selected, setSelected] = useState<Set<string>>(new Set());
1621
+
1622
+ // Filter messages related to this agent, exclude locally deleted
1623
+ const inbox = busLog.filter(m => m.to === agentId && m.type !== 'ack' && !deletedIds.has(m.id));
1624
+ const outbox = busLog.filter(m => m.from === agentId && m.to !== '_system' && m.type !== 'ack' && !deletedIds.has(m.id));
1625
+ const [tab, setTab] = useState<'inbox' | 'outbox'>('inbox');
1626
+ const messages = tab === 'inbox' ? inbox : outbox;
1627
+
1628
+ const handleDelete = async (msgId: string) => {
1629
+ await wsApi(workspaceId, 'delete_message', { messageId: msgId });
1630
+ setDeletedIds(prev => new Set(prev).add(msgId));
1631
+ };
1632
+
1633
+ const toggleSelect = (msgId: string) => {
1634
+ setSelected(prev => { const s = new Set(prev); s.has(msgId) ? s.delete(msgId) : s.add(msgId); return s; });
1635
+ };
1636
+
1637
+ const selectAll = () => {
1638
+ const deletable = messages.filter(m => m.status === 'done' || m.status === 'failed');
1639
+ setSelected(new Set(deletable.map(m => m.id)));
1640
+ };
1641
+
1642
+ const handleBatchDelete = async () => {
1643
+ for (const id of selected) {
1644
+ await wsApi(workspaceId, 'delete_message', { messageId: id });
1645
+ setDeletedIds(prev => new Set(prev).add(id));
1646
+ }
1647
+ setSelected(new Set());
1648
+ };
1649
+
1650
+ const handleAbortAllPending = async () => {
1651
+ const pendingMsgs = messages.filter(m => m.status === 'pending');
1652
+ await Promise.all(pendingMsgs.map(m =>
1653
+ wsApi(workspaceId, 'abort_message', { messageId: m.id }).catch(() => {})
1654
+ ));
1655
+ };
1656
+
1657
+ return (
1658
+ <div className="fixed inset-0 z-50 flex items-center justify-center" style={{ background: 'rgba(0,0,0,0.85)' }}
1659
+ onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
1660
+ <div className="flex flex-col rounded-xl overflow-hidden shadow-2xl" style={{ width: '60vw', height: '50vh', border: '1px solid #30363d', background: '#0d1117' }}>
1661
+ <div className="flex items-center gap-2 px-4 py-2 border-b border-[#30363d] shrink-0">
1662
+ <span className="text-sm">📨</span>
1663
+ <span className="text-sm font-bold text-white">{agentLabel}</span>
1664
+ <div className="flex gap-1 ml-3">
1665
+ <button onClick={() => setTab('inbox')}
1666
+ className={`text-[9px] px-2 py-0.5 rounded ${tab === 'inbox' ? 'bg-[#21262d] text-white' : 'text-gray-500 hover:text-gray-300'}`}>
1667
+ Inbox ({inbox.length})
1668
+ </button>
1669
+ <button onClick={() => setTab('outbox')}
1670
+ className={`text-[9px] px-2 py-0.5 rounded ${tab === 'outbox' ? 'bg-[#21262d] text-white' : 'text-gray-500 hover:text-gray-300'}`}>
1671
+ Outbox ({outbox.length})
1672
+ </button>
1673
+ </div>
1674
+ {selected.size > 0 && (
1675
+ <div className="flex items-center gap-2 ml-3">
1676
+ <span className="text-[9px] text-gray-400">{selected.size} selected</span>
1677
+ <button onClick={handleBatchDelete}
1678
+ className="text-[8px] px-2 py-0.5 rounded bg-red-600/20 text-red-400 hover:bg-red-600/30">
1679
+ Delete selected
1680
+ </button>
1681
+ <button onClick={() => setSelected(new Set())}
1682
+ className="text-[8px] px-2 py-0.5 rounded bg-gray-600/20 text-gray-400 hover:bg-gray-600/30">
1683
+ Clear
1684
+ </button>
1685
+ </div>
1686
+ )}
1687
+ {selected.size === 0 && (
1688
+ <div className="flex items-center gap-2 ml-3">
1689
+ {messages.some(m => m.status === 'done' || m.status === 'failed') && (
1690
+ <button onClick={selectAll}
1691
+ className="text-[8px] px-2 py-0.5 rounded text-gray-500 hover:text-gray-300">
1692
+ Select all completed
1693
+ </button>
1694
+ )}
1695
+ {messages.some(m => m.status === 'pending') && (
1696
+ <button onClick={handleAbortAllPending}
1697
+ className="text-[8px] px-2 py-0.5 rounded bg-red-600/20 text-red-400 hover:bg-red-600/30">
1698
+ Abort all pending ({messages.filter(m => m.status === 'pending').length})
1699
+ </button>
1700
+ )}
1701
+ </div>
1702
+ )}
1703
+ <button onClick={onClose} className="text-gray-500 hover:text-white text-sm ml-auto">✕</button>
1704
+ </div>
1705
+ <div className="flex-1 overflow-auto p-3 space-y-1.5">
1706
+ {messages.length === 0 && (
1707
+ <div className="text-gray-600 text-center mt-8">No {tab} messages</div>
1708
+ )}
1709
+ {[...messages].reverse().map((msg, i) => {
1710
+ const isTicket = msg.category === 'ticket';
1711
+ const canSelect = msg.status === 'done' || msg.status === 'failed';
1712
+ return (
1713
+ <div key={i} className="flex items-start gap-2 px-3 py-2 rounded text-[10px]" style={{
1714
+ background: '#161b22',
1715
+ border: `1px solid ${isTicket ? '#6e40c9' : '#21262d'}`,
1716
+ borderLeft: isTicket ? '3px solid #a371f7' : undefined,
1717
+ }}>
1718
+ {canSelect && (
1719
+ <input type="checkbox" checked={selected.has(msg.id)} onChange={() => toggleSelect(msg.id)}
1720
+ className="mt-1 shrink-0 accent-[#58a6ff]" />
1721
+ )}
1722
+ <div className="flex-1 min-w-0">
1723
+ <div className="flex items-center gap-2 mb-1 flex-wrap">
1724
+ <span className="text-[8px] text-gray-600">{new Date(msg.timestamp).toLocaleString()}</span>
1725
+ {tab === 'inbox' ? (
1726
+ <span className="text-blue-400">← {getLabel(msg.from)}</span>
1727
+ ) : (
1728
+ <span className="text-green-400">→ {getLabel(msg.to)}</span>
1729
+ )}
1730
+ {/* Category badge */}
1731
+ {isTicket && (
1732
+ <span className="px-1 py-0.5 rounded text-[7px] bg-purple-500/20 text-purple-400">TICKET</span>
1733
+ )}
1734
+ {/* Action badge */}
1735
+ <span className={`px-1.5 py-0.5 rounded text-[8px] ${
1736
+ msg.payload?.action === 'fix_request' || msg.payload?.action === 'bug_report' ? 'bg-red-500/20 text-red-400' :
1737
+ msg.payload?.action === 'update_notify' || msg.payload?.action === 'request_complete' ? 'bg-blue-500/20 text-blue-400' :
1738
+ msg.payload?.action === 'question' ? 'bg-yellow-500/20 text-yellow-400' :
1739
+ 'bg-gray-500/20 text-gray-400'
1740
+ }`}>{msg.payload?.action}</span>
1741
+ {/* Ticket status */}
1742
+ {isTicket && msg.ticketStatus && (
1743
+ <span className={`text-[7px] px-1 rounded ${
1744
+ msg.ticketStatus === 'open' ? 'bg-yellow-500/20 text-yellow-400' :
1745
+ msg.ticketStatus === 'in_progress' ? 'bg-blue-500/20 text-blue-400' :
1746
+ msg.ticketStatus === 'fixed' ? 'bg-green-500/20 text-green-400' :
1747
+ msg.ticketStatus === 'verified' ? 'bg-green-600/20 text-green-300' :
1748
+ msg.ticketStatus === 'closed' ? 'bg-gray-500/20 text-gray-400' :
1749
+ 'bg-gray-500/20 text-gray-400'
1750
+ }`}>{msg.ticketStatus}</span>
1751
+ )}
1752
+ {/* Message delivery status */}
1753
+ <span className={`text-[7px] ${msg.status === 'done' ? 'text-green-500' : msg.status === 'running' ? 'text-blue-400' : msg.status === 'failed' ? 'text-red-500' : msg.status === 'pending_approval' ? 'text-orange-400' : 'text-yellow-500'}`}>
1754
+ {msg.status || 'pending'}
1755
+ </span>
1756
+ {/* Retry count for tickets */}
1757
+ {isTicket && (msg.ticketRetries || 0) > 0 && (
1758
+ <span className="text-[7px] text-orange-400">retry {msg.ticketRetries}/{msg.maxRetries || 3}</span>
1759
+ )}
1760
+ {/* CausedBy trace */}
1761
+ {msg.causedBy && (
1762
+ <span className="text-[7px] text-gray-600" title={`Triggered by message from ${getLabel(msg.causedBy.from)}`}>
1763
+ ← {getLabel(msg.causedBy.from)}
1764
+ </span>
1765
+ )}
1766
+ {/* Actions */}
1767
+ {msg.status === 'pending_approval' && (
1768
+ <div className="flex gap-1 ml-auto">
1769
+ <button onClick={() => wsApi(workspaceId, 'approve_message', { messageId: msg.id })}
1770
+ className="text-[7px] px-1.5 py-0.5 rounded bg-green-600/20 text-green-400 hover:bg-green-600/30">
1771
+ ✓ Approve
1772
+ </button>
1773
+ <button onClick={() => wsApi(workspaceId, 'reject_message', { messageId: msg.id })}
1774
+ className="text-[7px] px-1.5 py-0.5 rounded bg-red-600/20 text-red-400 hover:bg-red-600/30">
1775
+ ✕ Reject
1776
+ </button>
1777
+ </div>
1778
+ )}
1779
+ {(msg.status === 'pending' || msg.status === 'running') && msg.type !== 'ack' && (
1780
+ <div className="flex gap-1 ml-auto">
1781
+ <button onClick={() => wsApi(workspaceId, 'message_done', { messageId: msg.id })}
1782
+ className="text-[7px] px-1.5 py-0.5 rounded bg-green-600/20 text-green-400 hover:bg-green-600/30">
1783
+ ✓ Done
1784
+ </button>
1785
+ <button onClick={() => wsApi(workspaceId, 'abort_message', { messageId: msg.id })}
1786
+ className="text-[7px] px-1.5 py-0.5 rounded bg-red-600/20 text-red-400 hover:bg-red-600/30">
1787
+ ✕ Abort
1788
+ </button>
1789
+ </div>
1790
+ )}
1791
+ {(msg.status === 'done' || msg.status === 'failed') && msg.type !== 'ack' && (
1792
+ <div className="flex gap-1 ml-auto">
1793
+ <button onClick={() => wsApi(workspaceId, 'retry_message', { messageId: msg.id })}
1794
+ className="text-[7px] px-1.5 py-0.5 rounded bg-orange-600/20 text-orange-400 hover:bg-orange-600/30">
1795
+ {msg.status === 'done' ? '↻ Re-run' : '↻ Retry'}
1796
+ </button>
1797
+ <button onClick={() => handleDelete(msg.id)}
1798
+ className="text-[7px] px-1.5 py-0.5 rounded bg-gray-600/20 text-gray-400 hover:bg-red-600/20 hover:text-red-400">
1799
+ 🗑
1800
+ </button>
1801
+ </div>
1802
+ )}
1803
+ </div>
1804
+ <div className="text-gray-300">{msg.payload?.content || ''}</div>
1805
+ {msg.payload?.files?.length > 0 && (
1806
+ <div className="text-[8px] text-gray-600 mt-1">Files: {msg.payload.files.join(', ')}</div>
1807
+ )}
1808
+ </div>
1809
+ </div>
1810
+ );
1811
+ })}
1812
+ </div>
1813
+ </div>
1814
+ </div>
1815
+ );
1816
+ }
1817
+
1818
+ function BusPanel({ busLog, agents, onClose }: {
1819
+ busLog: any[]; agents: AgentConfig[]; onClose: () => void;
1820
+ }) {
1821
+ const labelMap = new Map(agents.map(a => [a.id, `${a.icon} ${a.label}`]));
1822
+ const getLabel = (id: string) => labelMap.get(id) || id;
1823
+
1824
+ return (
1825
+ <div className="fixed inset-0 z-50 flex items-center justify-center" style={{ background: 'rgba(0,0,0,0.85)' }}
1826
+ onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
1827
+ <div className="flex flex-col rounded-xl overflow-hidden shadow-2xl" style={{ width: '65vw', height: '55vh', border: '1px solid #30363d', background: '#0d1117' }}>
1828
+ <div className="flex items-center gap-2 px-4 py-2 border-b border-[#30363d] shrink-0">
1829
+ <span className="text-sm">📡</span>
1830
+ <span className="text-sm font-bold text-white">Agent Communication Logs</span>
1831
+ <span className="text-[9px] text-gray-500">{busLog.length} messages</span>
1832
+ <button onClick={onClose} className="text-gray-500 hover:text-white text-sm ml-auto">✕</button>
1833
+ </div>
1834
+ <div className="flex-1 overflow-auto p-3 space-y-1">
1835
+ {busLog.length === 0 && <div className="text-gray-600 text-center mt-8">No messages yet</div>}
1836
+ {[...busLog].reverse().map((msg, i) => (
1837
+ <div key={i} className="flex items-start gap-2 text-[10px] px-3 py-1.5 rounded"
1838
+ style={{ background: '#161b22', border: '1px solid #21262d' }}>
1839
+ <span className="text-gray-600 shrink-0 w-14">{new Date(msg.timestamp).toLocaleTimeString()}</span>
1840
+ <span className="text-blue-400 shrink-0">{getLabel(msg.from)}</span>
1841
+ <span className="text-gray-600">→</span>
1842
+ <span className="text-green-400 shrink-0">{msg.to === '_system' ? '📡 system' : getLabel(msg.to)}</span>
1843
+ <span className={`px-1 rounded text-[8px] ${
1844
+ msg.payload?.action === 'fix_request' ? 'bg-red-500/20 text-red-400' :
1845
+ msg.payload?.action === 'task_complete' ? 'bg-green-500/20 text-green-400' :
1846
+ msg.payload?.action === 'ack' ? 'bg-gray-500/20 text-gray-500' :
1847
+ 'bg-blue-500/20 text-blue-400'
1848
+ }`}>{msg.payload?.action}</span>
1849
+ <span className="text-gray-400 truncate flex-1">{msg.payload?.content || ''}</span>
1850
+ {msg.status && msg.status !== 'done' && (
1851
+ <span className={`text-[7px] px-1 rounded ${
1852
+ msg.status === 'done' ? 'text-green-500' : msg.status === 'failed' ? 'text-red-500' : 'text-yellow-500'
1853
+ }`}>{msg.status}</span>
1854
+ )}
1855
+ </div>
1856
+ ))}
1857
+ </div>
1858
+ </div>
1859
+ </div>
1860
+ );
1861
+ }
1862
+
1863
+ // ─── Terminal Launch Dialog ───────────────────────────────
1864
+
1865
+ function SessionItem({ session, formatTime, formatSize, onSelect }: {
1866
+ session: { id: string; modified: string; size: number };
1867
+ formatTime: (iso: string) => string;
1868
+ formatSize: (bytes: number) => string;
1869
+ onSelect: () => void;
1870
+ }) {
1871
+ const [expanded, setExpanded] = useState(false);
1872
+ const [copied, setCopied] = useState(false);
1873
+
1874
+ const copyId = (e: React.MouseEvent) => {
1875
+ e.stopPropagation();
1876
+ navigator.clipboard.writeText(session.id).then(() => {
1877
+ setCopied(true);
1878
+ setTimeout(() => setCopied(false), 1500);
1879
+ });
1880
+ };
1881
+
1882
+ return (
1883
+ <div className="rounded border border-[#21262d] hover:border-[#30363d] hover:bg-[#161b22] transition-colors">
1884
+ <div className="flex items-center gap-2 px-3 py-1.5 cursor-pointer" onClick={() => setExpanded(!expanded)}>
1885
+ <span className="text-[8px] text-gray-600">{expanded ? '▼' : '▶'}</span>
1886
+ <span className="text-[9px] text-gray-400 font-mono">{session.id.slice(0, 8)}</span>
1887
+ <span className="text-[8px] text-gray-600">{formatTime(session.modified)}</span>
1888
+ <span className="text-[8px] text-gray-600">{formatSize(session.size)}</span>
1889
+ <button onClick={(e) => { e.stopPropagation(); onSelect(); }}
1890
+ className="ml-auto text-[8px] px-1.5 py-0.5 rounded bg-[#238636]/20 text-[#3fb950] hover:bg-[#238636]/40">Resume</button>
1891
+ </div>
1892
+ {expanded && (
1893
+ <div className="px-3 pb-2 flex items-center gap-1.5">
1894
+ <code className="text-[8px] text-gray-500 font-mono bg-[#161b22] px-1.5 py-0.5 rounded border border-[#21262d] select-all flex-1 overflow-hidden text-ellipsis">
1895
+ {session.id}
1896
+ </code>
1897
+ <button onClick={copyId}
1898
+ className="text-[8px] px-1.5 py-0.5 rounded bg-[#30363d] text-gray-400 hover:text-white hover:bg-[#484f58] shrink-0">
1899
+ {copied ? '✓' : 'Copy'}
1900
+ </button>
1901
+ </div>
1902
+ )}
1903
+ </div>
1904
+ );
1905
+ }
1906
+
1907
+ function TerminalLaunchDialog({ agent, workDir, sessName, projectPath, workspaceId, supportsSession, onLaunch, onCancel }: {
1908
+ agent: AgentConfig; workDir?: string; sessName: string; projectPath: string; workspaceId: string;
1909
+ supportsSession?: boolean;
1910
+ onLaunch: (resumeMode: boolean, sessionId?: string) => void; onCancel: () => void;
1911
+ }) {
1912
+ const [sessions, setSessions] = useState<{ id: string; modified: string; size: number }[]>([]);
1913
+ const [showSessions, setShowSessions] = useState(false);
1914
+ // Use resolved supportsSession from API (defaults to true for backwards compat)
1915
+ const isClaude = supportsSession !== false;
1916
+
1917
+ // Fetch recent sessions (only for claude-based agents)
1918
+ useEffect(() => {
1919
+ if (!isClaude) return;
1920
+ fetch(`/api/workspace/${workspaceId}/smith`, {
1921
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
1922
+ body: JSON.stringify({ action: 'sessions', agentId: agent.id }),
1923
+ }).then(r => r.json()).then(d => {
1924
+ if (d.sessions?.length) setSessions(d.sessions);
1925
+ }).catch(() => {});
1926
+ }, [workspaceId, isClaude]);
1927
+
1928
+ const formatTime = (iso: string) => {
1929
+ const d = new Date(iso);
1930
+ const now = new Date();
1931
+ const diff = now.getTime() - d.getTime();
1932
+ if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
1933
+ if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
1934
+ return d.toLocaleDateString();
1935
+ };
1936
+
1937
+ const formatSize = (bytes: number) => {
1938
+ if (bytes < 1024) return `${bytes}B`;
1939
+ if (bytes < 1048576) return `${(bytes / 1024).toFixed(0)}KB`;
1940
+ return `${(bytes / 1048576).toFixed(1)}MB`;
1941
+ };
1942
+
1943
+ return (
1944
+ <div className="fixed inset-0 z-50 flex items-center justify-center" style={{ background: 'rgba(0,0,0,0.75)' }}
1945
+ onClick={e => { if (e.target === e.currentTarget) onCancel(); }}>
1946
+ <div className="w-80 rounded-lg border border-[#30363d] p-4 shadow-xl" style={{ background: '#0d1117' }}>
1947
+ <div className="text-sm font-bold text-white mb-3">⌨️ {agent.label}</div>
1948
+
1949
+ <div className="space-y-2">
1950
+ <button onClick={() => onLaunch(false)}
1951
+ className="w-full text-left px-3 py-2 rounded border border-[#30363d] hover:border-[#58a6ff] hover:bg-[#161b22] transition-colors">
1952
+ <div className="text-xs text-white font-semibold">{isClaude ? 'New Session' : 'Open Terminal'}</div>
1953
+ <div className="text-[9px] text-gray-500">{isClaude ? 'Start fresh claude session' : `Launch ${agent.agentId || 'agent'}`}</div>
1954
+ </button>
1955
+
1956
+ {isClaude && sessions.length > 0 && (
1957
+ <button onClick={() => onLaunch(true)}
1958
+ className="w-full text-left px-3 py-2 rounded border border-[#30363d] hover:border-[#3fb950] hover:bg-[#161b22] transition-colors">
1959
+ <div className="text-xs text-white font-semibold">Resume Latest</div>
1960
+ <div className="text-[9px] text-gray-500">
1961
+ {sessions[0].id.slice(0, 8)} · {formatTime(sessions[0].modified)} · {formatSize(sessions[0].size)}
1962
+ </div>
1963
+ </button>
1964
+ )}
1965
+
1966
+ {isClaude && sessions.length > 1 && (
1967
+ <button onClick={() => setShowSessions(!showSessions)}
1968
+ className="w-full text-[9px] text-gray-500 hover:text-white py-1">
1969
+ {showSessions ? '▼' : '▶'} All sessions ({sessions.length})
1970
+ </button>
1971
+ )}
1972
+
1973
+ {showSessions && sessions.map(s => (
1974
+ <SessionItem key={s.id} session={s} formatTime={formatTime} formatSize={formatSize}
1975
+ onSelect={() => onLaunch(true, s.id)} />
1976
+ ))}
1977
+ </div>
1978
+
1979
+ <button onClick={onCancel}
1980
+ className="w-full mt-3 text-[9px] text-gray-500 hover:text-white">Cancel</button>
1981
+ </div>
1982
+ </div>
1983
+ );
1984
+ }
1985
+
1986
+ // ─── Floating Terminal ────────────────────────────────────
1987
+
1988
+ function getWsUrl() {
1989
+ if (typeof window === 'undefined') return 'ws://localhost:8404';
1990
+ const p = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
1991
+ const h = window.location.hostname;
1992
+ if (h !== 'localhost' && h !== '127.0.0.1') return `${p}//${window.location.host}/terminal-ws`;
1993
+ const port = parseInt(window.location.port) || 8403;
1994
+ return `${p}//${h}:${port + 1}`;
1995
+ }
1996
+
1997
+ // ─── Terminal Dock (right side panel with tabs) ──────────
1998
+ type TerminalEntry = { agentId: string; label: string; icon: string; cliId: string; cliCmd?: string; cliType?: string; workDir?: string; tmuxSession?: string; sessionName: string; resumeMode?: boolean; resumeSessionId?: string; profileEnv?: Record<string, string> };
1999
+
2000
+ function TerminalDock({ terminals, projectPath, workspaceId, onSessionReady, onClose }: {
2001
+ terminals: TerminalEntry[];
2002
+ projectPath: string;
2003
+ workspaceId: string | null;
2004
+ onSessionReady: (agentId: string, name: string) => void;
2005
+ onClose: (agentId: string) => void;
2006
+ }) {
2007
+ const [activeTab, setActiveTab] = useState(terminals[0]?.agentId || '');
2008
+ const [width, setWidth] = useState(520);
2009
+ const dragRef = useRef<{ startX: number; origW: number } | null>(null);
2010
+
2011
+ // Auto-select new tab when added
2012
+ useEffect(() => {
2013
+ if (terminals.length > 0 && !terminals.find(t => t.agentId === activeTab)) {
2014
+ setActiveTab(terminals[terminals.length - 1].agentId);
2015
+ }
2016
+ }, [terminals, activeTab]);
2017
+
2018
+ const active = terminals.find(t => t.agentId === activeTab);
2019
+
2020
+ return (
2021
+ <div className="flex shrink-0" style={{ width }}>
2022
+ {/* Resize handle */}
2023
+ <div
2024
+ className="w-1 cursor-col-resize hover:bg-[#58a6ff]/30 active:bg-[#58a6ff]/50 transition-colors"
2025
+ style={{ background: '#21262d' }}
2026
+ onMouseDown={(e) => {
2027
+ e.preventDefault();
2028
+ dragRef.current = { startX: e.clientX, origW: width };
2029
+ const onMove = (ev: MouseEvent) => {
2030
+ if (!dragRef.current) return;
2031
+ const newW = dragRef.current.origW - (ev.clientX - dragRef.current.startX);
2032
+ setWidth(Math.max(300, Math.min(1200, newW)));
2033
+ };
2034
+ const onUp = () => { dragRef.current = null; window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
2035
+ window.addEventListener('mousemove', onMove);
2036
+ window.addEventListener('mouseup', onUp);
2037
+ }}
2038
+ />
2039
+ <div className="flex-1 flex flex-col min-w-0 bg-[#0d1117] border-l border-[#30363d]">
2040
+ {/* Tabs */}
2041
+ <div className="flex items-center bg-[#161b22] border-b border-[#30363d] overflow-x-auto shrink-0">
2042
+ {terminals.map(t => (
2043
+ <div
2044
+ key={t.agentId}
2045
+ onClick={() => setActiveTab(t.agentId)}
2046
+ className={`flex items-center gap-1.5 px-3 py-1.5 text-[10px] border-r border-[#30363d] shrink-0 cursor-pointer ${
2047
+ t.agentId === activeTab
2048
+ ? 'bg-[#0d1117] text-white border-b-2 border-b-[#58a6ff]'
2049
+ : 'text-gray-500 hover:text-gray-300 hover:bg-[#1c2128]'
2050
+ }`}
2051
+ >
2052
+ <span>{t.icon}</span>
2053
+ <span className="font-medium">{t.label}</span>
2054
+ <span
2055
+ onClick={(e) => { e.stopPropagation(); onClose(t.agentId); }}
2056
+ className="ml-1 text-gray-600 hover:text-red-400 text-[8px] cursor-pointer"
2057
+ >✕</span>
2058
+ </div>
2059
+ ))}
2060
+ </div>
2061
+ {/* Active terminal */}
2062
+ {active && (
2063
+ <div className="flex-1 min-h-0" key={active.agentId}>
2064
+ <FloatingTerminalInline
2065
+ agentLabel={active.label}
2066
+ agentIcon={active.icon}
2067
+ projectPath={projectPath}
2068
+ agentCliId={active.cliId}
2069
+ cliCmd={active.cliCmd}
2070
+ cliType={active.cliType}
2071
+ workDir={active.workDir}
2072
+ preferredSessionName={active.sessionName}
2073
+ existingSession={active.tmuxSession}
2074
+ resumeMode={active.resumeMode}
2075
+ resumeSessionId={active.resumeSessionId}
2076
+ profileEnv={active.profileEnv}
2077
+ onSessionReady={(name) => onSessionReady(active.agentId, name)}
2078
+ />
2079
+ </div>
2080
+ )}
2081
+ </div>
2082
+ </div>
2083
+ );
2084
+ }
2085
+
2086
+ // ─── Inline Terminal (no drag/resize, fills parent) ──────
2087
+ function FloatingTerminalInline({ agentLabel, agentIcon, projectPath, agentCliId, cliCmd: cliCmdProp, cliType, workDir, preferredSessionName, existingSession, resumeMode, resumeSessionId, profileEnv, isPrimary, skipPermissions, boundSessionId, onSessionReady }: {
2088
+ agentLabel: string;
2089
+ agentIcon: string;
2090
+ projectPath: string;
2091
+ agentCliId: string;
2092
+ cliCmd?: string;
2093
+ cliType?: string;
2094
+ workDir?: string;
2095
+ preferredSessionName?: string;
2096
+ existingSession?: string;
2097
+ resumeMode?: boolean;
2098
+ resumeSessionId?: string;
2099
+ profileEnv?: Record<string, string>;
2100
+ isPrimary?: boolean;
2101
+ skipPermissions?: boolean;
2102
+ boundSessionId?: string;
2103
+ onSessionReady?: (name: string) => void;
2104
+ }) {
2105
+ const containerRef = useRef<HTMLDivElement>(null);
2106
+
2107
+ useEffect(() => {
2108
+ const el = containerRef.current;
2109
+ if (!el) return;
2110
+ let disposed = false;
2111
+
2112
+ Promise.all([
2113
+ import('@xterm/xterm'),
2114
+ import('@xterm/addon-fit'),
2115
+ ]).then(([{ Terminal }, { FitAddon }]) => {
2116
+ if (disposed) return;
2117
+
2118
+ const term = new Terminal({
2119
+ cursorBlink: true, fontSize: 13,
2120
+ fontFamily: 'Menlo, Monaco, "Courier New", monospace',
2121
+ scrollback: 5000,
2122
+ theme: { background: '#0d1117', foreground: '#c9d1d9', cursor: '#58a6ff' },
2123
+ });
2124
+ const fitAddon = new FitAddon();
2125
+ term.loadAddon(fitAddon);
2126
+ term.open(el);
2127
+ setTimeout(() => { try { fitAddon.fit(); } catch {} }, 100);
2128
+
2129
+ const ro = new ResizeObserver(() => { try { fitAddon.fit(); } catch {} });
2130
+ ro.observe(el);
2131
+
2132
+ // Connect to terminal server
2133
+ const wsUrl = getWsUrl();
2134
+ const ws = new WebSocket(wsUrl);
2135
+ ws.binaryType = 'arraybuffer';
2136
+ const decoder = new TextDecoder();
2137
+
2138
+ ws.onopen = () => {
2139
+ ws.send(JSON.stringify({
2140
+ type: 'create',
2141
+ cols: term.cols, rows: term.rows,
2142
+ sessionName: existingSession || preferredSessionName,
2143
+ existingSession: existingSession || undefined,
2144
+ }));
2145
+ };
2146
+ ws.onmessage = async (event) => {
2147
+ try {
2148
+ const msg = JSON.parse(typeof event.data === 'string' ? event.data : decoder.decode(event.data));
2149
+ if (msg.type === 'data') {
2150
+ term.write(typeof msg.data === 'string' ? msg.data : new Uint8Array(Object.values(msg.data)));
2151
+ } else if (msg.type === 'created') {
2152
+ onSessionReady?.(msg.sessionName);
2153
+ // Auto-run CLI on newly created session
2154
+ if (!existingSession) {
2155
+ const cli = cliCmdProp || 'claude';
2156
+ const targetDir = workDir ? `${projectPath}/${workDir}` : projectPath;
2157
+ const cdCmd = `mkdir -p "${targetDir}" && cd "${targetDir}"`;
2158
+ const isClaude = (cliType || 'claude-code') === 'claude-code';
2159
+ const modelFlag = isClaude && profileEnv?.CLAUDE_MODEL ? ` --model ${profileEnv.CLAUDE_MODEL}` : '';
2160
+ const envWithoutModel = profileEnv ? Object.fromEntries(
2161
+ Object.entries(profileEnv).filter(([k]) => k !== 'CLAUDE_MODEL')
2162
+ ) : {};
2163
+ // Build commands as separate short lines
2164
+ const commands: string[] = [];
2165
+ const profileVarsToReset = ['ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL', 'ANTHROPIC_SMALL_FAST_MODEL', 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC', 'DISABLE_TELEMETRY', 'DISABLE_ERROR_REPORTING', 'DISABLE_AUTOUPDATER', 'DISABLE_NON_ESSENTIAL_MODEL_CALLS', 'CLAUDE_MODEL'];
2166
+ commands.push(profileVarsToReset.map(v => `unset ${v}`).join('; '));
2167
+ const envWithoutForge = Object.entries(envWithoutModel).filter(([k]) => !k.startsWith('FORGE_'));
2168
+ if (envWithoutForge.length > 0) {
2169
+ commands.push(envWithoutForge.map(([k, v]) => `export ${k}="${v}"`).join('; '));
2170
+ }
2171
+ const forgeVars = Object.entries(envWithoutModel).filter(([k]) => k.startsWith('FORGE_'));
2172
+ if (forgeVars.length > 0) {
2173
+ commands.push(forgeVars.map(([k, v]) => `export ${k}="${v}"`).join('; '));
2174
+ }
2175
+ let resumeId = resumeSessionId || boundSessionId;
2176
+ if (isClaude && !resumeId && isPrimary) {
2177
+ try {
2178
+ const { resolveFixedSession } = await import('@/lib/session-utils');
2179
+ resumeId = (await resolveFixedSession(projectPath)) || undefined;
2180
+ } catch {}
2181
+ }
2182
+ const resumeFlag = isClaude && resumeId ? ` --resume ${resumeId}` : '';
2183
+ let mcpFlag = '';
2184
+ if (isClaude) { try { const { getMcpFlag } = await import('@/lib/session-utils'); mcpFlag = await getMcpFlag(projectPath); } catch {} }
2185
+ const sf = skipPermissions ? (cliType === 'codex' ? ' --full-auto' : cliType === 'aider' ? ' --yes' : ' --dangerously-skip-permissions') : '';
2186
+ commands.push(`${cdCmd} && ${cli}${resumeFlag}${modelFlag}${sf}${mcpFlag}`);
2187
+ commands.forEach((cmd, i) => {
2188
+ setTimeout(() => {
2189
+ if (!disposed && ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'input', data: cmd + '\n' }));
2190
+ }, 300 + i * 300);
2191
+ });
2192
+ }
2193
+ }
2194
+ } catch {}
2195
+ };
2196
+
2197
+ term.onData(data => { if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'input', data })); });
2198
+ term.onResize(({ cols, rows }) => { if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'resize', cols, rows })); });
2199
+
2200
+ return () => {
2201
+ disposed = true;
2202
+ ro.disconnect();
2203
+ ws.close();
2204
+ term.dispose();
2205
+ };
2206
+ });
2207
+
2208
+ return () => { disposed = true; };
2209
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
2210
+
2211
+ return <div ref={containerRef} className="w-full h-full" style={{ background: '#0d1117' }} />;
2212
+ }
2213
+
2214
+ function FloatingTerminal({ agentLabel, agentIcon, projectPath, agentCliId, cliCmd: cliCmdProp, cliType, workDir, preferredSessionName, existingSession, resumeMode, resumeSessionId, profileEnv, isPrimary, skipPermissions, persistentSession, boundSessionId, initialPos, onSessionReady, onClose }: {
2215
+ agentLabel: string;
2216
+ agentIcon: string;
2217
+ projectPath: string;
2218
+ agentCliId: string;
2219
+ cliCmd?: string; // resolved CLI binary (claude/codex/aider)
2220
+ cliType?: string; // claude-code/codex/aider/generic
2221
+ workDir?: string;
2222
+ preferredSessionName?: string;
2223
+ existingSession?: string;
2224
+ resumeMode?: boolean;
2225
+ resumeSessionId?: string;
2226
+ profileEnv?: Record<string, string>;
2227
+ isPrimary?: boolean;
2228
+ skipPermissions?: boolean;
2229
+ persistentSession?: boolean;
2230
+ boundSessionId?: string;
2231
+ initialPos?: { x: number; y: number };
2232
+ onSessionReady?: (name: string) => void;
2233
+ onClose: (killSession: boolean) => void;
2234
+ }) {
2235
+ const containerRef = useRef<HTMLDivElement>(null);
2236
+ const wsRef = useRef<WebSocket | null>(null);
2237
+ const sessionNameRef = useRef('');
2238
+ const [pos, setPos] = useState(initialPos || { x: 80, y: 60 });
2239
+ const [userDragged, setUserDragged] = useState(false);
2240
+ // Follow node position unless user manually dragged the terminal
2241
+ useEffect(() => {
2242
+ if (initialPos && !userDragged) setPos(initialPos);
2243
+ }, [initialPos?.x, initialPos?.y]); // eslint-disable-line react-hooks/exhaustive-deps
2244
+ const [size, setSize] = useState({ w: 500, h: 300 });
2245
+ const [showCloseDialog, setShowCloseDialog] = useState(false);
2246
+ const dragRef = useRef<{ startX: number; startY: number; origX: number; origY: number } | null>(null);
2247
+ const resizeRef = useRef<{ startX: number; startY: number; origW: number; origH: number } | null>(null);
2248
+
2249
+ useEffect(() => {
2250
+ const el = containerRef.current;
2251
+ if (!el) return;
2252
+ let disposed = false;
2253
+
2254
+ // Dynamic import xterm to avoid SSR issues
2255
+ Promise.all([
2256
+ import('@xterm/xterm'),
2257
+ import('@xterm/addon-fit'),
2258
+ ]).then(([{ Terminal }, { FitAddon }]) => {
2259
+ if (disposed) return;
2260
+
2261
+ const term = new Terminal({
2262
+ cursorBlink: true, fontSize: 10,
2263
+ fontFamily: 'Menlo, Monaco, "Courier New", monospace',
2264
+ scrollback: 5000,
2265
+ theme: { background: '#0d1117', foreground: '#c9d1d9', cursor: '#58a6ff' },
2266
+ });
2267
+ const fitAddon = new FitAddon();
2268
+ term.loadAddon(fitAddon);
2269
+ term.open(el);
2270
+ setTimeout(() => { try { fitAddon.fit(); } catch {} }, 100);
2271
+
2272
+ // Scale font: min 10 at small size, max 13 at large size
2273
+ const ro = new ResizeObserver(() => {
2274
+ try {
2275
+ const w = el.clientWidth;
2276
+ const newSize = Math.min(13, Math.max(10, Math.floor(w / 60)));
2277
+ if (term.options.fontSize !== newSize) term.options.fontSize = newSize;
2278
+ fitAddon.fit();
2279
+ } catch {}
2280
+ });
2281
+ ro.observe(el);
2282
+
2283
+ // Connect WebSocket — attach to existing or create new
2284
+ const ws = new WebSocket(getWsUrl());
2285
+ wsRef.current = ws;
2286
+ ws.onopen = () => {
2287
+ if (existingSession) {
2288
+ ws.send(JSON.stringify({ type: 'attach', sessionName: existingSession, cols: term.cols, rows: term.rows }));
2289
+ } else {
2290
+ // Use fixed session name so it survives refresh/suspend
2291
+ ws.send(JSON.stringify({ type: 'create', sessionName: preferredSessionName, cols: term.cols, rows: term.rows }));
2292
+ }
2293
+ };
2294
+
2295
+ ws.onerror = () => {
2296
+ if (!disposed) term.write('\r\n\x1b[91m[Connection error]\x1b[0m\r\n');
2297
+ };
2298
+ ws.onclose = () => {
2299
+ if (!disposed) term.write('\r\n\x1b[90m[Disconnected]\x1b[0m\r\n');
2300
+ };
2301
+
2302
+ let launched = false;
2303
+ ws.onmessage = async (event) => {
2304
+ if (disposed) return;
2305
+ try {
2306
+ const msg = JSON.parse(event.data);
2307
+ if (msg.type === 'output') { try { term.write(msg.data); } catch {} }
2308
+ else if (msg.type === 'error') {
2309
+ // Session no longer exists — fall back to creating a new one
2310
+ if (msg.message?.includes('no longer exists') || msg.message?.includes('not found')) {
2311
+ term.write(`\r\n\x1b[93m[Session lost — creating new one]\x1b[0m\r\n`);
2312
+ ws.send(JSON.stringify({ type: 'create', cols: term.cols, rows: term.rows }));
2313
+ // Clear existing session so next connected triggers CLI launch
2314
+ (existingSession as any) = undefined;
2315
+ } else {
2316
+ term.write(`\r\n\x1b[91m[${msg.message || 'error'}]\x1b[0m\r\n`);
2317
+ }
2318
+ }
2319
+ else if (msg.type === 'connected') {
2320
+ if (msg.sessionName) {
2321
+ sessionNameRef.current = msg.sessionName;
2322
+ // Save session name (on create or if session changed after fallback)
2323
+ onSessionReady?.(msg.sessionName);
2324
+ }
2325
+ if (launched) return;
2326
+ launched = true;
2327
+ if (existingSession) {
2328
+ // Force terminal redraw for attached session
2329
+ setTimeout(() => {
2330
+ if (!disposed && ws.readyState === WebSocket.OPEN) {
2331
+ ws.send(JSON.stringify({ type: 'resize', cols: term.cols - 1, rows: term.rows }));
2332
+ setTimeout(() => {
2333
+ if (!disposed && ws.readyState === WebSocket.OPEN)
2334
+ ws.send(JSON.stringify({ type: 'resize', cols: term.cols, rows: term.rows }));
2335
+ }, 50);
2336
+ }
2337
+ }, 200);
2338
+ return;
2339
+ }
2340
+ const targetDir = workDir ? `${projectPath}/${workDir}` : projectPath;
2341
+ const cli = cliCmdProp || 'claude';
2342
+
2343
+ const cdCmd = `mkdir -p "${targetDir}" && cd "${targetDir}"`;
2344
+ const isClaude = (cliType || 'claude-code') === 'claude-code';
2345
+ const modelFlag = isClaude && profileEnv?.CLAUDE_MODEL ? ` --model ${profileEnv.CLAUDE_MODEL}` : '';
2346
+ const envWithoutModel = profileEnv ? Object.fromEntries(
2347
+ Object.entries(profileEnv).filter(([k]) => k !== 'CLAUDE_MODEL')
2348
+ ) : {};
2349
+ // Build commands as separate short lines to avoid terminal truncation
2350
+ const commands: string[] = [];
2351
+
2352
+ // 1. Unset old profile vars
2353
+ const profileVarsToReset = ['ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL', 'ANTHROPIC_SMALL_FAST_MODEL', 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC', 'DISABLE_TELEMETRY', 'DISABLE_ERROR_REPORTING', 'DISABLE_AUTOUPDATER', 'DISABLE_NON_ESSENTIAL_MODEL_CALLS', 'CLAUDE_MODEL'];
2354
+ commands.push(profileVarsToReset.map(v => `unset ${v}`).join('; '));
2355
+
2356
+ // 2. Export new profile vars (if any)
2357
+ const envWithoutForge = Object.entries(envWithoutModel).filter(([k]) => !k.startsWith('FORGE_'));
2358
+ if (envWithoutForge.length > 0) {
2359
+ commands.push(envWithoutForge.map(([k, v]) => `export ${k}="${v}"`).join('; '));
2360
+ }
2361
+
2362
+ // 3. Export FORGE vars
2363
+ const forgeVars = Object.entries(envWithoutModel).filter(([k]) => k.startsWith('FORGE_'));
2364
+ if (forgeVars.length > 0) {
2365
+ commands.push(forgeVars.map(([k, v]) => `export ${k}="${v}"`).join('; '));
2366
+ }
2367
+
2368
+ // 4. CLI command
2369
+ let resumeId = resumeSessionId || boundSessionId;
2370
+ if (isClaude && !resumeId && isPrimary) {
2371
+ try {
2372
+ const { resolveFixedSession } = await import('@/lib/session-utils');
2373
+ resumeId = (await resolveFixedSession(projectPath)) || undefined;
2374
+ } catch {}
2375
+ }
2376
+ const resumeFlag = isClaude && resumeId ? ` --resume ${resumeId}` : '';
2377
+ let mcpFlag = '';
2378
+ if (isClaude) { try { const { getMcpFlag } = await import('@/lib/session-utils'); mcpFlag = await getMcpFlag(projectPath); } catch {} }
2379
+ const sf = skipPermissions ? (cliType === 'codex' ? ' --full-auto' : cliType === 'aider' ? ' --yes' : ' --dangerously-skip-permissions') : '';
2380
+ commands.push(`${cdCmd} && ${cli}${resumeFlag}${modelFlag}${sf}${mcpFlag}`);
2381
+
2382
+ // Send each command with delay between them
2383
+ commands.forEach((cmd, i) => {
2384
+ setTimeout(() => {
2385
+ if (!disposed && ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'input', data: cmd + '\n' }));
2386
+ }, 300 + i * 300);
2387
+ });
2388
+ }
2389
+ } catch {}
2390
+ };
2391
+
2392
+ term.onData(data => {
2393
+ if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'input', data }));
2394
+ });
2395
+ term.onResize(({ cols, rows }) => {
2396
+ if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'resize', cols, rows }));
2397
+ });
2398
+
2399
+ return () => {
2400
+ disposed = true;
2401
+ ro.disconnect();
2402
+ ws.close();
2403
+ term.dispose();
2404
+ };
2405
+ });
2406
+
2407
+ return () => { disposed = true; };
2408
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
2409
+
2410
+ return (
2411
+ <div
2412
+ className="fixed z-50 bg-[#0d1117] border border-[#30363d] rounded-lg shadow-2xl flex flex-col overflow-hidden"
2413
+ style={{ left: pos.x, top: pos.y, width: size.w, height: size.h }}
2414
+ >
2415
+ {/* Draggable header */}
2416
+ <div
2417
+ className="flex items-center gap-2 px-3 py-1.5 bg-[#161b22] border-b border-[#30363d] cursor-move shrink-0 select-none"
2418
+ onMouseDown={(e) => {
2419
+ e.preventDefault();
2420
+ dragRef.current = { startX: e.clientX, startY: e.clientY, origX: pos.x, origY: pos.y };
2421
+ setUserDragged(true);
2422
+ const onMove = (ev: MouseEvent) => {
2423
+ if (!dragRef.current) return;
2424
+ setPos({ x: Math.max(0, dragRef.current.origX + ev.clientX - dragRef.current.startX), y: Math.max(0, dragRef.current.origY + ev.clientY - dragRef.current.startY) });
2425
+ };
2426
+ const onUp = () => { dragRef.current = null; window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
2427
+ window.addEventListener('mousemove', onMove);
2428
+ window.addEventListener('mouseup', onUp);
2429
+ }}
2430
+ >
2431
+ <span className="text-sm">{agentIcon}</span>
2432
+ <span className="text-[11px] font-semibold text-white">{agentLabel}</span>
2433
+ <span className="text-[8px] text-gray-500">⌨️ manual terminal</span>
2434
+ <button onClick={() => setShowCloseDialog(true)} className="ml-auto text-gray-500 hover:text-white text-sm">✕</button>
2435
+ </div>
2436
+
2437
+ {/* Terminal */}
2438
+ <div ref={containerRef} className="flex-1 min-h-0" style={{ background: '#0d1117' }} />
2439
+
2440
+ {/* Resize handle */}
2441
+ <div
2442
+ className="absolute bottom-0 right-0 w-4 h-4 cursor-se-resize"
2443
+ onMouseDown={(e) => {
2444
+ e.preventDefault();
2445
+ e.stopPropagation();
2446
+ resizeRef.current = { startX: e.clientX, startY: e.clientY, origW: size.w, origH: size.h };
2447
+ const onMove = (ev: MouseEvent) => {
2448
+ if (!resizeRef.current) return;
2449
+ setSize({ w: Math.max(400, resizeRef.current.origW + ev.clientX - resizeRef.current.startX), h: Math.max(250, resizeRef.current.origH + ev.clientY - resizeRef.current.startY) });
2450
+ };
2451
+ const onUp = () => { resizeRef.current = null; window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
2452
+ window.addEventListener('mousemove', onMove);
2453
+ window.addEventListener('mouseup', onUp);
2454
+ }}
2455
+ >
2456
+ <svg viewBox="0 0 16 16" className="w-3 h-3 absolute bottom-0.5 right-0.5 text-gray-600">
2457
+ <path d="M14 14L8 14L14 8Z" fill="currentColor" />
2458
+ </svg>
2459
+ </div>
2460
+
2461
+ {/* Close confirmation dialog */}
2462
+ {showCloseDialog && (
2463
+ <div className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50" onClick={() => setShowCloseDialog(false)}>
2464
+ <div className="bg-[#161b22] border border-[#30363d] rounded-lg p-4 shadow-xl max-w-sm" onClick={e => e.stopPropagation()}>
2465
+ <h3 className="text-sm font-semibold text-white mb-2">Close Terminal — {agentLabel}</h3>
2466
+ <p className="text-xs text-gray-400 mb-3">
2467
+ This agent has an active terminal session.
2468
+ </p>
2469
+ <div className="flex gap-2">
2470
+ <button onClick={() => { setShowCloseDialog(false); onClose(false); }}
2471
+ className="flex-1 px-3 py-1.5 text-[11px] rounded bg-[#2a2a4a] text-gray-300 hover:bg-[#3a3a5a] hover:text-white">
2472
+ Suspend
2473
+ <span className="block text-[9px] text-gray-500 mt-0.5">Hide panel, session keeps running</span>
2474
+ </button>
2475
+ <button onClick={() => {
2476
+ setShowCloseDialog(false);
2477
+ if (wsRef.current?.readyState === WebSocket.OPEN && sessionNameRef.current) {
2478
+ wsRef.current.send(JSON.stringify({ type: 'kill', sessionName: sessionNameRef.current }));
2479
+ }
2480
+ onClose(true);
2481
+ }}
2482
+ className={`flex-1 px-3 py-1.5 text-[11px] rounded ${persistentSession ? 'bg-yellow-500/20 text-yellow-400 hover:bg-yellow-500/30' : 'bg-red-500/20 text-red-400 hover:bg-red-500/30'}`}>
2483
+ {persistentSession ? 'Restart Session' : 'Kill Session'}
2484
+ <span className={`block text-[9px] mt-0.5 ${persistentSession ? 'text-yellow-400/60' : 'text-red-400/60'}`}>
2485
+ {persistentSession ? 'Kill and restart with fresh env' : 'End session permanently'}
2486
+ </span>
2487
+ </button>
2488
+ </div>
2489
+ <button onClick={() => setShowCloseDialog(false)}
2490
+ className="w-full mt-2 px-3 py-1 text-[10px] text-gray-500 hover:text-gray-300">
2491
+ Cancel
2492
+ </button>
2493
+ </div>
2494
+ </div>
2495
+ )}
2496
+ </div>
2497
+ );
2498
+ }
2499
+
2500
+ // ─── ReactFlow Input Node ────────────────────────────────
2501
+
2502
+ interface InputNodeData {
2503
+ config: AgentConfig;
2504
+ state: AgentState;
2505
+ onSubmit: (content: string) => void;
2506
+ onEdit: () => void;
2507
+ onRemove: () => void;
2508
+ [key: string]: unknown;
2509
+ }
2510
+
2511
+ function InputFlowNode({ data }: NodeProps<Node<InputNodeData>>) {
2512
+ const { config, state, onSubmit, onEdit, onRemove } = data;
2513
+ const isDone = state?.taskStatus === 'done';
2514
+ const [text, setText] = useState('');
2515
+ const entries = config.entries || [];
2516
+
2517
+ return (
2518
+ <div className="w-60 flex flex-col rounded-lg select-none"
2519
+ style={{ border: `1px solid ${isDone ? '#58a6ff60' : '#30363d50'}`, background: '#0d1117',
2520
+ boxShadow: isDone ? '0 0 10px #58a6ff15' : 'none' }}>
2521
+ <Handle type="source" position={Position.Right} style={{ background: '#58a6ff', width: 8, height: 8, border: 'none' }} />
2522
+
2523
+ {/* Header */}
2524
+ <div className="flex items-center gap-2 px-3 py-2" style={{ borderBottom: '1px solid #21262d' }}>
2525
+ <span className="text-sm">{config.icon || '📝'}</span>
2526
+ <span className="text-xs font-semibold text-white flex-1">{config.label || 'Input'}</span>
2527
+ {entries.length > 0 && <span className="text-[8px] text-gray-600">{entries.length}</span>}
2528
+ <div className="w-2 h-2 rounded-full" style={{ background: isDone ? '#58a6ff' : '#484f58', boxShadow: isDone ? '0 0 6px #58a6ff' : 'none' }} />
2529
+ </div>
2530
+
2531
+ {/* History entries (scrollable, compact) */}
2532
+ {entries.length > 0 && (
2533
+ <div className="max-h-24 overflow-auto px-3 py-1.5 space-y-1" style={{ borderBottom: '1px solid #21262d' }}
2534
+ onPointerDown={e => e.stopPropagation()}>
2535
+ {entries.map((e, i) => (
2536
+ <div key={i} className={`text-[9px] leading-relaxed ${i === entries.length - 1 ? 'text-gray-300' : 'text-gray-600'}`}>
2537
+ <span className="text-[7px] text-gray-700 mr-1">#{i + 1}</span>
2538
+ {e.content.length > 80 ? e.content.slice(0, 80) + '…' : e.content}
2539
+ </div>
2540
+ ))}
2541
+ </div>
2542
+ )}
2543
+
2544
+ {/* New input */}
2545
+ <div className="px-3 py-2">
2546
+ <textarea value={text} onChange={e => setText(e.target.value)} rows={2}
2547
+ placeholder={entries.length > 0 ? 'Add new requirement or change...' : 'Describe requirements...'}
2548
+ className="w-full text-[10px] bg-[#0d1117] border border-[#21262d] rounded px-2 py-1.5 text-gray-300 placeholder-gray-600 focus:outline-none focus:border-[#58a6ff]/50 resize-none"
2549
+ onPointerDown={e => e.stopPropagation()} />
2550
+ </div>
2551
+
2552
+ {/* Actions */}
2553
+ <div className="flex items-center gap-1 px-2 py-1.5" style={{ borderTop: '1px solid #21262d' }}>
2554
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => {
2555
+ e.stopPropagation();
2556
+ if (!text.trim()) return;
2557
+ onSubmit(text.trim());
2558
+ setText('');
2559
+ }}
2560
+ className="text-[9px] px-2 py-0.5 rounded bg-[#238636]/20 text-[#3fb950] hover:bg-[#238636]/30 disabled:opacity-30"
2561
+ disabled={!text.trim()}>
2562
+ {entries.length > 0 ? '+ Add' : '✓ Submit'}
2563
+ </button>
2564
+ <div className="flex-1" />
2565
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onRemove(); }}
2566
+ className="text-[9px] text-gray-700 hover:text-red-400 px-1">✕</button>
2567
+ </div>
2568
+ </div>
2569
+ );
2570
+ }
2571
+
2572
+ // ─── ReactFlow Agent Node ────────────────────────────────
2573
+
2574
+ interface AgentNodeData {
2575
+ config: AgentConfig;
2576
+ state: AgentState;
2577
+ colorIdx: number;
2578
+ previewLines: string[];
2579
+ projectPath: string;
2580
+ workspaceId: string | null;
2581
+ onRun: () => void;
2582
+ onPause: () => void;
2583
+ onStop: () => void;
2584
+ onRetry: () => void;
2585
+ onEdit: () => void;
2586
+ onRemove: () => void;
2587
+ onMessage: () => void;
2588
+ onApprove: () => void;
2589
+ onShowLog: () => void;
2590
+ onShowMemory: () => void;
2591
+ onShowInbox: () => void;
2592
+ onOpenTerminal: () => void;
2593
+ onSwitchSession: () => void;
2594
+ onSaveAsTemplate: () => void;
2595
+ mascotTheme: MascotTheme;
2596
+ onMarkIdle?: () => void;
2597
+ onMarkDone?: (notify: boolean) => void;
2598
+ onMarkFailed?: (notify: boolean) => void;
2599
+ inboxPending?: number;
2600
+ inboxFailed?: number;
2601
+ [key: string]: unknown;
2602
+ }
2603
+
2604
+ // PortalTerminal/NodeTerminal removed — xterm cannot render inside React Flow nodes
2605
+ // and createPortal causes event routing issues. Using FloatingTerminal instead.
2606
+
2607
+ // ─── Worker Mascot — SVG stick figure with pose-based animations ──────────────
2608
+ const MASCOT_STYLES = `
2609
+ @keyframes mascot-sleep {
2610
+ 0%, 100% { transform: translateY(0) rotate(-3deg); opacity: 0.6; }
2611
+ 50% { transform: translateY(-2px) rotate(3deg); opacity: 0.9; }
2612
+ }
2613
+ @keyframes mascot-work {
2614
+ 0%, 100% { transform: translateY(0) rotate(0deg); }
2615
+ 25% { transform: translateY(-2px) rotate(-6deg); }
2616
+ 50% { transform: translateY(0) rotate(0deg); }
2617
+ 75% { transform: translateY(-2px) rotate(6deg); }
2618
+ }
2619
+ @keyframes mascot-celebrate {
2620
+ 0% { transform: translateY(0) scale(1); }
2621
+ 12% { transform: translateY(-6px) scale(1.15) rotate(-10deg); }
2622
+ 25% { transform: translateY(-3px) scale(1.1) rotate(0deg); }
2623
+ 37% { transform: translateY(-6px) scale(1.15) rotate(10deg); }
2624
+ 50% { transform: translateY(0) scale(1) rotate(0deg); }
2625
+ 100% { transform: translateY(0) scale(1) rotate(0deg); }
2626
+ }
2627
+ @keyframes mascot-fall {
2628
+ 0% { transform: translateY(0) rotate(0deg); }
2629
+ 30% { transform: translateY(2px) rotate(-15deg); }
2630
+ 60% { transform: translateY(4px) rotate(-90deg); }
2631
+ 100% { transform: translateY(4px) rotate(-90deg); opacity: 0.6; }
2632
+ }
2633
+ @keyframes mascot-idle {
2634
+ 0%, 100% { transform: translateY(0); }
2635
+ 50% { transform: translateY(-1px); }
2636
+ }
2637
+ @keyframes mascot-blink { 0%, 95%, 100% { opacity: 1; } 97% { opacity: 0.3; } }
2638
+ @keyframes stick-arm-hammer {
2639
+ 0%, 100% { transform: rotate(-40deg); }
2640
+ 50% { transform: rotate(20deg); }
2641
+ }
2642
+ @keyframes stick-arm-wave {
2643
+ 0%, 100% { transform: rotate(-120deg); }
2644
+ 50% { transform: rotate(-150deg); }
2645
+ }
2646
+ @keyframes stick-leg-walk-l {
2647
+ 0%, 100% { transform: rotate(-10deg); }
2648
+ 50% { transform: rotate(10deg); }
2649
+ }
2650
+ @keyframes stick-leg-walk-r {
2651
+ 0%, 100% { transform: rotate(10deg); }
2652
+ 50% { transform: rotate(-10deg); }
2653
+ }
2654
+ @keyframes stick-zzz {
2655
+ 0% { opacity: 0; transform: translate(0, 0) scale(0.5); }
2656
+ 50% { opacity: 1; transform: translate(4px, -6px) scale(1); }
2657
+ 100% { opacity: 0; transform: translate(8px, -12px) scale(1.2); }
2658
+ }
2659
+ @keyframes stick-spark {
2660
+ 0%, 100% { opacity: 0; }
2661
+ 50% { opacity: 1; }
2662
+ }
2663
+ @keyframes stick-spark-burst {
2664
+ 0% { opacity: 0; transform: scale(0.5); }
2665
+ 30% { opacity: 1; transform: scale(1.2); }
2666
+ 70% { opacity: 1; transform: scale(1); }
2667
+ 100% { opacity: 0; transform: scale(0.8); }
2668
+ }
2669
+ `;
2670
+ type MascotPose = 'idle' | 'work' | 'done' | 'fail' | 'sleep' | 'wake';
2671
+ export type MascotTheme = 'off' | 'stick' | 'cat' | 'dog' | 'pig' | 'emoji';
2672
+
2673
+ function StickCat({ pose, color, accentColor }: { pose: MascotPose; color: string; accentColor: string }) {
2674
+ const strokeProps = { stroke: color, strokeWidth: 1.5, strokeLinecap: 'round' as const, fill: 'none' };
2675
+ const body = (tailAnim: string) => (
2676
+ <>
2677
+ {/* head */}
2678
+ <circle cx="10" cy="18" r="5" stroke={color} strokeWidth="1.5" fill="none" />
2679
+ {/* ears */}
2680
+ <path d="M 6 15 L 7 11 L 10 14 Z" fill={color} />
2681
+ <path d="M 14 15 L 13 11 L 10 14 Z" fill={color} />
2682
+ {/* eyes */}
2683
+ <circle cx="8" cy="18" r="0.8" fill={accentColor} />
2684
+ <circle cx="12" cy="18" r="0.8" fill={accentColor} />
2685
+ {/* nose */}
2686
+ <path d="M 9.5 19.5 L 10 20 L 10.5 19.5" stroke={accentColor} strokeWidth="0.8" fill="none" strokeLinecap="round" />
2687
+ {/* whiskers */}
2688
+ <line x1="5" y1="19" x2="2" y2="18" stroke={color} strokeWidth="0.6" />
2689
+ <line x1="5" y1="20" x2="2" y2="20" stroke={color} strokeWidth="0.6" />
2690
+ <line x1="15" y1="19" x2="18" y2="18" stroke={color} strokeWidth="0.6" />
2691
+ <line x1="15" y1="20" x2="18" y2="20" stroke={color} strokeWidth="0.6" />
2692
+ {/* body — oval */}
2693
+ <ellipse cx="18" cy="26" rx="8" ry="5" stroke={color} strokeWidth="1.5" fill="none" />
2694
+ {/* tail */}
2695
+ <g style={{ transformOrigin: '26px 26px', animation: tailAnim }}>
2696
+ <path d="M 26 26 Q 30 22 28 18" {...strokeProps} />
2697
+ </g>
2698
+ {/* legs */}
2699
+ <line x1="13" y1="30" x2="13" y2="36" {...strokeProps} />
2700
+ <line x1="23" y1="30" x2="23" y2="36" {...strokeProps} />
2701
+ <line x1="16" y1="31" x2="16" y2="36" {...strokeProps} />
2702
+ <line x1="20" y1="31" x2="20" y2="36" {...strokeProps} />
2703
+ </>
2704
+ );
2705
+
2706
+ if (pose === 'sleep') {
2707
+ return (
2708
+ <svg width="32" height="40" viewBox="0 0 32 40">
2709
+ {/* curled up cat — circle with tail */}
2710
+ <circle cx="16" cy="30" r="8" stroke={color} strokeWidth="1.5" fill="none" />
2711
+ <circle cx="10" cy="28" r="3" stroke={color} strokeWidth="1.5" fill="none" />
2712
+ <line x1="9" y1="27" x2="9" y2="29" stroke={color} strokeWidth="0.8" />
2713
+ <line x1="11" y1="27" x2="11" y2="29" stroke={color} strokeWidth="0.8" />
2714
+ <path d="M 23 32 Q 28 32 26 26" {...strokeProps} />
2715
+ {/* zzz */}
2716
+ <text x="20" y="20" fill={accentColor} fontSize="6" fontWeight="bold" style={{ animation: 'stick-zzz 2s ease-out infinite' }}>z</text>
2717
+ <text x="24" y="14" fill={accentColor} fontSize="4" fontWeight="bold" style={{ animation: 'stick-zzz 2s ease-out infinite 0.7s' }}>z</text>
2718
+ </svg>
2719
+ );
2720
+ }
2721
+
2722
+ if (pose === 'fail') {
2723
+ return (
2724
+ <svg width="32" height="40" viewBox="0 0 32 40">
2725
+ {/* belly up */}
2726
+ <ellipse cx="18" cy="26" rx="8" ry="5" stroke={color} strokeWidth="1.5" fill="none" />
2727
+ <circle cx="10" cy="24" r="4" stroke={color} strokeWidth="1.5" fill="none" />
2728
+ <line x1="8" y1="23" x2="9" y2="24" stroke={accentColor} strokeWidth="0.8" />
2729
+ <line x1="9" y1="23" x2="8" y2="24" stroke={accentColor} strokeWidth="0.8" />
2730
+ <line x1="11" y1="23" x2="12" y2="24" stroke={accentColor} strokeWidth="0.8" />
2731
+ <line x1="12" y1="23" x2="11" y2="24" stroke={accentColor} strokeWidth="0.8" />
2732
+ {/* legs up */}
2733
+ <line x1="14" y1="22" x2="14" y2="16" {...strokeProps} />
2734
+ <line x1="18" y1="22" x2="18" y2="15" {...strokeProps} />
2735
+ <line x1="22" y1="22" x2="22" y2="16" {...strokeProps} />
2736
+ </svg>
2737
+ );
2738
+ }
2739
+
2740
+ if (pose === 'done') {
2741
+ return (
2742
+ <svg width="32" height="40" viewBox="0 0 32 40">
2743
+ {/* jumping — body elevated */}
2744
+ <g style={{ transform: 'translateY(-2px)' }}>
2745
+ {body('none')}
2746
+ </g>
2747
+ <text x="2" y="8" fill="#ffd700" fontSize="6" style={{ animation: 'stick-spark-burst 1.2s ease-out forwards' }}>✦</text>
2748
+ <text x="26" y="10" fill="#ffd700" fontSize="8" style={{ animation: 'stick-spark-burst 1.2s ease-out forwards 0.3s' }}>✦</text>
2749
+ </svg>
2750
+ );
2751
+ }
2752
+
2753
+ if (pose === 'work') {
2754
+ return (
2755
+ <svg width="32" height="40" viewBox="0 0 32 40">
2756
+ {body('stick-arm-hammer 0.4s ease-in-out infinite')}
2757
+ </svg>
2758
+ );
2759
+ }
2760
+
2761
+ if (pose === 'wake') {
2762
+ return (
2763
+ <svg width="32" height="40" viewBox="0 0 32 40">
2764
+ {/* stretching — elongated body */}
2765
+ <circle cx="8" cy="22" r="4" stroke={color} strokeWidth="1.5" fill="none" />
2766
+ <path d="M 4 19 L 5 16 L 8 18 Z" fill={color} />
2767
+ <path d="M 12 19 L 11 16 L 8 18 Z" fill={color} />
2768
+ <circle cx="6.5" cy="22" r="0.6" fill={accentColor} />
2769
+ <circle cx="9.5" cy="22" r="0.6" fill={accentColor} />
2770
+ <ellipse cx="20" cy="28" rx="10" ry="4" stroke={color} strokeWidth="1.5" fill="none" />
2771
+ <line x1="14" y1="32" x2="14" y2="38" {...strokeProps} />
2772
+ <line x1="26" y1="32" x2="26" y2="38" {...strokeProps} />
2773
+ <path d="M 30 28 Q 32 24 30 20" {...strokeProps} />
2774
+ </svg>
2775
+ );
2776
+ }
2777
+
2778
+ // idle — tail swaying
2779
+ return (
2780
+ <svg width="32" height="40" viewBox="0 0 32 40">
2781
+ {body('stick-arm-wave 2s ease-in-out infinite')}
2782
+ </svg>
2783
+ );
2784
+ }
2785
+
2786
+ function StickDog({ pose, color, accentColor }: { pose: MascotPose; color: string; accentColor: string }) {
2787
+ // Side-profile dog — elongated snout forward, triangular perked ear, visible tail
2788
+ // Designed to read clearly at small sizes with distinct dog silhouette
2789
+
2790
+ if (pose === 'sleep') {
2791
+ return (
2792
+ <svg width="40" height="40" viewBox="0 0 40 40">
2793
+ {/* body lying down */}
2794
+ <ellipse cx="22" cy="32" rx="12" ry="4" stroke={color} strokeWidth="1.8" fill={color} fillOpacity="0.15" />
2795
+ {/* head resting on paws — side profile */}
2796
+ <path d="M 10 32 Q 6 30 4 32 Q 2 33 3 35 L 10 35 Z" stroke={color} strokeWidth="1.8" fill={color} fillOpacity="0.2" strokeLinejoin="round" />
2797
+ {/* long snout */}
2798
+ <path d="M 3 34 L 1 35 L 3 36" stroke={color} strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round" />
2799
+ {/* floppy ear */}
2800
+ <path d="M 8 30 Q 6 33 9 34" stroke={color} strokeWidth="2" fill={color} fillOpacity="0.35" strokeLinecap="round" />
2801
+ {/* closed eye */}
2802
+ <path d="M 6 33 Q 7 32.5 8 33" stroke={color} strokeWidth="0.8" fill="none" strokeLinecap="round" />
2803
+ {/* nose */}
2804
+ <ellipse cx="1.5" cy="35" rx="0.9" ry="0.7" fill={color} />
2805
+ {/* curled tail */}
2806
+ <path d="M 33 32 Q 38 30 36 26 Q 35 25 36 24" stroke={color} strokeWidth="2" fill="none" strokeLinecap="round" />
2807
+ <text x="18" y="18" fill={accentColor} fontSize="7" fontWeight="bold" style={{ animation: 'stick-zzz 2s ease-out infinite' }}>z</text>
2808
+ <text x="24" y="12" fill={accentColor} fontSize="5" fontWeight="bold" style={{ animation: 'stick-zzz 2s ease-out infinite 0.7s' }}>z</text>
2809
+ </svg>
2810
+ );
2811
+ }
2812
+
2813
+ if (pose === 'fail') {
2814
+ return (
2815
+ <svg width="40" height="40" viewBox="0 0 40 40">
2816
+ {/* belly up */}
2817
+ <ellipse cx="22" cy="32" rx="12" ry="4" stroke={color} strokeWidth="1.8" fill={color} fillOpacity="0.15" />
2818
+ {/* head upside down */}
2819
+ <path d="M 10 32 Q 6 34 4 32 Q 2 31 3 29 L 10 29 Z" stroke={color} strokeWidth="1.8" fill={color} fillOpacity="0.2" strokeLinejoin="round" />
2820
+ <path d="M 3 30 L 1 29 L 3 28" stroke={color} strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round" />
2821
+ {/* X eyes */}
2822
+ <line x1="5" y1="30" x2="6.5" y2="31.5" stroke={accentColor} strokeWidth="1.2" strokeLinecap="round" />
2823
+ <line x1="6.5" y1="30" x2="5" y2="31.5" stroke={accentColor} strokeWidth="1.2" strokeLinecap="round" />
2824
+ {/* tongue hanging out sideways */}
2825
+ <path d="M 2 30 Q 1 27 2 25" stroke="#ff6b9d" strokeWidth="1.5" fill="none" strokeLinecap="round" />
2826
+ {/* all 4 legs sticking up */}
2827
+ <line x1="14" y1="28" x2="13" y2="20" stroke={color} strokeWidth="1.8" strokeLinecap="round" />
2828
+ <line x1="18" y1="28" x2="18" y2="18" stroke={color} strokeWidth="1.8" strokeLinecap="round" />
2829
+ <line x1="26" y1="28" x2="26" y2="18" stroke={color} strokeWidth="1.8" strokeLinecap="round" />
2830
+ <line x1="30" y1="28" x2="31" y2="20" stroke={color} strokeWidth="1.8" strokeLinecap="round" />
2831
+ {/* limp tail */}
2832
+ <path d="M 33 32 L 37 34" stroke={color} strokeWidth="2" fill="none" strokeLinecap="round" />
2833
+ </svg>
2834
+ );
2835
+ }
2836
+
2837
+ // Standing side-profile dog
2838
+ const standingDog = (tailAnim: string, bounce: string = '') => (
2839
+ <g style={bounce ? { transform: bounce } : {}}>
2840
+ {/* body — side profile, clearly elongated horizontal */}
2841
+ <path d="M 11 22 L 28 22 Q 32 22 32 26 L 32 30 Q 32 32 30 32 L 9 32 Q 7 32 7 30 L 7 27 Q 7 23 11 22 Z"
2842
+ stroke={color} strokeWidth="1.8" fill={color} fillOpacity="0.15" strokeLinejoin="round" />
2843
+ {/* head — side profile with long snout pointing LEFT */}
2844
+ <path d="M 11 22 Q 8 22 6 20 Q 4 20 2 22 Q 0 23 1 25 Q 1 27 3 27 L 8 27 Q 10 27 11 25 Z"
2845
+ stroke={color} strokeWidth="1.8" fill={color} fillOpacity="0.2" strokeLinejoin="round" />
2846
+ {/* triangular perked ear (pointing up-back) */}
2847
+ <path d="M 8 22 L 10 15 L 12 20 Z" stroke={color} strokeWidth="1.5" fill={color} fillOpacity="0.4" strokeLinejoin="round" />
2848
+ {/* big black nose at tip */}
2849
+ <ellipse cx="1" cy="24" rx="1.3" ry="1" fill={color} />
2850
+ {/* eye */}
2851
+ <circle cx="7" cy="23" r="1" fill={accentColor} />
2852
+ <circle cx="6.7" cy="22.7" r="0.3" fill="#fff" />
2853
+ {/* mouth line */}
2854
+ <path d="M 1 25.5 Q 3 27 6 26" stroke={color} strokeWidth="0.8" fill="none" strokeLinecap="round" />
2855
+ {/* tongue hanging out */}
2856
+ <path d="M 2.5 26.5 Q 3 28.5 2 29" stroke="#ff6b9d" strokeWidth="1.2" fill="#ff6b9d" strokeLinecap="round" />
2857
+ {/* curled tail (pointing up-right) — wags */}
2858
+ <g style={{ transformOrigin: '32px 26px', animation: tailAnim }}>
2859
+ <path d="M 32 26 Q 37 24 36 19 Q 36 17 38 17" stroke={color} strokeWidth="2.2" fill="none" strokeLinecap="round" />
2860
+ </g>
2861
+ {/* 4 legs — visible in side profile (2 front + 2 back, front pair visible) */}
2862
+ <line x1="10" y1="32" x2="10" y2="38" stroke={color} strokeWidth="1.8" strokeLinecap="round" />
2863
+ <line x1="14" y1="32" x2="14" y2="38" stroke={color} strokeWidth="1.8" strokeLinecap="round" />
2864
+ <line x1="26" y1="32" x2="26" y2="38" stroke={color} strokeWidth="1.8" strokeLinecap="round" />
2865
+ <line x1="30" y1="32" x2="30" y2="38" stroke={color} strokeWidth="1.8" strokeLinecap="round" />
2866
+ {/* paws */}
2867
+ <rect x="8.5" y="37.5" width="3" height="1.5" fill={color} rx="0.5" />
2868
+ <rect x="12.5" y="37.5" width="3" height="1.5" fill={color} rx="0.5" />
2869
+ <rect x="24.5" y="37.5" width="3" height="1.5" fill={color} rx="0.5" />
2870
+ <rect x="28.5" y="37.5" width="3" height="1.5" fill={color} rx="0.5" />
2871
+ {/* collar */}
2872
+ <line x1="9" y1="27" x2="12" y2="27" stroke={accentColor} strokeWidth="1.5" strokeLinecap="round" />
2873
+ <circle cx="10.5" cy="28" r="0.9" fill={accentColor} />
2874
+ </g>
2875
+ );
2876
+
2877
+ if (pose === 'done') {
2878
+ return (
2879
+ <svg width="40" height="40" viewBox="0 0 40 40">
2880
+ {standingDog('stick-arm-wave 0.3s ease-in-out infinite', 'translateY(-3px)')}
2881
+ <text x="2" y="10" fill="#ffd700" fontSize="7" style={{ animation: 'stick-spark-burst 1.2s ease-out forwards' }}>✦</text>
2882
+ <text x="34" y="12" fill="#ffd700" fontSize="9" style={{ animation: 'stick-spark-burst 1.2s ease-out forwards 0.3s' }}>✦</text>
2883
+ <text x="18" y="6" fill="#ffd700" fontSize="6" style={{ animation: 'stick-spark-burst 1.2s ease-out forwards 0.5s' }}>✦</text>
2884
+ </svg>
2885
+ );
2886
+ }
2887
+
2888
+ if (pose === 'work') {
2889
+ return (
2890
+ <svg width="40" height="40" viewBox="0 0 40 40">
2891
+ {standingDog('stick-arm-wave 0.25s ease-in-out infinite')}
2892
+ {/* bone in mouth */}
2893
+ <g transform="translate(-6, 0)">
2894
+ <circle cx="0" cy="25" r="1.5" fill={accentColor} stroke={color} strokeWidth="0.6" />
2895
+ <rect x="0" y="24.2" width="5" height="1.6" fill={accentColor} stroke={color} strokeWidth="0.6" rx="0.4" />
2896
+ <circle cx="5" cy="25" r="1.5" fill={accentColor} stroke={color} strokeWidth="0.6" />
2897
+ </g>
2898
+ </svg>
2899
+ );
2900
+ }
2901
+
2902
+ if (pose === 'wake') {
2903
+ return (
2904
+ <svg width="40" height="40" viewBox="0 0 40 40">
2905
+ {standingDog('stick-arm-wave 1.5s ease-in-out infinite')}
2906
+ {/* yawn — open mouth replaces tongue */}
2907
+ <ellipse cx="2" cy="26" rx="1.3" ry="1.8" fill={color} opacity="0.5" />
2908
+ </svg>
2909
+ );
2910
+ }
2911
+
2912
+ // idle — standing, happy tail wag
2913
+ return (
2914
+ <svg width="40" height="40" viewBox="0 0 40 40">
2915
+ {standingDog('stick-arm-wave 0.6s ease-in-out infinite')}
2916
+ </svg>
2917
+ );
2918
+ }
2919
+
2920
+ function StickPig({ pose, color, accentColor }: { pose: MascotPose; color: string; accentColor: string }) {
2921
+ const pink = '#ff9ecb';
2922
+ const pinkFill = '#ff9ecb';
2923
+
2924
+ if (pose === 'sleep') {
2925
+ return (
2926
+ <svg width="32" height="40" viewBox="0 0 32 40">
2927
+ <ellipse cx="16" cy="30" rx="12" ry="7" stroke={pink} strokeWidth="1.8" fill={pinkFill} fillOpacity="0.25" />
2928
+ <circle cx="8" cy="28" r="4" stroke={pink} strokeWidth="1.8" fill={pinkFill} fillOpacity="0.3" />
2929
+ {/* pig snout disc */}
2930
+ <ellipse cx="4.5" cy="29" rx="2.5" ry="1.8" stroke={pink} strokeWidth="1.5" fill={pinkFill} fillOpacity="0.4" />
2931
+ <circle cx="4" cy="29" r="0.5" fill={color} />
2932
+ <circle cx="5" cy="29" r="0.5" fill={color} />
2933
+ {/* pointy triangular ears */}
2934
+ <path d="M 5 24 L 6 22 L 8 25 Z" fill={pinkFill} stroke={pink} strokeWidth="1" />
2935
+ <path d="M 9 24 L 10 22 L 11 25 Z" fill={pinkFill} stroke={pink} strokeWidth="1" />
2936
+ {/* closed eyes */}
2937
+ <path d="M 7 27 Q 7.5 26.5 8 27" stroke={color} strokeWidth="0.8" fill="none" strokeLinecap="round" />
2938
+ <path d="M 9 27 Q 9.5 26.5 10 27" stroke={color} strokeWidth="0.8" fill="none" strokeLinecap="round" />
2939
+ {/* curly tail */}
2940
+ <path d="M 28 28 Q 30 26 28 24 Q 26 24 28 22" stroke={pink} strokeWidth="2" fill="none" strokeLinecap="round" />
2941
+ <text x="16" y="16" fill={accentColor} fontSize="7" fontWeight="bold" style={{ animation: 'stick-zzz 2s ease-out infinite' }}>z</text>
2942
+ <text x="21" y="10" fill={accentColor} fontSize="5" fontWeight="bold" style={{ animation: 'stick-zzz 2s ease-out infinite 0.7s' }}>z</text>
2943
+ </svg>
2944
+ );
2945
+ }
2946
+
2947
+ if (pose === 'fail') {
2948
+ return (
2949
+ <svg width="32" height="40" viewBox="0 0 32 40">
2950
+ <ellipse cx="18" cy="29" rx="11" ry="5" stroke={pink} strokeWidth="1.8" fill={pinkFill} fillOpacity="0.25" />
2951
+ <circle cx="8" cy="27" r="4.5" stroke={pink} strokeWidth="1.8" fill={pinkFill} fillOpacity="0.3" />
2952
+ <ellipse cx="7" cy="23" rx="2.5" ry="2" stroke={pink} strokeWidth="1.5" fill={pinkFill} fillOpacity="0.5" />
2953
+ <circle cx="6.3" cy="23" r="0.5" fill={color} />
2954
+ <circle cx="7.7" cy="23" r="0.5" fill={color} />
2955
+ <path d="M 4 28 Q 0 30 2 34" fill={pinkFill} stroke={pink} strokeWidth="1.8" strokeLinecap="round" />
2956
+ <path d="M 12 28 Q 16 30 14 34" fill={pinkFill} stroke={pink} strokeWidth="1.8" strokeLinecap="round" />
2957
+ <line x1="6" y1="26" x2="7.5" y2="27.5" stroke={color} strokeWidth="1.2" strokeLinecap="round" />
2958
+ <line x1="7.5" y1="26" x2="6" y2="27.5" stroke={color} strokeWidth="1.2" strokeLinecap="round" />
2959
+ <line x1="9" y1="26" x2="10.5" y2="27.5" stroke={color} strokeWidth="1.2" strokeLinecap="round" />
2960
+ <line x1="10.5" y1="26" x2="9" y2="27.5" stroke={color} strokeWidth="1.2" strokeLinecap="round" />
2961
+ <line x1="14" y1="25" x2="13" y2="17" stroke={pink} strokeWidth="1.8" strokeLinecap="round" />
2962
+ <line x1="18" y1="25" x2="18" y2="16" stroke={pink} strokeWidth="1.8" strokeLinecap="round" />
2963
+ <line x1="22" y1="25" x2="22" y2="17" stroke={pink} strokeWidth="1.8" strokeLinecap="round" />
2964
+ <line x1="26" y1="25" x2="27" y2="18" stroke={pink} strokeWidth="1.8" strokeLinecap="round" />
2965
+ </svg>
2966
+ );
2967
+ }
2968
+
2969
+ const pigBody = (tailAnim: string, bounce: string = '') => (
2970
+ <g style={bounce ? { transform: bounce } : {}}>
2971
+ {/* round pig body */}
2972
+ <ellipse cx="18" cy="27" rx="11" ry="6.5" stroke={pink} strokeWidth="1.8" fill={pinkFill} fillOpacity="0.25" />
2973
+ {/* round head */}
2974
+ <circle cx="9" cy="18" r="6" stroke={pink} strokeWidth="1.8" fill={pinkFill} fillOpacity="0.3" />
2975
+ {/* pig snout — flat disc with nostrils */}
2976
+ <ellipse cx="5" cy="20" rx="3" ry="2.2" stroke={pink} strokeWidth="1.5" fill={pinkFill} fillOpacity="0.5" />
2977
+ <circle cx="4" cy="20" r="0.6" fill={color} />
2978
+ <circle cx="6" cy="20" r="0.6" fill={color} />
2979
+ {/* triangular pointed ears */}
2980
+ <path d="M 6 13 L 7 10 L 9 14 Z" fill={pinkFill} stroke={pink} strokeWidth="1.2" strokeLinejoin="round" />
2981
+ <path d="M 11 13 L 12 10 L 14 14 Z" fill={pinkFill} stroke={pink} strokeWidth="1.2" strokeLinejoin="round" />
2982
+ {/* eyes */}
2983
+ <circle cx="8" cy="17" r="1" fill={color} />
2984
+ <circle cx="12" cy="17" r="1" fill={color} />
2985
+ <circle cx="7.7" cy="16.7" r="0.3" fill="#fff" />
2986
+ <circle cx="11.7" cy="16.7" r="0.3" fill="#fff" />
2987
+ {/* smile */}
2988
+ <path d="M 7 22 Q 9 23 11 22" stroke={color} strokeWidth="0.8" fill="none" strokeLinecap="round" />
2989
+ {/* curly tail — wagging */}
2990
+ <g style={{ transformOrigin: '29px 25px', animation: tailAnim }}>
2991
+ <path d="M 29 25 Q 32 23 30 21 Q 28 21 30 19 Q 31 18 32 19" stroke={pink} strokeWidth="2" fill="none" strokeLinecap="round" />
2992
+ </g>
2993
+ {/* trotter legs */}
2994
+ <line x1="12" y1="32" x2="12" y2="38" stroke={pink} strokeWidth="2" strokeLinecap="round" />
2995
+ <line x1="16" y1="33" x2="16" y2="38" stroke={pink} strokeWidth="2" strokeLinecap="round" />
2996
+ <line x1="20" y1="33" x2="20" y2="38" stroke={pink} strokeWidth="2" strokeLinecap="round" />
2997
+ <line x1="24" y1="32" x2="24" y2="38" stroke={pink} strokeWidth="2" strokeLinecap="round" />
2998
+ {/* hooves */}
2999
+ <rect x="10.5" y="37.5" width="3" height="1.8" fill={color} rx="0.3" />
3000
+ <rect x="14.5" y="37.5" width="3" height="1.8" fill={color} rx="0.3" />
3001
+ <rect x="18.5" y="37.5" width="3" height="1.8" fill={color} rx="0.3" />
3002
+ <rect x="22.5" y="37.5" width="3" height="1.8" fill={color} rx="0.3" />
3003
+ </g>
3004
+ );
3005
+
3006
+ if (pose === 'done') {
3007
+ return (
3008
+ <svg width="32" height="40" viewBox="0 0 32 40">
3009
+ {pigBody('stick-arm-wave 0.4s ease-in-out infinite', 'translateY(-2px)')}
3010
+ <text x="2" y="8" fill="#ffd700" fontSize="6" style={{ animation: 'stick-spark-burst 1.2s ease-out forwards' }}>✦</text>
3011
+ <text x="26" y="10" fill="#ffd700" fontSize="8" style={{ animation: 'stick-spark-burst 1.2s ease-out forwards 0.3s' }}>✦</text>
3012
+ </svg>
3013
+ );
3014
+ }
3015
+
3016
+ if (pose === 'work') {
3017
+ return (
3018
+ <svg width="32" height="40" viewBox="0 0 32 40">
3019
+ {pigBody('stick-arm-wave 0.3s ease-in-out infinite')}
3020
+ </svg>
3021
+ );
3022
+ }
3023
+
3024
+ if (pose === 'wake') {
3025
+ return (
3026
+ <svg width="32" height="40" viewBox="0 0 32 40">
3027
+ {pigBody('stick-arm-wave 1.5s ease-in-out infinite')}
3028
+ <ellipse cx="9" cy="20" rx="1.3" ry="1.5" fill={color} opacity="0.4" />
3029
+ </svg>
3030
+ );
3031
+ }
3032
+
3033
+ return (
3034
+ <svg width="32" height="40" viewBox="0 0 32 40">
3035
+ {pigBody('stick-arm-wave 0.7s ease-in-out infinite')}
3036
+ </svg>
3037
+ );
3038
+ }
3039
+
3040
+ function EmojiMascot({ pose, seed }: { pose: MascotPose; seed: number }) {
3041
+ const characters = ['🦊', '🐱', '🐼', '🦉', '🐸', '🦝', '🐙', '🦖', '🐰', '🦄', '🐺', '🧙‍♂️', '🧝‍♀️', '🦸‍♂️', '🥷', '🐲'];
3042
+ const character = characters[seed % characters.length];
3043
+ let display = character;
3044
+ if (pose === 'sleep') display = ['😴', '💤', '🌙', '💤'][Math.floor(Date.now() / 1200) % 4];
3045
+ else if (pose === 'work') { const tools = ['🔨', '⚙️', '🛠️', '⚡']; const tick = Math.floor(Date.now() / 400); display = tick % 3 === 0 ? character : tools[tick % tools.length]; }
3046
+ else if (pose === 'done') display = ['🎉', '🎊', '🥳', '🌟'][Math.floor(Date.now() / 600) % 4];
3047
+ else if (pose === 'fail') display = ['😵', '💫', '🤕', '😿'][seed % 4];
3048
+ else if (pose === 'wake') display = ['🥱', '☕', '🌅'][Math.floor(Date.now() / 1000) % 3];
3049
+ return <div style={{ fontSize: '24px', lineHeight: 1 }}>{display}</div>;
3050
+ }
3051
+
3052
+ function StickFigure({ pose, color, accentColor }: { pose: MascotPose; color: string; accentColor: string }) {
3053
+ // viewBox 32×40: head at (16,8), body (16,12)→(16,26), arms from (16,14), legs from (16,26)
3054
+ const strokeProps = { stroke: color, strokeWidth: 2, strokeLinecap: 'round' as const, fill: 'none' };
3055
+
3056
+ if (pose === 'sleep') {
3057
+ // Lying down, sleeping
3058
+ return (
3059
+ <svg width="32" height="40" viewBox="0 0 32 40">
3060
+ {/* body horizontal */}
3061
+ <circle cx="8" cy="30" r="3" {...strokeProps} fill={color} />
3062
+ <line x1="11" y1="30" x2="26" y2="30" {...strokeProps} />
3063
+ <line x1="14" y1="30" x2="18" y2="26" {...strokeProps} />
3064
+ <line x1="20" y1="30" x2="24" y2="34" {...strokeProps} />
3065
+ {/* zzz */}
3066
+ <text x="18" y="14" fill={accentColor} fontSize="8" fontWeight="bold" style={{ animation: 'stick-zzz 2s ease-out infinite' }}>z</text>
3067
+ <text x="22" y="10" fill={accentColor} fontSize="6" fontWeight="bold" style={{ animation: 'stick-zzz 2s ease-out infinite 0.7s' }}>z</text>
3068
+ </svg>
3069
+ );
3070
+ }
3071
+
3072
+ if (pose === 'wake') {
3073
+ // Stretching — arms up
3074
+ return (
3075
+ <svg width="32" height="40" viewBox="0 0 32 40">
3076
+ <circle cx="16" cy="8" r="3" {...strokeProps} fill={color} />
3077
+ <line x1="16" y1="11" x2="16" y2="26" {...strokeProps} />
3078
+ <line x1="16" y1="14" x2="10" y2="6" {...strokeProps} />
3079
+ <line x1="16" y1="14" x2="22" y2="6" {...strokeProps} />
3080
+ <line x1="16" y1="26" x2="12" y2="34" {...strokeProps} />
3081
+ <line x1="16" y1="26" x2="20" y2="34" {...strokeProps} />
3082
+ {/* ☼ */}
3083
+ <circle cx="26" cy="6" r="2" fill={accentColor} opacity="0.8" />
3084
+ </svg>
3085
+ );
3086
+ }
3087
+
3088
+ if (pose === 'done') {
3089
+ // Victory pose — both arms up, legs apart
3090
+ return (
3091
+ <svg width="32" height="40" viewBox="0 0 32 40">
3092
+ <circle cx="16" cy="8" r="3" {...strokeProps} fill={color} />
3093
+ {/* smile */}
3094
+ <path d="M 14 8 Q 16 10 18 8" stroke={accentColor} strokeWidth="1" fill="none" strokeLinecap="round" />
3095
+ <line x1="16" y1="11" x2="16" y2="26" {...strokeProps} />
3096
+ <line x1="16" y1="14" x2="8" y2="4" {...strokeProps} />
3097
+ <line x1="16" y1="14" x2="24" y2="4" {...strokeProps} />
3098
+ <line x1="16" y1="26" x2="10" y2="36" {...strokeProps} />
3099
+ <line x1="16" y1="26" x2="22" y2="36" {...strokeProps} />
3100
+ {/* sparkles */}
3101
+ <text x="4" y="4" fill="#ffd700" fontSize="6" style={{ animation: 'stick-spark-burst 1.2s ease-out forwards' }}>✦</text>
3102
+ <text x="26" y="6" fill="#ffd700" fontSize="8" style={{ animation: 'stick-spark-burst 1.2s ease-out forwards 0.3s' }}>✦</text>
3103
+ <text x="2" y="20" fill="#ffd700" fontSize="5" style={{ animation: 'stick-spark-burst 1.2s ease-out forwards 0.5s' }}>✦</text>
3104
+ </svg>
3105
+ );
3106
+ }
3107
+
3108
+ if (pose === 'fail') {
3109
+ // Fallen down — lying on back, X eyes (handled via external rotate)
3110
+ return (
3111
+ <svg width="32" height="40" viewBox="0 0 32 40">
3112
+ <circle cx="16" cy="8" r="3" {...strokeProps} fill={color} />
3113
+ {/* X eyes */}
3114
+ <line x1="14" y1="6" x2="15" y2="7" stroke={accentColor} strokeWidth="1" strokeLinecap="round" />
3115
+ <line x1="15" y1="6" x2="14" y2="7" stroke={accentColor} strokeWidth="1" strokeLinecap="round" />
3116
+ <line x1="17" y1="6" x2="18" y2="7" stroke={accentColor} strokeWidth="1" strokeLinecap="round" />
3117
+ <line x1="18" y1="6" x2="17" y2="7" stroke={accentColor} strokeWidth="1" strokeLinecap="round" />
3118
+ <line x1="16" y1="11" x2="16" y2="26" {...strokeProps} />
3119
+ <line x1="16" y1="14" x2="8" y2="18" {...strokeProps} />
3120
+ <line x1="16" y1="14" x2="24" y2="18" {...strokeProps} />
3121
+ <line x1="16" y1="26" x2="10" y2="34" {...strokeProps} />
3122
+ <line x1="16" y1="26" x2="22" y2="34" {...strokeProps} />
3123
+ </svg>
3124
+ );
3125
+ }
3126
+
3127
+ if (pose === 'work') {
3128
+ // Hammering — left arm stable, right arm swinging with hammer
3129
+ return (
3130
+ <svg width="32" height="40" viewBox="0 0 32 40">
3131
+ <circle cx="16" cy="8" r="3" {...strokeProps} fill={color} />
3132
+ <line x1="16" y1="11" x2="16" y2="26" {...strokeProps} />
3133
+ {/* left arm holding nail */}
3134
+ <line x1="16" y1="14" x2="10" y2="20" {...strokeProps} />
3135
+ {/* right arm swinging hammer */}
3136
+ <g style={{ transformOrigin: '16px 14px', animation: 'stick-arm-hammer 0.5s ease-in-out infinite' }}>
3137
+ <line x1="16" y1="14" x2="24" y2="14" {...strokeProps} />
3138
+ {/* hammer */}
3139
+ <rect x="24" y="11" width="5" height="6" fill={accentColor} stroke={color} strokeWidth="1" rx="1" />
3140
+ </g>
3141
+ {/* legs walking */}
3142
+ <g style={{ transformOrigin: '16px 26px', animation: 'stick-leg-walk-l 0.5s ease-in-out infinite' }}>
3143
+ <line x1="16" y1="26" x2="12" y2="36" {...strokeProps} />
3144
+ </g>
3145
+ <g style={{ transformOrigin: '16px 26px', animation: 'stick-leg-walk-r 0.5s ease-in-out infinite' }}>
3146
+ <line x1="16" y1="26" x2="20" y2="36" {...strokeProps} />
3147
+ </g>
3148
+ {/* sparks from hammer */}
3149
+ <text x="26" y="22" fill="#ff9500" fontSize="6" style={{ animation: 'stick-spark 0.5s ease-in-out infinite' }}>✦</text>
3150
+ </svg>
3151
+ );
3152
+ }
3153
+
3154
+ // idle — standing, waving
3155
+ return (
3156
+ <svg width="32" height="40" viewBox="0 0 32 40">
3157
+ <circle cx="16" cy="8" r="3" {...strokeProps} fill={color} />
3158
+ {/* eyes dots */}
3159
+ <circle cx="15" cy="7" r="0.6" fill={accentColor} />
3160
+ <circle cx="17" cy="7" r="0.6" fill={accentColor} />
3161
+ <line x1="16" y1="11" x2="16" y2="26" {...strokeProps} />
3162
+ {/* left arm down */}
3163
+ <line x1="16" y1="14" x2="12" y2="22" {...strokeProps} />
3164
+ {/* right arm waving */}
3165
+ <g style={{ transformOrigin: '16px 14px', animation: 'stick-arm-wave 2s ease-in-out infinite' }}>
3166
+ <line x1="16" y1="14" x2="22" y2="14" {...strokeProps} />
3167
+ </g>
3168
+ <line x1="16" y1="26" x2="12" y2="36" {...strokeProps} />
3169
+ <line x1="16" y1="26" x2="20" y2="36" {...strokeProps} />
3170
+ </svg>
3171
+ );
3172
+ }
3173
+
3174
+ function WorkerMascot({ taskStatus, smithStatus, seed, accentColor, theme }: { taskStatus: string; smithStatus: string; seed: number; accentColor: string; theme: MascotTheme }) {
3175
+ if (theme === 'off') return null;
3176
+
3177
+ let pose: MascotPose = 'idle';
3178
+ let animation = 'mascot-idle 3s ease-in-out infinite';
3179
+ let title = 'Ready for work';
3180
+ const color = '#e6edf3';
3181
+
3182
+ if (smithStatus === 'down') {
3183
+ pose = 'sleep';
3184
+ animation = 'mascot-sleep 2.5s ease-in-out infinite';
3185
+ title = 'Smith is down — sleeping';
3186
+ } else if (taskStatus === 'running') {
3187
+ pose = 'work';
3188
+ animation = 'mascot-work 0.6s ease-in-out infinite';
3189
+ title = 'Hard at work!';
3190
+ } else if (taskStatus === 'done') {
3191
+ pose = 'done';
3192
+ // Celebrate 2 times (~2.4s total), then hold the pose quietly
3193
+ animation = 'mascot-celebrate 2.4s ease-in-out forwards';
3194
+ title = 'Task done!';
3195
+ } else if (taskStatus === 'failed') {
3196
+ pose = 'fail';
3197
+ animation = 'mascot-fall 0.8s ease-out forwards';
3198
+ title = 'Task failed';
3199
+ } else if (smithStatus === 'starting') {
3200
+ pose = 'wake';
3201
+ animation = 'mascot-sleep 1.8s ease-in-out infinite';
3202
+ title = 'Waking up...';
3203
+ } else {
3204
+ animation = 'mascot-idle 3s ease-in-out infinite';
3205
+ title = 'Ready for work';
3206
+ }
3207
+
3208
+ let figure: React.ReactNode;
3209
+ if (theme === 'stick') figure = <StickFigure pose={pose} color={color} accentColor={accentColor} />;
3210
+ else if (theme === 'cat') figure = <StickCat pose={pose} color={color} accentColor={accentColor} />;
3211
+ else if (theme === 'dog') figure = <StickDog pose={pose} color={color} accentColor={accentColor} />;
3212
+ else if (theme === 'pig') figure = <StickPig pose={pose} color={color} accentColor={accentColor} />;
3213
+ else figure = <EmojiMascot pose={pose} seed={seed} />;
3214
+
3215
+ return (
3216
+ <div
3217
+ className="absolute pointer-events-none select-none"
3218
+ style={{
3219
+ top: '-36px',
3220
+ right: '-8px',
3221
+ animation,
3222
+ filter: 'drop-shadow(0 2px 3px rgba(0,0,0,0.6))',
3223
+ zIndex: 10,
3224
+ transformOrigin: 'bottom center',
3225
+ }}
3226
+ title={title}
3227
+ >
3228
+ {figure}
3229
+ </div>
3230
+ );
3231
+ }
3232
+
3233
+ function AgentFlowNode({ data }: NodeProps<Node<AgentNodeData>>) {
3234
+ const { config, state, colorIdx, previewLines, projectPath, workspaceId, onRun, onPause, onStop, onRetry, onEdit, onRemove, onMessage, onApprove, onShowLog, onShowMemory, onShowInbox, onOpenTerminal, onSwitchSession, onSaveAsTemplate, mascotTheme, inboxPending = 0, inboxFailed = 0 } = data;
3235
+ const c = COLORS[colorIdx % COLORS.length];
3236
+ const smithStatus = state?.smithStatus || 'down';
3237
+ const taskStatus = state?.taskStatus || 'idle';
3238
+ const hasTmux = !!state?.tmuxSession;
3239
+ const smithInfo = SMITH_STATUS[smithStatus] || SMITH_STATUS.down;
3240
+ const taskInfo = TASK_STATUS[taskStatus] || TASK_STATUS.idle;
3241
+ const currentStep = state?.currentStep;
3242
+ const step = currentStep !== undefined ? config.steps[currentStep] : undefined;
3243
+ const isApprovalPending = taskStatus === 'idle' && smithStatus === 'active';
3244
+
3245
+ // Stable seed for mascot character from agent id
3246
+ const mascotSeed = config.id.split('').reduce((a, c) => a + c.charCodeAt(0), 0);
3247
+
3248
+ return (
3249
+ <div className="w-52 flex flex-col rounded-lg select-none relative"
3250
+ style={{ border: `1px solid ${c.border}${taskStatus === 'running' ? '90' : '40'}`, background: c.bg,
3251
+ boxShadow: taskInfo.glow ? `0 0 12px ${taskInfo.color}25` : smithInfo.glow ? `0 0 8px ${smithInfo.color}15` : 'none' }}>
3252
+ <style>{MASCOT_STYLES}</style>
3253
+ <WorkerMascot taskStatus={taskStatus} smithStatus={smithStatus} seed={mascotSeed} accentColor={c.accent} theme={mascotTheme} />
3254
+ <Handle type="target" position={Position.Left} style={{ background: c.accent, width: 8, height: 8, border: 'none' }} />
3255
+ <Handle type="source" position={Position.Right} style={{ background: c.accent, width: 8, height: 8, border: 'none' }} />
3256
+
3257
+ {/* Primary badge */}
3258
+ {config.primary && <div className="bg-[#f0883e]/20 text-[#f0883e] text-[7px] font-bold text-center py-0.5 rounded-t-lg">PRIMARY</div>}
3259
+
3260
+ {/* Header */}
3261
+ <div className="flex items-center gap-2 px-3 py-2">
3262
+ <span className="text-sm">{config.icon}</span>
3263
+ <div className="flex-1 min-w-0">
3264
+ <div className="text-xs font-semibold text-white truncate">{config.label}</div>
3265
+ <div className="text-[8px]" style={{ color: c.accent }}>{config.backend === 'api' ? config.provider || 'api' : config.agentId || 'cli'}</div>
3266
+ </div>
3267
+ {/* Status: smith + terminal + task */}
3268
+ <div className="flex flex-col items-end gap-0.5">
3269
+ <div className="flex items-center gap-1">
3270
+ <div className="w-1.5 h-1.5 rounded-full" style={{ background: smithInfo.color, boxShadow: smithInfo.glow ? `0 0 4px ${smithInfo.color}` : 'none' }} />
3271
+ <span className="text-[7px]" style={{ color: smithInfo.color }}>{smithInfo.label}</span>
3272
+ </div>
3273
+ <div className="flex items-center gap-1">
3274
+ {(() => {
3275
+ // Execution mode is determined by config, not tmux state
3276
+ const isTerminalMode = config.persistentSession;
3277
+ const isActive = smithStatus === 'active';
3278
+ const color = isTerminalMode
3279
+ ? (hasTmux ? '#3fb950' : '#f0883e') // terminal: green (up) / orange (down)
3280
+ : (isActive ? '#58a6ff' : '#484f58'); // headless: blue (active) / gray (down)
3281
+ const label = isTerminalMode
3282
+ ? (hasTmux ? 'terminal' : 'terminal (down)')
3283
+ : (isActive ? 'headless' : 'headless (down)');
3284
+ return (<>
3285
+ <div className="w-1.5 h-1.5 rounded-full" style={{ background: color }} />
3286
+ <span className="text-[7px] font-medium" style={{ color }}>{label}</span>
3287
+ </>);
3288
+ })()}
3289
+ </div>
3290
+ <div className="flex items-center gap-1">
3291
+ <div className="w-1.5 h-1.5 rounded-full" style={{ background: taskInfo.color, boxShadow: taskInfo.glow ? `0 0 4px ${taskInfo.color}` : 'none' }} />
3292
+ <span className="text-[7px]" style={{ color: taskInfo.color }}>{taskInfo.label}</span>
3293
+ </div>
3294
+ {config.watch?.enabled && (
3295
+ <div className="flex items-center gap-1">
3296
+ <span className="text-[7px]" style={{ color: (state as any)?.lastWatchAlert ? '#f0883e' : '#6e7681' }}>
3297
+ {(state as any)?.lastWatchAlert ? '👁 alert' : '👁 watching'}
3298
+ </span>
3299
+ </div>
3300
+ )}
3301
+ </div>
3302
+ </div>
3303
+
3304
+ {/* Current step */}
3305
+ {step && taskStatus === 'running' && (
3306
+ <div className="px-3 pb-1 text-[8px] text-yellow-400/80" style={{ borderTop: `1px solid ${c.border}15` }}>
3307
+ Step {(currentStep || 0) + 1}/{config.steps.length}: {step.label}
3308
+ </div>
3309
+ )}
3310
+
3311
+ {/* Error */}
3312
+ {state?.error && (
3313
+ <div className="px-3 pb-1 text-[8px] text-red-400 truncate" style={{ borderTop: `1px solid ${c.border}15` }}>
3314
+ {state.error}
3315
+ </div>
3316
+ )}
3317
+
3318
+ {/* Preview lines */}
3319
+ {previewLines.length > 0 && (
3320
+ <div className="px-3 pb-2 space-y-0.5 cursor-pointer" style={{ borderTop: `1px solid ${c.border}15` }}
3321
+ onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onShowLog(); }}>
3322
+ {previewLines.map((line, i) => (
3323
+ <div key={i} className="text-[8px] text-gray-500 font-mono truncate">{line}</div>
3324
+ ))}
3325
+ </div>
3326
+ )}
3327
+
3328
+ {/* Inbox — prominent, shows pending/failed counts */}
3329
+ {(inboxPending > 0 || inboxFailed > 0) && (
3330
+ <div className="px-2 py-1" style={{ borderTop: `1px solid ${c.border}15` }}>
3331
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onShowInbox(); }}
3332
+ className="w-full text-[9px] px-2 py-1 rounded flex items-center justify-center gap-1.5 bg-orange-600/15 text-orange-400 hover:bg-orange-600/25 border border-orange-600/30">
3333
+ 📨 Inbox
3334
+ {inboxPending > 0 && <span className="px-1 rounded-full bg-yellow-600/30 text-yellow-400 text-[8px]">{inboxPending} pending</span>}
3335
+ {inboxFailed > 0 && <span className="px-1 rounded-full bg-red-600/30 text-red-400 text-[8px]">{inboxFailed} failed</span>}
3336
+ </button>
3337
+ </div>
3338
+ )}
3339
+
3340
+ {/* Actions */}
3341
+ <div className="flex items-center gap-1 px-2 py-1.5" style={{ borderTop: `1px solid ${c.border}15` }}>
3342
+ {taskStatus === 'running' && (
3343
+ <>
3344
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); data.onMarkIdle?.(); }}
3345
+ className="text-[9px] px-1 py-0.5 rounded bg-gray-600/20 text-gray-400 hover:bg-gray-600/30" title="Silent stop — no notifications">■</button>
3346
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); data.onMarkDone?.(true); }}
3347
+ className="text-[9px] px-1 py-0.5 rounded bg-green-600/20 text-green-400 hover:bg-green-600/30" title="Mark done + notify">✓</button>
3348
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); data.onMarkFailed?.(true); }}
3349
+ className="text-[9px] px-1 py-0.5 rounded bg-red-600/20 text-red-400 hover:bg-red-600/30" title="Mark failed + notify">✕</button>
3350
+ </>
3351
+ )}
3352
+ {/* Message button — send instructions to agent */}
3353
+ {smithStatus === 'active' && taskStatus !== 'running' && (
3354
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onMessage(); }}
3355
+ className="text-[9px] px-1.5 py-0.5 rounded bg-blue-600/20 text-blue-400 hover:bg-blue-600/30">💬 Message</button>
3356
+ )}
3357
+ <div className="flex-1" />
3358
+ <span className="flex items-center">
3359
+ <button onPointerDown={e => e.stopPropagation()}
3360
+ onClick={e => { e.stopPropagation(); if (smithStatus === 'active') onOpenTerminal(); }}
3361
+ disabled={smithStatus !== 'active'}
3362
+ className={`text-[9px] px-1 ${smithStatus !== 'active' ? 'text-gray-700 cursor-not-allowed' : hasTmux && taskStatus === 'running' ? 'text-green-400 animate-pulse' : 'text-gray-600 hover:text-green-400'}`}
3363
+ title={smithStatus === 'starting' ? 'Starting session…' : smithStatus === 'down' ? 'Smith not started' : 'Open terminal'}>⌨️</button>
3364
+ {hasTmux && !config.primary && (
3365
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onSwitchSession(); }}
3366
+ className="text-[10px] text-gray-600 hover:text-yellow-400 px-0.5 py-0.5" title="Switch session">▾</button>
3367
+ )}
3368
+ </span>
3369
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onShowInbox(); }}
3370
+ className="text-[9px] text-gray-600 hover:text-orange-400 px-1" title="Messages (inbox/outbox)">📨</button>
3371
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onShowMemory(); }}
3372
+ className="text-[9px] text-gray-600 hover:text-purple-400 px-1" title="Memory">🧠</button>
3373
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onShowLog(); }}
3374
+ className="text-[9px] text-gray-600 hover:text-gray-300 px-1" title="Logs">📋</button>
3375
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onSaveAsTemplate(); }}
3376
+ className="text-[9px] text-gray-600 hover:text-yellow-400 px-1" title="Save as template">💾</button>
3377
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onEdit(); }}
3378
+ className="text-[9px] text-gray-600 hover:text-blue-400 px-1">✏️</button>
3379
+ <button onPointerDown={e => e.stopPropagation()} onClick={e => { e.stopPropagation(); onRemove(); }}
3380
+ className="text-[9px] text-gray-600 hover:text-red-400 px-1">✕</button>
3381
+ </div>
3382
+ </div>
3383
+ );
3384
+ }
3385
+
3386
+ const nodeTypes = { agent: AgentFlowNode, input: InputFlowNode };
3387
+
3388
+ // ─── Main Workspace ──────────────────────────────────────
3389
+
3390
+ export interface WorkspaceViewHandle {
3391
+ focusAgent: (agentId: string) => void;
3392
+ }
3393
+
3394
+ function WorkspaceViewInner({ projectPath, projectName, onClose }: {
3395
+ projectPath: string;
3396
+ projectName: string;
3397
+ onClose: () => void;
3398
+ }, ref: React.Ref<WorkspaceViewHandle>) {
3399
+ const reactFlow = useReactFlow();
3400
+ const [workspaceId, setWorkspaceId] = useState<string | null>(null);
3401
+ const [rfNodes, setRfNodes] = useState<Node<any>[]>([]);
3402
+ const [modal, setModal] = useState<{ mode: 'add' | 'edit'; initial: Partial<AgentConfig>; editId?: string } | null>(null);
3403
+ const [messageTarget, setMessageTarget] = useState<{ id: string; label: string } | null>(null);
3404
+ const [logTarget, setLogTarget] = useState<{ id: string; label: string } | null>(null);
3405
+ const [runPromptTarget, setRunPromptTarget] = useState<{ id: string; label: string } | null>(null);
3406
+ const [userInputRequest, setUserInputRequest] = useState<{ agentId: string; fromAgent: string; question: string } | null>(null);
3407
+ const [memoryTarget, setMemoryTarget] = useState<{ id: string; label: string } | null>(null);
3408
+ const [inboxTarget, setInboxTarget] = useState<{ id: string; label: string } | null>(null);
3409
+ const [showBusPanel, setShowBusPanel] = useState(false);
3410
+ const [mascotTheme, setMascotTheme] = useState<MascotTheme>(() => {
3411
+ if (typeof window === 'undefined') return 'off';
3412
+ return (localStorage.getItem('forge.mascotTheme') as MascotTheme) || 'off';
3413
+ });
3414
+ const updateMascotTheme = (t: MascotTheme) => {
3415
+ setMascotTheme(t);
3416
+ if (typeof window !== 'undefined') localStorage.setItem('forge.mascotTheme', t);
3417
+ };
3418
+ const [floatingTerminals, setFloatingTerminals] = useState<{ agentId: string; label: string; icon: string; cliId: string; cliCmd?: string; cliType?: string; workDir?: string; tmuxSession?: string; sessionName: string; resumeMode?: boolean; resumeSessionId?: string; profileEnv?: Record<string, string>; isPrimary?: boolean; skipPermissions?: boolean; persistentSession?: boolean; boundSessionId?: string; initialPos?: { x: number; y: number } }[]>([]);
3419
+ const [termPicker, setTermPicker] = useState<{ agent: AgentConfig; sessName: string; workDir?: string; supportsSession?: boolean; currentSessionId: string | null; initialPos?: { x: number; y: number } } | null>(null);
3420
+
3421
+ // Expose focusAgent to parent
3422
+ useImperativeHandle(ref, () => ({
3423
+ focusAgent(agentId: string) {
3424
+ const node = rfNodes.find(n => n.id === agentId);
3425
+ if (node && node.measured?.width) {
3426
+ reactFlow.setCenter(
3427
+ node.position.x + (node.measured.width / 2),
3428
+ node.position.y + ((node.measured.height || 100) / 2),
3429
+ { zoom: 1.2, duration: 400 }
3430
+ );
3431
+ // Flash highlight via selection
3432
+ reactFlow.setNodes(nodes => nodes.map(n => ({ ...n, selected: n.id === agentId })));
3433
+ setTimeout(() => {
3434
+ reactFlow.setNodes(nodes => nodes.map(n => ({ ...n, selected: false })));
3435
+ }, 1500);
3436
+ }
3437
+ },
3438
+ }), [rfNodes, reactFlow]);
3439
+
3440
+ // Initialize workspace
3441
+ useEffect(() => {
3442
+ ensureWorkspace(projectPath, projectName).then(setWorkspaceId).catch(() => {});
3443
+ }, [projectPath, projectName]);
3444
+
3445
+ // SSE stream — server is the single source of truth
3446
+ const { agents, states, logPreview, busLog, daemonActive: daemonActiveFromStream, setDaemonActive: setDaemonActiveFromStream } = useWorkspaceStream(workspaceId, (event) => {
3447
+ if (event.type === 'user_input_request') {
3448
+ setUserInputRequest(event);
3449
+ }
3450
+ });
3451
+
3452
+ // Auto-open terminals removed — persistent sessions run in background tmux.
3453
+ // User opens terminal via ⌨️ button when needed.
3454
+
3455
+ // Rebuild nodes when agents/states/preview change — preserve existing positions + dimensions
3456
+ useEffect(() => {
3457
+ setRfNodes(prev => {
3458
+ const prevMap = new Map(prev.map(n => [n.id, n]));
3459
+ return agents.map((agent, i) => {
3460
+ const existing = prevMap.get(agent.id);
3461
+ const base = {
3462
+ id: agent.id,
3463
+ position: existing?.position ?? { x: i * 260, y: 60 },
3464
+ ...(existing?.measured ? { measured: existing.measured } : {}),
3465
+ ...(existing?.width ? { width: existing.width, height: existing.height } : {}),
3466
+ };
3467
+
3468
+ // Input node
3469
+ if (agent.type === 'input') {
3470
+ return {
3471
+ ...base,
3472
+ type: 'input' as const,
3473
+ data: {
3474
+ config: agent,
3475
+ state: states[agent.id] || { smithStatus: 'down', taskStatus: 'idle', artifacts: [] },
3476
+ onSubmit: (content: string) => {
3477
+ // Optimistic update
3478
+ wsApi(workspaceId!, 'complete_input', { agentId: agent.id, content });
3479
+ },
3480
+ onEdit: () => setModal({ mode: 'edit', initial: agent, editId: agent.id }),
3481
+ onRemove: () => {
3482
+ if (!confirm(`Remove "${agent.label}"?`)) return;
3483
+ wsApi(workspaceId!, 'remove', { agentId: agent.id });
3484
+ },
3485
+ } satisfies InputNodeData,
3486
+ };
3487
+ }
3488
+
3489
+ // Agent node
3490
+ return {
3491
+ ...base,
3492
+ type: 'agent' as const,
3493
+ data: {
3494
+ config: agent,
3495
+ state: states[agent.id] || { smithStatus: 'down', taskStatus: 'idle', artifacts: [] },
3496
+ colorIdx: i,
3497
+ previewLines: logPreview[agent.id] || [],
3498
+ projectPath,
3499
+ workspaceId,
3500
+ onRun: () => {
3501
+ wsApi(workspaceId!, 'run', { agentId: agent.id });
3502
+ },
3503
+ onPause: () => wsApi(workspaceId!, 'pause', { agentId: agent.id }),
3504
+ onStop: () => wsApi(workspaceId!, 'stop', { agentId: agent.id }),
3505
+ mascotTheme,
3506
+ onMarkIdle: () => wsApi(workspaceId!, 'mark_done', { agentId: agent.id, notify: false }),
3507
+ onMarkDone: (notify: boolean) => wsApi(workspaceId!, 'mark_done', { agentId: agent.id, notify }),
3508
+ onMarkFailed: (notify: boolean) => wsApi(workspaceId!, 'mark_failed', { agentId: agent.id, notify }),
3509
+ onRetry: () => wsApi(workspaceId!, 'retry', { agentId: agent.id }),
3510
+ onEdit: () => setModal({ mode: 'edit', initial: agent, editId: agent.id }),
3511
+ onRemove: () => {
3512
+ if (!confirm(`Remove "${agent.label}"?`)) return;
3513
+ wsApi(workspaceId!, 'remove', { agentId: agent.id });
3514
+ },
3515
+ onMessage: () => setMessageTarget({ id: agent.id, label: agent.label }),
3516
+ onApprove: () => wsApi(workspaceId!, 'approve', { agentId: agent.id }),
3517
+ onShowLog: () => setLogTarget({ id: agent.id, label: agent.label }),
3518
+ onShowMemory: () => setMemoryTarget({ id: agent.id, label: agent.label }),
3519
+ onShowInbox: () => setInboxTarget({ id: agent.id, label: agent.label }),
3520
+ inboxPending: busLog.filter(m => m.to === agent.id && (m.status === 'pending' || m.status === 'pending_approval') && m.type !== 'ack').length,
3521
+ inboxFailed: busLog.filter(m => m.to === agent.id && m.status === 'failed' && m.type !== 'ack').length,
3522
+ onOpenTerminal: async () => {
3523
+ if (!workspaceId) return;
3524
+ // Sync stale daemonActiveFromStream from agent states
3525
+ const anyActive = Object.values(states).some(s => s?.smithStatus === 'active');
3526
+ if (anyActive && !daemonActiveFromStream) setDaemonActiveFromStream(true);
3527
+ // Close existing terminal (config may have changed)
3528
+ setFloatingTerminals(prev => prev.filter(t => t.agentId !== agent.id));
3529
+
3530
+ const nodeEl = document.querySelector(`[data-id="${agent.id}"]`);
3531
+ const nodeRect = nodeEl?.getBoundingClientRect();
3532
+ const initialPos = nodeRect ? { x: nodeRect.left, y: nodeRect.bottom + 4 } : { x: 80, y: 60 };
3533
+ const safeName = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').slice(0, 20);
3534
+ const sessName = `mw-forge-${safeName(projectName)}-${safeName(agent.label)}`;
3535
+ const workDir = agent.workDir && agent.workDir !== './' && agent.workDir !== '.' ? agent.workDir : undefined;
3536
+ // All agents: show picker (current session / new session / other sessions)
3537
+ const resolveRes = await wsApi(workspaceId, 'open_terminal', { agentId: agent.id, resolveOnly: true }).catch(() => ({})) as any;
3538
+ const currentSessionId = resolveRes?.currentSessionId ?? null;
3539
+ setTermPicker({ agent, sessName, workDir, supportsSession: resolveRes?.supportsSession ?? true, currentSessionId, initialPos });
3540
+ },
3541
+ onSaveAsTemplate: async () => {
3542
+ const name = prompt('Template name:', agent.label);
3543
+ if (!name) return;
3544
+ const desc = prompt('Description (optional):', '');
3545
+ try {
3546
+ await fetch('/api/smith-templates', {
3547
+ method: 'POST',
3548
+ headers: { 'Content-Type': 'application/json' },
3549
+ body: JSON.stringify({ config: agent, name, icon: agent.icon, description: desc || '' }),
3550
+ });
3551
+ } catch {
3552
+ alert('Failed to save template');
3553
+ }
3554
+ },
3555
+ onSwitchSession: async () => {
3556
+ if (!workspaceId) return;
3557
+ setFloatingTerminals(prev => prev.filter(t => t.agentId !== agent.id));
3558
+ if (agent.id) wsApi(workspaceId, 'close_terminal', { agentId: agent.id });
3559
+ const nodeEl = document.querySelector(`[data-id="${agent.id}"]`);
3560
+ const nodeRect = nodeEl?.getBoundingClientRect();
3561
+ const initialPos = nodeRect ? { x: nodeRect.left, y: nodeRect.bottom + 4 } : { x: 80, y: 60 };
3562
+ const safeName = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').slice(0, 20);
3563
+ const sessName = `mw-forge-${safeName(projectName)}-${safeName(agent.label)}`;
3564
+ const workDir = agent.workDir && agent.workDir !== './' && agent.workDir !== '.' ? agent.workDir : undefined;
3565
+ const resolveRes = await wsApi(workspaceId, 'open_terminal', { agentId: agent.id, resolveOnly: true }).catch(() => ({})) as any;
3566
+ const currentSessionId = resolveRes?.currentSessionId ?? null;
3567
+ setTermPicker({ agent, sessName, workDir, supportsSession: resolveRes?.supportsSession ?? true, currentSessionId, initialPos });
3568
+ },
3569
+ } satisfies AgentNodeData,
3570
+ };
3571
+ });
3572
+ });
3573
+ }, [agents, states, logPreview, workspaceId, mascotTheme]); // eslint-disable-line react-hooks/exhaustive-deps
3574
+
3575
+ // Derive edges from dependsOn
3576
+ const rfEdges = useMemo(() => {
3577
+ const edges: any[] = [];
3578
+ for (const agent of agents) {
3579
+ for (const depId of agent.dependsOn) {
3580
+ const depState = states[depId];
3581
+ const targetState = states[agent.id];
3582
+ const depTask = depState?.taskStatus || 'idle';
3583
+ const targetTask = targetState?.taskStatus || 'idle';
3584
+ const isFlowing = depTask === 'running' || targetTask === 'running';
3585
+ const isCompleted = depTask === 'done';
3586
+ const color = isFlowing ? '#58a6ff70' : isCompleted ? '#58a6ff40' : '#30363d60';
3587
+
3588
+ // Find last bus message between these two agents
3589
+ const lastMsg = [...busLog].reverse().find(m =>
3590
+ (m.from === depId && m.to === agent.id) || (m.from === agent.id && m.to === depId)
3591
+ );
3592
+ const edgeLabel = lastMsg?.payload?.action && lastMsg.payload.action !== 'task_complete' && lastMsg.payload.action !== 'ack'
3593
+ ? `${lastMsg.payload.action}${lastMsg.payload.content ? ': ' + lastMsg.payload.content.slice(0, 30) : ''}`
3594
+ : undefined;
3595
+
3596
+ edges.push({
3597
+ id: `${depId}-${agent.id}`,
3598
+ source: depId,
3599
+ target: agent.id,
3600
+ animated: isFlowing,
3601
+ label: edgeLabel,
3602
+ labelStyle: { fill: '#8b949e', fontSize: 8 },
3603
+ labelBgStyle: { fill: '#0d1117', fillOpacity: 0.8 },
3604
+ labelBgPadding: [4, 2] as [number, number],
3605
+ style: { stroke: color, strokeWidth: isFlowing ? 2 : isCompleted ? 1.5 : 1 },
3606
+ markerEnd: { type: MarkerType.ArrowClosed, color },
3607
+ });
3608
+ }
3609
+ }
3610
+ return edges;
3611
+ }, [agents, states]);
3612
+
3613
+ // Let ReactFlow manage all node changes (position, dimensions, selection, etc.)
3614
+ const onNodesChange = useCallback((changes: NodeChange[]) => {
3615
+ setRfNodes(prev => applyNodeChanges(changes, prev) as Node<AgentNodeData>[]);
3616
+ }, []);
3617
+
3618
+ const handleAddAgent = async (cfg: Omit<AgentConfig, 'id'>) => {
3619
+ if (!workspaceId) return;
3620
+ const config: AgentConfig = { ...cfg, id: `${cfg.label.toLowerCase().replace(/\s+/g, '-')}-${Date.now()}` };
3621
+ // Auto-install base plugins if not already installed (for preset templates)
3622
+ // User-selected instances are already installed, so this is a no-op for them
3623
+ if (cfg.plugins?.length) {
3624
+ await Promise.all(cfg.plugins.map(pluginId =>
3625
+ fetch('/api/plugins', {
3626
+ method: 'POST',
3627
+ headers: { 'Content-Type': 'application/json' },
3628
+ body: JSON.stringify({ action: 'install', id: pluginId, config: {} }),
3629
+ }).catch(() => {})
3630
+ ));
3631
+ }
3632
+ // Optimistic update — show immediately
3633
+ setModal(null);
3634
+ await wsApi(workspaceId, 'add', { config });
3635
+ };
3636
+
3637
+ const handleEditAgent = async (cfg: Omit<AgentConfig, 'id'>) => {
3638
+ if (!workspaceId || !modal?.editId) return;
3639
+ const config: AgentConfig = { ...cfg, id: modal.editId };
3640
+ // Optimistic update
3641
+ setModal(null);
3642
+ await wsApi(workspaceId, 'update', { agentId: modal.editId, config });
3643
+ };
3644
+
3645
+ const handleAddInput = async () => {
3646
+ if (!workspaceId) return;
3647
+ const config: AgentConfig = {
3648
+ id: `input-${Date.now()}`, label: 'Requirements', icon: '📝',
3649
+ type: 'input', content: '', entries: [], role: '', backend: 'cli',
3650
+ dependsOn: [], outputs: [], steps: [],
3651
+ };
3652
+ await wsApi(workspaceId, 'add', { config });
3653
+ };
3654
+
3655
+ const handleCreatePipeline = async () => {
3656
+ if (!workspaceId) return;
3657
+ // Create pipeline via API — server uses presets with full prompts
3658
+ const res = await fetch(`/api/workspace/${workspaceId}/agents`, {
3659
+ method: 'POST',
3660
+ headers: { 'Content-Type': 'application/json' },
3661
+ body: JSON.stringify({ action: 'create_pipeline' }),
3662
+ });
3663
+ const data = await res.json();
3664
+ if (!res.ok && data.error) alert(`Error: ${data.error}`);
3665
+ };
3666
+
3667
+ const handleExportTemplate = async () => {
3668
+ if (!workspaceId) return;
3669
+ try {
3670
+ const res = await fetch(`/api/workspace?export=${workspaceId}`);
3671
+ const template = await res.json();
3672
+ // Download as JSON file
3673
+ const blob = new Blob([JSON.stringify(template, null, 2)], { type: 'application/json' });
3674
+ const url = URL.createObjectURL(blob);
3675
+ const a = document.createElement('a');
3676
+ a.href = url;
3677
+ a.download = `workspace-template-${projectName.replace(/\s+/g, '-')}.json`;
3678
+ a.click();
3679
+ URL.revokeObjectURL(url);
3680
+ } catch {
3681
+ alert('Export failed');
3682
+ }
3683
+ };
3684
+
3685
+ const handleImportTemplate = async (file: File) => {
3686
+ if (!workspaceId) return;
3687
+ try {
3688
+ const text = await file.text();
3689
+ const template = JSON.parse(text);
3690
+ await fetch('/api/workspace', {
3691
+ method: 'POST',
3692
+ headers: { 'Content-Type': 'application/json' },
3693
+ body: JSON.stringify({ projectPath, projectName, template }),
3694
+ });
3695
+ // Reload page to pick up new workspace
3696
+ window.location.reload();
3697
+ } catch {
3698
+ alert('Import failed — invalid template file');
3699
+ }
3700
+ };
3701
+
3702
+ const handleRunAll = () => { if (workspaceId) wsApi(workspaceId, 'run_all'); };
3703
+ const handleStartDaemon = async () => {
3704
+ if (!workspaceId) return;
3705
+ const result = await wsApi(workspaceId, 'start_daemon');
3706
+ if (result.ok) setDaemonActiveFromStream(true);
3707
+ };
3708
+ const handleStopDaemon = async () => {
3709
+ if (!workspaceId) return;
3710
+ const result = await wsApi(workspaceId, 'stop_daemon');
3711
+ if (result.ok) setDaemonActiveFromStream(false);
3712
+ };
3713
+
3714
+ return (
3715
+ <div className="flex-1 flex flex-col min-h-0" style={{ background: '#080810' }}>
3716
+ {/* Header */}
3717
+ <div className="flex items-center gap-2 px-3 py-1.5 border-b border-[#2a2a3a] shrink-0">
3718
+ <button onClick={onClose} className="text-gray-400 hover:text-white text-sm">←</button>
3719
+ <span className="text-xs font-bold text-white">Workspace</span>
3720
+ <span className="text-[9px] text-gray-500">{projectName}</span>
3721
+ {agents.length > 0 && !daemonActiveFromStream && (
3722
+ <>
3723
+ <button onClick={handleRunAll}
3724
+ className="text-[8px] px-2 py-0.5 rounded bg-green-600/20 text-green-400 hover:bg-green-600/30 ml-2">
3725
+ ▶ Run All
3726
+ </button>
3727
+ <button onClick={handleStartDaemon}
3728
+ className="text-[8px] px-2 py-0.5 rounded bg-emerald-600/20 text-emerald-400 hover:bg-emerald-600/30">
3729
+ ⚡ Start Daemon
3730
+ </button>
3731
+ </>
3732
+ )}
3733
+ {daemonActiveFromStream && (
3734
+ <>
3735
+ <span className="text-[8px] px-2 py-0.5 rounded bg-green-600/30 text-green-400 ml-2 animate-pulse">
3736
+ ● Daemon Active
3737
+ </span>
3738
+ <button onClick={handleStopDaemon}
3739
+ className="text-[8px] px-2 py-0.5 rounded bg-red-600/20 text-red-400 hover:bg-red-600/30">
3740
+ ■ Stop
3741
+ </button>
3742
+ </>
3743
+ )}
3744
+ <div className="ml-auto flex items-center gap-2">
3745
+ <select value={mascotTheme} onChange={e => updateMascotTheme(e.target.value as MascotTheme)}
3746
+ className="text-[8px] px-1.5 py-0.5 rounded border border-[#30363d] bg-[#0d1117] text-gray-500 hover:text-white hover:border-[#58a6ff]/60 cursor-pointer focus:outline-none"
3747
+ title="Mascot theme">
3748
+ <option value="stick">🏃 Stick</option>
3749
+ <option value="cat">🐱 Cat</option>
3750
+ <option value="dog">🐶 Dog</option>
3751
+ <option value="pig">🐷 Pig</option>
3752
+ <option value="emoji">🎭 Emoji</option>
3753
+ <option value="off">⊘ Off</option>
3754
+ </select>
3755
+ <button onClick={() => setShowBusPanel(true)}
3756
+ className={`text-[8px] px-2 py-0.5 rounded border border-[#30363d] hover:border-[#58a6ff]/60 ${busLog.length > 0 ? 'text-[#58a6ff]' : 'text-gray-500'}`}>
3757
+ 📡 Logs{busLog.length > 0 ? ` (${busLog.length})` : ''}
3758
+ </button>
3759
+ {agents.length > 0 && (
3760
+ <button onClick={handleExportTemplate}
3761
+ className="text-[8px] px-2 py-0.5 rounded border border-[#30363d] text-gray-500 hover:text-white hover:border-[#58a6ff]/60">
3762
+ 📤 Export
3763
+ </button>
3764
+ )}
3765
+ <button onClick={handleAddInput}
3766
+ className="text-[8px] px-2 py-0.5 rounded border border-[#30363d] text-gray-400 hover:text-white hover:border-[#58a6ff]/60">
3767
+ 📝 + Input
3768
+ </button>
3769
+ <button onClick={() => setModal({ mode: 'add', initial: {} })}
3770
+ className="text-[8px] px-2 py-0.5 rounded border border-[#30363d] text-gray-400 hover:text-white hover:border-[#58a6ff]/60">
3771
+ + Add Agent
3772
+ </button>
3773
+ </div>
3774
+ </div>
3775
+
3776
+ {/* Graph area */}
3777
+ {agents.length === 0 ? (
3778
+ <div className="flex-1 flex flex-col items-center justify-center gap-3">
3779
+ <span className="text-3xl">🚀</span>
3780
+ <div className="text-sm text-gray-400">Set up your workspace</div>
3781
+ {/* Primary agent prompt */}
3782
+ <button onClick={() => setModal({ mode: 'add', initial: {
3783
+ label: 'Engineer', icon: '👨‍💻', primary: true, persistentSession: true,
3784
+ role: 'Primary engineer — handles coding tasks in the project root.',
3785
+ backend: 'cli' as const, agentId: 'claude', workDir: './', dependsOn: [], outputs: [], steps: [],
3786
+ }})}
3787
+ className="flex items-center gap-3 px-5 py-3 rounded-lg border-2 border-dashed border-[#f0883e]/50 bg-[#f0883e]/5 hover:bg-[#f0883e]/10 hover:border-[#f0883e]/80 transition-colors">
3788
+ <span className="text-2xl">👨‍💻</span>
3789
+ <div className="text-left">
3790
+ <div className="text-[11px] font-semibold text-[#f0883e]">Add Primary Agent</div>
3791
+ <div className="text-[9px] text-gray-500">Terminal-only, root directory, fixed session</div>
3792
+ </div>
3793
+ </button>
3794
+ <div className="text-[9px] text-gray-600 mt-1">or add other agents:</div>
3795
+ <div className="flex gap-2 flex-wrap justify-center">
3796
+ {PRESET_AGENTS.map((p, i) => (
3797
+ <button key={i} onClick={() => setModal({ mode: 'add', initial: p })}
3798
+ className="text-[10px] px-3 py-1.5 rounded border border-[#30363d] text-gray-300 hover:text-white hover:border-[#58a6ff]/60 flex items-center gap-1">
3799
+ {p.icon} {p.label}
3800
+ </button>
3801
+ ))}
3802
+ </div>
3803
+ <div className="flex gap-2 mt-1">
3804
+ <button onClick={() => setModal({ mode: 'add', initial: {} })}
3805
+ className="text-[10px] px-3 py-1.5 rounded border border-dashed border-[#30363d] text-gray-500 hover:text-white hover:border-[#58a6ff]/60">
3806
+ ⚙️ Custom
3807
+ </button>
3808
+ <button onClick={handleCreatePipeline}
3809
+ className="text-[10px] px-3 py-1.5 rounded border border-[#238636] text-[#3fb950] hover:bg-[#238636]/20">
3810
+ 🚀 Dev Pipeline
3811
+ </button>
3812
+ <label className="text-[10px] px-3 py-1.5 rounded border border-dashed border-[#30363d] text-gray-500 hover:text-white hover:border-[#58a6ff]/60 cursor-pointer">
3813
+ 📥 Import
3814
+ <input type="file" accept=".json" className="hidden" onChange={e => {
3815
+ const file = e.target.files?.[0];
3816
+ if (file) handleImportTemplate(file);
3817
+ e.target.value = '';
3818
+ }} />
3819
+ </label>
3820
+ </div>
3821
+ </div>
3822
+ ) : (
3823
+ <div className="flex-1 min-h-0 flex flex-col">
3824
+ {/* No primary agent hint */}
3825
+ {!agents.some(a => a.primary) && (
3826
+ <div className="flex items-center gap-2 px-3 py-1.5 bg-[#f0883e]/10 border-b border-[#f0883e]/20 shrink-0">
3827
+ <span className="text-[10px] text-[#f0883e]">No primary agent set.</span>
3828
+ <button onClick={() => setModal({ mode: 'add', initial: {
3829
+ label: 'Engineer', icon: '👨‍💻', primary: true, persistentSession: true,
3830
+ role: 'Primary engineer — handles coding tasks in the project root.',
3831
+ backend: 'cli' as const, agentId: 'claude', workDir: './', dependsOn: [], outputs: [], steps: [],
3832
+ }})}
3833
+ className="text-[10px] text-[#f0883e] underline hover:text-white">Add one</button>
3834
+ <span className="text-[9px] text-gray-600">or edit an existing agent to set as primary.</span>
3835
+ </div>
3836
+ )}
3837
+ <ReactFlow
3838
+ nodes={rfNodes}
3839
+ edges={rfEdges}
3840
+ onNodesChange={onNodesChange}
3841
+ onNodeDragStop={() => {
3842
+ // Reposition terminals to follow their nodes
3843
+ setFloatingTerminals(prev => prev.map(ft => {
3844
+ const nodeEl = document.querySelector(`[data-id="${ft.agentId}"]`);
3845
+ const rect = nodeEl?.getBoundingClientRect();
3846
+ return rect ? { ...ft, initialPos: { x: rect.left, y: rect.bottom + 4 } } : ft;
3847
+ }));
3848
+ }}
3849
+ onMoveEnd={() => {
3850
+ // Reposition after pan/zoom
3851
+ setFloatingTerminals(prev => prev.map(ft => {
3852
+ const nodeEl = document.querySelector(`[data-id="${ft.agentId}"]`);
3853
+ const rect = nodeEl?.getBoundingClientRect();
3854
+ return rect ? { ...ft, initialPos: { x: rect.left, y: rect.bottom + 4 } } : ft;
3855
+ }));
3856
+ }}
3857
+ nodeTypes={nodeTypes}
3858
+ fitView
3859
+ fitViewOptions={{ padding: 0.3 }}
3860
+ minZoom={0.3}
3861
+ maxZoom={2}
3862
+ proOptions={{ hideAttribution: true }}
3863
+ >
3864
+ <Background color="#1a1a2e" gap={20} size={1} />
3865
+ <Controls style={{ background: '#0d1117', border: '1px solid #30363d' }} showInteractive={false} />
3866
+ </ReactFlow>
3867
+ </div>
3868
+ )}
3869
+
3870
+ {/* Config modal */}
3871
+ {modal && (
3872
+ <AgentConfigModal
3873
+ initial={modal.initial}
3874
+ mode={modal.mode}
3875
+ existingAgents={agents}
3876
+ projectPath={projectPath}
3877
+ onConfirm={modal.mode === 'add' ? handleAddAgent : handleEditAgent}
3878
+ onCancel={() => setModal(null)}
3879
+ />
3880
+ )}
3881
+
3882
+ {/* Run prompt dialog (for agents with no dependencies) */}
3883
+ {runPromptTarget && workspaceId && (
3884
+ <RunPromptDialog
3885
+ agentLabel={runPromptTarget.label}
3886
+ onRun={input => {
3887
+ wsApi(workspaceId, 'run', { agentId: runPromptTarget.id, input: input || undefined });
3888
+ setRunPromptTarget(null);
3889
+ }}
3890
+ onCancel={() => setRunPromptTarget(null)}
3891
+ />
3892
+ )}
3893
+
3894
+ {/* Message dialog */}
3895
+ {messageTarget && workspaceId && (
3896
+ <MessageDialog
3897
+ agentLabel={messageTarget.label}
3898
+ onSend={msg => {
3899
+ wsApi(workspaceId, 'message', { agentId: messageTarget.id, content: msg });
3900
+ setMessageTarget(null);
3901
+ }}
3902
+ onCancel={() => setMessageTarget(null)}
3903
+ />
3904
+ )}
3905
+
3906
+ {/* Log panel */}
3907
+ {logTarget && workspaceId && (
3908
+ <LogPanel
3909
+ agentId={logTarget.id}
3910
+ agentLabel={logTarget.label}
3911
+ workspaceId={workspaceId}
3912
+ onClose={() => setLogTarget(null)}
3913
+ />
3914
+ )}
3915
+
3916
+ {/* Bus message panel */}
3917
+ {showBusPanel && (
3918
+ <BusPanel busLog={busLog} agents={agents} onClose={() => setShowBusPanel(false)} />
3919
+ )}
3920
+
3921
+ {/* Memory panel */}
3922
+ {memoryTarget && workspaceId && (
3923
+ <MemoryPanel
3924
+ agentId={memoryTarget.id}
3925
+ agentLabel={memoryTarget.label}
3926
+ workspaceId={workspaceId}
3927
+ onClose={() => setMemoryTarget(null)}
3928
+ />
3929
+ )}
3930
+
3931
+ {/* Inbox panel */}
3932
+ {inboxTarget && workspaceId && (
3933
+ <InboxPanel
3934
+ agentId={inboxTarget.id}
3935
+ agentLabel={inboxTarget.label}
3936
+ busLog={busLog}
3937
+ agents={agents}
3938
+ workspaceId={workspaceId}
3939
+ onClose={() => setInboxTarget(null)}
3940
+ />
3941
+ )}
3942
+
3943
+ {/* Terminal session picker */}
3944
+ {termPicker && workspaceId && (
3945
+ <TerminalSessionPickerLazy
3946
+ agentLabel={termPicker.agent.label}
3947
+ currentSessionId={termPicker.currentSessionId}
3948
+ fetchSessions={() => fetchAgentSessions(workspaceId, termPicker.agent.id)}
3949
+ supportsSession={termPicker.supportsSession}
3950
+ onSelect={async (selection: PickerSelection) => {
3951
+ const { agent, sessName, workDir } = termPicker;
3952
+ const pickerInitialPos = termPicker.initialPos;
3953
+ setTermPicker(null);
3954
+
3955
+ let boundSessionId = agent.boundSessionId;
3956
+ if (selection.mode === 'session') {
3957
+ // Bind to a specific session
3958
+ await wsApi(workspaceId, 'update', { agentId: agent.id, config: { ...agent, boundSessionId: selection.sessionId } }).catch(() => {});
3959
+ boundSessionId = selection.sessionId;
3960
+ } else if (selection.mode === 'new') {
3961
+ // Clear bound session → fresh start
3962
+ if (agent.boundSessionId) {
3963
+ await wsApi(workspaceId, 'update', { agentId: agent.id, config: { ...agent, boundSessionId: undefined } }).catch(() => {});
3964
+ }
3965
+ boundSessionId = undefined;
3966
+ }
3967
+ // mode === 'current': keep existing boundSessionId
3968
+
3969
+ // 'current': just attach — claude is running, don't interrupt.
3970
+ // 'session' or 'new': forceRestart — rebuild launch script with correct --resume.
3971
+ const forceRestart = selection.mode !== 'current';
3972
+ const res = await wsApi(workspaceId, 'open_terminal', { agentId: agent.id, forceRestart }).catch(() => ({})) as any;
3973
+ const tmux = res?.tmuxSession || sessName;
3974
+ setFloatingTerminals(prev => [...prev, {
3975
+ agentId: agent.id, label: agent.label, icon: agent.icon,
3976
+ cliId: agent.agentId || 'claude', workDir,
3977
+ tmuxSession: tmux, sessionName: sessName,
3978
+ isPrimary: agent.primary, skipPermissions: agent.skipPermissions !== false,
3979
+ persistentSession: agent.persistentSession, boundSessionId, initialPos: pickerInitialPos,
3980
+ }]);
3981
+ }}
3982
+ onCancel={() => setTermPicker(null)}
3983
+ />
3984
+ )}
3985
+
3986
+ {/* Floating terminals — positioned near their agent node */}
3987
+ {floatingTerminals.map(ft => (
3988
+ <FloatingTerminal
3989
+ key={ft.agentId}
3990
+ agentLabel={ft.label}
3991
+ agentIcon={ft.icon}
3992
+ projectPath={projectPath}
3993
+ agentCliId={ft.cliId}
3994
+ cliCmd={ft.cliCmd}
3995
+ cliType={ft.cliType}
3996
+ workDir={ft.workDir}
3997
+ preferredSessionName={ft.sessionName}
3998
+ existingSession={ft.tmuxSession}
3999
+ resumeMode={ft.resumeMode}
4000
+ resumeSessionId={ft.resumeSessionId}
4001
+ profileEnv={ft.profileEnv}
4002
+ isPrimary={ft.isPrimary}
4003
+ skipPermissions={ft.skipPermissions}
4004
+ persistentSession={ft.persistentSession}
4005
+ boundSessionId={ft.boundSessionId}
4006
+ initialPos={ft.initialPos}
4007
+ onSessionReady={(name) => {
4008
+ if (workspaceId) wsApi(workspaceId, 'set_tmux_session', { agentId: ft.agentId, sessionName: name });
4009
+ setFloatingTerminals(prev => prev.map(t => t.agentId === ft.agentId ? { ...t, tmuxSession: name } : t));
4010
+ }}
4011
+ onClose={(killSession) => {
4012
+ setFloatingTerminals(prev => prev.filter(t => t.agentId !== ft.agentId));
4013
+ if (workspaceId) wsApi(workspaceId, 'close_terminal', { agentId: ft.agentId, kill: killSession });
4014
+ }}
4015
+ />
4016
+ ))}
4017
+
4018
+ {/* User input request from agent (via bus) */}
4019
+ {userInputRequest && workspaceId && (
4020
+ <RunPromptDialog
4021
+ agentLabel={`${agents.find(a => a.id === userInputRequest.fromAgent)?.label || 'Agent'} asks`}
4022
+ onRun={input => {
4023
+ // Send response to the requesting agent's target (Input node)
4024
+ wsApi(workspaceId, 'complete_input', {
4025
+ agentId: userInputRequest.agentId,
4026
+ content: input || userInputRequest.question,
4027
+ });
4028
+ setUserInputRequest(null);
4029
+ }}
4030
+ onCancel={() => setUserInputRequest(null)}
4031
+ />
4032
+ )}
4033
+ </div>
4034
+ );
4035
+ }
4036
+
4037
+ const WorkspaceViewWithRef = forwardRef(WorkspaceViewInner);
4038
+
4039
+ // Wrap with ReactFlowProvider so useReactFlow works
4040
+ export default forwardRef<WorkspaceViewHandle, { projectPath: string; projectName: string; onClose: () => void }>(
4041
+ function WorkspaceView(props, ref) {
4042
+ return (
4043
+ <ReactFlowProvider>
4044
+ <WorkspaceViewWithRef {...props} ref={ref} />
4045
+ </ReactFlowProvider>
4046
+ );
4047
+ }
4048
+ );