@aion0/forge 0.5.26 → 0.5.28

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 (255) hide show
  1. package/.forge/worktrees/pipeline-4dd8dc2d/CLAUDE.md +86 -0
  2. package/.forge/worktrees/pipeline-4dd8dc2d/README.md +136 -0
  3. package/.forge/worktrees/pipeline-4dd8dc2d/RELEASE_NOTES.md +36 -0
  4. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/agents/route.ts +17 -0
  5. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/[...nextauth]/route.ts +3 -0
  6. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/verify/route.ts +46 -0
  7. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/route.ts +31 -0
  8. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/stream/route.ts +63 -0
  9. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/route.ts +28 -0
  10. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  11. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  12. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/route.ts +37 -0
  13. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/sync/route.ts +17 -0
  14. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-templates/route.ts +145 -0
  15. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/code/route.ts +299 -0
  16. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/[id]/route.ts +62 -0
  17. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/route.ts +40 -0
  18. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/detect-cli/route.ts +46 -0
  19. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/route.ts +176 -0
  20. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/sessions/route.ts +54 -0
  21. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/favorites/route.ts +26 -0
  22. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/route.ts +6 -0
  23. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/run/route.ts +19 -0
  24. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/git/route.ts +149 -0
  25. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/help/route.ts +84 -0
  26. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/issue-scanner/route.ts +116 -0
  27. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/logs/route.ts +100 -0
  28. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/mobile-chat/route.ts +115 -0
  29. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/monitor/route.ts +74 -0
  30. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notifications/route.ts +42 -0
  31. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notify/test/route.ts +33 -0
  32. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/online/route.ts +40 -0
  33. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/[id]/route.ts +41 -0
  34. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/route.ts +90 -0
  35. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/plugins/route.ts +75 -0
  36. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/[...path]/route.ts +64 -0
  37. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/route.ts +156 -0
  38. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-pipelines/route.ts +91 -0
  39. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-sessions/route.ts +61 -0
  40. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/projects/route.ts +26 -0
  41. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/chat/route.ts +64 -0
  42. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/messages/route.ts +9 -0
  43. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/route.ts +17 -0
  44. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/route.ts +20 -0
  45. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/settings/route.ts +64 -0
  46. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/local/route.ts +228 -0
  47. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/route.ts +182 -0
  48. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/smith-templates/route.ts +81 -0
  49. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/status/route.ts +12 -0
  50. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tabs/route.ts +25 -0
  51. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/route.ts +51 -0
  52. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/stream/route.ts +77 -0
  53. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/link/route.ts +37 -0
  54. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/route.ts +44 -0
  55. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/session/route.ts +14 -0
  56. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/telegram/route.ts +23 -0
  57. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/templates/route.ts +6 -0
  58. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-bell/route.ts +39 -0
  59. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-cwd/route.ts +19 -0
  60. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-state/route.ts +15 -0
  61. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tunnel/route.ts +26 -0
  62. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/upgrade/route.ts +43 -0
  63. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/usage/route.ts +20 -0
  64. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/version/route.ts +78 -0
  65. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/watchers/route.ts +33 -0
  66. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/agents/route.ts +35 -0
  67. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/memory/route.ts +23 -0
  68. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/smith/route.ts +22 -0
  69. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/stream/route.ts +31 -0
  70. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/route.ts +79 -0
  71. package/.forge/worktrees/pipeline-4dd8dc2d/app/global-error.tsx +21 -0
  72. package/.forge/worktrees/pipeline-4dd8dc2d/app/globals.css +52 -0
  73. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.ico +0 -0
  74. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.png +0 -0
  75. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.svg +106 -0
  76. package/.forge/worktrees/pipeline-4dd8dc2d/app/layout.tsx +17 -0
  77. package/.forge/worktrees/pipeline-4dd8dc2d/app/login/LoginForm.tsx +96 -0
  78. package/.forge/worktrees/pipeline-4dd8dc2d/app/login/page.tsx +10 -0
  79. package/.forge/worktrees/pipeline-4dd8dc2d/app/mobile/page.tsx +10 -0
  80. package/.forge/worktrees/pipeline-4dd8dc2d/app/page.tsx +22 -0
  81. package/.forge/worktrees/pipeline-4dd8dc2d/bin/forge-server.mjs +484 -0
  82. package/.forge/worktrees/pipeline-4dd8dc2d/check-forge-status.sh +71 -0
  83. package/.forge/worktrees/pipeline-4dd8dc2d/cli/mw.ts +579 -0
  84. package/.forge/worktrees/pipeline-4dd8dc2d/components/BrowserPanel.tsx +175 -0
  85. package/.forge/worktrees/pipeline-4dd8dc2d/components/ChatPanel.tsx +191 -0
  86. package/.forge/worktrees/pipeline-4dd8dc2d/components/ClaudeTerminal.tsx +267 -0
  87. package/.forge/worktrees/pipeline-4dd8dc2d/components/CodeViewer.tsx +787 -0
  88. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationEditor.tsx +411 -0
  89. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationGraphView.tsx +347 -0
  90. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationTerminalView.tsx +303 -0
  91. package/.forge/worktrees/pipeline-4dd8dc2d/components/Dashboard.tsx +807 -0
  92. package/.forge/worktrees/pipeline-4dd8dc2d/components/DashboardWrapper.tsx +9 -0
  93. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryFlowEditor.tsx +491 -0
  94. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryList.tsx +230 -0
  95. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryWorkspace.tsx +589 -0
  96. package/.forge/worktrees/pipeline-4dd8dc2d/components/DocTerminal.tsx +187 -0
  97. package/.forge/worktrees/pipeline-4dd8dc2d/components/DocsViewer.tsx +574 -0
  98. package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpDialog.tsx +169 -0
  99. package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpTerminal.tsx +141 -0
  100. package/.forge/worktrees/pipeline-4dd8dc2d/components/InlinePipelineView.tsx +111 -0
  101. package/.forge/worktrees/pipeline-4dd8dc2d/components/LogViewer.tsx +194 -0
  102. package/.forge/worktrees/pipeline-4dd8dc2d/components/MarkdownContent.tsx +73 -0
  103. package/.forge/worktrees/pipeline-4dd8dc2d/components/MobileView.tsx +385 -0
  104. package/.forge/worktrees/pipeline-4dd8dc2d/components/MonitorPanel.tsx +122 -0
  105. package/.forge/worktrees/pipeline-4dd8dc2d/components/NewSessionModal.tsx +93 -0
  106. package/.forge/worktrees/pipeline-4dd8dc2d/components/NewTaskModal.tsx +492 -0
  107. package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineEditor.tsx +570 -0
  108. package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineView.tsx +1018 -0
  109. package/.forge/worktrees/pipeline-4dd8dc2d/components/PluginsPanel.tsx +472 -0
  110. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectDetail.tsx +1618 -0
  111. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectList.tsx +108 -0
  112. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectManager.tsx +401 -0
  113. package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionList.tsx +74 -0
  114. package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionView.tsx +726 -0
  115. package/.forge/worktrees/pipeline-4dd8dc2d/components/SettingsModal.tsx +1647 -0
  116. package/.forge/worktrees/pipeline-4dd8dc2d/components/SkillsPanel.tsx +969 -0
  117. package/.forge/worktrees/pipeline-4dd8dc2d/components/StatusBar.tsx +99 -0
  118. package/.forge/worktrees/pipeline-4dd8dc2d/components/TabBar.tsx +46 -0
  119. package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskBoard.tsx +113 -0
  120. package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskDetail.tsx +372 -0
  121. package/.forge/worktrees/pipeline-4dd8dc2d/components/TerminalLauncher.tsx +398 -0
  122. package/.forge/worktrees/pipeline-4dd8dc2d/components/TunnelToggle.tsx +206 -0
  123. package/.forge/worktrees/pipeline-4dd8dc2d/components/UsagePanel.tsx +207 -0
  124. package/.forge/worktrees/pipeline-4dd8dc2d/components/WebTerminal.tsx +1743 -0
  125. package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceTree.tsx +221 -0
  126. package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceView.tsx +4048 -0
  127. package/.forge/worktrees/pipeline-4dd8dc2d/dev-test.sh +5 -0
  128. package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Memory_Layer_Design.docx +0 -0
  129. package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Strategy_Research_2026.docx +0 -0
  130. package/.forge/worktrees/pipeline-4dd8dc2d/docs/LOCAL-DEPLOY.md +144 -0
  131. package/.forge/worktrees/pipeline-4dd8dc2d/docs/roadmap-multi-agent-workflow.md +330 -0
  132. package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.png +0 -0
  133. package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.svg +106 -0
  134. package/.forge/worktrees/pipeline-4dd8dc2d/hooks/useSidebarResize.ts +52 -0
  135. package/.forge/worktrees/pipeline-4dd8dc2d/install.sh +29 -0
  136. package/.forge/worktrees/pipeline-4dd8dc2d/instrumentation.ts +35 -0
  137. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/claude-adapter.ts +104 -0
  138. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/generic-adapter.ts +64 -0
  139. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/index.ts +245 -0
  140. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/types.ts +70 -0
  141. package/.forge/worktrees/pipeline-4dd8dc2d/lib/artifacts.ts +106 -0
  142. package/.forge/worktrees/pipeline-4dd8dc2d/lib/auth.ts +62 -0
  143. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/docker.yaml +70 -0
  144. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/http.yaml +66 -0
  145. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/jenkins.yaml +92 -0
  146. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/llm-vision.yaml +85 -0
  147. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/playwright.yaml +111 -0
  148. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/shell-command.yaml +60 -0
  149. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/slack.yaml +48 -0
  150. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/webhook.yaml +56 -0
  151. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-process.ts +361 -0
  152. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-sessions.ts +266 -0
  153. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-templates.ts +227 -0
  154. package/.forge/worktrees/pipeline-4dd8dc2d/lib/cloudflared.ts +424 -0
  155. package/.forge/worktrees/pipeline-4dd8dc2d/lib/crypto.ts +67 -0
  156. package/.forge/worktrees/pipeline-4dd8dc2d/lib/delivery.ts +787 -0
  157. package/.forge/worktrees/pipeline-4dd8dc2d/lib/dirs.ts +99 -0
  158. package/.forge/worktrees/pipeline-4dd8dc2d/lib/flows.ts +86 -0
  159. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-mcp-server.ts +732 -0
  160. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-inbox.md +38 -0
  161. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-send.md +47 -0
  162. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-status.md +32 -0
  163. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-workspace-sync.md +37 -0
  164. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/00-overview.md +40 -0
  165. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/01-settings.md +194 -0
  166. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/02-telegram.md +41 -0
  167. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/03-tunnel.md +31 -0
  168. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/04-tasks.md +52 -0
  169. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/05-pipelines.md +460 -0
  170. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/06-skills.md +43 -0
  171. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/07-projects.md +73 -0
  172. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/08-rules.md +53 -0
  173. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/09-issue-autofix.md +55 -0
  174. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/10-troubleshooting.md +89 -0
  175. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/11-workspace.md +810 -0
  176. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/CLAUDE.md +62 -0
  177. package/.forge/worktrees/pipeline-4dd8dc2d/lib/init.ts +266 -0
  178. package/.forge/worktrees/pipeline-4dd8dc2d/lib/issue-scanner.ts +298 -0
  179. package/.forge/worktrees/pipeline-4dd8dc2d/lib/logger.ts +79 -0
  180. package/.forge/worktrees/pipeline-4dd8dc2d/lib/notifications.ts +75 -0
  181. package/.forge/worktrees/pipeline-4dd8dc2d/lib/notify.ts +108 -0
  182. package/.forge/worktrees/pipeline-4dd8dc2d/lib/password.ts +97 -0
  183. package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline-scheduler.ts +373 -0
  184. package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline.ts +1565 -0
  185. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/executor.ts +347 -0
  186. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/registry.ts +228 -0
  187. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/types.ts +103 -0
  188. package/.forge/worktrees/pipeline-4dd8dc2d/lib/project-sessions.ts +53 -0
  189. package/.forge/worktrees/pipeline-4dd8dc2d/lib/projects.ts +86 -0
  190. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-manager.ts +156 -0
  191. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-utils.ts +53 -0
  192. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-watcher.ts +345 -0
  193. package/.forge/worktrees/pipeline-4dd8dc2d/lib/settings.ts +195 -0
  194. package/.forge/worktrees/pipeline-4dd8dc2d/lib/skills.ts +458 -0
  195. package/.forge/worktrees/pipeline-4dd8dc2d/lib/task-manager.ts +951 -0
  196. package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-bot.ts +1477 -0
  197. package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-standalone.ts +83 -0
  198. package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-server.ts +70 -0
  199. package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-standalone.ts +438 -0
  200. package/.forge/worktrees/pipeline-4dd8dc2d/lib/usage-scanner.ts +249 -0
  201. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/state-machine.test.ts +388 -0
  202. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/workspace.test.ts +311 -0
  203. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-bus.ts +416 -0
  204. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-worker.ts +655 -0
  205. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/api-backend.ts +262 -0
  206. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/cli-backend.ts +491 -0
  207. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/index.ts +84 -0
  208. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/manager.ts +136 -0
  209. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/orchestrator.ts +3415 -0
  210. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/persistence.ts +309 -0
  211. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/presets.ts +649 -0
  212. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/requests.ts +287 -0
  213. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/session-monitor.ts +240 -0
  214. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/skill-installer.ts +275 -0
  215. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/smith-memory.ts +498 -0
  216. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/types.ts +241 -0
  217. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/watch-manager.ts +560 -0
  218. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace-standalone.ts +978 -0
  219. package/.forge/worktrees/pipeline-4dd8dc2d/middleware.ts +51 -0
  220. package/.forge/worktrees/pipeline-4dd8dc2d/next.config.ts +26 -0
  221. package/.forge/worktrees/pipeline-4dd8dc2d/package.json +74 -0
  222. package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-lock.yaml +3719 -0
  223. package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-workspace.yaml +1 -0
  224. package/.forge/worktrees/pipeline-4dd8dc2d/postcss.config.mjs +7 -0
  225. package/.forge/worktrees/pipeline-4dd8dc2d/publish.sh +133 -0
  226. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/README.md +66 -0
  227. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/results/.gitignore +2 -0
  228. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/run.ts +635 -0
  229. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/task.md +26 -0
  230. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  231. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  232. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/task.md +48 -0
  233. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  234. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  235. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  236. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  237. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/verify-usage.ts +178 -0
  238. package/.forge/worktrees/pipeline-4dd8dc2d/src/config/index.ts +129 -0
  239. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/db/database.ts +259 -0
  240. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/memory/strategy.ts +32 -0
  241. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/chat.ts +65 -0
  242. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/registry.ts +60 -0
  243. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/session/manager.ts +190 -0
  244. package/.forge/worktrees/pipeline-4dd8dc2d/src/types/index.ts +129 -0
  245. package/.forge/worktrees/pipeline-4dd8dc2d/start.sh +32 -0
  246. package/.forge/worktrees/pipeline-4dd8dc2d/templates/smith-lead.json +45 -0
  247. package/.forge/worktrees/pipeline-4dd8dc2d/tsconfig.json +42 -0
  248. package/RELEASE_NOTES.md +10 -29
  249. package/app/api/terminal-bell/route.ts +6 -2
  250. package/app/api/terminal-cwd/route.ts +7 -4
  251. package/components/CodeViewer.tsx +3 -31
  252. package/components/Dashboard.tsx +34 -20
  253. package/components/WebTerminal.tsx +36 -2
  254. package/lib/terminal-standalone.ts +19 -2
  255. package/package.json +1 -1
