@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,73 @@
1
+ 'use client';
2
+
3
+ import Markdown from 'react-markdown';
4
+ import remarkGfm from 'remark-gfm';
5
+
6
+ export default function MarkdownContent({ content }: { content: string }) {
7
+ return (
8
+ <Markdown
9
+ remarkPlugins={[remarkGfm]}
10
+ components={{
11
+ h1: ({ children }) => <h1 className="text-base font-bold text-[var(--text-primary)] mt-3 mb-1">{children}</h1>,
12
+ h2: ({ children }) => <h2 className="text-sm font-bold text-[var(--text-primary)] mt-3 mb-1">{children}</h2>,
13
+ h3: ({ children }) => <h3 className="text-xs font-bold text-[var(--text-primary)] mt-2 mb-1">{children}</h3>,
14
+ p: ({ children }) => <p className="text-xs text-[var(--text-primary)] mb-1.5 leading-relaxed">{children}</p>,
15
+ ul: ({ children }) => <ul className="text-xs text-[var(--text-primary)] mb-1.5 ml-4 list-disc space-y-0.5">{children}</ul>,
16
+ ol: ({ children }) => <ol className="text-xs text-[var(--text-primary)] mb-1.5 ml-4 list-decimal space-y-0.5">{children}</ol>,
17
+ li: ({ children }) => <li className="leading-relaxed">{children}</li>,
18
+ strong: ({ children }) => <strong className="font-semibold text-[var(--text-primary)]">{children}</strong>,
19
+ em: ({ children }) => <em className="italic text-[var(--text-secondary)]">{children}</em>,
20
+ a: ({ href, children }) => <a href={href} className="text-[var(--accent)] hover:underline" target="_blank" rel="noopener">{children}</a>,
21
+ blockquote: ({ children }) => <blockquote className="border-l-2 border-[var(--accent)]/40 pl-3 my-1.5 text-[var(--text-secondary)] text-xs italic">{children}</blockquote>,
22
+ code: ({ className, children, node, ...props }) => {
23
+ // Block code: has language class OR parent is <pre> (checked via node)
24
+ const isBlock = !!className?.includes('language-');
25
+
26
+ if (isBlock) {
27
+ const lang = className?.replace('language-', '') || '';
28
+ return (
29
+ <div className="my-2 rounded border border-[var(--border)] overflow-hidden max-w-full">
30
+ {lang && (
31
+ <div className="px-3 py-1 bg-[var(--bg-tertiary)] border-b border-[var(--border)] text-[9px] text-[var(--text-secondary)] font-mono">
32
+ {lang}
33
+ </div>
34
+ )}
35
+ <pre className="p-3 bg-[var(--bg-tertiary)] overflow-x-auto max-w-full">
36
+ <code className="text-[12px] font-mono text-[var(--text-primary)] whitespace-pre leading-[1.4]" style={{ fontFamily: 'Menlo, Monaco, "Courier New", monospace' }}>{children}</code>
37
+ </pre>
38
+ </div>
39
+ );
40
+ }
41
+ return (
42
+ <code className="text-[11px] font-mono bg-[var(--bg-tertiary)] text-[var(--accent)] px-1 py-0.5 rounded">
43
+ {children}
44
+ </code>
45
+ );
46
+ },
47
+ pre: ({ children, ...props }) => {
48
+ // If code child already rendered as block (has language-), just pass through
49
+ // Otherwise wrap plain code blocks (no language) with proper styling
50
+ const child = (children as any)?.props;
51
+ if (child?.className?.includes('language-')) return <>{children}</>;
52
+ return (
53
+ <div className="my-2 rounded border border-[var(--border)] overflow-hidden max-w-full">
54
+ <pre className="p-3 bg-[var(--bg-tertiary)] overflow-x-auto max-w-full">
55
+ <code className="text-[12px] font-mono text-[var(--text-primary)] whitespace-pre leading-[1.4]" style={{ fontFamily: 'Menlo, Monaco, "Courier New", monospace' }}>{child?.children || children}</code>
56
+ </pre>
57
+ </div>
58
+ );
59
+ },
60
+ hr: () => <hr className="my-3 border-[var(--border)]" />,
61
+ table: ({ children }) => (
62
+ <div className="my-3 overflow-x-auto">
63
+ <table className="text-xs border-collapse">{children}</table>
64
+ </div>
65
+ ),
66
+ th: ({ children }) => <th className="border border-[var(--border)] px-3 py-1.5 bg-[var(--bg-tertiary)] text-left font-semibold text-[11px] whitespace-nowrap">{children}</th>,
67
+ td: ({ children }) => <td className="border border-[var(--border)] px-3 py-1.5 text-[11px]">{children}</td>,
68
+ }}
69
+ >
70
+ {content}
71
+ </Markdown>
72
+ );
73
+ }
@@ -0,0 +1,385 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useRef, useCallback } from 'react';
4
+
5
+ interface Project { name: string; path: string }
6
+ interface SessionInfo { sessionId: string; summary?: string; firstPrompt?: string; modified?: string }
7
+ interface ChatMessage { role: 'user' | 'assistant' | 'system'; content: string; timestamp: string }
8
+
9
+ export default function MobileView() {
10
+ const [projects, setProjects] = useState<Project[]>([]);
11
+ const [selectedProject, setSelectedProject] = useState<Project | null>(null);
12
+ const [sessions, setSessions] = useState<SessionInfo[]>([]);
13
+ const [showSessions, setShowSessions] = useState(false);
14
+ const [messages, setMessages] = useState<ChatMessage[]>([]);
15
+ const [input, setInput] = useState('');
16
+ const [loading, setLoading] = useState(false);
17
+ const [tunnelUrl, setTunnelUrl] = useState<string | null>(null);
18
+ const [debug, setDebug] = useState<string[]>([]);
19
+ const [debugLevel, setDebugLevel] = useState<'off' | 'simple' | 'verbose'>('off');
20
+ const debugLevelRef = useRef<'off' | 'simple' | 'verbose'>('off');
21
+ const [hasSession, setHasSession] = useState(false);
22
+ const [availableAgents, setAvailableAgents] = useState<{ id: string; name: string }[]>([]);
23
+ const [selectedAgent, setSelectedAgent] = useState('claude');
24
+ const scrollRef = useRef<HTMLDivElement>(null);
25
+ const inputRef = useRef<HTMLInputElement>(null);
26
+ const abortRef = useRef<AbortController | null>(null);
27
+
28
+ // Fetch projects
29
+ useEffect(() => {
30
+ fetch('/api/projects').then(r => r.json())
31
+ .then(data => { if (Array.isArray(data)) setProjects(data); })
32
+ .catch(() => {});
33
+ fetch('/api/tunnel').then(r => r.json())
34
+ .then(data => { setTunnelUrl(data.url || null); })
35
+ .catch(() => {});
36
+ fetch('/api/agents').then(r => r.json())
37
+ .then(data => {
38
+ const agents = (data.agents || []).filter((a: any) => a.enabled && a.detected !== false);
39
+ setAvailableAgents(agents);
40
+ setSelectedAgent(data.defaultAgent || 'claude');
41
+ }).catch(() => {});
42
+ }, []);
43
+
44
+ // Auto-scroll
45
+ useEffect(() => {
46
+ if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
47
+ }, [messages]);
48
+
49
+ // Fetch sessions for project
50
+ const fetchSessions = useCallback(async (projectName: string) => {
51
+ try {
52
+ const res = await fetch(`/api/claude-sessions/${encodeURIComponent(projectName)}`);
53
+ const data = await res.json();
54
+ const list = Array.isArray(data) ? data : [];
55
+ setSessions(list);
56
+ setHasSession(list.length > 0);
57
+ return list;
58
+ } catch { setSessions([]); return []; }
59
+ }, []);
60
+
61
+ // Load session history
62
+ const loadHistory = useCallback(async (projectName: string, sessionId: string) => {
63
+ try {
64
+ const res = await fetch(`/api/claude-sessions/${encodeURIComponent(projectName)}/entries?sessionId=${encodeURIComponent(sessionId)}`);
65
+ const data = await res.json();
66
+ const entries = data.entries || [];
67
+ // Convert entries to chat messages (only user + assistant_text)
68
+ const chatMessages: ChatMessage[] = [];
69
+ for (const e of entries) {
70
+ if (e.type === 'user') {
71
+ chatMessages.push({ role: 'user', content: e.content, timestamp: e.timestamp || '' });
72
+ } else if (e.type === 'assistant_text') {
73
+ chatMessages.push({ role: 'assistant', content: e.content, timestamp: e.timestamp || '' });
74
+ }
75
+ }
76
+ setMessages(chatMessages);
77
+ } catch {}
78
+ }, []);
79
+
80
+ // Select project
81
+ const selectProject = useCallback(async (project: Project) => {
82
+ setSelectedProject(project);
83
+ setShowSessions(false);
84
+ setMessages([]);
85
+
86
+ const sessionList = await fetchSessions(project.name);
87
+ // Load last session history if exists
88
+ if (sessionList.length > 0) {
89
+ await loadHistory(project.name, sessionList[0].sessionId);
90
+ }
91
+ }, [fetchSessions, loadHistory]);
92
+
93
+ // View specific session
94
+ const viewSession = useCallback(async (sessionId: string) => {
95
+ if (!selectedProject) return;
96
+ setShowSessions(false);
97
+ setMessages([]);
98
+ await loadHistory(selectedProject.name, sessionId);
99
+ }, [selectedProject, loadHistory]);
100
+
101
+ // Send message
102
+ const sendMessage = async () => {
103
+ const text = input.trim();
104
+ if (!text || !selectedProject || loading) return;
105
+
106
+ // Add user message
107
+ setMessages(prev => [...prev, { role: 'user', content: text, timestamp: new Date().toISOString() }]);
108
+ setInput('');
109
+ setLoading(true);
110
+ setDebug(d => [...d.slice(-20), `Send: "${text.slice(0, 40)}"`]);
111
+ inputRef.current?.focus();
112
+
113
+ // Stream response from API
114
+ const abort = new AbortController();
115
+ abortRef.current = abort;
116
+ let assistantText = '';
117
+ const startTime = Date.now();
118
+
119
+ try {
120
+ const res = await fetch('/api/mobile-chat', {
121
+ method: 'POST',
122
+ headers: { 'Content-Type': 'application/json' },
123
+ body: JSON.stringify({
124
+ message: text,
125
+ projectPath: selectedProject.path,
126
+ resume: false,
127
+ agent: selectedAgent,
128
+ }),
129
+ signal: abort.signal,
130
+ });
131
+
132
+ const reader = res.body?.getReader();
133
+ if (!reader) throw new Error('No reader');
134
+
135
+ const decoder = new TextDecoder();
136
+
137
+ // Add empty assistant message to fill in
138
+ setMessages(prev => [...prev, { role: 'assistant', content: '...', timestamp: new Date().toISOString() }]);
139
+
140
+ while (true) {
141
+ const { value, done } = await reader.read();
142
+ if (done) break;
143
+
144
+ const chunk = decoder.decode(value, { stream: true });
145
+ for (const line of chunk.split('\n')) {
146
+ if (!line.startsWith('data: ')) continue;
147
+ try {
148
+ const data = JSON.parse(line.slice(6));
149
+ if (data.type === 'chunk') {
150
+ assistantText += data.text;
151
+ if (debugLevelRef.current === 'verbose') {
152
+ // Show content preview in verbose mode
153
+ const preview = data.text.replace(/\n/g, '↵').slice(0, 80);
154
+ setDebug(d => [...d.slice(-50), `chunk: ${preview}`]);
155
+ }
156
+ } else if (data.type === 'stderr') {
157
+ if (debugLevelRef.current !== 'off') {
158
+ setDebug(d => [...d.slice(-50), `stderr: ${data.text.trim().slice(0, 100)}`]);
159
+ }
160
+ } else if (data.type === 'error') {
161
+ assistantText = `Error: ${data.message}`;
162
+ setDebug(d => [...d.slice(-50), `ERROR: ${data.message}`]);
163
+ } else if (data.type === 'done') {
164
+ if (debugLevelRef.current !== 'off') setDebug(d => [...d.slice(-50), `done: exit ${data.code}`]);
165
+ }
166
+ } catch {}
167
+ }
168
+
169
+ // Update assistant message with latest text
170
+ if (assistantText) {
171
+ let displayText = assistantText;
172
+ try {
173
+ const parsed = JSON.parse(assistantText);
174
+ if (parsed.result) displayText = parsed.result;
175
+ } catch {}
176
+ setMessages(prev => {
177
+ const updated = [...prev];
178
+ updated[updated.length - 1] = { role: 'assistant', content: displayText, timestamp: new Date().toISOString() };
179
+ return updated;
180
+ });
181
+ }
182
+ }
183
+
184
+ // Final parse
185
+ try {
186
+ const parsed = JSON.parse(assistantText);
187
+ const finalText = parsed.result || assistantText;
188
+ setMessages(prev => {
189
+ const updated = [...prev];
190
+ updated[updated.length - 1] = { role: 'assistant', content: finalText, timestamp: new Date().toISOString() };
191
+ return updated;
192
+ });
193
+ } catch {}
194
+
195
+ // After first message, future ones should use -c
196
+ setHasSession(true);
197
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
198
+ setDebug(d => [...d.slice(-50), `Response complete (${elapsed}s, ${assistantText.length} chars)`]);
199
+ } catch (e: any) {
200
+ if (e.name !== 'AbortError') {
201
+ setDebug(d => [...d.slice(-20), `Error: ${e.message}`]);
202
+ setMessages(prev => [...prev.slice(0, -1), { role: 'system', content: `Failed: ${e.message}`, timestamp: new Date().toISOString() }]);
203
+ }
204
+ }
205
+
206
+ setLoading(false);
207
+ abortRef.current = null;
208
+ };
209
+
210
+ // Stop generation
211
+ const stopGeneration = () => {
212
+ if (abortRef.current) abortRef.current.abort();
213
+ };
214
+
215
+ // Close tunnel
216
+ const closeTunnel = async () => {
217
+ if (!confirm('Close tunnel? You will lose remote access.')) return;
218
+ await fetch('/api/tunnel', {
219
+ method: 'POST',
220
+ headers: { 'Content-Type': 'application/json' },
221
+ body: JSON.stringify({ action: 'stop' }),
222
+ });
223
+ setTunnelUrl(null);
224
+ };
225
+
226
+ return (
227
+ <div className="h-[100dvh] flex flex-col bg-[#0d1117] text-[#e6edf3]">
228
+ {/* Header */}
229
+ <header className="shrink-0 flex items-center gap-1.5 px-2 py-2 bg-[#161b22] border-b border-[#30363d]">
230
+ <span className="text-xs font-bold text-[#7c5bf0]">Forge</span>
231
+ <select
232
+ value={selectedProject?.path || ''}
233
+ onChange={e => {
234
+ const p = projects.find(p => p.path === e.target.value);
235
+ if (p) selectProject(p);
236
+ }}
237
+ className="flex-1 bg-[#0d1117] border border-[#30363d] rounded px-2 py-1 text-xs text-[#e6edf3] min-w-0"
238
+ >
239
+ <option value="">Project</option>
240
+ {projects.map(p => (
241
+ <option key={p.path} value={p.path}>{p.name}</option>
242
+ ))}
243
+ </select>
244
+ {selectedProject && (
245
+ <>
246
+ <button
247
+ onClick={() => { setShowSessions(v => !v); if (!showSessions) fetchSessions(selectedProject.name); }}
248
+ className="text-xs px-2 py-1 border border-[#30363d] rounded text-[#8b949e] active:bg-[#30363d]"
249
+ >Sessions</button>
250
+ <button
251
+ onClick={async () => {
252
+ const list = await fetchSessions(selectedProject.name);
253
+ if (list.length > 0) {
254
+ await loadHistory(selectedProject.name, list[0].sessionId);
255
+ setDebug(d => [...d.slice(-20), `Refreshed: ${list[0].sessionId.slice(0, 8)}`]);
256
+ }
257
+ }}
258
+ className="text-sm px-3 py-1 border border-[#30363d] rounded text-[#8b949e] active:bg-[#30363d]"
259
+ >↻</button>
260
+ {availableAgents.length > 1 && (
261
+ <select
262
+ value={selectedAgent}
263
+ onChange={e => setSelectedAgent(e.target.value)}
264
+ className="bg-[#0d1117] border border-[#30363d] rounded px-1 py-1 text-[10px] text-[#e6edf3] w-16"
265
+ >
266
+ {availableAgents.map(a => (
267
+ <option key={a.id} value={a.id}>{a.name.split(' ')[0]}</option>
268
+ ))}
269
+ </select>
270
+ )}
271
+ </>
272
+ )}
273
+ {tunnelUrl && (
274
+ <button onClick={closeTunnel} className="text-xs px-1.5 py-1 border border-green-700 rounded text-green-400" title={tunnelUrl}>●</button>
275
+ )}
276
+ <a href="/?force=desktop" className="text-[9px] px-1.5 py-1 border border-[#30363d] rounded text-[#8b949e] active:bg-[#30363d]" title="Switch to desktop view">PC</a>
277
+ </header>
278
+
279
+ {/* Session list */}
280
+ {showSessions && (
281
+ <div className="shrink-0 max-h-[40vh] overflow-y-auto bg-[#161b22] border-b border-[#30363d]">
282
+ {sessions.length === 0 ? (
283
+ <div className="px-3 py-4 text-xs text-[#8b949e] text-center">No sessions found</div>
284
+ ) : sessions.map(s => (
285
+ <button
286
+ key={s.sessionId}
287
+ onClick={() => viewSession(s.sessionId)}
288
+ className="w-full text-left px-3 py-2 border-b border-[#30363d]/50 hover:bg-[#1c2128] text-xs"
289
+ >
290
+ <div className="flex items-center gap-2">
291
+ <span className="text-[#e6edf3] font-mono truncate">{s.sessionId.slice(0, 12)}</span>
292
+ {s.modified && <span className="text-[#8b949e] ml-auto shrink-0">{new Date(s.modified).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })}</span>}
293
+ </div>
294
+ {(s.summary || s.firstPrompt) && (
295
+ <div className="text-[#8b949e] mt-0.5 truncate">{s.summary || s.firstPrompt}</div>
296
+ )}
297
+ </button>
298
+ ))}
299
+ </div>
300
+ )}
301
+
302
+ {/* Messages */}
303
+ <div ref={scrollRef} className="flex-1 overflow-y-auto px-3 py-2 min-h-0 space-y-3">
304
+ {!selectedProject ? (
305
+ <div className="h-full flex items-center justify-center text-sm text-[#8b949e]">
306
+ Select a project to start
307
+ </div>
308
+ ) : messages.length === 0 ? (
309
+ <div className="h-full flex items-center justify-center text-sm text-[#8b949e]">
310
+ {hasSession ? 'Session loaded. Type a message.' : 'No sessions yet. Type a message to start.'}
311
+ </div>
312
+ ) : messages.map((msg, i) => (
313
+ <div key={i} className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>
314
+ <div className={`max-w-[85%] rounded-2xl px-3 py-2 text-sm whitespace-pre-wrap break-words ${
315
+ msg.role === 'user'
316
+ ? 'bg-[#7c5bf0] text-white rounded-br-sm'
317
+ : msg.role === 'system'
318
+ ? 'bg-red-900/30 text-red-300 rounded-bl-sm'
319
+ : 'bg-[#1c2128] text-[#e6edf3] rounded-bl-sm'
320
+ }`}>
321
+ {msg.content}
322
+ </div>
323
+ </div>
324
+ ))}
325
+ {loading && (
326
+ <div className="flex justify-start">
327
+ <div className="bg-[#1c2128] rounded-2xl rounded-bl-sm px-3 py-2 text-sm text-[#8b949e]">
328
+ Thinking...
329
+ </div>
330
+ </div>
331
+ )}
332
+ </div>
333
+
334
+ {/* Input */}
335
+ <div className="shrink-0 flex items-center gap-2 px-3 py-2 bg-[#161b22] border-t border-[#30363d]">
336
+ <input
337
+ ref={inputRef}
338
+ type="text"
339
+ value={input}
340
+ onChange={e => setInput(e.target.value)}
341
+ onKeyDown={e => { if (e.key === 'Enter' && !loading) sendMessage(); }}
342
+ placeholder={selectedProject ? 'Type a message...' : 'Select a project first'}
343
+ disabled={!selectedProject}
344
+ className="flex-1 bg-[#0d1117] border border-[#30363d] rounded-lg px-3 py-2 text-sm text-[#e6edf3] focus:outline-none focus:border-[#7c5bf0] disabled:opacity-50 min-w-0"
345
+ autoComplete="off"
346
+ autoCorrect="off"
347
+ />
348
+ {loading ? (
349
+ <button
350
+ onClick={stopGeneration}
351
+ className="px-4 py-2 bg-red-600 text-white rounded-lg text-sm font-medium shrink-0"
352
+ >Stop</button>
353
+ ) : (
354
+ <button
355
+ onClick={sendMessage}
356
+ disabled={!selectedProject || !input.trim()}
357
+ className="px-4 py-2 bg-[#7c5bf0] text-white rounded-lg text-sm font-medium disabled:opacity-50 shrink-0"
358
+ >Send</button>
359
+ )}
360
+ </div>
361
+
362
+ {/* Debug log */}
363
+ <div className="shrink-0 bg-[#0d1117] border-t border-[#30363d]">
364
+ <div className="flex items-center gap-2 px-3 py-1">
365
+ <span className="text-[9px] text-[#8b949e]">Debug:</span>
366
+ {(['off', 'simple', 'verbose'] as const).map(level => (
367
+ <button
368
+ key={level}
369
+ onClick={() => { setDebugLevel(level); debugLevelRef.current = level; if (level === 'off') setDebug([]); }}
370
+ className={`text-[9px] px-1.5 py-0.5 rounded ${debugLevel === level ? 'bg-[#30363d] text-[#e6edf3]' : 'text-[#8b949e]'}`}
371
+ >{level}</button>
372
+ ))}
373
+ {debug.length > 0 && (
374
+ <button onClick={() => setDebug([])} className="text-[9px] text-[#8b949e] ml-auto">Clear</button>
375
+ )}
376
+ </div>
377
+ {debugLevel !== 'off' && debug.length > 0 && (
378
+ <div className="px-3 py-1 max-h-32 overflow-y-auto border-t border-[#30363d]/50">
379
+ {debug.map((d, i) => <div key={i} className="text-[9px] text-[#8b949e] font-mono">{d}</div>)}
380
+ </div>
381
+ )}
382
+ </div>
383
+ </div>
384
+ );
385
+ }
@@ -0,0 +1,122 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useCallback } from 'react';
4
+
5
+ interface MonitorData {
6
+ processes: {
7
+ nextjs: { running: boolean; pid: string; startedAt?: string };
8
+ terminal: { running: boolean; pid: string; startedAt?: string };
9
+ telegram: { running: boolean; pid: string; startedAt?: string };
10
+ workspace: { running: boolean; pid: string; startedAt?: string };
11
+ tunnel: { running: boolean; pid: string; url: string };
12
+ mcp: { running: boolean; port: number; sessions: number };
13
+ };
14
+ sessions: { name: string; created: string; attached: boolean; windows: number }[];
15
+ uptime: string;
16
+ }
17
+
18
+ export default function MonitorPanel({ onClose }: { onClose: () => void }) {
19
+ const [data, setData] = useState<MonitorData | null>(null);
20
+
21
+ const refresh = useCallback(() => {
22
+ fetch('/api/monitor').then(r => r.json()).then(setData).catch(() => {});
23
+ }, []);
24
+
25
+ useEffect(() => {
26
+ refresh();
27
+ const timer = setInterval(refresh, 5000);
28
+ return () => clearInterval(timer);
29
+ }, [refresh]);
30
+
31
+ return (
32
+ <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onClick={onClose}>
33
+ <div className="bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg w-[500px] max-h-[80vh] overflow-y-auto shadow-xl" onClick={e => e.stopPropagation()}>
34
+ <div className="px-4 py-3 border-b border-[var(--border)] flex items-center justify-between">
35
+ <h2 className="text-sm font-bold text-[var(--text-primary)]">Monitor</h2>
36
+ <div className="flex items-center gap-2">
37
+ <button onClick={refresh} className="text-[10px] text-[var(--text-secondary)] hover:text-[var(--text-primary)]">↻</button>
38
+ <button onClick={onClose} className="text-[10px] text-[var(--text-secondary)] hover:text-[var(--text-primary)]">Close</button>
39
+ </div>
40
+ </div>
41
+
42
+ {data ? (
43
+ <div className="p-4 space-y-4">
44
+ {/* Processes */}
45
+ <div>
46
+ <h3 className="text-[10px] font-semibold text-[var(--text-secondary)] uppercase mb-2">Processes</h3>
47
+ <div className="space-y-1.5">
48
+ {[
49
+ { label: 'Next.js', ...data.processes.nextjs },
50
+ { label: 'Terminal Server', ...data.processes.terminal },
51
+ { label: 'Telegram Bot', ...data.processes.telegram },
52
+ { label: 'Workspace Daemon', ...data.processes.workspace },
53
+ { label: 'Tunnel', ...data.processes.tunnel },
54
+ ].map(p => (
55
+ <div key={p.label} className="flex items-center gap-2 text-xs">
56
+ <span className={p.running ? 'text-green-400' : 'text-gray-500'}>●</span>
57
+ <span className="text-[var(--text-primary)] w-28">{p.label}</span>
58
+ {p.running ? (
59
+ <>
60
+ <span className="text-[var(--text-secondary)] font-mono text-[10px]">pid: {p.pid}</span>
61
+ {(p as any).startedAt && <span className="text-gray-500 font-mono text-[9px]">{(p as any).startedAt}</span>}
62
+ </>
63
+ ) : (
64
+ <span className="text-gray-500 text-[10px]">stopped</span>
65
+ )}
66
+ </div>
67
+ ))}
68
+ {/* MCP Server — separate row (no pid, uses port + sessions) */}
69
+ <div className="flex items-center gap-2 text-xs">
70
+ <span className={data.processes.mcp?.running ? 'text-green-400' : 'text-gray-500'}>●</span>
71
+ <span className="text-[var(--text-primary)] w-28">MCP Server</span>
72
+ {data.processes.mcp?.running ? (
73
+ <>
74
+ <span className="text-[var(--text-secondary)] font-mono text-[10px]">port: {data.processes.mcp.port}</span>
75
+ <span className="text-gray-500 font-mono text-[9px]">{data.processes.mcp.sessions} session(s)</span>
76
+ </>
77
+ ) : (
78
+ <span className="text-gray-500 text-[10px]">stopped</span>
79
+ )}
80
+ </div>
81
+ {data.processes.tunnel.running && data.processes.tunnel.url && (
82
+ <div className="pl-6 text-[10px] text-[var(--accent)] truncate">{data.processes.tunnel.url}</div>
83
+ )}
84
+ <div className="border-t border-[var(--border)] my-1" />
85
+ </div>
86
+ </div>
87
+
88
+ {/* Uptime */}
89
+ {data.uptime && (
90
+ <div className="text-[10px] text-[var(--text-secondary)]">
91
+ Uptime: {data.uptime}
92
+ </div>
93
+ )}
94
+
95
+ {/* Sessions */}
96
+ <div>
97
+ <h3 className="text-[10px] font-semibold text-[var(--text-secondary)] uppercase mb-2">
98
+ Terminal Sessions ({data.sessions.length})
99
+ </h3>
100
+ {data.sessions.length === 0 ? (
101
+ <div className="text-[10px] text-[var(--text-secondary)]">No sessions</div>
102
+ ) : (
103
+ <div className="space-y-1">
104
+ {data.sessions.map(s => (
105
+ <div key={s.name} className="flex items-center gap-2 text-[11px]">
106
+ <span className={s.attached ? 'text-green-400' : 'text-yellow-500'}>●</span>
107
+ <span className="font-mono text-[var(--text-primary)] truncate flex-1">{s.name}</span>
108
+ <span className="text-[9px] text-[var(--text-secondary)]">{s.attached ? 'attached' : 'detached'}</span>
109
+ <span className="text-[9px] text-[var(--text-secondary)]">{new Date(s.created).toLocaleTimeString()}</span>
110
+ </div>
111
+ ))}
112
+ </div>
113
+ )}
114
+ </div>
115
+ </div>
116
+ ) : (
117
+ <div className="p-8 text-center text-xs text-[var(--text-secondary)]">Loading...</div>
118
+ )}
119
+ </div>
120
+ </div>
121
+ );
122
+ }
@@ -0,0 +1,93 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+ import type { SessionTemplate } from '@/src/types';
5
+
6
+ export default function NewSessionModal({
7
+ onClose,
8
+ onCreate,
9
+ }: {
10
+ onClose: () => void;
11
+ onCreate: (data: { name: string; templateId: string }) => void;
12
+ }) {
13
+ const [templates, setTemplates] = useState<SessionTemplate[]>([]);
14
+ const [selectedTemplate, setSelectedTemplate] = useState('');
15
+ const [name, setName] = useState('');
16
+
17
+ useEffect(() => {
18
+ fetch('/api/templates')
19
+ .then(r => r.json())
20
+ .then((t: SessionTemplate[]) => {
21
+ setTemplates(t);
22
+ if (t.length > 0) setSelectedTemplate(t[0].id);
23
+ });
24
+ }, []);
25
+
26
+ const handleCreate = () => {
27
+ if (!selectedTemplate) return;
28
+ const finalName = name || `${selectedTemplate}-${Date.now().toString(36)}`;
29
+ onCreate({ name: finalName, templateId: selectedTemplate });
30
+ };
31
+
32
+ return (
33
+ <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50" onClick={onClose}>
34
+ <div
35
+ className="bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg w-96 p-5 space-y-4"
36
+ onClick={e => e.stopPropagation()}
37
+ >
38
+ <h2 className="text-sm font-bold">New Session</h2>
39
+
40
+ {/* Template selection */}
41
+ <div className="space-y-2">
42
+ <label className="text-xs text-[var(--text-secondary)]">Template</label>
43
+ <div className="grid grid-cols-2 gap-2">
44
+ {templates.map(t => (
45
+ <button
46
+ key={t.id}
47
+ onClick={() => setSelectedTemplate(t.id)}
48
+ className={`p-2 rounded border text-left text-xs transition-colors ${
49
+ selectedTemplate === t.id
50
+ ? 'border-[var(--accent)] bg-[var(--bg-tertiary)]'
51
+ : 'border-[var(--border)] hover:border-[var(--text-secondary)]'
52
+ }`}
53
+ >
54
+ <div className="font-medium">{t.ui?.icon} {t.name}</div>
55
+ <div className="text-[10px] text-[var(--text-secondary)] mt-0.5">
56
+ {t.provider} · {t.memory.strategy}
57
+ </div>
58
+ </button>
59
+ ))}
60
+ </div>
61
+ </div>
62
+
63
+ {/* Name */}
64
+ <div className="space-y-1">
65
+ <label className="text-xs text-[var(--text-secondary)]">Session Name (optional)</label>
66
+ <input
67
+ value={name}
68
+ onChange={e => setName(e.target.value)}
69
+ placeholder={selectedTemplate || 'auto-generated'}
70
+ className="w-full px-3 py-2 bg-[var(--bg-tertiary)] border border-[var(--border)] rounded text-sm text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent)]"
71
+ />
72
+ </div>
73
+
74
+ {/* Actions */}
75
+ <div className="flex justify-end gap-2">
76
+ <button
77
+ onClick={onClose}
78
+ className="px-3 py-1.5 text-xs text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
79
+ >
80
+ Cancel
81
+ </button>
82
+ <button
83
+ onClick={handleCreate}
84
+ disabled={!selectedTemplate}
85
+ className="px-3 py-1.5 text-xs bg-[var(--accent)] text-white rounded hover:opacity-90 disabled:opacity-50"
86
+ >
87
+ Create
88
+ </button>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ );
93
+ }