@aion0/forge 0.5.26 → 0.5.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/.forge/worktrees/pipeline-4dd8dc2d/CLAUDE.md +86 -0
  2. package/.forge/worktrees/pipeline-4dd8dc2d/README.md +136 -0
  3. package/.forge/worktrees/pipeline-4dd8dc2d/RELEASE_NOTES.md +36 -0
  4. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/agents/route.ts +17 -0
  5. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/[...nextauth]/route.ts +3 -0
  6. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/verify/route.ts +46 -0
  7. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/route.ts +31 -0
  8. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/stream/route.ts +63 -0
  9. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/route.ts +28 -0
  10. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  11. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  12. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/route.ts +37 -0
  13. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/sync/route.ts +17 -0
  14. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-templates/route.ts +145 -0
  15. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/code/route.ts +299 -0
  16. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/[id]/route.ts +62 -0
  17. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/route.ts +40 -0
  18. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/detect-cli/route.ts +46 -0
  19. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/route.ts +176 -0
  20. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/sessions/route.ts +54 -0
  21. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/favorites/route.ts +26 -0
  22. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/route.ts +6 -0
  23. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/run/route.ts +19 -0
  24. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/git/route.ts +149 -0
  25. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/help/route.ts +84 -0
  26. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/issue-scanner/route.ts +116 -0
  27. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/logs/route.ts +100 -0
  28. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/mobile-chat/route.ts +115 -0
  29. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/monitor/route.ts +74 -0
  30. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notifications/route.ts +42 -0
  31. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notify/test/route.ts +33 -0
  32. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/online/route.ts +40 -0
  33. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/[id]/route.ts +41 -0
  34. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/route.ts +90 -0
  35. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/plugins/route.ts +75 -0
  36. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/[...path]/route.ts +64 -0
  37. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/route.ts +156 -0
  38. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-pipelines/route.ts +91 -0
  39. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-sessions/route.ts +61 -0
  40. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/projects/route.ts +26 -0
  41. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/chat/route.ts +64 -0
  42. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/messages/route.ts +9 -0
  43. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/route.ts +17 -0
  44. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/route.ts +20 -0
  45. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/settings/route.ts +64 -0
  46. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/local/route.ts +228 -0
  47. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/route.ts +182 -0
  48. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/smith-templates/route.ts +81 -0
  49. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/status/route.ts +12 -0
  50. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tabs/route.ts +25 -0
  51. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/route.ts +51 -0
  52. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/stream/route.ts +77 -0
  53. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/link/route.ts +37 -0
  54. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/route.ts +44 -0
  55. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/session/route.ts +14 -0
  56. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/telegram/route.ts +23 -0
  57. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/templates/route.ts +6 -0
  58. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-bell/route.ts +39 -0
  59. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-cwd/route.ts +19 -0
  60. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-state/route.ts +15 -0
  61. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tunnel/route.ts +26 -0
  62. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/upgrade/route.ts +43 -0
  63. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/usage/route.ts +20 -0
  64. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/version/route.ts +78 -0
  65. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/watchers/route.ts +33 -0
  66. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/agents/route.ts +35 -0
  67. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/memory/route.ts +23 -0
  68. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/smith/route.ts +22 -0
  69. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/stream/route.ts +31 -0
  70. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/route.ts +79 -0
  71. package/.forge/worktrees/pipeline-4dd8dc2d/app/global-error.tsx +21 -0
  72. package/.forge/worktrees/pipeline-4dd8dc2d/app/globals.css +52 -0
  73. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.ico +0 -0
  74. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.png +0 -0
  75. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.svg +106 -0
  76. package/.forge/worktrees/pipeline-4dd8dc2d/app/layout.tsx +17 -0
  77. package/.forge/worktrees/pipeline-4dd8dc2d/app/login/LoginForm.tsx +96 -0
  78. package/.forge/worktrees/pipeline-4dd8dc2d/app/login/page.tsx +10 -0
  79. package/.forge/worktrees/pipeline-4dd8dc2d/app/mobile/page.tsx +10 -0
  80. package/.forge/worktrees/pipeline-4dd8dc2d/app/page.tsx +22 -0
  81. package/.forge/worktrees/pipeline-4dd8dc2d/bin/forge-server.mjs +484 -0
  82. package/.forge/worktrees/pipeline-4dd8dc2d/check-forge-status.sh +71 -0
  83. package/.forge/worktrees/pipeline-4dd8dc2d/cli/mw.ts +579 -0
  84. package/.forge/worktrees/pipeline-4dd8dc2d/components/BrowserPanel.tsx +175 -0
  85. package/.forge/worktrees/pipeline-4dd8dc2d/components/ChatPanel.tsx +191 -0
  86. package/.forge/worktrees/pipeline-4dd8dc2d/components/ClaudeTerminal.tsx +267 -0
  87. package/.forge/worktrees/pipeline-4dd8dc2d/components/CodeViewer.tsx +787 -0
  88. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationEditor.tsx +411 -0
  89. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationGraphView.tsx +347 -0
  90. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationTerminalView.tsx +303 -0
  91. package/.forge/worktrees/pipeline-4dd8dc2d/components/Dashboard.tsx +807 -0
  92. package/.forge/worktrees/pipeline-4dd8dc2d/components/DashboardWrapper.tsx +9 -0
  93. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryFlowEditor.tsx +491 -0
  94. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryList.tsx +230 -0
  95. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryWorkspace.tsx +589 -0
  96. package/.forge/worktrees/pipeline-4dd8dc2d/components/DocTerminal.tsx +187 -0
  97. package/.forge/worktrees/pipeline-4dd8dc2d/components/DocsViewer.tsx +574 -0
  98. package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpDialog.tsx +169 -0
  99. package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpTerminal.tsx +141 -0
  100. package/.forge/worktrees/pipeline-4dd8dc2d/components/InlinePipelineView.tsx +111 -0
  101. package/.forge/worktrees/pipeline-4dd8dc2d/components/LogViewer.tsx +194 -0
  102. package/.forge/worktrees/pipeline-4dd8dc2d/components/MarkdownContent.tsx +73 -0
  103. package/.forge/worktrees/pipeline-4dd8dc2d/components/MobileView.tsx +385 -0
  104. package/.forge/worktrees/pipeline-4dd8dc2d/components/MonitorPanel.tsx +122 -0
  105. package/.forge/worktrees/pipeline-4dd8dc2d/components/NewSessionModal.tsx +93 -0
  106. package/.forge/worktrees/pipeline-4dd8dc2d/components/NewTaskModal.tsx +492 -0
  107. package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineEditor.tsx +570 -0
  108. package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineView.tsx +1018 -0
  109. package/.forge/worktrees/pipeline-4dd8dc2d/components/PluginsPanel.tsx +472 -0
  110. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectDetail.tsx +1618 -0
  111. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectList.tsx +108 -0
  112. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectManager.tsx +401 -0
  113. package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionList.tsx +74 -0
  114. package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionView.tsx +726 -0
  115. package/.forge/worktrees/pipeline-4dd8dc2d/components/SettingsModal.tsx +1647 -0
  116. package/.forge/worktrees/pipeline-4dd8dc2d/components/SkillsPanel.tsx +969 -0
  117. package/.forge/worktrees/pipeline-4dd8dc2d/components/StatusBar.tsx +99 -0
  118. package/.forge/worktrees/pipeline-4dd8dc2d/components/TabBar.tsx +46 -0
  119. package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskBoard.tsx +113 -0
  120. package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskDetail.tsx +372 -0
  121. package/.forge/worktrees/pipeline-4dd8dc2d/components/TerminalLauncher.tsx +398 -0
  122. package/.forge/worktrees/pipeline-4dd8dc2d/components/TunnelToggle.tsx +206 -0
  123. package/.forge/worktrees/pipeline-4dd8dc2d/components/UsagePanel.tsx +207 -0
  124. package/.forge/worktrees/pipeline-4dd8dc2d/components/WebTerminal.tsx +1743 -0
  125. package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceTree.tsx +221 -0
  126. package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceView.tsx +4048 -0
  127. package/.forge/worktrees/pipeline-4dd8dc2d/dev-test.sh +5 -0
  128. package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Memory_Layer_Design.docx +0 -0
  129. package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Strategy_Research_2026.docx +0 -0
  130. package/.forge/worktrees/pipeline-4dd8dc2d/docs/LOCAL-DEPLOY.md +144 -0
  131. package/.forge/worktrees/pipeline-4dd8dc2d/docs/roadmap-multi-agent-workflow.md +330 -0
  132. package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.png +0 -0
  133. package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.svg +106 -0
  134. package/.forge/worktrees/pipeline-4dd8dc2d/hooks/useSidebarResize.ts +52 -0
  135. package/.forge/worktrees/pipeline-4dd8dc2d/install.sh +29 -0
  136. package/.forge/worktrees/pipeline-4dd8dc2d/instrumentation.ts +35 -0
  137. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/claude-adapter.ts +104 -0
  138. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/generic-adapter.ts +64 -0
  139. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/index.ts +245 -0
  140. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/types.ts +70 -0
  141. package/.forge/worktrees/pipeline-4dd8dc2d/lib/artifacts.ts +106 -0
  142. package/.forge/worktrees/pipeline-4dd8dc2d/lib/auth.ts +62 -0
  143. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/docker.yaml +70 -0
  144. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/http.yaml +66 -0
  145. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/jenkins.yaml +92 -0
  146. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/llm-vision.yaml +85 -0
  147. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/playwright.yaml +111 -0
  148. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/shell-command.yaml +60 -0
  149. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/slack.yaml +48 -0
  150. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/webhook.yaml +56 -0
  151. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-process.ts +361 -0
  152. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-sessions.ts +266 -0
  153. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-templates.ts +227 -0
  154. package/.forge/worktrees/pipeline-4dd8dc2d/lib/cloudflared.ts +424 -0
  155. package/.forge/worktrees/pipeline-4dd8dc2d/lib/crypto.ts +67 -0
  156. package/.forge/worktrees/pipeline-4dd8dc2d/lib/delivery.ts +787 -0
  157. package/.forge/worktrees/pipeline-4dd8dc2d/lib/dirs.ts +99 -0
  158. package/.forge/worktrees/pipeline-4dd8dc2d/lib/flows.ts +86 -0
  159. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-mcp-server.ts +732 -0
  160. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-inbox.md +38 -0
  161. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-send.md +47 -0
  162. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-status.md +32 -0
  163. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-workspace-sync.md +37 -0
  164. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/00-overview.md +40 -0
  165. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/01-settings.md +194 -0
  166. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/02-telegram.md +41 -0
  167. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/03-tunnel.md +31 -0
  168. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/04-tasks.md +52 -0
  169. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/05-pipelines.md +460 -0
  170. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/06-skills.md +43 -0
  171. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/07-projects.md +73 -0
  172. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/08-rules.md +53 -0
  173. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/09-issue-autofix.md +55 -0
  174. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/10-troubleshooting.md +89 -0
  175. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/11-workspace.md +810 -0
  176. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/CLAUDE.md +62 -0
  177. package/.forge/worktrees/pipeline-4dd8dc2d/lib/init.ts +266 -0
  178. package/.forge/worktrees/pipeline-4dd8dc2d/lib/issue-scanner.ts +298 -0
  179. package/.forge/worktrees/pipeline-4dd8dc2d/lib/logger.ts +79 -0
  180. package/.forge/worktrees/pipeline-4dd8dc2d/lib/notifications.ts +75 -0
  181. package/.forge/worktrees/pipeline-4dd8dc2d/lib/notify.ts +108 -0
  182. package/.forge/worktrees/pipeline-4dd8dc2d/lib/password.ts +97 -0
  183. package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline-scheduler.ts +373 -0
  184. package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline.ts +1565 -0
  185. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/executor.ts +347 -0
  186. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/registry.ts +228 -0
  187. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/types.ts +103 -0
  188. package/.forge/worktrees/pipeline-4dd8dc2d/lib/project-sessions.ts +53 -0
  189. package/.forge/worktrees/pipeline-4dd8dc2d/lib/projects.ts +86 -0
  190. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-manager.ts +156 -0
  191. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-utils.ts +53 -0
  192. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-watcher.ts +345 -0
  193. package/.forge/worktrees/pipeline-4dd8dc2d/lib/settings.ts +195 -0
  194. package/.forge/worktrees/pipeline-4dd8dc2d/lib/skills.ts +458 -0
  195. package/.forge/worktrees/pipeline-4dd8dc2d/lib/task-manager.ts +951 -0
  196. package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-bot.ts +1477 -0
  197. package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-standalone.ts +83 -0
  198. package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-server.ts +70 -0
  199. package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-standalone.ts +438 -0
  200. package/.forge/worktrees/pipeline-4dd8dc2d/lib/usage-scanner.ts +249 -0
  201. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/state-machine.test.ts +388 -0
  202. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/workspace.test.ts +311 -0
  203. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-bus.ts +416 -0
  204. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-worker.ts +655 -0
  205. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/api-backend.ts +262 -0
  206. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/cli-backend.ts +491 -0
  207. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/index.ts +84 -0
  208. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/manager.ts +136 -0
  209. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/orchestrator.ts +3415 -0
  210. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/persistence.ts +309 -0
  211. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/presets.ts +649 -0
  212. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/requests.ts +287 -0
  213. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/session-monitor.ts +240 -0
  214. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/skill-installer.ts +275 -0
  215. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/smith-memory.ts +498 -0
  216. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/types.ts +241 -0
  217. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/watch-manager.ts +560 -0
  218. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace-standalone.ts +978 -0
  219. package/.forge/worktrees/pipeline-4dd8dc2d/middleware.ts +51 -0
  220. package/.forge/worktrees/pipeline-4dd8dc2d/next.config.ts +26 -0
  221. package/.forge/worktrees/pipeline-4dd8dc2d/package.json +74 -0
  222. package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-lock.yaml +3719 -0
  223. package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-workspace.yaml +1 -0
  224. package/.forge/worktrees/pipeline-4dd8dc2d/postcss.config.mjs +7 -0
  225. package/.forge/worktrees/pipeline-4dd8dc2d/publish.sh +133 -0
  226. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/README.md +66 -0
  227. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/results/.gitignore +2 -0
  228. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/run.ts +635 -0
  229. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/task.md +26 -0
  230. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  231. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  232. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/task.md +48 -0
  233. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  234. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  235. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  236. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  237. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/verify-usage.ts +178 -0
  238. package/.forge/worktrees/pipeline-4dd8dc2d/src/config/index.ts +129 -0
  239. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/db/database.ts +259 -0
  240. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/memory/strategy.ts +32 -0
  241. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/chat.ts +65 -0
  242. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/registry.ts +60 -0
  243. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/session/manager.ts +190 -0
  244. package/.forge/worktrees/pipeline-4dd8dc2d/src/types/index.ts +129 -0
  245. package/.forge/worktrees/pipeline-4dd8dc2d/start.sh +32 -0
  246. package/.forge/worktrees/pipeline-4dd8dc2d/templates/smith-lead.json +45 -0
  247. package/.forge/worktrees/pipeline-4dd8dc2d/tsconfig.json +42 -0
  248. package/RELEASE_NOTES.md +11 -28
  249. package/app/api/terminal-bell/route.ts +6 -2
  250. package/components/WebTerminal.tsx +36 -2
  251. package/lib/terminal-standalone.ts +19 -2
  252. package/next-env.d.ts +1 -1
  253. package/package.json +1 -1