@@ -0,0 +1,175 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useRef, useCallback } from 'react';
4
+
5
+ interface PreviewEntry {
6
+ port: number;
7
+ url: string | null;
8
+ status: string;
9
+ label?: string;
10
+ }
11
+
12
+ export default function BrowserPanel({ onClose }: { onClose?: () => void }) {
13
+ const [browserUrl, setBrowserUrl] = useState(() => typeof window !== 'undefined' ? localStorage.getItem('forge-browser-url') || '' : '');
14
+ const [browserKey, setBrowserKey] = useState(0);
15
+ const [previews, setPreviews] = useState<PreviewEntry[]>([]);
16
+ const [tunnelStarting, setTunnelStarting] = useState(false);
17
+ const browserUrlRef = useRef<HTMLInputElement>(null);
18
+ const isRemote = typeof window !== 'undefined' && !['localhost', '127.0.0.1'].includes(window.location.hostname);
19
+
20
+ const fetchPreviews = useCallback(() => {
21
+ fetch('/api/preview').then(r => r.json()).then(data => {
22
+ if (Array.isArray(data)) setPreviews(data.filter((p: any) => p.status === 'running'));
23
+ }).catch(() => {});
24
+ }, []);
25
+
26
+ useEffect(() => {
27
+ fetchPreviews();
28
+ const timer = setInterval(fetchPreviews, 10000);
29
+ return () => clearInterval(timer);
30
+ }, [fetchPreviews]);
31
+
32
+ const normalizeUrl = (val: string): string => {
33
+ if (/^\d+$/.test(val)) return `http://localhost:${val}`;
34
+ if (!/^https?:\/\//i.test(val)) return `http://${val}`;
35
+ return val;
36
+ };
37
+
38
+ const navigate = (url: string) => {
39
+ const normalized = normalizeUrl(url);
40
+ setBrowserUrl(normalized);
41
+ localStorage.setItem('forge-browser-url', normalized);
42
+ if (browserUrlRef.current) browserUrlRef.current.value = normalized;
43
+ setBrowserKey(k => k + 1);
44
+ };
45
+
46
+ const handleTunnel = async () => {
47
+ const input = prompt('Enter port(s) to create tunnel (e.g. 3100 or 3100,8080):');
48
+ if (!input) return;
49
+ const ports = input.split(',').map(s => parseInt(s.trim())).filter(p => p > 0 && p <= 65535);
50
+ if (ports.length === 0) { alert('Invalid port(s)'); return; }
51
+ setTunnelStarting(true);
52
+ const results: string[] = [];
53
+ for (const port of ports) {
54
+ try {
55
+ const res = await fetch('/api/preview', {
56
+ method: 'POST',
57
+ headers: { 'Content-Type': 'application/json' },
58
+ body: JSON.stringify({ action: 'start', port }),
59
+ });
60
+ const data = await res.json();
61
+ if (data.url) {
62
+ results.push(data.url);
63
+ setPreviews(prev => {
64
+ const exists = prev.find(p => p.port === port);
65
+ if (exists) return prev.map(p => p.port === port ? { ...p, url: data.url, status: 'running' } : p);
66
+ return [...prev, { port, url: data.url, status: 'running' }];
67
+ });
68
+ } else if (data.status === 'starting' || data.status === 'stopped') {
69
+ // Tunnel started but URL not ready yet or exited
70
+ results.push('');
71
+ } else {
72
+ alert(`Port ${port}: ${data.error || 'Failed'}`);
73
+ }
74
+ } catch { alert(`Port ${port}: Failed to start tunnel`); }
75
+ }
76
+ // Navigate to first successful URL
77
+ const firstUrl = results.find(u => u);
78
+ if (firstUrl) navigate(firstUrl);
79
+ // Refresh list to pick up any that were still starting
80
+ setTimeout(fetchPreviews, 3000);
81
+ setTunnelStarting(false);
82
+ };
83
+
84
+ const stopTunnel = async (port: number) => {
85
+ await fetch('/api/preview', {
86
+ method: 'POST',
87
+ headers: { 'Content-Type': 'application/json' },
88
+ body: JSON.stringify({ action: 'stop', port }),
89
+ });
90
+ setPreviews(prev => prev.filter(x => x.port !== port));
91
+ };
92
+
93
+ return (
94
+ <div className="flex-1 flex flex-col min-h-0">
95
+ {/* URL bar */}
96
+ <div className="flex items-center gap-1 px-2 py-1 border-b border-[var(--border)] bg-[var(--bg-tertiary)] shrink-0">
97
+ <input
98
+ ref={browserUrlRef}
99
+ type="text"
100
+ defaultValue={browserUrl}
101
+ placeholder="Enter URL"
102
+ onKeyDown={e => {
103
+ if (e.key === 'Enter') {
104
+ const val = (e.target as HTMLInputElement).value.trim();
105
+ if (!val) return;
106
+ navigate(val);
107
+ }
108
+ }}
109
+ className="flex-1 bg-[var(--bg-secondary)] border border-[var(--border)] rounded px-2 py-0.5 text-[10px] text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)] min-w-0"
110
+ />
111
+ <button onClick={() => setBrowserKey(k => k + 1)} className="text-[10px] text-[var(--text-secondary)] hover:text-[var(--text-primary)] px-1" title="Refresh">↻</button>
112
+ <button onClick={() => window.open(browserUrl, '_blank')} className="text-[10px] text-[var(--text-secondary)] hover:text-[var(--text-primary)] px-1" title="Open in new tab">↗</button>
113
+ <button
114
+ disabled={tunnelStarting}
115
+ onClick={handleTunnel}
116
+ className="text-[9px] px-1.5 py-0.5 rounded border border-[var(--border)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:border-[var(--accent)] disabled:opacity-50"
117
+ title="Create tunnel for a port (remote access)"
118
+ >{tunnelStarting ? 'Starting...' : 'Tunnel'}</button>
119
+ {onClose && (
120
+ <button onClick={onClose} className="text-[10px] text-[var(--text-secondary)] hover:text-[var(--red)] px-1" title="Close">✕</button>
121
+ )}
122
+ </div>
123
+ {/* Active tunnels bar */}
124
+ {previews.length > 0 && (
125
+ <div className="flex items-center gap-1 px-2 py-0.5 border-b border-[var(--border)]/50 bg-[var(--bg-secondary)] shrink-0 overflow-x-auto">
126
+ {previews.map(p => (
127
+ <div key={p.port} className="flex items-center gap-1 shrink-0">
128
+ <button
129
+ onClick={() => {
130
+ const url = isRemote && p.url ? p.url : `http://localhost:${p.port}`;
131
+ navigate(url);
132
+ }}
133
+ className="text-[9px] px-1.5 py-0.5 rounded bg-[var(--bg-tertiary)] text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
134
+ >
135
+ <span className="text-green-400 mr-0.5">●</span>
136
+ :{p.port}
137
+ </button>
138
+ {p.url && (
139
+ <button
140
+ onClick={() => navigator.clipboard.writeText(p.url!).then(() => alert('Tunnel URL copied'))}
141
+ className="text-[8px] text-green-400 hover:underline truncate max-w-[120px]"
142
+ title={p.url}
143
+ >{p.url.replace('https://', '').slice(0, 20)}...</button>
144
+ )}
145
+ <button onClick={() => stopTunnel(p.port)} className="text-[8px] text-red-400 hover:text-red-300">✕</button>
146
+ </div>
147
+ ))}
148
+ </div>
149
+ )}
150
+ {/* Content */}
151
+ <div className="flex-1 relative">
152
+ {tunnelStarting && (
153
+ <div className="absolute inset-0 flex items-center justify-center text-[var(--text-secondary)] text-xs z-10 bg-[var(--bg-primary)]/80">
154
+ Creating tunnel... this may take up to 30 seconds
155
+ </div>
156
+ )}
157
+ {browserUrl ? (
158
+ <iframe
159
+ key={browserKey}
160
+ src={browserUrl}
161
+ className="absolute inset-0 w-full h-full border-0 bg-white"
162
+ sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
163
+ />
164
+ ) : (
165
+ <div className="absolute inset-0 flex items-center justify-center text-[var(--text-secondary)] text-xs">
166
+ <div className="text-center space-y-1">
167
+ <p>Enter a URL or port number and press Enter</p>
168
+ <p className="text-[9px]">Click Tunnel to create a public URL for remote access</p>
169
+ </div>
170
+ </div>
171
+ )}
172
+ </div>
173
+ </div>
174
+ );
175
+ }
@@ -0,0 +1,191 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useRef } from 'react';
4
+ import type { Session, Message } from '@/src/types';
5
+
6
+ const providerLabels: Record<string, string> = {
7
+ anthropic: 'Claude',
8
+ google: 'Gemini',
9
+ openai: 'OpenAI',
10
+ grok: 'Grok',
11
+ };
12
+
13
+ export default function ChatPanel({
14
+ session,
15
+ onUpdate,
16
+ }: {
17
+ session: Session;
18
+ onUpdate: () => void;
19
+ }) {
20
+ const [messages, setMessages] = useState<Message[]>([]);
21
+ const [input, setInput] = useState('');
22
+ const [streaming, setStreaming] = useState(false);
23
+ const [streamContent, setStreamContent] = useState('');
24
+ const messagesEndRef = useRef<HTMLDivElement>(null);
25
+ const inputRef = useRef<HTMLTextAreaElement>(null);
26
+
27
+ // Load messages when session changes
28
+ useEffect(() => {
29
+ fetch(`/api/sessions/${session.id}/messages`)
30
+ .then(r => r.json())
31
+ .then(setMessages);
32
+ }, [session.id]);
33
+
34
+ // Auto-scroll
35
+ useEffect(() => {
36
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
37
+ }, [messages, streamContent]);
38
+
39
+ const sendMessage = async () => {
40
+ const text = input.trim();
41
+ if (!text || streaming) return;
42
+
43
+ setInput('');
44
+ setStreaming(true);
45
+ setStreamContent('');
46
+
47
+ // Optimistic: add user message
48
+ const userMsg: Message = {
49
+ id: Date.now(),
50
+ sessionId: session.id,
51
+ role: 'user',
52
+ content: text,
53
+ provider: session.provider,
54
+ model: session.model,
55
+ createdAt: new Date().toISOString(),
56
+ };
57
+ setMessages(prev => [...prev, userMsg]);
58
+
59
+ try {
60
+ const res = await fetch(`/api/sessions/${session.id}/chat`, {
61
+ method: 'POST',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: JSON.stringify({ message: text }),
64
+ });
65
+
66
+ const reader = res.body?.getReader();
67
+ const decoder = new TextDecoder();
68
+ let fullContent = '';
69
+
70
+ if (reader) {
71
+ while (true) {
72
+ const { done, value } = await reader.read();
73
+ if (done) break;
74
+
75
+ const chunk = decoder.decode(value);
76
+ const lines = chunk.split('\n');
77
+
78
+ for (const line of lines) {
79
+ if (!line.startsWith('data: ')) continue;
80
+ try {
81
+ const data = JSON.parse(line.slice(6));
82
+ if (data.token) {
83
+ fullContent += data.token;
84
+ setStreamContent(fullContent);
85
+ }
86
+ if (data.done) {
87
+ // Add final assistant message
88
+ const assistantMsg: Message = {
89
+ id: Date.now() + 1,
90
+ sessionId: session.id,
91
+ role: 'assistant',
92
+ content: fullContent,
93
+ provider: session.provider,
94
+ model: session.model,
95
+ createdAt: new Date().toISOString(),
96
+ };
97
+ setMessages(prev => [...prev, assistantMsg]);
98
+ setStreamContent('');
99
+ }
100
+ if (data.error) {
101
+ setStreamContent(`Error: ${data.error}`);
102
+ }
103
+ } catch {}
104
+ }
105
+ }
106
+ }
107
+ } catch (err: any) {
108
+ setStreamContent(`Error: ${err.message}`);
109
+ }
110
+
111
+ setStreaming(false);
112
+ onUpdate();
113
+ inputRef.current?.focus();
114
+ };
115
+
116
+ const handleKeyDown = (e: React.KeyboardEvent) => {
117
+ if (e.key === 'Enter' && !e.shiftKey) {
118
+ e.preventDefault();
119
+ sendMessage();
120
+ }
121
+ };
122
+
123
+ return (
124
+ <div className="flex-1 flex flex-col min-h-0">
125
+ {/* Session header */}
126
+ <div className="h-10 border-b border-[var(--border)] flex items-center px-4 gap-3 shrink-0">
127
+ <span className="text-sm font-semibold">{session.name}</span>
128
+ <span className="text-[10px] px-1.5 py-0.5 bg-[var(--bg-tertiary)] rounded text-[var(--text-secondary)]">
129
+ {providerLabels[session.provider] || session.provider}
130
+ </span>
131
+ <span className="text-[10px] px-1.5 py-0.5 bg-[var(--bg-tertiary)] rounded text-[var(--text-secondary)]">
132
+ {session.memory.strategy}
133
+ </span>
134
+ <span className="text-[10px] text-[var(--text-secondary)]">
135
+ {session.messageCount} messages
136
+ </span>
137
+ </div>
138
+
139
+ {/* Messages */}
140
+ <div className="flex-1 overflow-y-auto p-4 space-y-4">
141
+ {messages.map(msg => (
142
+ <div key={msg.id} className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>
143
+ <div
144
+ className={`max-w-[80%] px-3 py-2 rounded-lg text-sm whitespace-pre-wrap ${
145
+ msg.role === 'user'
146
+ ? 'bg-[var(--accent)] text-white'
147
+ : 'bg-[var(--bg-tertiary)] text-[var(--text-primary)]'
148
+ }`}
149
+ >
150
+ {msg.content}
151
+ </div>
152
+ </div>
153
+ ))}
154
+
155
+ {streamContent && (
156
+ <div className="flex justify-start">
157
+ <div className="max-w-[80%] px-3 py-2 rounded-lg text-sm bg-[var(--bg-tertiary)] text-[var(--text-primary)] whitespace-pre-wrap">
158
+ {streamContent}
159
+ <span className="animate-pulse">▌</span>
160
+ </div>
161
+ </div>
162
+ )}
163
+
164
+ <div ref={messagesEndRef} />
165
+ </div>
166
+
167
+ {/* Input */}
168
+ <div className="border-t border-[var(--border)] p-3 shrink-0">
169
+ <div className="flex gap-2">
170
+ <textarea
171
+ ref={inputRef}
172
+ value={input}
173
+ onChange={e => setInput(e.target.value)}
174
+ onKeyDown={handleKeyDown}
175
+ placeholder={streaming ? 'Waiting for response...' : 'Type a message... (Enter to send, Shift+Enter for newline)'}
176
+ disabled={streaming}
177
+ rows={1}
178
+ className="flex-1 px-3 py-2 bg-[var(--bg-tertiary)] border border-[var(--border)] rounded text-sm text-[var(--text-primary)] resize-none focus:outline-none focus:border-[var(--accent)] disabled:opacity-50"
179
+ />
180
+ <button
181
+ onClick={sendMessage}
182
+ disabled={streaming || !input.trim()}
183
+ className="px-4 py-2 bg-[var(--accent)] text-white text-sm rounded hover:opacity-90 disabled:opacity-50"
184
+ >
185
+ Send
186
+ </button>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ );
191
+ }
@@ -0,0 +1,267 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useRef } from 'react';
4
+
5
+ interface ClaudeMessage {
6
+ type: 'system' | 'assistant' | 'result';
7
+ subtype?: string;
8
+ content: string;
9
+ tool?: string;
10
+ costUSD?: number;
11
+ sessionId?: string;
12
+ timestamp: string;
13
+ }
14
+
15
+ interface ClaudeProcess {
16
+ id: string;
17
+ projectName: string;
18
+ projectPath: string;
19
+ status: string;
20
+ conversationId?: string;
21
+ }
22
+
23
+ export default function ClaudeTerminal({
24
+ process: proc,
25
+ onKill,
26
+ }: {
27
+ process: ClaudeProcess;
28
+ onKill: (id: string) => void;
29
+ }) {
30
+ const [messages, setMessages] = useState<ClaudeMessage[]>([]);
31
+ const [input, setInput] = useState('');
32
+ const [isRunning, setIsRunning] = useState(false);
33
+ const [conversationId, setConversationId] = useState<string | undefined>(proc.conversationId);
34
+ const messagesEndRef = useRef<HTMLDivElement>(null);
35
+ const eventSourceRef = useRef<EventSource | null>(null);
36
+
37
+ // Connect SSE stream with auto-reconnect
38
+ useEffect(() => {
39
+ let cancelled = false;
40
+ let es: EventSource | null = null;
41
+ let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
42
+
43
+ function connect() {
44
+ if (cancelled) return;
45
+ es = new EventSource(`/api/claude/${proc.id}/stream`);
46
+ eventSourceRef.current = es;
47
+
48
+ es.onmessage = (event) => {
49
+ try {
50
+ const msg: ClaudeMessage = JSON.parse(event.data);
51
+ setMessages(prev => {
52
+ // Deduplicate by timestamp+content on reconnect
53
+ if (prev.some(m => m.timestamp === msg.timestamp && m.content === msg.content)) {
54
+ return prev;
55
+ }
56
+ return [...prev, msg];
57
+ });
58
+
59
+ if (msg.sessionId) {
60
+ setConversationId(msg.sessionId);
61
+ }
62
+
63
+ if (msg.subtype === 'complete') {
64
+ setIsRunning(false);
65
+ }
66
+ } catch {}
67
+ };
68
+
69
+ es.onerror = () => {
70
+ es?.close();
71
+ eventSourceRef.current = null;
72
+ // Auto-reconnect after 2s
73
+ if (!cancelled) {
74
+ reconnectTimer = setTimeout(connect, 2000);
75
+ }
76
+ };
77
+ }
78
+
79
+ connect();
80
+
81
+ return () => {
82
+ cancelled = true;
83
+ if (reconnectTimer) clearTimeout(reconnectTimer);
84
+ es?.close();
85
+ eventSourceRef.current = null;
86
+ };
87
+ }, [proc.id]);
88
+
89
+ // Auto-scroll
90
+ useEffect(() => {
91
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
92
+ }, [messages]);
93
+
94
+ const sendMessage = async () => {
95
+ const text = input.trim();
96
+ if (!text || isRunning) return;
97
+
98
+ setInput('');
99
+ setIsRunning(true);
100
+
101
+ await fetch(`/api/claude/${proc.id}`, {
102
+ method: 'POST',
103
+ headers: { 'Content-Type': 'application/json' },
104
+ body: JSON.stringify({ type: 'message', content: text, conversationId }),
105
+ });
106
+ };
107
+
108
+ const handleKeyDown = (e: React.KeyboardEvent) => {
109
+ if (e.key === 'Enter' && !e.shiftKey) {
110
+ e.preventDefault();
111
+ sendMessage();
112
+ }
113
+ };
114
+
115
+ return (
116
+ <div className="flex flex-col h-full">
117
+ {/* Header */}
118
+ <div className="h-8 border-b border-[var(--border)] flex items-center justify-between px-3 shrink-0">
119
+ <div className="flex items-center gap-2">
120
+ <span className={`text-[10px] ${proc.status === 'running' || isRunning ? 'text-[var(--green)]' : 'text-[var(--text-secondary)]'}`}>●</span>
121
+ <span className="text-xs font-semibold">Claude Code</span>
122
+ <span className="text-[10px] text-[var(--text-secondary)]">{proc.projectName}</span>
123
+ {isRunning && <span className="text-[10px] text-[var(--accent)] animate-pulse">thinking...</span>}
124
+ </div>
125
+ <button
126
+ onClick={() => onKill(proc.id)}
127
+ className="text-[10px] px-2 py-0.5 text-[var(--red)] hover:bg-[var(--red)] hover:text-white rounded transition-colors"
128
+ >
129
+ Kill
130
+ </button>
131
+ </div>
132
+
133
+ {/* Messages */}
134
+ <div className="flex-1 overflow-y-auto p-3 space-y-2 font-mono text-xs">
135
+ {messages.length === 0 && (
136
+ <div className="text-center text-[var(--text-secondary)] py-8">
137
+ <p>Send a message to start working with Claude Code</p>
138
+ <p className="text-[10px] mt-1">Working in: {proc.projectPath}</p>
139
+ </div>
140
+ )}
141
+
142
+ {messages.map((msg, i) => (
143
+ <MessageBubble key={i} msg={msg} />
144
+ ))}
145
+ <div ref={messagesEndRef} />
146
+ </div>
147
+
148
+ {/* Input */}
149
+ <div className="border-t border-[var(--border)] p-3">
150
+ <div className="flex gap-2">
151
+ <textarea
152
+ value={input}
153
+ onChange={e => setInput(e.target.value)}
154
+ onKeyDown={handleKeyDown}
155
+ placeholder={isRunning ? 'Waiting for response...' : 'Send a message to Claude Code...'}
156
+ disabled={isRunning}
157
+ rows={2}
158
+ className="flex-1 px-3 py-2 bg-[var(--bg-tertiary)] border border-[var(--border)] rounded text-xs text-[var(--text-primary)] resize-none focus:outline-none focus:border-[var(--accent)] disabled:opacity-50"
159
+ />
160
+ <button
161
+ onClick={sendMessage}
162
+ disabled={isRunning || !input.trim()}
163
+ className="px-4 py-2 bg-[var(--accent)] text-white rounded text-xs hover:opacity-90 disabled:opacity-50 self-end"
164
+ >
165
+ Send
166
+ </button>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ );
171
+ }
172
+
173
+ function MessageBubble({ msg }: { msg: ClaudeMessage }) {
174
+ // User input
175
+ if (msg.type === 'system' && msg.subtype === 'user_input') {
176
+ return (
177
+ <div className="flex justify-end">
178
+ <div className="max-w-[80%] px-3 py-2 bg-[var(--accent)] text-white rounded-lg rounded-br-sm">
179
+ <pre className="whitespace-pre-wrap break-words">{msg.content}</pre>
180
+ </div>
181
+ </div>
182
+ );
183
+ }
184
+
185
+ // System init — show model info subtly
186
+ if (msg.type === 'system' && msg.subtype === 'init') {
187
+ return (
188
+ <div className="text-center text-[10px] text-[var(--text-secondary)] py-1">
189
+ {msg.content}
190
+ </div>
191
+ );
192
+ }
193
+
194
+ // Error
195
+ if (msg.subtype === 'error') {
196
+ return (
197
+ <div className="px-3 py-2 bg-red-900/20 border border-red-800/30 rounded text-[var(--red)]">
198
+ <pre className="whitespace-pre-wrap break-words">{msg.content}</pre>
199
+ </div>
200
+ );
201
+ }
202
+
203
+ // Completion notice
204
+ if (msg.subtype === 'complete') {
205
+ return (
206
+ <div className="text-center text-[10px] text-[var(--text-secondary)] py-1">
207
+ {msg.content}
208
+ {msg.costUSD != null && ` · $${msg.costUSD.toFixed(4)}`}
209
+ </div>
210
+ );
211
+ }
212
+
213
+ // Tool use
214
+ if (msg.subtype === 'tool_use') {
215
+ return (
216
+ <div className="px-3 py-2 bg-[var(--bg-tertiary)] border border-[var(--border)] rounded">
217
+ <div className="flex items-center gap-2 mb-1">
218
+ <span className="text-[10px] px-1.5 py-0.5 bg-[var(--accent)]/20 text-[var(--accent)] rounded">
219
+ {msg.tool || 'tool'}
220
+ </span>
221
+ </div>
222
+ <pre className="whitespace-pre-wrap break-words text-[var(--text-secondary)] max-h-40 overflow-y-auto">
223
+ {formatToolContent(msg.content)}
224
+ </pre>
225
+ </div>
226
+ );
227
+ }
228
+
229
+ // Tool result
230
+ if (msg.subtype === 'tool_result') {
231
+ return (
232
+ <div className="px-3 py-2 bg-[var(--bg-tertiary)] border-l-2 border-[var(--accent)] rounded-r">
233
+ <pre className="whitespace-pre-wrap break-words text-[var(--text-secondary)] max-h-60 overflow-y-auto">
234
+ {formatToolContent(msg.content)}
235
+ </pre>
236
+ </div>
237
+ );
238
+ }
239
+
240
+ // Final result
241
+ if (msg.type === 'result') {
242
+ return (
243
+ <div className="px-3 py-2 bg-green-900/10 border border-green-800/20 rounded">
244
+ <pre className="whitespace-pre-wrap break-words">{msg.content}</pre>
245
+ {msg.costUSD != null && (
246
+ <div className="text-[10px] text-[var(--text-secondary)] mt-1">Cost: ${msg.costUSD.toFixed(4)}</div>
247
+ )}
248
+ </div>
249
+ );
250
+ }
251
+
252
+ // Regular assistant text
253
+ return (
254
+ <div className="px-3 py-2">
255
+ <pre className="whitespace-pre-wrap break-words text-[var(--text-primary)]">{msg.content}</pre>
256
+ </div>
257
+ );
258
+ }
259
+
260
+ function formatToolContent(content: string): string {
261
+ try {
262
+ const parsed = JSON.parse(content);
263
+ return JSON.stringify(parsed, null, 2);
264
+ } catch {
265
+ return content;
266
+ }
267
+ }