@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.
- package/.forge/worktrees/pipeline-4dd8dc2d/CLAUDE.md +86 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/README.md +136 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/RELEASE_NOTES.md +36 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/agents/route.ts +17 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/[...nextauth]/route.ts +3 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/verify/route.ts +46 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/route.ts +31 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/stream/route.ts +63 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/route.ts +28 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/route.ts +37 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/sync/route.ts +17 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-templates/route.ts +145 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/code/route.ts +299 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/[id]/route.ts +62 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/route.ts +40 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/detect-cli/route.ts +46 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/route.ts +176 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/sessions/route.ts +54 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/favorites/route.ts +26 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/route.ts +6 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/run/route.ts +19 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/git/route.ts +149 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/help/route.ts +84 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/issue-scanner/route.ts +116 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/logs/route.ts +100 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/mobile-chat/route.ts +115 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/monitor/route.ts +74 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notifications/route.ts +42 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notify/test/route.ts +33 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/online/route.ts +40 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/[id]/route.ts +41 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/route.ts +90 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/plugins/route.ts +75 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/[...path]/route.ts +64 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/route.ts +156 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-pipelines/route.ts +91 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-sessions/route.ts +61 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/projects/route.ts +26 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/chat/route.ts +64 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/messages/route.ts +9 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/route.ts +17 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/route.ts +20 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/settings/route.ts +64 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/local/route.ts +228 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/route.ts +182 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/smith-templates/route.ts +81 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/status/route.ts +12 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tabs/route.ts +25 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/route.ts +51 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/stream/route.ts +77 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/link/route.ts +37 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/route.ts +44 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/session/route.ts +14 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/telegram/route.ts +23 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/templates/route.ts +6 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-bell/route.ts +39 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-cwd/route.ts +19 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-state/route.ts +15 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tunnel/route.ts +26 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/upgrade/route.ts +43 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/usage/route.ts +20 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/version/route.ts +78 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/watchers/route.ts +33 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/agents/route.ts +35 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/memory/route.ts +23 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/smith/route.ts +22 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/stream/route.ts +31 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/route.ts +79 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/global-error.tsx +21 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/globals.css +52 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.ico +0 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.png +0 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.svg +106 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/layout.tsx +17 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/login/LoginForm.tsx +96 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/login/page.tsx +10 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/mobile/page.tsx +10 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/app/page.tsx +22 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/bin/forge-server.mjs +484 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/check-forge-status.sh +71 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/cli/mw.ts +579 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/BrowserPanel.tsx +175 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ChatPanel.tsx +191 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ClaudeTerminal.tsx +267 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/CodeViewer.tsx +787 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationEditor.tsx +411 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationGraphView.tsx +347 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationTerminalView.tsx +303 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/Dashboard.tsx +807 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DashboardWrapper.tsx +9 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryFlowEditor.tsx +491 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryList.tsx +230 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryWorkspace.tsx +589 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DocTerminal.tsx +187 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/DocsViewer.tsx +574 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpDialog.tsx +169 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpTerminal.tsx +141 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/InlinePipelineView.tsx +111 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/LogViewer.tsx +194 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/MarkdownContent.tsx +73 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/MobileView.tsx +385 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/MonitorPanel.tsx +122 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/NewSessionModal.tsx +93 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/NewTaskModal.tsx +492 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineEditor.tsx +570 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineView.tsx +1018 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/PluginsPanel.tsx +472 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectDetail.tsx +1618 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectList.tsx +108 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectManager.tsx +401 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionList.tsx +74 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionView.tsx +726 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/SettingsModal.tsx +1647 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/SkillsPanel.tsx +969 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/StatusBar.tsx +99 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/TabBar.tsx +46 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskBoard.tsx +113 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskDetail.tsx +372 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/TerminalLauncher.tsx +398 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/TunnelToggle.tsx +206 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/UsagePanel.tsx +207 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/WebTerminal.tsx +1743 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceTree.tsx +221 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceView.tsx +4048 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/dev-test.sh +5 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Memory_Layer_Design.docx +0 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Strategy_Research_2026.docx +0 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/docs/LOCAL-DEPLOY.md +144 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/docs/roadmap-multi-agent-workflow.md +330 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.png +0 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.svg +106 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/hooks/useSidebarResize.ts +52 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/install.sh +29 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/instrumentation.ts +35 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/claude-adapter.ts +104 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/generic-adapter.ts +64 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/index.ts +245 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/types.ts +70 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/artifacts.ts +106 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/auth.ts +62 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/docker.yaml +70 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/http.yaml +66 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/jenkins.yaml +92 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/playwright.yaml +111 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/shell-command.yaml +60 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/slack.yaml +48 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/webhook.yaml +56 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-process.ts +361 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-sessions.ts +266 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-templates.ts +227 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/cloudflared.ts +424 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/crypto.ts +67 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/delivery.ts +787 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/dirs.ts +99 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/flows.ts +86 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-mcp-server.ts +732 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-inbox.md +38 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-send.md +47 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-status.md +32 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-workspace-sync.md +37 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/00-overview.md +40 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/01-settings.md +194 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/02-telegram.md +41 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/03-tunnel.md +31 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/04-tasks.md +52 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/05-pipelines.md +460 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/06-skills.md +43 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/07-projects.md +73 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/08-rules.md +53 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/09-issue-autofix.md +55 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/10-troubleshooting.md +89 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/11-workspace.md +810 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/CLAUDE.md +62 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/init.ts +266 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/issue-scanner.ts +298 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/logger.ts +79 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/notifications.ts +75 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/notify.ts +108 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/password.ts +97 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline-scheduler.ts +373 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline.ts +1565 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/executor.ts +347 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/registry.ts +228 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/types.ts +103 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/project-sessions.ts +53 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/projects.ts +86 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-manager.ts +156 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-utils.ts +53 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-watcher.ts +345 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/settings.ts +195 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/skills.ts +458 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/task-manager.ts +951 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-bot.ts +1477 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-standalone.ts +83 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-server.ts +70 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-standalone.ts +438 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/usage-scanner.ts +249 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/state-machine.test.ts +388 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/workspace.test.ts +311 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-bus.ts +416 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-worker.ts +655 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/api-backend.ts +262 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/cli-backend.ts +491 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/index.ts +84 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/manager.ts +136 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/orchestrator.ts +3415 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/persistence.ts +309 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/presets.ts +649 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/requests.ts +287 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/session-monitor.ts +240 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/skill-installer.ts +275 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/smith-memory.ts +498 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/types.ts +241 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/watch-manager.ts +560 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace-standalone.ts +978 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/middleware.ts +51 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/next.config.ts +26 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/package.json +74 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-lock.yaml +3719 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-workspace.yaml +1 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/postcss.config.mjs +7 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/publish.sh +133 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/README.md +66 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/results/.gitignore +2 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/run.ts +635 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/task.md +26 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/setup.sh +19 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/task.md +48 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/validator.sh +69 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/task.md +30 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/scripts/verify-usage.ts +178 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/config/index.ts +129 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/core/db/database.ts +259 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/core/memory/strategy.ts +32 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/chat.ts +65 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/registry.ts +60 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/core/session/manager.ts +190 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/src/types/index.ts +129 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/start.sh +32 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/templates/smith-lead.json +45 -0
- package/.forge/worktrees/pipeline-4dd8dc2d/tsconfig.json +42 -0
- package/RELEASE_NOTES.md +11 -28
- package/app/api/terminal-bell/route.ts +6 -2
- package/components/WebTerminal.tsx +36 -2
- package/lib/terminal-standalone.ts +19 -2
- package/next-env.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import {
|
|
3
|
+
listTemplates,
|
|
4
|
+
getTemplate,
|
|
5
|
+
saveTemplate,
|
|
6
|
+
deleteTemplate,
|
|
7
|
+
isInjected,
|
|
8
|
+
getInjectedTemplates,
|
|
9
|
+
injectTemplate,
|
|
10
|
+
removeTemplate,
|
|
11
|
+
setTemplateDefault,
|
|
12
|
+
applyDefaultTemplates,
|
|
13
|
+
} from '@/lib/claude-templates';
|
|
14
|
+
import { loadSettings } from '@/lib/settings';
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
|
|
19
|
+
function getProjectPaths(): { name: string; path: string }[] {
|
|
20
|
+
const settings = loadSettings();
|
|
21
|
+
const roots = (settings.projectRoots || []).map((r: string) => r.replace(/^~/, homedir()));
|
|
22
|
+
const projects: { name: string; path: string }[] = [];
|
|
23
|
+
for (const root of roots) {
|
|
24
|
+
try {
|
|
25
|
+
const { readdirSync, statSync } = require('node:fs');
|
|
26
|
+
for (const name of readdirSync(root)) {
|
|
27
|
+
const p = join(root, name);
|
|
28
|
+
try { if (statSync(p).isDirectory() && !name.startsWith('.')) projects.push({ name, path: p }); } catch {}
|
|
29
|
+
}
|
|
30
|
+
} catch {}
|
|
31
|
+
}
|
|
32
|
+
return projects.sort((a, b) => a.name.localeCompare(b.name));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// GET /api/claude-templates
|
|
36
|
+
// ?action=list — list all templates
|
|
37
|
+
// ?action=status&project=PATH — get injection status for a project
|
|
38
|
+
// ?action=read-claude-md&project=PATH — read project's CLAUDE.md content
|
|
39
|
+
export async function GET(req: Request) {
|
|
40
|
+
const { searchParams } = new URL(req.url);
|
|
41
|
+
const action = searchParams.get('action') || 'list';
|
|
42
|
+
|
|
43
|
+
if (action === 'list') {
|
|
44
|
+
const templates = listTemplates();
|
|
45
|
+
const projects = getProjectPaths();
|
|
46
|
+
return NextResponse.json({ templates, projects });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (action === 'status') {
|
|
50
|
+
const projectPath = searchParams.get('project');
|
|
51
|
+
if (!projectPath) return NextResponse.json({ error: 'project required' }, { status: 400 });
|
|
52
|
+
const claudeMd = join(projectPath, 'CLAUDE.md');
|
|
53
|
+
const injected = getInjectedTemplates(claudeMd);
|
|
54
|
+
const templates = listTemplates();
|
|
55
|
+
const status = templates.map(t => ({
|
|
56
|
+
id: t.id,
|
|
57
|
+
name: t.name,
|
|
58
|
+
injected: injected.includes(t.id),
|
|
59
|
+
}));
|
|
60
|
+
return NextResponse.json({ status, hasClaudeMd: existsSync(claudeMd) });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (action === 'read-claude-md') {
|
|
64
|
+
const projectPath = searchParams.get('project');
|
|
65
|
+
if (!projectPath) return NextResponse.json({ error: 'project required' }, { status: 400 });
|
|
66
|
+
const claudeMd = join(projectPath, 'CLAUDE.md');
|
|
67
|
+
if (!existsSync(claudeMd)) return NextResponse.json({ content: '', exists: false });
|
|
68
|
+
return NextResponse.json({ content: readFileSync(claudeMd, 'utf-8'), exists: true });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// POST /api/claude-templates
|
|
75
|
+
export async function POST(req: Request) {
|
|
76
|
+
const body = await req.json();
|
|
77
|
+
|
|
78
|
+
// Save/create template
|
|
79
|
+
if (body.action === 'save') {
|
|
80
|
+
const { id, name, description, tags, content, isDefault } = body;
|
|
81
|
+
if (!id || !name || !content) return NextResponse.json({ error: 'id, name, content required' }, { status: 400 });
|
|
82
|
+
saveTemplate(id, name, description || '', tags || [], content, isDefault);
|
|
83
|
+
return NextResponse.json({ ok: true });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Toggle default flag
|
|
87
|
+
if (body.action === 'set-default') {
|
|
88
|
+
const { id, isDefault } = body;
|
|
89
|
+
if (!id) return NextResponse.json({ error: 'id required' }, { status: 400 });
|
|
90
|
+
const ok = setTemplateDefault(id, !!isDefault);
|
|
91
|
+
return NextResponse.json({ ok });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Apply default templates to a project
|
|
95
|
+
if (body.action === 'apply-defaults') {
|
|
96
|
+
const { project } = body;
|
|
97
|
+
if (!project) return NextResponse.json({ error: 'project required' }, { status: 400 });
|
|
98
|
+
const injected = applyDefaultTemplates(project);
|
|
99
|
+
return NextResponse.json({ ok: true, injected });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Delete template
|
|
103
|
+
if (body.action === 'delete') {
|
|
104
|
+
const ok = deleteTemplate(body.id);
|
|
105
|
+
if (!ok) return NextResponse.json({ error: 'Cannot delete (builtin or not found)' }, { status: 400 });
|
|
106
|
+
return NextResponse.json({ ok: true });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Inject template into project(s)
|
|
110
|
+
if (body.action === 'inject') {
|
|
111
|
+
const { templateId, projects } = body; // projects: string[] of paths
|
|
112
|
+
if (!templateId || !projects?.length) return NextResponse.json({ error: 'templateId and projects required' }, { status: 400 });
|
|
113
|
+
const results: { project: string; injected: boolean; reason?: string }[] = [];
|
|
114
|
+
for (const projectPath of projects) {
|
|
115
|
+
const claudeMd = join(projectPath, 'CLAUDE.md');
|
|
116
|
+
if (isInjected(claudeMd, templateId)) {
|
|
117
|
+
results.push({ project: projectPath, injected: false, reason: 'already exists' });
|
|
118
|
+
} else {
|
|
119
|
+
const ok = injectTemplate(claudeMd, templateId);
|
|
120
|
+
results.push({ project: projectPath, injected: ok });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return NextResponse.json({ ok: true, results });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Remove template from project
|
|
127
|
+
if (body.action === 'remove') {
|
|
128
|
+
const { templateId, project } = body;
|
|
129
|
+
if (!templateId || !project) return NextResponse.json({ error: 'templateId and project required' }, { status: 400 });
|
|
130
|
+
const claudeMd = join(project, 'CLAUDE.md');
|
|
131
|
+
const ok = removeTemplate(claudeMd, templateId);
|
|
132
|
+
return NextResponse.json({ ok });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Save CLAUDE.md content directly
|
|
136
|
+
if (body.action === 'save-claude-md') {
|
|
137
|
+
const { project, content } = body;
|
|
138
|
+
if (!project) return NextResponse.json({ error: 'project required' }, { status: 400 });
|
|
139
|
+
const { writeFileSync: wf } = require('node:fs');
|
|
140
|
+
wf(join(project, 'CLAUDE.md'), content, 'utf-8');
|
|
141
|
+
return NextResponse.json({ ok: true });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
|
|
145
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join, relative, extname } from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { execSync } from 'node:child_process';
|
|
6
|
+
import { loadSettings } from '@/lib/settings';
|
|
7
|
+
|
|
8
|
+
interface FileNode {
|
|
9
|
+
name: string;
|
|
10
|
+
path: string;
|
|
11
|
+
type: 'file' | 'dir';
|
|
12
|
+
children?: FileNode[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const IGNORE = new Set([
|
|
16
|
+
'node_modules', '.git', '.next', 'dist', 'build', '.idea', '.vscode',
|
|
17
|
+
'.DS_Store', 'coverage', '__pycache__', '.cache', '.output', 'target',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
const CODE_EXTS = new Set([
|
|
21
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
22
|
+
'.py', '.go', '.rs', '.java', '.kt', '.swift', '.c', '.cpp', '.h',
|
|
23
|
+
'.css', '.scss', '.html', '.json', '.yaml', '.yml', '.toml',
|
|
24
|
+
'.md', '.txt', '.sh', '.bash', '.zsh', '.fish',
|
|
25
|
+
'.sql', '.graphql', '.proto', '.env', '.gitignore',
|
|
26
|
+
'.xml', '.csv', '.lock', '.properties', '.gradle', '.groovy',
|
|
27
|
+
'.scala', '.clj', '.cljs', '.jsp', '.erb', '.vue', '.svelte',
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.bmp', '.ico', '.avif']);
|
|
31
|
+
|
|
32
|
+
function isCodeFile(name: string): boolean {
|
|
33
|
+
if (name.startsWith('.') && !name.startsWith('.env') && !name.startsWith('.git')) return false;
|
|
34
|
+
const ext = extname(name);
|
|
35
|
+
if (!ext) return !name.includes('.'); // files like Makefile, Dockerfile
|
|
36
|
+
return CODE_EXTS.has(ext) || IMAGE_EXTS.has(ext);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function scanDir(dir: string, base: string, depth: number = 0): FileNode[] {
|
|
40
|
+
if (depth > 10) return [];
|
|
41
|
+
try {
|
|
42
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
43
|
+
const nodes: FileNode[] = [];
|
|
44
|
+
|
|
45
|
+
const sorted = entries
|
|
46
|
+
.filter(e => !IGNORE.has(e.name))
|
|
47
|
+
.sort((a, b) => {
|
|
48
|
+
if (a.isDirectory() && !b.isDirectory()) return -1;
|
|
49
|
+
if (!a.isDirectory() && b.isDirectory()) return 1;
|
|
50
|
+
return a.name.localeCompare(b.name);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
for (const entry of sorted) {
|
|
54
|
+
const fullPath = join(dir, entry.name);
|
|
55
|
+
const relPath = relative(base, fullPath);
|
|
56
|
+
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
const children = scanDir(fullPath, base, depth + 1);
|
|
59
|
+
nodes.push({ name: entry.name, path: relPath, type: 'dir', children });
|
|
60
|
+
} else {
|
|
61
|
+
nodes.push({ name: entry.name, path: relPath, type: 'file' });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return nodes;
|
|
65
|
+
} catch {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// GET /api/code?dir=<absolute-path>&file=<relative-path>
|
|
71
|
+
// dir mode: returns file tree for the given directory
|
|
72
|
+
// file mode: returns file content
|
|
73
|
+
export async function GET(req: Request) {
|
|
74
|
+
const { searchParams } = new URL(req.url);
|
|
75
|
+
const dir = searchParams.get('dir');
|
|
76
|
+
const filePath = searchParams.get('file');
|
|
77
|
+
|
|
78
|
+
if (!dir) {
|
|
79
|
+
return NextResponse.json({ tree: [], dirName: '' });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const resolvedDir = dir.replace(/^~/, homedir());
|
|
83
|
+
|
|
84
|
+
// Security: dir must be under a projectRoot
|
|
85
|
+
const settings = loadSettings();
|
|
86
|
+
const projectRoots = (settings.projectRoots || []).map(r => r.replace(/^~/, homedir()));
|
|
87
|
+
const allowed = projectRoots.some(root => resolvedDir.startsWith(root) || resolvedDir === root);
|
|
88
|
+
if (!allowed) {
|
|
89
|
+
return NextResponse.json({ error: 'Directory not under any project root' }, { status: 403 });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Code search (grep)
|
|
93
|
+
const searchQuery = searchParams.get('search');
|
|
94
|
+
if (searchQuery) {
|
|
95
|
+
try {
|
|
96
|
+
const { execSync } = require('node:child_process');
|
|
97
|
+
const safeQuery = searchQuery.replace(/['"\\]/g, '\\$&');
|
|
98
|
+
// Use grep -rn with limits to prevent huge output
|
|
99
|
+
let result = '';
|
|
100
|
+
try {
|
|
101
|
+
result = execSync(
|
|
102
|
+
`grep -rn --exclude-dir=node_modules --exclude-dir=.next --exclude-dir=.git --exclude-dir=dist --exclude-dir=build --include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' --include='*.py' --include='*.java' --include='*.go' --include='*.rs' --include='*.md' --include='*.json' --include='*.yaml' --include='*.yml' --include='*.css' --include='*.html' --include='*.vue' --include='*.svelte' -m 5 '${safeQuery}' . | head -100`,
|
|
103
|
+
{ cwd: resolvedDir, encoding: 'utf-8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
104
|
+
).trim();
|
|
105
|
+
} catch (e: any) {
|
|
106
|
+
// grep exit code 1 = no match (not an error)
|
|
107
|
+
result = e.stdout?.trim() || '';
|
|
108
|
+
}
|
|
109
|
+
const matches = result ? result.split('\n').map((line: string) => {
|
|
110
|
+
const match = line.match(/^\.\/(.+?):(\d+):(.*)$/);
|
|
111
|
+
if (!match) return null;
|
|
112
|
+
return { file: match[1], line: parseInt(match[2]), content: match[3].trim().slice(0, 200) };
|
|
113
|
+
}).filter(Boolean) : [];
|
|
114
|
+
return NextResponse.json({ matches });
|
|
115
|
+
} catch {
|
|
116
|
+
return NextResponse.json({ matches: [] });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Git diff for a specific file
|
|
121
|
+
const diffFile = searchParams.get('diff');
|
|
122
|
+
if (diffFile) {
|
|
123
|
+
const fullPath = join(resolvedDir, diffFile);
|
|
124
|
+
if (!fullPath.startsWith(resolvedDir)) {
|
|
125
|
+
return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
// Try staged + unstaged diff
|
|
129
|
+
let diff = '';
|
|
130
|
+
try { diff = execSync(`git diff -- "${diffFile}"`, { cwd: resolvedDir, encoding: 'utf-8', timeout: 5000 }); } catch {}
|
|
131
|
+
if (!diff) {
|
|
132
|
+
try { diff = execSync(`git diff HEAD -- "${diffFile}"`, { cwd: resolvedDir, encoding: 'utf-8', timeout: 5000 }); } catch {}
|
|
133
|
+
}
|
|
134
|
+
if (!diff) {
|
|
135
|
+
// Untracked file — show entire content as added
|
|
136
|
+
try {
|
|
137
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
138
|
+
diff = content.split('\n').map(l => `+${l}`).join('\n');
|
|
139
|
+
} catch {}
|
|
140
|
+
}
|
|
141
|
+
return NextResponse.json({ diff: diff || 'No changes' });
|
|
142
|
+
} catch {
|
|
143
|
+
return NextResponse.json({ diff: 'Failed to get diff' });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Read file content
|
|
148
|
+
if (filePath) {
|
|
149
|
+
const fullPath = join(resolvedDir, filePath);
|
|
150
|
+
if (!fullPath.startsWith(resolvedDir)) {
|
|
151
|
+
return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const stat = statSync(fullPath);
|
|
155
|
+
const ext = extname(fullPath).replace('.', '').toLowerCase();
|
|
156
|
+
const size = stat.size;
|
|
157
|
+
const sizeKB = Math.round(size / 1024);
|
|
158
|
+
const sizeMB = (size / (1024 * 1024)).toFixed(1);
|
|
159
|
+
|
|
160
|
+
// Binary/unsupported file types
|
|
161
|
+
const BINARY_EXTS = new Set([
|
|
162
|
+
'png', 'jpg', 'jpeg', 'gif', 'bmp', 'ico', 'webp', 'svg', 'avif',
|
|
163
|
+
'mp3', 'mp4', 'wav', 'ogg', 'webm', 'mov', 'avi',
|
|
164
|
+
'zip', 'gz', 'tar', 'bz2', 'xz', '7z', 'rar',
|
|
165
|
+
'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
|
|
166
|
+
'exe', 'dll', 'so', 'dylib', 'bin', 'o', 'a',
|
|
167
|
+
'woff', 'woff2', 'ttf', 'eot', 'otf',
|
|
168
|
+
'sqlite', 'db', 'sqlite3',
|
|
169
|
+
'class', 'jar', 'war',
|
|
170
|
+
'pyc', 'pyo', 'wasm',
|
|
171
|
+
]);
|
|
172
|
+
// Image files — return base64 for preview
|
|
173
|
+
const IMAGE_PREVIEW = new Set(['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'bmp', 'ico', 'avif']);
|
|
174
|
+
if (IMAGE_PREVIEW.has(ext)) {
|
|
175
|
+
if (size > 5_000_000) {
|
|
176
|
+
return NextResponse.json({ binary: true, fileType: ext, size, sizeLabel: `${sizeMB} MB`, message: 'Image too large to preview (> 5 MB)' });
|
|
177
|
+
}
|
|
178
|
+
const data = readFileSync(fullPath);
|
|
179
|
+
const mime = ext === 'svg' ? 'image/svg+xml' : `image/${ext === 'jpg' ? 'jpeg' : ext}`;
|
|
180
|
+
const base64 = `data:${mime};base64,${data.toString('base64')}`;
|
|
181
|
+
return NextResponse.json({ image: true, dataUrl: base64, fileType: ext, size, sizeLabel: sizeKB > 1024 ? `${sizeMB} MB` : `${sizeKB} KB` });
|
|
182
|
+
}
|
|
183
|
+
if (BINARY_EXTS.has(ext)) {
|
|
184
|
+
return NextResponse.json({ binary: true, fileType: ext, size, sizeLabel: sizeKB > 1024 ? `${sizeMB} MB` : `${sizeKB} KB` });
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const force = searchParams.get('force') === '1';
|
|
188
|
+
|
|
189
|
+
// Large file warning (> 200KB needs confirmation, > 2MB blocked)
|
|
190
|
+
if (size > 2_000_000) {
|
|
191
|
+
return NextResponse.json({ tooLarge: true, size, sizeLabel: `${sizeMB} MB`, message: 'File exceeds 2 MB limit' });
|
|
192
|
+
}
|
|
193
|
+
if (size > 200_000 && !force) {
|
|
194
|
+
return NextResponse.json({ large: true, size, sizeLabel: `${sizeKB} KB`, language: ext });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
198
|
+
return NextResponse.json({ content, language: ext, size });
|
|
199
|
+
} catch {
|
|
200
|
+
return NextResponse.json({ error: 'File not found' }, { status: 404 });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Return file tree
|
|
205
|
+
const tree = scanDir(resolvedDir, resolvedDir);
|
|
206
|
+
const dirName = resolvedDir.split('/').pop() || resolvedDir;
|
|
207
|
+
|
|
208
|
+
// Git status: scan for git repos (could be root dir or subdirectories)
|
|
209
|
+
interface GitRepo {
|
|
210
|
+
name: string; // repo dir name (or '.' for root)
|
|
211
|
+
branch: string;
|
|
212
|
+
remote: string; // remote URL
|
|
213
|
+
changes: { path: string; status: string }[];
|
|
214
|
+
}
|
|
215
|
+
const gitRepos: GitRepo[] = [];
|
|
216
|
+
|
|
217
|
+
const gitOpts = { encoding: 'utf-8' as const, timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] as any };
|
|
218
|
+
|
|
219
|
+
function scanGitStatus(dir: string, repoName: string, pathPrefix: string) {
|
|
220
|
+
try {
|
|
221
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', { ...gitOpts, cwd: dir, timeout: 3000 }).toString().trim();
|
|
222
|
+
const statusOut = execSync('git status --porcelain -u', { ...gitOpts, cwd: dir }).toString();
|
|
223
|
+
const changes = statusOut.replace(/\n$/, '').split('\n').filter(Boolean)
|
|
224
|
+
.map(line => {
|
|
225
|
+
if (line.length < 4) return null;
|
|
226
|
+
return {
|
|
227
|
+
status: line.substring(0, 2).trim() || 'M',
|
|
228
|
+
path: pathPrefix ? `${pathPrefix}/${line.substring(3).replace(/\/$/, '')}` : line.substring(3).replace(/\/$/, ''),
|
|
229
|
+
};
|
|
230
|
+
})
|
|
231
|
+
.filter((g): g is { status: string; path: string } => g !== null && !!g.path && !g.path.includes(' -> '));
|
|
232
|
+
let remote = '';
|
|
233
|
+
try { remote = execSync('git remote get-url origin', { ...gitOpts, cwd: dir, timeout: 2000 }).toString().trim(); } catch {}
|
|
234
|
+
if (branch || changes.length > 0) {
|
|
235
|
+
gitRepos.push({ name: repoName, branch, remote, changes });
|
|
236
|
+
}
|
|
237
|
+
} catch {}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Check if root is a git repo
|
|
241
|
+
try {
|
|
242
|
+
execSync('git rev-parse --git-dir', { cwd: resolvedDir, encoding: 'utf-8', timeout: 2000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
243
|
+
scanGitStatus(resolvedDir, '.', '');
|
|
244
|
+
} catch {
|
|
245
|
+
// Root is not a git repo — scan subdirectories
|
|
246
|
+
try {
|
|
247
|
+
for (const entry of readdirSync(resolvedDir, { withFileTypes: true })) {
|
|
248
|
+
if (!entry.isDirectory() || entry.name.startsWith('.') || IGNORE.has(entry.name)) continue;
|
|
249
|
+
const subDir = join(resolvedDir, entry.name);
|
|
250
|
+
try {
|
|
251
|
+
execSync('git rev-parse --git-dir', { cwd: subDir, encoding: 'utf-8', timeout: 2000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
252
|
+
scanGitStatus(subDir, entry.name, entry.name);
|
|
253
|
+
} catch {}
|
|
254
|
+
}
|
|
255
|
+
} catch {}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Flatten for backward compat
|
|
259
|
+
const gitChanges = gitRepos.flatMap(r => r.changes);
|
|
260
|
+
const gitBranch = gitRepos.length === 1 ? gitRepos[0].branch : '';
|
|
261
|
+
|
|
262
|
+
return NextResponse.json({ tree, dirName, dirPath: resolvedDir, gitBranch, gitChanges, gitRepos });
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// PUT /api/code — save file content
|
|
266
|
+
export async function PUT(req: Request) {
|
|
267
|
+
const { dir, file, content } = await req.json() as {
|
|
268
|
+
dir: string;
|
|
269
|
+
file: string;
|
|
270
|
+
content: string;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
if (!dir || !file) {
|
|
274
|
+
return NextResponse.json({ error: 'Invalid request' }, { status: 400 });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const resolvedDir = dir.replace(/^~/, homedir());
|
|
278
|
+
const fullPath = join(resolvedDir, file);
|
|
279
|
+
|
|
280
|
+
// Security: ensure path is within the directory
|
|
281
|
+
if (!fullPath.startsWith(resolvedDir)) {
|
|
282
|
+
return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Verify dir is under a configured project root
|
|
286
|
+
const settings = loadSettings();
|
|
287
|
+
const roots = (settings.projectRoots || []).map(r => r.replace(/^~/, homedir()));
|
|
288
|
+
const allowed = roots.some(r => resolvedDir.startsWith(r));
|
|
289
|
+
if (!allowed) {
|
|
290
|
+
return NextResponse.json({ error: 'Directory not in project roots' }, { status: 403 });
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
writeFileSync(fullPath, content, 'utf-8');
|
|
295
|
+
return NextResponse.json({ ok: true });
|
|
296
|
+
} catch {
|
|
297
|
+
return NextResponse.json({ error: 'Failed to save' }, { status: 500 });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import {
|
|
3
|
+
getDelivery, cancelDelivery, deleteDelivery,
|
|
4
|
+
approveDeliveryPhase, rejectDeliveryPhase,
|
|
5
|
+
sendToAgent, retryPhase,
|
|
6
|
+
} from '@/lib/delivery';
|
|
7
|
+
import { listArtifacts, getArtifact } from '@/lib/artifacts';
|
|
8
|
+
import type { PhaseName } from '@/lib/delivery';
|
|
9
|
+
|
|
10
|
+
// GET /api/delivery/:id — get delivery state + artifacts
|
|
11
|
+
export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
12
|
+
const { id } = await params;
|
|
13
|
+
const delivery = getDelivery(id);
|
|
14
|
+
if (!delivery) return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
|
15
|
+
|
|
16
|
+
const artifacts = listArtifacts(id);
|
|
17
|
+
return NextResponse.json({ ...delivery, artifacts });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// POST /api/delivery/:id — actions
|
|
21
|
+
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
22
|
+
const { id } = await params;
|
|
23
|
+
const body = await req.json();
|
|
24
|
+
const { action } = body;
|
|
25
|
+
|
|
26
|
+
if (action === 'cancel') {
|
|
27
|
+
return NextResponse.json({ ok: cancelDelivery(id) });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (action === 'delete') {
|
|
31
|
+
return NextResponse.json({ ok: deleteDelivery(id) });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (action === 'approve') {
|
|
35
|
+
return NextResponse.json({ ok: approveDeliveryPhase(id, body.feedback) });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (action === 'reject') {
|
|
39
|
+
if (!body.feedback) return NextResponse.json({ error: 'feedback required' }, { status: 400 });
|
|
40
|
+
return NextResponse.json({ ok: rejectDeliveryPhase(id, body.feedback) });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (action === 'send') {
|
|
44
|
+
const { phase, message } = body;
|
|
45
|
+
if (!phase || !message) return NextResponse.json({ error: 'phase and message required' }, { status: 400 });
|
|
46
|
+
return NextResponse.json({ ok: sendToAgent(id, phase as PhaseName, message) });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (action === 'retry') {
|
|
50
|
+
const { phase } = body;
|
|
51
|
+
if (!phase) return NextResponse.json({ error: 'phase required' }, { status: 400 });
|
|
52
|
+
return NextResponse.json({ ok: retryPhase(id, phase as PhaseName) });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return NextResponse.json({ error: 'Unknown action' }, { status: 400 });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// DELETE /api/delivery/:id
|
|
59
|
+
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
60
|
+
const { id } = await params;
|
|
61
|
+
return NextResponse.json({ ok: deleteDelivery(id) });
|
|
62
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { createDelivery, listDeliveries, ROLE_PRESETS } from '@/lib/delivery';
|
|
3
|
+
|
|
4
|
+
// GET /api/delivery — list deliveries, or get role presets
|
|
5
|
+
export async function GET(req: Request) {
|
|
6
|
+
const { searchParams } = new URL(req.url);
|
|
7
|
+
|
|
8
|
+
if (searchParams.get('type') === 'presets') {
|
|
9
|
+
return NextResponse.json(ROLE_PRESETS);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const deliveries = listDeliveries();
|
|
13
|
+
return NextResponse.json(deliveries);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// POST /api/delivery — create new delivery
|
|
17
|
+
export async function POST(req: Request) {
|
|
18
|
+
const body = await req.json();
|
|
19
|
+
const { title, project, projectPath, prUrl, description, agentId, phases } = body;
|
|
20
|
+
|
|
21
|
+
if (!project || !projectPath) {
|
|
22
|
+
return NextResponse.json({ error: 'project and projectPath required' }, { status: 400 });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const delivery = createDelivery({
|
|
27
|
+
title: title || description?.slice(0, 50) || 'Delivery',
|
|
28
|
+
project,
|
|
29
|
+
projectPath,
|
|
30
|
+
prUrl,
|
|
31
|
+
description,
|
|
32
|
+
agentId,
|
|
33
|
+
customPhases: phases, // user-defined phases
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return NextResponse.json(delivery);
|
|
37
|
+
} catch (e: any) {
|
|
38
|
+
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { platform } from 'node:os';
|
|
4
|
+
|
|
5
|
+
interface CliInfo {
|
|
6
|
+
name: string;
|
|
7
|
+
path: string;
|
|
8
|
+
version: string;
|
|
9
|
+
installHint: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function detect(name: string, installHint: string): CliInfo {
|
|
13
|
+
try {
|
|
14
|
+
const path = execSync(`which ${name}`, { encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
15
|
+
let version = '';
|
|
16
|
+
try {
|
|
17
|
+
const out = execSync(`${path} --version`, { encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
18
|
+
// Extract version number from output (e.g. "@anthropic-ai/claude-code v1.2.3" or "codex 0.1.0")
|
|
19
|
+
const match = out.match(/v?(\d+\.\d+\.\d+)/);
|
|
20
|
+
version = match ? match[1] : out.slice(0, 50);
|
|
21
|
+
} catch {}
|
|
22
|
+
return { name, path, version, installHint };
|
|
23
|
+
} catch {
|
|
24
|
+
return { name, path: '', version: '', installHint };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function GET() {
|
|
29
|
+
const os = platform();
|
|
30
|
+
const isLinux = os === 'linux';
|
|
31
|
+
const isMac = os === 'darwin';
|
|
32
|
+
|
|
33
|
+
const results = [
|
|
34
|
+
detect('claude', isMac
|
|
35
|
+
? 'npm install -g @anthropic-ai/claude-code'
|
|
36
|
+
: isLinux
|
|
37
|
+
? 'npm install -g @anthropic-ai/claude-code'
|
|
38
|
+
: 'npm install -g @anthropic-ai/claude-code'),
|
|
39
|
+
detect('codex', 'npm install -g @openai/codex'),
|
|
40
|
+
detect('aider', isMac
|
|
41
|
+
? 'brew install aider or pip install aider-chat'
|
|
42
|
+
: 'pip install aider-chat'),
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
return NextResponse.json({ os, tools: results });
|
|
46
|
+
}
|