@@ -0,0 +1,176 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { readdirSync, statSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { join, relative, extname } from 'node:path';
4
+ import { homedir } from 'node:os';
5
+ import { loadSettings } from '@/lib/settings';
6
+
7
+ interface FileNode {
8
+ name: string;
9
+ path: string; // relative to docRoot
10
+ type: 'file' | 'dir';
11
+ fileType?: 'md' | 'image' | 'other';
12
+ children?: FileNode[];
13
+ }
14
+
15
+ const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.bmp', '.ico', '.avif']);
16
+
17
+ function scanDir(dir: string, base: string, depth: number = 0): FileNode[] {
18
+ if (depth > 6) return [];
19
+ try {
20
+ const entries = readdirSync(dir, { withFileTypes: true });
21
+ const nodes: FileNode[] = [];
22
+
23
+ // Sort: dirs first, then files, alphabetical
24
+ const sorted = entries
25
+ .filter(e => !e.name.startsWith('.') && e.name !== 'node_modules')
26
+ .sort((a, b) => {
27
+ if (a.isDirectory() && !b.isDirectory()) return -1;
28
+ if (!a.isDirectory() && b.isDirectory()) return 1;
29
+ return a.name.localeCompare(b.name);
30
+ });
31
+
32
+ for (const entry of sorted) {
33
+ const fullPath = join(dir, entry.name);
34
+ const relPath = relative(base, fullPath);
35
+
36
+ if (entry.isDirectory()) {
37
+ const children = scanDir(fullPath, base, depth + 1);
38
+ if (children.length > 0) {
39
+ nodes.push({ name: entry.name, path: relPath, type: 'dir', children });
40
+ }
41
+ } else {
42
+ const ext = extname(entry.name).toLowerCase();
43
+ if (ext === '.md') {
44
+ nodes.push({ name: entry.name, path: relPath, type: 'file', fileType: 'md' });
45
+ } else if (IMAGE_EXTS.has(ext)) {
46
+ nodes.push({ name: entry.name, path: relPath, type: 'file', fileType: 'image' });
47
+ } else if (!entry.name.startsWith('.')) {
48
+ nodes.push({ name: entry.name, path: relPath, type: 'file', fileType: 'other' });
49
+ }
50
+ }
51
+ }
52
+ return nodes;
53
+ } catch {
54
+ return [];
55
+ }
56
+ }
57
+
58
+ // GET /api/docs — list doc roots and their file trees
59
+ export async function GET(req: Request) {
60
+ const { searchParams } = new URL(req.url);
61
+ const filePath = searchParams.get('file');
62
+ const rootIdx = parseInt(searchParams.get('root') || '0');
63
+
64
+ const settings = loadSettings();
65
+ const docRoots = (settings.docRoots || []).map(r => r.replace(/^~/, homedir()));
66
+
67
+ if (docRoots.length === 0) {
68
+ return NextResponse.json({ roots: [], tree: [], content: null });
69
+ }
70
+
71
+ const rootNames = docRoots.map(r => r.split('/').pop() || r);
72
+
73
+ // Serve image
74
+ const imagePath = searchParams.get('image');
75
+ if (imagePath && rootIdx < docRoots.length) {
76
+ const root = docRoots[rootIdx];
77
+ const fullPath = join(root, imagePath);
78
+ if (!fullPath.startsWith(root)) {
79
+ return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
80
+ }
81
+ try {
82
+ const { readFileSync: readBin } = require('node:fs');
83
+ const data = readBin(fullPath);
84
+ const ext = extname(fullPath).toLowerCase();
85
+ const mimeMap: Record<string, string> = {
86
+ '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
87
+ '.gif': 'image/gif', '.svg': 'image/svg+xml', '.webp': 'image/webp',
88
+ '.bmp': 'image/bmp', '.ico': 'image/x-icon', '.avif': 'image/avif',
89
+ };
90
+ return new Response(data, {
91
+ headers: { 'Content-Type': mimeMap[ext] || 'application/octet-stream', 'Cache-Control': 'public, max-age=3600' },
92
+ });
93
+ } catch {
94
+ return NextResponse.json({ error: 'Image not found' }, { status: 404 });
95
+ }
96
+ }
97
+
98
+ // Read file content
99
+ if (filePath && rootIdx < docRoots.length) {
100
+ const root = docRoots[rootIdx];
101
+ const fullPath = join(root, filePath);
102
+ if (!fullPath.startsWith(root)) {
103
+ return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
104
+ }
105
+ try {
106
+ const stat = statSync(fullPath);
107
+ const size = stat.size;
108
+ const ext = extname(fullPath).replace('.', '').toLowerCase();
109
+ const sizeKB = Math.round(size / 1024);
110
+ const sizeMB = (size / (1024 * 1024)).toFixed(1);
111
+
112
+ // Binary file types
113
+ const BINARY_EXTS = new Set([
114
+ 'png', 'jpg', 'jpeg', 'gif', 'bmp', 'ico', 'webp', 'avif',
115
+ 'mp3', 'mp4', 'wav', 'ogg', 'webm', 'mov', 'avi',
116
+ 'zip', 'gz', 'tar', 'bz2', 'xz', '7z', 'rar',
117
+ 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
118
+ 'exe', 'dll', 'so', 'dylib', 'bin', 'o', 'a',
119
+ 'woff', 'woff2', 'ttf', 'eot', 'otf',
120
+ 'sqlite', 'db', 'sqlite3', 'class', 'jar', 'pyc', 'wasm',
121
+ ]);
122
+ if (BINARY_EXTS.has(ext)) {
123
+ return NextResponse.json({ binary: true, fileType: ext, size, sizeLabel: sizeKB > 1024 ? `${sizeMB} MB` : `${sizeKB} KB` });
124
+ }
125
+
126
+ if (size > 2_000_000) {
127
+ return NextResponse.json({ tooLarge: true, size, sizeLabel: `${sizeMB} MB`, message: 'File exceeds 2 MB limit' });
128
+ }
129
+ if (size > 200_000) {
130
+ return NextResponse.json({ large: true, size, sizeLabel: `${sizeKB} KB` });
131
+ }
132
+ const content = readFileSync(fullPath, 'utf-8');
133
+ return NextResponse.json({ content, language: ext });
134
+ } catch {
135
+ return NextResponse.json({ error: 'File not found' }, { status: 404 });
136
+ }
137
+ }
138
+
139
+ // Return tree for selected root
140
+ const idx = Math.min(rootIdx, docRoots.length - 1);
141
+ const root = docRoots[idx];
142
+ const tree = scanDir(root, root);
143
+
144
+ return NextResponse.json({ roots: rootNames, rootPaths: docRoots, tree });
145
+ }
146
+
147
+ // PUT /api/docs — save file content
148
+ export async function PUT(req: Request) {
149
+ const { root: rootIdx, file: filePath, content } = await req.json() as {
150
+ root: number;
151
+ file: string;
152
+ content: string;
153
+ };
154
+
155
+ const settings = loadSettings();
156
+ const docRoots = (settings.docRoots || []).map(r => r.replace(/^~/, homedir()));
157
+
158
+ if (rootIdx >= docRoots.length || !filePath) {
159
+ return NextResponse.json({ error: 'Invalid request' }, { status: 400 });
160
+ }
161
+
162
+ const root = docRoots[rootIdx];
163
+ const fullPath = join(root, filePath);
164
+
165
+ // Security: ensure path is within root
166
+ if (!fullPath.startsWith(root)) {
167
+ return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
168
+ }
169
+
170
+ try {
171
+ writeFileSync(fullPath, content, 'utf-8');
172
+ return NextResponse.json({ ok: true });
173
+ } catch (e) {
174
+ return NextResponse.json({ error: 'Failed to save' }, { status: 500 });
175
+ }
176
+ }
@@ -0,0 +1,54 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { getClaudeDir } from '@/lib/dirs';
5
+
6
+ export async function GET(req: Request) {
7
+ const { searchParams } = new URL(req.url);
8
+ const dir = searchParams.get('dir');
9
+ if (!dir) return NextResponse.json({ sessions: [] });
10
+
11
+ // Claude stores sessions at <claudeDir>/projects/<path-with-dashes>/
12
+ const hash = dir.replace(/\//g, '-');
13
+ const claudeDir = join(getClaudeDir(), 'projects', hash);
14
+
15
+ if (!existsSync(claudeDir)) {
16
+ return NextResponse.json({ sessions: [] });
17
+ }
18
+
19
+ try {
20
+ const files = readdirSync(claudeDir).filter(f => f.endsWith('.jsonl'));
21
+ const sessions = files.map(f => {
22
+ const sessionId = f.replace('.jsonl', '');
23
+ const filePath = join(claudeDir, f);
24
+ const stat = statSync(filePath);
25
+
26
+ // Read first line to get first prompt
27
+ let firstPrompt = '';
28
+ try {
29
+ const content = readFileSync(filePath, 'utf-8');
30
+ const lines = content.split('\n').filter(Boolean);
31
+ for (const line of lines) {
32
+ try {
33
+ const entry = JSON.parse(line);
34
+ if (entry.type === 'human' || entry.role === 'user') {
35
+ const text = typeof entry.message === 'string' ? entry.message
36
+ : entry.message?.content?.[0]?.text || '';
37
+ if (text) { firstPrompt = text.slice(0, 80); break; }
38
+ }
39
+ } catch {}
40
+ }
41
+ } catch {}
42
+
43
+ return {
44
+ sessionId,
45
+ firstPrompt,
46
+ modified: stat.mtime.toISOString(),
47
+ };
48
+ }).sort((a, b) => b.modified.localeCompare(a.modified));
49
+
50
+ return NextResponse.json({ sessions });
51
+ } catch {
52
+ return NextResponse.json({ sessions: [] });
53
+ }
54
+ }
@@ -0,0 +1,26 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { getDb } from '@/src/core/db/database';
3
+ import { getDbPath } from '@/src/config';
4
+
5
+ function db() { return getDb(getDbPath()); }
6
+
7
+ // GET /api/favorites — list all favorites
8
+ export async function GET() {
9
+ const rows = db().prepare('SELECT project_path FROM project_favorites ORDER BY created_at ASC').all() as any[];
10
+ return NextResponse.json(rows.map(r => r.project_path));
11
+ }
12
+
13
+ // POST /api/favorites — add or remove
14
+ export async function POST(req: Request) {
15
+ const { action, projectPath } = await req.json();
16
+ if (!projectPath) return NextResponse.json({ error: 'projectPath required' }, { status: 400 });
17
+
18
+ if (action === 'add') {
19
+ db().prepare('INSERT OR IGNORE INTO project_favorites (project_path) VALUES (?)').run(projectPath);
20
+ } else if (action === 'remove') {
21
+ db().prepare('DELETE FROM project_favorites WHERE project_path = ?').run(projectPath);
22
+ }
23
+
24
+ const rows = db().prepare('SELECT project_path FROM project_favorites ORDER BY created_at ASC').all() as any[];
25
+ return NextResponse.json(rows.map(r => r.project_path));
26
+ }
@@ -0,0 +1,6 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { listFlows } from '@/lib/flows';
3
+
4
+ export async function GET() {
5
+ return NextResponse.json(listFlows());
6
+ }
@@ -0,0 +1,19 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { runFlow } from '@/lib/flows';
3
+
4
+ export async function POST(req: Request) {
5
+ const { name } = await req.json();
6
+ if (!name) {
7
+ return NextResponse.json({ error: 'Flow name required' }, { status: 400 });
8
+ }
9
+
10
+ try {
11
+ const result = runFlow(name);
12
+ return NextResponse.json({
13
+ flow: result.flow.name,
14
+ tasks: result.tasks.map(t => ({ id: t.id, projectName: t.projectName, prompt: t.prompt })),
15
+ });
16
+ } catch (err: any) {
17
+ return NextResponse.json({ error: err.message }, { status: 400 });
18
+ }
19
+ }
@@ -0,0 +1,149 @@
1
+ import { NextResponse, type NextRequest } from 'next/server';
2
+ import { execSync, exec } from 'node:child_process';
3
+ import { existsSync, readdirSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { homedir } from 'node:os';
6
+ import { promisify } from 'node:util';
7
+ import { loadSettings } from '@/lib/settings';
8
+
9
+ function isUnderProjectRoot(dir: string): boolean {
10
+ const settings = loadSettings();
11
+ const roots = (settings.projectRoots || []).map(r => r.replace(/^~/, homedir()));
12
+ return roots.some(root => dir.startsWith(root) || dir === root);
13
+ }
14
+
15
+ const execAsync = promisify(exec);
16
+
17
+ function gitSync(cmd: string, cwd: string): string {
18
+ return execSync(`git ${cmd}`, { cwd, encoding: 'utf-8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }).toString().trim();
19
+ }
20
+
21
+ async function gitAsync(cmd: string, cwd: string): Promise<string> {
22
+ try {
23
+ const { stdout } = await execAsync(`git ${cmd}`, { cwd, encoding: 'utf-8', timeout: 10000 });
24
+ return stdout.trim();
25
+ } catch { return ''; }
26
+ }
27
+
28
+ // GET /api/git?dir=<path> — git status for a project
29
+ export async function GET(req: NextRequest) {
30
+ const dir = req.nextUrl.searchParams.get('dir');
31
+ if (!dir || !isUnderProjectRoot(dir)) {
32
+ return NextResponse.json({ error: 'Invalid directory' }, { status: 400 });
33
+ }
34
+
35
+ try {
36
+ // Run all git commands in parallel
37
+ const [branchOut, statusOut, remoteOut, lastCommitOut, logOut, branchListOut] = await Promise.all([
38
+ gitAsync('rev-parse --abbrev-ref HEAD', dir),
39
+ gitAsync('status --porcelain -u', dir),
40
+ gitAsync('remote get-url origin', dir),
41
+ gitAsync('log -1 --format="%h %s"', dir),
42
+ gitAsync('log --format="%h||%s||%an||%ar" -20', dir),
43
+ gitAsync('branch --format="%(refname:short)||%(upstream:short)||%(objectname:short)"', dir),
44
+ ]);
45
+
46
+ const branch = branchOut;
47
+ const changes = statusOut ? statusOut.split('\n').filter(Boolean).map(line => ({
48
+ status: line.substring(0, 2).trim() || 'M',
49
+ path: line.substring(3).replace(/\/$/, ''),
50
+ })) : [];
51
+
52
+ let ahead = 0, behind = 0;
53
+ try {
54
+ const counts = await gitAsync(`rev-list --left-right --count HEAD...origin/${branch}`, dir);
55
+ if (counts) { const [a, b] = counts.split('\t'); ahead = parseInt(a) || 0; behind = parseInt(b) || 0; }
56
+ } catch {}
57
+
58
+ const log = logOut ? logOut.split('\n').filter(Boolean).map(line => {
59
+ const [hash, message, author, date] = line.split('||');
60
+ return { hash, message, author, date };
61
+ }) : [];
62
+
63
+ const branches = branchListOut ? branchListOut.split('\n').filter(Boolean).map(line => {
64
+ const [name, upstream, hash] = line.split('||');
65
+ return { name, upstream: upstream || '', hash: hash || '', current: name === branch };
66
+ }) : [];
67
+
68
+ return NextResponse.json({ branch, branches, changes, remote: remoteOut, ahead, behind, lastCommit: lastCommitOut, log });
69
+ } catch (e: any) {
70
+ return NextResponse.json({ error: e.message }, { status: 500 });
71
+ }
72
+ }
73
+
74
+ // POST /api/git — git operations (commit, push, pull, clone)
75
+ export async function POST(req: NextRequest) {
76
+ const body = await req.json();
77
+ const { action, dir, message, files, repoUrl, targetDir } = body;
78
+
79
+ if (action === 'clone') {
80
+ // Clone a repo into a project root
81
+ if (!repoUrl) return NextResponse.json({ error: 'repoUrl required' }, { status: 400 });
82
+ const settings = loadSettings();
83
+ const roots = (settings.projectRoots || []).map(r => r.replace(/^~/, homedir()));
84
+ const cloneTarget = targetDir || roots[0];
85
+ if (!cloneTarget) return NextResponse.json({ error: 'No project root configured' }, { status: 400 });
86
+
87
+ try {
88
+ const output = execSync(`git clone "${repoUrl}"`, {
89
+ cwd: cloneTarget,
90
+ encoding: 'utf-8',
91
+ timeout: 60000,
92
+ });
93
+ // Extract cloned dir name from URL
94
+ const repoName = repoUrl.split('/').pop()?.replace(/\.git$/, '') || 'repo';
95
+ return NextResponse.json({ ok: true, path: join(cloneTarget, repoName), output });
96
+ } catch (e: any) {
97
+ return NextResponse.json({ error: e.message }, { status: 500 });
98
+ }
99
+ }
100
+
101
+ if (!dir || !isUnderProjectRoot(dir)) {
102
+ return NextResponse.json({ error: 'Invalid directory' }, { status: 400 });
103
+ }
104
+
105
+ try {
106
+ if (action === 'commit') {
107
+ if (!message) return NextResponse.json({ error: 'message required' }, { status: 400 });
108
+ if (files && files.length > 0) {
109
+ for (const f of files) {
110
+ gitSync(`add "${f}"`, dir);
111
+ }
112
+ } else {
113
+ gitSync('add -A', dir);
114
+ }
115
+ gitSync(`commit -m "${message.replace(/"/g, '\\"')}"`, dir);
116
+ return NextResponse.json({ ok: true });
117
+ }
118
+
119
+ if (action === 'push') {
120
+ const output = gitSync('push', dir);
121
+ return NextResponse.json({ ok: true, output });
122
+ }
123
+
124
+ if (action === 'pull') {
125
+ const output = gitSync('pull', dir);
126
+ return NextResponse.json({ ok: true, output });
127
+ }
128
+
129
+ if (action === 'checkout') {
130
+ const branch = body.branch;
131
+ if (!branch) return NextResponse.json({ error: 'branch required' }, { status: 400 });
132
+ const out = gitSync(`checkout ${branch}`, dir);
133
+ return NextResponse.json({ ok: true, output: out });
134
+ }
135
+
136
+ if (action === 'stage') {
137
+ if (files && files.length > 0) {
138
+ for (const f of files) gitSync(`add "${f}"`, dir);
139
+ } else {
140
+ gitSync('add -A', dir);
141
+ }
142
+ return NextResponse.json({ ok: true });
143
+ }
144
+
145
+ return NextResponse.json({ error: 'Unknown action' }, { status: 400 });
146
+ } catch (e: any) {
147
+ return NextResponse.json({ error: e.message }, { status: 500 });
148
+ }
149
+ }
@@ -0,0 +1,84 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { getConfigDir, getDataDir } from '@/lib/dirs';
5
+ import { loadSettings } from '@/lib/settings';
6
+ import { execSync } from 'node:child_process';
7
+
8
+ const HELP_DIR = join(getConfigDir(), 'help');
9
+ const SOURCE_HELP_DIR = join(process.cwd(), 'lib', 'help-docs');
10
+
11
+ /** Ensure help docs are copied to ~/.forge/help/ and CLAUDE.md to ~/.forge/data/ */
12
+ function ensureHelpDocs() {
13
+ if (!existsSync(HELP_DIR)) mkdirSync(HELP_DIR, { recursive: true });
14
+ if (existsSync(SOURCE_HELP_DIR)) {
15
+ for (const file of readdirSync(SOURCE_HELP_DIR)) {
16
+ if (!file.endsWith('.md')) continue;
17
+ const src = join(SOURCE_HELP_DIR, file);
18
+ const dest = join(HELP_DIR, file);
19
+ writeFileSync(dest, readFileSync(src));
20
+ }
21
+ }
22
+ // Copy CLAUDE.md to data dir so Help AI (working in ~/.forge/data/) picks it up
23
+ const dataDir = getDataDir();
24
+ const claudeMdSrc = join(HELP_DIR, 'CLAUDE.md');
25
+ const claudeMdDest = join(dataDir, 'CLAUDE.md');
26
+ if (existsSync(claudeMdSrc)) {
27
+ writeFileSync(claudeMdDest, readFileSync(claudeMdSrc));
28
+ }
29
+ }
30
+
31
+ /** Check if any agent CLI is available */
32
+ function detectAgent(): { name: string; path: string } | null {
33
+ const settings = loadSettings();
34
+ if (settings.claudePath) {
35
+ try {
36
+ execSync(`"${settings.claudePath}" --version`, { timeout: 5000, stdio: 'pipe' });
37
+ return { name: 'claude', path: settings.claudePath };
38
+ } catch {}
39
+ }
40
+ for (const agent of ['claude', 'codex', 'aider']) {
41
+ try {
42
+ const path = execSync(`which ${agent}`, { encoding: 'utf-8', timeout: 3000, stdio: 'pipe' }).trim();
43
+ if (path) return { name: agent, path };
44
+ } catch {}
45
+ }
46
+ return null;
47
+ }
48
+
49
+ // GET /api/help
50
+ export async function GET(req: Request) {
51
+ const { searchParams } = new URL(req.url);
52
+ const action = searchParams.get('action') || 'status';
53
+
54
+ if (action === 'status') {
55
+ const agent = detectAgent();
56
+ ensureHelpDocs();
57
+ const docs = existsSync(HELP_DIR)
58
+ ? readdirSync(HELP_DIR).filter(f => f.endsWith('.md')).sort()
59
+ : [];
60
+ return NextResponse.json({ agent, docsCount: docs.length, helpDir: HELP_DIR, dataDir: getDataDir() });
61
+ }
62
+
63
+ if (action === 'docs') {
64
+ ensureHelpDocs();
65
+ const docs = existsSync(HELP_DIR)
66
+ ? readdirSync(HELP_DIR).filter(f => f.endsWith('.md')).sort().map(f => ({
67
+ name: f,
68
+ title: f.replace(/^\d+-/, '').replace(/\.md$/, '').replace(/-/g, ' '),
69
+ }))
70
+ : [];
71
+ return NextResponse.json({ docs });
72
+ }
73
+
74
+ if (action === 'doc') {
75
+ const name = searchParams.get('name');
76
+ if (!name) return NextResponse.json({ error: 'name required' }, { status: 400 });
77
+ ensureHelpDocs();
78
+ const file = join(HELP_DIR, name);
79
+ if (!existsSync(file)) return NextResponse.json({ error: 'Not found' }, { status: 404 });
80
+ return NextResponse.json({ content: readFileSync(file, 'utf-8') });
81
+ }
82
+
83
+ return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
84
+ }
@@ -0,0 +1,116 @@
1
+ import { NextResponse } from 'next/server';
2
+ import {
3
+ getConfig,
4
+ saveConfig,
5
+ listConfigs,
6
+ scanAndTrigger,
7
+ restartScanner,
8
+ getProcessedIssues,
9
+ resetProcessedIssue,
10
+ getNextScanTime,
11
+ type IssueAutofixConfig,
12
+ } from '@/lib/issue-scanner';
13
+
14
+ // GET /api/issue-scanner?project=PATH — get config + processed issues
15
+ export async function GET(req: Request) {
16
+ const { searchParams } = new URL(req.url);
17
+ const projectPath = searchParams.get('project');
18
+
19
+ if (projectPath) {
20
+ const config = getConfig(projectPath);
21
+ const processed = getProcessedIssues(projectPath);
22
+ const scanTime = getNextScanTime(projectPath);
23
+ return NextResponse.json({ config, processed, ...scanTime });
24
+ }
25
+
26
+ // List all enabled configs
27
+ const configs = listConfigs();
28
+ return NextResponse.json({ configs });
29
+ }
30
+
31
+ // POST /api/issue-scanner
32
+ export async function POST(req: Request) {
33
+ const body = await req.json();
34
+
35
+ // Save config
36
+ if (body.action === 'save-config') {
37
+ const config: IssueAutofixConfig = {
38
+ projectPath: body.projectPath,
39
+ projectName: body.projectName,
40
+ enabled: !!body.enabled,
41
+ interval: body.interval || 30,
42
+ labels: body.labels || [],
43
+ baseBranch: body.baseBranch || '',
44
+ };
45
+ saveConfig(config);
46
+ restartScanner();
47
+ return NextResponse.json({ ok: true });
48
+ }
49
+
50
+ // Manual scan & trigger
51
+ if (body.action === 'scan') {
52
+ const config = getConfig(body.projectPath);
53
+ if (!config) return NextResponse.json({ error: 'Not configured' }, { status: 400 });
54
+ const result = scanAndTrigger(config);
55
+ return NextResponse.json(result);
56
+ }
57
+
58
+ // Manual trigger for a specific issue
59
+ if (body.action === 'trigger') {
60
+ const { startPipeline } = require('@/lib/pipeline');
61
+ const config = getConfig(body.projectPath);
62
+ const projectName = config?.projectName || body.projectName;
63
+ try {
64
+ const pipeline = startPipeline('issue-fix-and-review', {
65
+ issue_id: String(body.issueId),
66
+ project: projectName,
67
+ base_branch: config?.baseBranch || body.baseBranch || 'auto-detect',
68
+ });
69
+ // Track in processed issues
70
+ const { getDb } = require('@/src/core/db/database');
71
+ const { getDbPath } = require('@/src/config');
72
+ getDb(getDbPath()).prepare(`
73
+ INSERT OR REPLACE INTO issue_autofix_processed (project_path, issue_number, pipeline_id, status)
74
+ VALUES (?, ?, ?, 'processing')
75
+ `).run(body.projectPath, body.issueId, pipeline.id);
76
+ return NextResponse.json({ ok: true, pipelineId: pipeline.id });
77
+ } catch (e) {
78
+ return NextResponse.json({ ok: false, error: String(e) }, { status: 500 });
79
+ }
80
+ }
81
+
82
+ // Reset a processed issue (allow re-scan)
83
+ if (body.action === 'reset') {
84
+ resetProcessedIssue(body.projectPath, body.issueId);
85
+ return NextResponse.json({ ok: true });
86
+ }
87
+
88
+ // Retry with additional context/instructions
89
+ if (body.action === 'retry') {
90
+ const { startPipeline } = require('@/lib/pipeline');
91
+ const config = getConfig(body.projectPath);
92
+ const projectName = config?.projectName || body.projectName;
93
+ // Reset the processed record first, then re-create with new pipeline
94
+ resetProcessedIssue(body.projectPath, body.issueId);
95
+ try {
96
+ const pipeline = startPipeline('issue-fix-and-review', {
97
+ issue_id: String(body.issueId),
98
+ project: projectName,
99
+ base_branch: config?.baseBranch || 'auto-detect',
100
+ extra_context: body.context || '',
101
+ });
102
+ // Re-mark as processed with new pipeline ID
103
+ const { getDb } = require('@/src/core/db/database');
104
+ const { getDbPath } = require('@/src/config');
105
+ getDb(getDbPath()).prepare(`
106
+ INSERT OR REPLACE INTO issue_autofix_processed (project_path, issue_number, pipeline_id, status)
107
+ VALUES (?, ?, ?, 'processing')
108
+ `).run(body.projectPath, body.issueId, pipeline.id);
109
+ return NextResponse.json({ ok: true, pipelineId: pipeline.id });
110
+ } catch (e) {
111
+ return NextResponse.json({ ok: false, error: String(e) }, { status: 500 });
112
+ }
113
+ }
114
+
115
+ return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
116
+ }