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