@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,99 @@
1
+ 'use client';
2
+
3
+ import type { Session } from '@/src/types';
4
+
5
+ const providerLabels: Record<string, string> = {
6
+ anthropic: 'Claude',
7
+ google: 'Gemini',
8
+ openai: 'OpenAI',
9
+ grok: 'Grok',
10
+ };
11
+
12
+ export default function StatusBar({
13
+ providers,
14
+ usage,
15
+ sessions,
16
+ }: {
17
+ providers: any[];
18
+ usage: any[];
19
+ sessions: Session[];
20
+ }) {
21
+ const running = sessions.filter(s => s.status === 'running').length;
22
+ const idle = sessions.filter(s => s.status === 'idle').length;
23
+ const errored = sessions.filter(s => s.status === 'error').length;
24
+
25
+ return (
26
+ <div className="flex flex-col p-3 space-y-4 text-xs overflow-y-auto">
27
+ {/* Overview */}
28
+ <div>
29
+ <h3 className="font-semibold text-[var(--text-secondary)] uppercase mb-2">Status</h3>
30
+ <div className="space-y-1">
31
+ <div className="flex justify-between">
32
+ <span className="text-[var(--green)]">Running</span>
33
+ <span>{running}</span>
34
+ </div>
35
+ <div className="flex justify-between">
36
+ <span className="text-[var(--accent)]">Idle</span>
37
+ <span>{idle}</span>
38
+ </div>
39
+ {errored > 0 && (
40
+ <div className="flex justify-between">
41
+ <span className="text-[var(--red)]">Error</span>
42
+ <span>{errored}</span>
43
+ </div>
44
+ )}
45
+ </div>
46
+ </div>
47
+
48
+ {/* Providers */}
49
+ <div>
50
+ <h3 className="font-semibold text-[var(--text-secondary)] uppercase mb-2">Providers</h3>
51
+ <div className="space-y-2">
52
+ {providers.filter(p => p.enabled).map(p => {
53
+ const u = usage.find((u: any) => u.provider === p.name);
54
+ const totalTokens = u ? u.totalInput + u.totalOutput : 0;
55
+
56
+ return (
57
+ <div key={p.name} className="space-y-0.5">
58
+ <div className="flex items-center justify-between">
59
+ <div className="flex items-center gap-1.5">
60
+ <span className={`text-[10px] ${p.hasKey ? 'text-[var(--green)]' : 'text-[var(--yellow)]'}`}>
61
+ {p.hasKey ? '●' : '○'}
62
+ </span>
63
+ <span>{p.displayName}</span>
64
+ </div>
65
+ <span className="text-[var(--text-secondary)]">
66
+ {totalTokens > 0 ? `${(totalTokens / 1000).toFixed(1)}k` : '—'}
67
+ </span>
68
+ </div>
69
+ {totalTokens > 0 && (
70
+ <div className="ml-4 h-1 bg-[var(--bg-primary)] rounded overflow-hidden">
71
+ <div
72
+ className="h-full bg-[var(--accent)] rounded"
73
+ style={{ width: `${Math.min((totalTokens / 100000) * 100, 100)}%` }}
74
+ />
75
+ </div>
76
+ )}
77
+ </div>
78
+ );
79
+ })}
80
+ </div>
81
+ </div>
82
+
83
+ {/* Quick info */}
84
+ <div>
85
+ <h3 className="font-semibold text-[var(--text-secondary)] uppercase mb-2">Info</h3>
86
+ <div className="space-y-1 text-[var(--text-secondary)]">
87
+ <div className="flex justify-between">
88
+ <span>Sessions</span>
89
+ <span>{sessions.length}</span>
90
+ </div>
91
+ <div className="flex justify-between">
92
+ <span>Total msgs</span>
93
+ <span>{sessions.reduce((a, s) => a + s.messageCount, 0)}</span>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ );
99
+ }
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+
5
+ export interface Tab {
6
+ id: number;
7
+ label: string;
8
+ }
9
+
10
+ export interface TabBarProps {
11
+ tabs: Tab[];
12
+ activeId: number;
13
+ onActivate: (id: number) => void;
14
+ onClose: (id: number) => void;
15
+ }
16
+
17
+ export default function TabBar({ tabs, activeId, onActivate, onClose }: TabBarProps) {
18
+ if (tabs.length === 0) return null;
19
+
20
+ return (
21
+ <div className="flex items-center border-b border-[var(--border)] bg-[var(--bg-tertiary)] overflow-x-auto shrink-0 min-w-0 max-w-full">
22
+ {tabs.map(tab => (
23
+ <div
24
+ key={tab.id}
25
+ className={`flex items-center gap-1 px-3 py-1.5 text-[11px] cursor-pointer border-r border-[var(--border)]/30 shrink-0 group ${
26
+ tab.id === activeId
27
+ ? 'bg-[var(--bg-primary)] text-[var(--text-primary)] border-b-2 border-b-[var(--accent)]'
28
+ : 'text-[var(--text-secondary)] hover:bg-[var(--bg-secondary)] hover:text-[var(--text-primary)]'
29
+ }`}
30
+ onClick={() => onActivate(tab.id)}
31
+ >
32
+ <span className="truncate max-w-[120px]">{tab.label}</span>
33
+ <button
34
+ onClick={(e) => {
35
+ e.stopPropagation();
36
+ onClose(tab.id);
37
+ }}
38
+ className="text-[9px] w-4 h-4 flex items-center justify-center rounded hover:bg-[var(--bg-tertiary)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] opacity-0 group-hover:opacity-100 shrink-0"
39
+ >
40
+ x
41
+ </button>
42
+ </div>
43
+ ))}
44
+ </div>
45
+ );
46
+ }
@@ -0,0 +1,113 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import type { Task, TaskStatus } from '@/src/types';
5
+
6
+ const STATUS_COLORS: Record<TaskStatus, string> = {
7
+ queued: 'text-yellow-500',
8
+ running: 'text-[var(--green)]',
9
+ done: 'text-blue-400',
10
+ failed: 'text-[var(--red)]',
11
+ cancelled: 'text-[var(--text-secondary)]',
12
+ };
13
+
14
+ const STATUS_LABELS: Record<TaskStatus, string> = {
15
+ queued: 'queued',
16
+ running: 'running',
17
+ done: 'done',
18
+ failed: 'failed',
19
+ cancelled: 'cancelled',
20
+ };
21
+
22
+ export default function TaskBoard({
23
+ tasks,
24
+ activeId,
25
+ onSelect,
26
+ onRefresh,
27
+ }: {
28
+ tasks: Task[];
29
+ activeId: string | null;
30
+ onSelect: (id: string) => void;
31
+ onRefresh: () => void;
32
+ }) {
33
+ const [filter, setFilter] = useState<TaskStatus | 'all'>('all');
34
+
35
+ const filtered = filter === 'all' ? tasks : tasks.filter(t => t.status === filter);
36
+
37
+ return (
38
+ <div className="flex flex-col h-full">
39
+ {/* Filter tabs */}
40
+ <div className="p-2 border-b border-[var(--border)] flex gap-1 flex-wrap">
41
+ {(['all', 'running', 'queued', 'done', 'failed'] as const).map(f => (
42
+ <button
43
+ key={f}
44
+ onClick={() => setFilter(f)}
45
+ className={`text-[10px] px-2 py-0.5 rounded transition-colors ${
46
+ filter === f ? 'bg-[var(--accent)] text-white' : 'text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)]'
47
+ }`}
48
+ >
49
+ {f} {f !== 'all' ? `(${tasks.filter(t => t.status === f).length})` : `(${tasks.length})`}
50
+ </button>
51
+ ))}
52
+ </div>
53
+
54
+ {/* Task list */}
55
+ <div className="flex-1 overflow-y-auto">
56
+ {filtered.map(task => (
57
+ <button
58
+ key={task.id}
59
+ onClick={() => onSelect(task.id)}
60
+ className={`w-full text-left px-3 py-2 border-b border-[var(--border)] hover:bg-[var(--bg-tertiary)] transition-colors ${
61
+ activeId === task.id ? 'bg-[var(--bg-tertiary)]' : ''
62
+ }`}
63
+ >
64
+ <div className="flex items-center gap-2 mb-0.5">
65
+ <span className={`text-[10px] ${STATUS_COLORS[task.status]}`}>●</span>
66
+ <span className="text-xs font-medium truncate">{task.projectName}</span>
67
+ {(task as any).agent && (task as any).agent !== 'claude' && (
68
+ <span className="text-[8px] px-1 rounded bg-green-900/30 text-green-400">{(task as any).agent}</span>
69
+ )}
70
+ <span className={`text-[9px] ml-auto ${STATUS_COLORS[task.status]}`}>
71
+ {task.scheduledAt && task.status === 'queued'
72
+ ? `⏰ ${new Date(task.scheduledAt).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })}`
73
+ : STATUS_LABELS[task.status]}
74
+ </span>
75
+ </div>
76
+ <p className="text-[11px] text-[var(--text-secondary)] truncate pl-4">
77
+ {task.prompt.slice(0, 80)}{task.prompt.length > 80 ? '...' : ''}
78
+ </p>
79
+ <div className="flex items-center gap-2 pl-4 mt-0.5">
80
+ <span className="text-[9px] text-[var(--text-secondary)]">
81
+ {timeAgo(task.createdAt)}
82
+ </span>
83
+ {task.costUSD != null && (
84
+ <span className="text-[9px] text-[var(--text-secondary)]">
85
+ ${task.costUSD.toFixed(3)}
86
+ </span>
87
+ )}
88
+ </div>
89
+ </button>
90
+ ))}
91
+ {filtered.length === 0 && (
92
+ <div className="p-4 text-center text-xs text-[var(--text-secondary)]">
93
+ No tasks
94
+ </div>
95
+ )}
96
+ </div>
97
+
98
+ <div className="p-2 border-t border-[var(--border)] text-[10px] text-[var(--text-secondary)] text-center">
99
+ {tasks.length} tasks total
100
+ </div>
101
+ </div>
102
+ );
103
+ }
104
+
105
+ function timeAgo(dateStr: string): string {
106
+ const now = Date.now();
107
+ const then = new Date(dateStr).getTime();
108
+ const diff = now - then;
109
+ if (diff < 60000) return 'just now';
110
+ if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
111
+ if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
112
+ return `${Math.floor(diff / 86400000)}d ago`;
113
+ }
@@ -0,0 +1,372 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useRef } from 'react';
4
+ import MarkdownContent from './MarkdownContent';
5
+ import NewTaskModal from './NewTaskModal';
6
+ import type { Task, TaskLogEntry } from '@/src/types';
7
+
8
+ export default function TaskDetail({
9
+ task,
10
+ onRefresh,
11
+ onFollowUp,
12
+ }: {
13
+ task: Task;
14
+ onRefresh: () => void;
15
+ onFollowUp?: (data: { projectName: string; prompt: string; conversationId: string }) => void;
16
+ }) {
17
+ const [liveLog, setLiveLog] = useState<TaskLogEntry[]>(task.log);
18
+ const [liveStatus, setLiveStatus] = useState(task.status);
19
+ const [tab, setTab] = useState<'log' | 'diff' | 'result'>('log');
20
+ const [expandedTools, setExpandedTools] = useState<Set<number>>(new Set());
21
+ const [followUpText, setFollowUpText] = useState('');
22
+ const [editing, setEditing] = useState(false);
23
+ const logEndRef = useRef<HTMLDivElement>(null);
24
+
25
+ // SSE stream for running tasks
26
+ useEffect(() => {
27
+ if (task.status !== 'running' && task.status !== 'queued') {
28
+ setLiveLog(task.log);
29
+ setLiveStatus(task.status);
30
+ return;
31
+ }
32
+
33
+ setLiveLog([]);
34
+ const es = new EventSource(`/api/tasks/${task.id}/stream`);
35
+
36
+ es.onmessage = (event) => {
37
+ try {
38
+ const data = JSON.parse(event.data);
39
+ if (data.type === 'log') {
40
+ setLiveLog(prev => [...prev, data.entry]);
41
+ } else if (data.type === 'status') {
42
+ setLiveStatus(data.status);
43
+ if (data.status === 'done' || data.status === 'failed' || data.status === 'cancelled') {
44
+ onRefresh();
45
+ }
46
+ } else if (data.type === 'complete' && data.task) {
47
+ setLiveLog(data.task.log);
48
+ setLiveStatus(data.task.status);
49
+ }
50
+ } catch {}
51
+ };
52
+
53
+ es.onerror = () => {
54
+ es.close();
55
+ onRefresh();
56
+ };
57
+
58
+ return () => es.close();
59
+ }, [task.id, task.status, onRefresh]);
60
+
61
+ useEffect(() => {
62
+ logEndRef.current?.scrollIntoView({ behavior: 'smooth' });
63
+ }, [liveLog]);
64
+
65
+ const handleAction = async (action: string) => {
66
+ await fetch(`/api/tasks/${task.id}`, {
67
+ method: action === 'delete' ? 'DELETE' : 'POST',
68
+ headers: { 'Content-Type': 'application/json' },
69
+ body: action !== 'delete' ? JSON.stringify({ action }) : undefined,
70
+ });
71
+ onRefresh();
72
+ };
73
+
74
+ const toggleTool = (i: number) => {
75
+ setExpandedTools(prev => {
76
+ const next = new Set(prev);
77
+ next.has(i) ? next.delete(i) : next.add(i);
78
+ return next;
79
+ });
80
+ };
81
+
82
+ const displayLog = liveLog.length > 0 ? liveLog : task.log;
83
+
84
+ return (
85
+ <div className="flex flex-col h-full">
86
+ {/* Header */}
87
+ <div className="border-b border-[var(--border)] px-4 py-2 shrink-0">
88
+ <div className="flex items-center justify-between mb-1">
89
+ <div className="flex items-center gap-2">
90
+ <StatusBadge status={liveStatus} />
91
+ <span className="text-sm font-semibold">{task.projectName}</span>
92
+ <span className="text-[10px] text-[var(--text-secondary)] font-mono">{task.id}</span>
93
+ </div>
94
+ <div className="flex items-center gap-2">
95
+ <button onClick={() => setEditing(true)} className="text-[10px] px-2 py-0.5 text-[var(--accent)] border border-[var(--accent)]/30 rounded hover:bg-[var(--accent)] hover:text-white">
96
+ Edit
97
+ </button>
98
+ {(liveStatus === 'running' || liveStatus === 'queued') && (
99
+ <button onClick={() => handleAction('cancel')} className="text-[10px] px-2 py-0.5 text-[var(--red)] border border-[var(--red)]/30 rounded hover:bg-[var(--red)] hover:text-white">
100
+ Cancel
101
+ </button>
102
+ )}
103
+ {(liveStatus === 'failed' || liveStatus === 'cancelled') && (
104
+ <button onClick={() => handleAction('retry')} className="text-[10px] px-2 py-0.5 text-[var(--accent)] border border-[var(--accent)]/30 rounded hover:bg-[var(--accent)] hover:text-white">
105
+ Retry
106
+ </button>
107
+ )}
108
+ <button onClick={() => handleAction('delete')} className="text-[10px] px-2 py-0.5 text-[var(--text-secondary)] hover:text-[var(--red)]">
109
+ Delete
110
+ </button>
111
+ </div>
112
+ </div>
113
+ <p className="text-xs text-[var(--text-secondary)] mb-2">{task.prompt}</p>
114
+ <div className="flex items-center gap-3 text-[10px] text-[var(--text-secondary)]">
115
+ <span>Created: {new Date(task.createdAt).toLocaleString()}</span>
116
+ {task.startedAt && <span>Started: {new Date(task.startedAt).toLocaleString()}</span>}
117
+ {task.completedAt && <span>Completed: {new Date(task.completedAt).toLocaleString()}</span>}
118
+ {task.costUSD != null && <span>Cost: ${task.costUSD.toFixed(4)}</span>}
119
+ </div>
120
+ </div>
121
+
122
+ {/* Tabs */}
123
+ <div className="border-b border-[var(--border)] px-4 flex gap-0.5 shrink-0">
124
+ {(['log', 'result', 'diff'] as const).map(t => (
125
+ <button
126
+ key={t}
127
+ onClick={() => setTab(t)}
128
+ className={`text-[11px] px-3 py-1.5 border-b-2 transition-colors ${
129
+ tab === t ? 'border-[var(--accent)] text-[var(--accent)]' : 'border-transparent text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
130
+ }`}
131
+ >
132
+ {t === 'log' ? `Log (${displayLog.length})` : t === 'diff' ? 'Git Diff' : 'Result'}
133
+ </button>
134
+ ))}
135
+ </div>
136
+
137
+ {/* Content */}
138
+ <div className="flex-1 overflow-y-auto p-4 text-sm">
139
+ {tab === 'log' && (
140
+ <div className="space-y-2">
141
+ {displayLog.map((entry, i) => (
142
+ <LogEntry key={i} entry={entry} index={i} expanded={expandedTools.has(i)} onToggle={() => toggleTool(i)} />
143
+ ))}
144
+ {liveStatus === 'running' && (
145
+ <div className="text-[var(--accent)] animate-pulse py-1 text-xs">working...</div>
146
+ )}
147
+ <div ref={logEndRef} />
148
+ </div>
149
+ )}
150
+
151
+ {tab === 'result' && (
152
+ <div className="prose-container">
153
+ {task.resultSummary ? (
154
+ <MarkdownContent content={task.resultSummary} />
155
+ ) : task.error ? (
156
+ <div className="p-3 bg-red-900/10 border border-red-800/20 rounded">
157
+ <pre className="whitespace-pre-wrap break-words text-[var(--red)] text-xs font-mono">{task.error}</pre>
158
+ </div>
159
+ ) : (
160
+ <p className="text-[var(--text-secondary)] text-xs">
161
+ {liveStatus === 'running' || liveStatus === 'queued' ? 'Task is still running...' : 'No result'}
162
+ </p>
163
+ )}
164
+ </div>
165
+ )}
166
+
167
+ {tab === 'diff' && (
168
+ <div>
169
+ {task.gitDiff ? (
170
+ <div className="bg-[var(--bg-tertiary)] rounded border border-[var(--border)] overflow-hidden">
171
+ <pre className="p-3 text-xs font-mono overflow-x-auto">
172
+ {task.gitDiff.split('\n').map((line, i) => (
173
+ <div key={i} className={`px-2 ${
174
+ line.startsWith('+++') || line.startsWith('---') ? 'text-[var(--text-secondary)] font-semibold' :
175
+ line.startsWith('+') ? 'text-[var(--green)] bg-green-500/5' :
176
+ line.startsWith('-') ? 'text-[var(--red)] bg-red-500/5' :
177
+ line.startsWith('@@') ? 'text-[var(--accent)] bg-[var(--accent)]/5 font-semibold' :
178
+ line.startsWith('diff ') ? 'text-[var(--text-primary)] font-bold border-t border-[var(--border)] pt-2 mt-2' :
179
+ 'text-[var(--text-secondary)]'
180
+ }`}>
181
+ {line}
182
+ </div>
183
+ ))}
184
+ </pre>
185
+ </div>
186
+ ) : (
187
+ <p className="text-[var(--text-secondary)] text-xs">
188
+ {liveStatus === 'running' ? 'Diff will be captured after completion' : 'No changes detected'}
189
+ </p>
190
+ )}
191
+ </div>
192
+ )}
193
+ </div>
194
+
195
+ {/* Follow-up input for completed tasks */}
196
+ {liveStatus === 'done' && task.conversationId && onFollowUp && (
197
+ <div className="border-t border-[var(--border)] px-4 py-2 shrink-0">
198
+ <form
199
+ onSubmit={(e) => {
200
+ e.preventDefault();
201
+ if (!followUpText.trim()) return;
202
+ onFollowUp({
203
+ projectName: task.projectName,
204
+ prompt: followUpText.trim(),
205
+ conversationId: task.conversationId!,
206
+ });
207
+ setFollowUpText('');
208
+ }}
209
+ className="flex gap-2"
210
+ >
211
+ <input
212
+ type="text"
213
+ value={followUpText}
214
+ onChange={e => setFollowUpText(e.target.value)}
215
+ placeholder="Send follow-up message (continues this session)..."
216
+ className="flex-1 px-3 py-1.5 bg-[var(--bg-tertiary)] border border-[var(--border)] rounded text-xs text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)]"
217
+ />
218
+ <button
219
+ type="submit"
220
+ disabled={!followUpText.trim()}
221
+ className="text-xs px-3 py-1.5 bg-[var(--accent)] text-white rounded hover:opacity-90 disabled:opacity-50"
222
+ >
223
+ Send
224
+ </button>
225
+ </form>
226
+ <p className="text-[9px] text-[var(--text-secondary)] mt-1">
227
+ Session <span className="font-mono">{task.conversationId.slice(0, 12)}...</span> — creates a new task continuing this conversation
228
+ </p>
229
+ </div>
230
+ )}
231
+
232
+ {editing && (
233
+ <NewTaskModal
234
+ editTask={{ id: task.id, projectName: task.projectName, prompt: task.prompt, priority: task.priority, mode: task.mode, scheduledAt: task.scheduledAt }}
235
+ onClose={() => setEditing(false)}
236
+ onCreate={async (data) => {
237
+ await fetch(`/api/tasks/${task.id}`, {
238
+ method: 'PATCH',
239
+ headers: { 'Content-Type': 'application/json' },
240
+ body: JSON.stringify({ ...data, restart: true }),
241
+ });
242
+ setEditing(false);
243
+ onRefresh();
244
+ }}
245
+ />
246
+ )}
247
+ </div>
248
+ );
249
+ }
250
+
251
+ function StatusBadge({ status }: { status: string }) {
252
+ const colors: Record<string, string> = {
253
+ queued: 'bg-yellow-500/20 text-yellow-500',
254
+ running: 'bg-green-500/20 text-[var(--green)]',
255
+ done: 'bg-blue-500/20 text-blue-400',
256
+ failed: 'bg-red-500/20 text-[var(--red)]',
257
+ cancelled: 'bg-gray-500/20 text-[var(--text-secondary)]',
258
+ };
259
+ return (
260
+ <span className={`text-[10px] px-1.5 py-0.5 rounded font-medium ${colors[status] || ''}`}>
261
+ {status}
262
+ </span>
263
+ );
264
+ }
265
+
266
+ function LogEntry({ entry, index, expanded, onToggle }: {
267
+ entry: TaskLogEntry;
268
+ index: number;
269
+ expanded: boolean;
270
+ onToggle: () => void;
271
+ }) {
272
+ // System init
273
+ if (entry.type === 'system' && entry.subtype === 'init') {
274
+ return (
275
+ <div className="text-[10px] text-[var(--text-secondary)] py-0.5 flex items-center gap-1">
276
+ <span className="opacity-50">⚙</span> {entry.content}
277
+ </div>
278
+ );
279
+ }
280
+
281
+ // Error
282
+ if (entry.subtype === 'error') {
283
+ return (
284
+ <div className="p-2 bg-red-900/10 border border-red-800/20 rounded text-xs">
285
+ <pre className="whitespace-pre-wrap break-words text-[var(--red)] font-mono">{entry.content}</pre>
286
+ </div>
287
+ );
288
+ }
289
+
290
+ // Tool use — collapsible
291
+ if (entry.subtype === 'tool_use') {
292
+ const toolContent = formatToolContent(entry.content);
293
+ const isLong = toolContent.length > 80;
294
+
295
+ return (
296
+ <div className="border border-[var(--border)] rounded overflow-hidden">
297
+ <button
298
+ onClick={onToggle}
299
+ className="w-full flex items-center gap-2 px-2 py-1.5 bg-[var(--bg-tertiary)] hover:bg-[var(--border)]/30 transition-colors text-left"
300
+ >
301
+ <span className="text-[10px] px-1.5 py-0.5 bg-[var(--accent)]/15 text-[var(--accent)] rounded font-medium font-mono">
302
+ {entry.tool || 'tool'}
303
+ </span>
304
+ <span className="text-[11px] text-[var(--text-secondary)] truncate flex-1 font-mono">
305
+ {isLong && !expanded ? toolContent.slice(0, 80) + '...' : (!isLong ? toolContent : '')}
306
+ </span>
307
+ {isLong && (
308
+ <span className="text-[9px] text-[var(--text-secondary)] shrink-0">{expanded ? '▲' : '▼'}</span>
309
+ )}
310
+ </button>
311
+ {(expanded || !isLong) && isLong && (
312
+ <pre className="px-3 py-2 text-[11px] text-[var(--text-secondary)] font-mono whitespace-pre-wrap break-words max-h-60 overflow-y-auto border-t border-[var(--border)]">
313
+ {toolContent}
314
+ </pre>
315
+ )}
316
+ </div>
317
+ );
318
+ }
319
+
320
+ // Tool result — collapsible with border accent
321
+ if (entry.subtype === 'tool_result') {
322
+ const content = formatToolContent(entry.content);
323
+ const isLong = content.length > 150;
324
+
325
+ return (
326
+ <div className="ml-4 border-l-2 border-[var(--accent)]/30 pl-3">
327
+ <pre className={`text-[11px] text-[var(--text-secondary)] font-mono whitespace-pre-wrap break-words ${isLong && !expanded ? 'max-h-16 overflow-hidden' : 'max-h-80 overflow-y-auto'}`}>
328
+ {content}
329
+ </pre>
330
+ {isLong && !expanded && (
331
+ <button onClick={onToggle} className="text-[9px] text-[var(--accent)] hover:underline mt-0.5">
332
+ show more
333
+ </button>
334
+ )}
335
+ </div>
336
+ );
337
+ }
338
+
339
+ // Final result
340
+ if (entry.type === 'result') {
341
+ return (
342
+ <div className="p-3 bg-green-900/5 border border-green-800/15 rounded">
343
+ <MarkdownContent content={entry.content} />
344
+ </div>
345
+ );
346
+ }
347
+
348
+ // Assistant text — render as markdown
349
+ return (
350
+ <div className="py-1">
351
+ <MarkdownContent content={entry.content} />
352
+ </div>
353
+ );
354
+ }
355
+
356
+ // MarkdownContent is now imported from ./MarkdownContent
357
+
358
+ function formatToolContent(content: string): string {
359
+ try {
360
+ const parsed = JSON.parse(content);
361
+ if (typeof parsed === 'object') {
362
+ // For common tool patterns, show a cleaner format
363
+ if (parsed.command) return `$ ${parsed.command}`;
364
+ if (parsed.file_path) return parsed.file_path;
365
+ if (parsed.pattern) return `/${parsed.pattern}/`;
366
+ return JSON.stringify(parsed, null, 2);
367
+ }
368
+ return content;
369
+ } catch {
370
+ return content;
371
+ }
372
+ }