@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,129 @@
|
|
|
1
|
+
export type MemoryStrategy = 'none' | 'sliding_window' | 'full' | 'full_with_summary' | 'external';
|
|
2
|
+
|
|
3
|
+
export type SessionStatus = 'running' | 'idle' | 'paused' | 'archived' | 'error';
|
|
4
|
+
|
|
5
|
+
export type ProviderName = 'anthropic' | 'google' | 'openai' | 'grok';
|
|
6
|
+
|
|
7
|
+
export interface ProviderConfig {
|
|
8
|
+
name: ProviderName;
|
|
9
|
+
displayName: string;
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
defaultModel: string;
|
|
12
|
+
models: string[];
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface MemoryConfig {
|
|
17
|
+
strategy: MemoryStrategy;
|
|
18
|
+
windowSize?: number; // for sliding_window
|
|
19
|
+
compressAfter?: number; // for full — compress after N messages
|
|
20
|
+
summaryModel?: string; // model to use for summarization
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SessionTemplate {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
description: string;
|
|
27
|
+
provider: ProviderName;
|
|
28
|
+
model?: string;
|
|
29
|
+
fallbackProvider?: ProviderName;
|
|
30
|
+
memory: MemoryConfig;
|
|
31
|
+
systemPrompt: string;
|
|
32
|
+
context?: {
|
|
33
|
+
files?: string[];
|
|
34
|
+
};
|
|
35
|
+
commands?: Record<string, { description: string; action: string; prompt?: string }>;
|
|
36
|
+
ui?: {
|
|
37
|
+
icon?: string;
|
|
38
|
+
color?: string;
|
|
39
|
+
pinned?: boolean;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface Message {
|
|
44
|
+
id: number;
|
|
45
|
+
sessionId: string;
|
|
46
|
+
role: 'user' | 'assistant' | 'system';
|
|
47
|
+
content: string;
|
|
48
|
+
provider: ProviderName;
|
|
49
|
+
model: string;
|
|
50
|
+
tokenCount?: number;
|
|
51
|
+
createdAt: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface Session {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
templateId: string;
|
|
58
|
+
provider: ProviderName;
|
|
59
|
+
model: string;
|
|
60
|
+
status: SessionStatus;
|
|
61
|
+
memory: MemoryConfig;
|
|
62
|
+
systemPrompt: string;
|
|
63
|
+
messageCount: number;
|
|
64
|
+
createdAt: string;
|
|
65
|
+
updatedAt: string;
|
|
66
|
+
lastMessage?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface UsageRecord {
|
|
70
|
+
provider: ProviderName;
|
|
71
|
+
model: string;
|
|
72
|
+
inputTokens: number;
|
|
73
|
+
outputTokens: number;
|
|
74
|
+
cost: number;
|
|
75
|
+
date: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type TaskStatus = 'queued' | 'running' | 'done' | 'failed' | 'cancelled';
|
|
79
|
+
export type TaskMode = 'prompt' | 'monitor' | 'shell';
|
|
80
|
+
|
|
81
|
+
export interface WatchConfig {
|
|
82
|
+
condition: 'change' | 'idle' | 'complete' | 'error' | 'keyword';
|
|
83
|
+
keyword?: string; // for 'keyword' condition
|
|
84
|
+
idleMinutes?: number; // for 'idle' condition (default 10)
|
|
85
|
+
action: 'notify' | 'message' | 'task';
|
|
86
|
+
actionPrompt?: string; // message to send or task prompt
|
|
87
|
+
actionProject?: string; // for 'task' action
|
|
88
|
+
repeat?: boolean; // keep watching after trigger (default false)
|
|
89
|
+
notifyIntervalSeconds?: number; // min seconds between notifications (default 60)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface Task {
|
|
93
|
+
id: string;
|
|
94
|
+
projectName: string;
|
|
95
|
+
projectPath: string;
|
|
96
|
+
prompt: string;
|
|
97
|
+
mode: TaskMode;
|
|
98
|
+
status: TaskStatus;
|
|
99
|
+
priority: number;
|
|
100
|
+
conversationId?: string;
|
|
101
|
+
watchConfig?: WatchConfig;
|
|
102
|
+
log: TaskLogEntry[];
|
|
103
|
+
resultSummary?: string;
|
|
104
|
+
gitDiff?: string;
|
|
105
|
+
gitBranch?: string;
|
|
106
|
+
costUSD?: number;
|
|
107
|
+
error?: string;
|
|
108
|
+
createdAt: string;
|
|
109
|
+
startedAt?: string;
|
|
110
|
+
completedAt?: string;
|
|
111
|
+
scheduledAt?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface TaskLogEntry {
|
|
115
|
+
type: 'system' | 'assistant' | 'result';
|
|
116
|
+
subtype?: string;
|
|
117
|
+
content: string;
|
|
118
|
+
tool?: string;
|
|
119
|
+
timestamp: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface AppConfig {
|
|
123
|
+
dataDir: string;
|
|
124
|
+
providers: Record<ProviderName, ProviderConfig>;
|
|
125
|
+
server: {
|
|
126
|
+
host: string;
|
|
127
|
+
port: number;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# start.sh — Start Forge locally (kill old processes, build, start)
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# ./start.sh # production mode
|
|
6
|
+
# ./start.sh dev # dev mode (hot-reload)
|
|
7
|
+
|
|
8
|
+
# Kill all old forge processes
|
|
9
|
+
pkill -f 'telegram-standalone' 2>/dev/null
|
|
10
|
+
pkill -f 'terminal-standalone' 2>/dev/null
|
|
11
|
+
pkill -f 'workspace-standalone' 2>/dev/null
|
|
12
|
+
pkill -f 'cloudflared tunnel' 2>/dev/null
|
|
13
|
+
# Wait for workspace daemon port to be released
|
|
14
|
+
for i in 1 2 3; do
|
|
15
|
+
lsof -ti:${WORKSPACE_PORT:-8405} >/dev/null 2>&1 || break
|
|
16
|
+
sleep 1
|
|
17
|
+
done
|
|
18
|
+
pkill -f 'next-server' 2>/dev/null
|
|
19
|
+
pkill -f 'next start' 2>/dev/null
|
|
20
|
+
pkill -f 'next dev' 2>/dev/null
|
|
21
|
+
sleep 1
|
|
22
|
+
|
|
23
|
+
export PORT=${PORT:-8403}
|
|
24
|
+
export TERMINAL_PORT=${TERMINAL_PORT:-8404}
|
|
25
|
+
export WORKSPACE_PORT=${WORKSPACE_PORT:-8405}
|
|
26
|
+
|
|
27
|
+
if [ "$1" = "dev" ]; then
|
|
28
|
+
export FORGE_DEV=1
|
|
29
|
+
pnpm dev
|
|
30
|
+
else
|
|
31
|
+
pnpm build && pnpm start
|
|
32
|
+
fi
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Lead",
|
|
3
|
+
"icon": "👑",
|
|
4
|
+
"description": "Primary coordinator — SOP-driven requirement intake, delegation, gap coverage, and quality gate",
|
|
5
|
+
"config": {
|
|
6
|
+
"label": "Lead",
|
|
7
|
+
"icon": "👑",
|
|
8
|
+
"backend": "cli",
|
|
9
|
+
"agentId": "claude",
|
|
10
|
+
"primary": true,
|
|
11
|
+
"persistentSession": true,
|
|
12
|
+
"workDir": "./",
|
|
13
|
+
"outputs": ["docs/lead/"],
|
|
14
|
+
"plugins": ["playwright", "shell-command"],
|
|
15
|
+
"role": "You are the Lead — primary coordinator of this workspace.\n\nYour context automatically includes a \"Workspace Team\" section showing all agents, their roles, status, and missing standard roles. Read it before every action.\n\n## SOP: Requirement Intake\n\nWhen you receive a requirement (from user input or inbox message):\n\n```\n1. Read the Workspace Team section in your context\n2. Classify the requirement:\n - Single task → one request document\n - Multi-module → break into independent request documents, group in a batch\n3. Route based on available roles:\n\n HAS Architect?\n └─ YES → create_request with full description → Architect breaks it down further\n └─ NO → you break it down yourself, then:\n\n HAS Engineer?\n └─ YES → create_request for each module (status: open)\n Engineers claim via claim_request\n └─ NO → implement it yourself in src/\n Record files_changed in docs/lead/impl-notes.md\n\n4. After implementation (by you or Engineer):\n\n HAS QA?\n └─ YES → update_response(section: engineer) triggers auto-notify to QA\n └─ NO → you test it:\n - Read acceptance_criteria from the request\n - Write tests in tests/ or run manually\n - Record results: update_response(section: qa, result: passed/failed)\n\n5. After testing:\n\n HAS Reviewer?\n └─ YES → auto-notified when QA passes\n └─ NO → you review it:\n - Check code quality, security, PRD compliance\n - Record: update_response(section: review, result: approved/changes_requested)\n - If changes_requested → send_message to Engineer or fix yourself\n```\n\n## SOP: Monitoring & Coordination\n\nWhile work is in progress:\n\n```\n1. get_status → check all agents' smith/task status\n2. list_requests → check request progress\n\nIF agent taskStatus = failed:\n → Read their error from get_status\n → send_message asking what went wrong\n → If no response or unfixable: handle the request yourself\n\nIF agent taskStatus = running for too long:\n → send_message to check progress\n\nIF request stuck in one status:\n → Check which agent should be handling it\n → send_message to nudge, or cover it yourself\n\nIF multiple Engineers exist and request unclaimed:\n → send_message to available Engineer suggesting they claim_request\n```\n\n## SOP: Quality Gate (before declaring done)\n\n```\n1. list_requests(batch: current_batch)\n2. ALL requests status = done?\n └─ NO → identify stuck ones, apply Monitoring SOP\n └─ YES → continue\n3. Any request with review.result = changes_requested?\n └─ YES → verify changes were made, re-review if no Reviewer\n4. Any request with qa.result = failed?\n └─ YES → verify fix was applied, re-test if no QA\n5. Write summary to docs/lead/delivery-summary.md:\n - Requirements covered\n - Requests created and their final status\n - Roles you covered due to gaps\n - Any open issues\n```\n\n## Gap Coverage Reference\n\nWhen you cover a missing role, follow that role's standards:\n\n| Missing Role | What You Do | Output |\n|---|---|---|\n| PM/Architect | Break requirements into modules with acceptance_criteria | request documents via create_request |\n| Engineer | Read request → implement in src/ → update_response(section: engineer) | source code + files_changed |\n| QA | Read acceptance_criteria → write/run tests → update_response(section: qa) | test results in tests/ or docs/qa/ |\n| Reviewer | Read code changes → check quality/security → update_response(section: review) | review findings |\n\n## Rules\n\n- Workspace Team is in your context — don't call get_agents redundantly at start, just read it\n- DO call get_agents/get_status when you need live status updates mid-task\n- Every delegated task MUST go through request documents (create_request) — never just send_message with vague instructions\n- Each request needs concrete acceptance_criteria that QA can verify\n- Do NOT duplicate work an active agent is already doing — check status first\n- When covering a gap, be thorough — don't half-do it just because it's not your \"main\" role",
|
|
16
|
+
"steps": [
|
|
17
|
+
{
|
|
18
|
+
"id": "intake",
|
|
19
|
+
"label": "Intake & Analyze",
|
|
20
|
+
"prompt": "Read the Workspace Team section in your context. Identify: (1) which standard roles are present and missing, (2) incoming requirements from upstream input or inbox. For each requirement, decide scope: single task or multi-module. List what you will delegate vs handle yourself."
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "delegate",
|
|
24
|
+
"label": "Create Requests & Route",
|
|
25
|
+
"prompt": "For each module/task: create_request with title, description, acceptance_criteria, and batch name. If Architect exists, create high-level requests for them to break down. If only Engineers exist, create implementation-ready requests. If no one to delegate to, note which requests you will implement yourself. Verify with list_requests that all were created."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "cover-gaps",
|
|
29
|
+
"label": "Cover Missing Roles",
|
|
30
|
+
"prompt": "Handle all work for missing roles. If no Engineer: implement code, then update_response(section: engineer). If no QA: write/run tests against acceptance_criteria, then update_response(section: qa). If no Reviewer: review code changes for quality and security, then update_response(section: review). Use get_status between tasks to check if other agents have completed their work."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "monitor",
|
|
34
|
+
"label": "Monitor & Unblock",
|
|
35
|
+
"prompt": "Run get_status and list_requests. For each stuck/failed agent: diagnose and send_message to unblock, or take over the request yourself. For unclaimed requests: nudge available agents. Continue until all requests show progress."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "gate",
|
|
39
|
+
"label": "Quality Gate & Summary",
|
|
40
|
+
"prompt": "list_requests for the current batch. Verify ALL requests are status=done with review.result=approved and qa.result=passed. If any are not: apply gap coverage. Write docs/lead/delivery-summary.md with: requirements covered, request statuses, roles you covered, open issues."
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"exportedAt": 1743724800000
|
|
45
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": [
|
|
5
|
+
"dom",
|
|
6
|
+
"dom.iterable",
|
|
7
|
+
"esnext"
|
|
8
|
+
],
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"module": "esnext",
|
|
15
|
+
"moduleResolution": "bundler",
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"jsx": "react-jsx",
|
|
19
|
+
"incremental": true,
|
|
20
|
+
"plugins": [
|
|
21
|
+
{
|
|
22
|
+
"name": "next"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"paths": {
|
|
26
|
+
"@/*": [
|
|
27
|
+
"./*"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"include": [
|
|
32
|
+
"next-env.d.ts",
|
|
33
|
+
"**/*.ts",
|
|
34
|
+
"**/*.tsx",
|
|
35
|
+
".next/types/**/*.ts",
|
|
36
|
+
".next/dev/types/**/*.ts",
|
|
37
|
+
".next/dev/dev/types/**/*.ts"
|
|
38
|
+
],
|
|
39
|
+
"exclude": [
|
|
40
|
+
"node_modules"
|
|
41
|
+
]
|
|
42
|
+
}
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,36 +1,17 @@
|
|
|
1
|
-
# Forge v0.5.
|
|
1
|
+
# Forge v0.5.28
|
|
2
2
|
|
|
3
|
-
Released: 2026-04-
|
|
3
|
+
Released: 2026-04-09
|
|
4
4
|
|
|
5
|
-
## Changes since v0.5.
|
|
6
|
-
|
|
7
|
-
### Features
|
|
8
|
-
- feat: add review-mr as builtin pipeline
|
|
9
|
-
- feat: all pipeline steps use worktree, no shell mode exception
|
|
10
|
-
- feat: pipeline steps auto-use git worktree for isolated execution
|
|
5
|
+
## Changes since v0.5.27
|
|
11
6
|
|
|
12
7
|
### Bug Fixes
|
|
13
|
-
- fix:
|
|
14
|
-
- fix: task model selection — treat 'default' as fall-through
|
|
15
|
-
- fix: ignore stdin + parse worktree field in pipeline nodes
|
|
16
|
-
- fix: spawn claude without shell, resolve path via which
|
|
17
|
-
- fix: remove shell: '/bin/zsh' from claude spawn to prevent arg interpretation
|
|
18
|
-
- fix: all pipeline steps use worktree, shell gets env vars
|
|
19
|
-
- fix: all pipeline steps use worktree, shell gets env vars
|
|
20
|
-
- fix: shell/plugin pipeline steps skip worktree, run in project dir
|
|
21
|
-
- fix: only auto-worktree for agent/prompt mode, not shell steps
|
|
22
|
-
- fix: resolve rebase conflicts and fix anti-loop guard in messaging
|
|
23
|
-
|
|
24
|
-
### Documentation
|
|
25
|
-
- docs: update pipeline help with worktree env vars for shell steps
|
|
8
|
+
- fix: restore notification polling for Telegram, add Suspense wrappers
|
|
26
9
|
|
|
27
|
-
###
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
- fix terminal copy/paste and selection
|
|
33
|
-
- add noreply and inbox to workspace messaging
|
|
10
|
+
### Performance
|
|
11
|
+
- perf: notifications fetch on-demand instead of polling
|
|
12
|
+
- perf: remove task completion polling (replaced by hook stop)
|
|
13
|
+
- perf: reduce polling frequency and lazy-load non-essential components
|
|
14
|
+
- perf: async terminal-cwd to avoid blocking event loop
|
|
34
15
|
|
|
35
16
|
|
|
36
|
-
**Full Changelog**: https://github.com/aiwatching/forge/compare/v0.5.
|
|
17
|
+
**Full Changelog**: https://github.com/aiwatching/forge/compare/v0.5.27...v0.5.28
|
|
@@ -4,16 +4,20 @@ import { addNotification } from '@/lib/notifications';
|
|
|
4
4
|
|
|
5
5
|
export async function POST(req: Request) {
|
|
6
6
|
const { tabLabel } = await req.json();
|
|
7
|
-
|
|
8
7
|
const label = tabLabel || 'Terminal';
|
|
9
8
|
|
|
9
|
+
// Check settings — terminal bell notifications can be disabled
|
|
10
|
+
const settings = loadSettings();
|
|
11
|
+
if ((settings as any).terminalBellEnabled === false) {
|
|
12
|
+
return NextResponse.json({ ok: true, skipped: true });
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
// In-app notification
|
|
11
16
|
try {
|
|
12
17
|
addNotification('terminal_bell', `Terminal idle: ${label}`, `Claude appears to have finished in "${label}".`);
|
|
13
18
|
} catch {}
|
|
14
19
|
|
|
15
20
|
// Telegram notification
|
|
16
|
-
const settings = loadSettings();
|
|
17
21
|
const { telegramBotToken, telegramChatId } = settings;
|
|
18
22
|
if (telegramBotToken && telegramChatId) {
|
|
19
23
|
try {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
|
-
import {
|
|
2
|
+
import { exec } from 'node:child_process';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
|
|
5
|
+
const execAsync = promisify(exec);
|
|
3
6
|
|
|
4
7
|
export async function GET(req: Request) {
|
|
5
8
|
const { searchParams } = new URL(req.url);
|
|
@@ -8,11 +11,11 @@ export async function GET(req: Request) {
|
|
|
8
11
|
return NextResponse.json({ path: null });
|
|
9
12
|
}
|
|
10
13
|
try {
|
|
11
|
-
const
|
|
14
|
+
const { stdout } = await execAsync(`tmux display-message -p -t ${session} '#{pane_current_path}'`, {
|
|
12
15
|
encoding: 'utf-8',
|
|
13
16
|
timeout: 3000,
|
|
14
|
-
})
|
|
15
|
-
return NextResponse.json({ path:
|
|
17
|
+
});
|
|
18
|
+
return NextResponse.json({ path: stdout.trim() || null });
|
|
16
19
|
} catch {
|
|
17
20
|
return NextResponse.json({ path: null });
|
|
18
21
|
}
|
|
@@ -224,8 +224,8 @@ export default function CodeViewer({ terminalRef }: { terminalRef: React.RefObje
|
|
|
224
224
|
};
|
|
225
225
|
|
|
226
226
|
fetchCwd();
|
|
227
|
-
// Poll cwd every
|
|
228
|
-
const timer = setInterval(fetchCwd,
|
|
227
|
+
// Poll cwd every 15s (user might cd to a different directory)
|
|
228
|
+
const timer = setInterval(fetchCwd, 15000);
|
|
229
229
|
return () => { cancelled = true; clearInterval(timer); };
|
|
230
230
|
}, [activeSession]);
|
|
231
231
|
|
|
@@ -247,35 +247,7 @@ export default function CodeViewer({ terminalRef }: { terminalRef: React.RefObje
|
|
|
247
247
|
fetchDir();
|
|
248
248
|
}, [currentDir]);
|
|
249
249
|
|
|
250
|
-
//
|
|
251
|
-
useEffect(() => {
|
|
252
|
-
if (!currentDir) return;
|
|
253
|
-
const dirName = currentDir.split('/').pop() || '';
|
|
254
|
-
const check = async () => {
|
|
255
|
-
try {
|
|
256
|
-
const res = await fetch('/api/tasks?status=done');
|
|
257
|
-
const tasks = await res.json();
|
|
258
|
-
if (!Array.isArray(tasks) || tasks.length === 0) return;
|
|
259
|
-
const latest = tasks.find((t: any) => t.projectPath === currentDir || t.projectName === dirName);
|
|
260
|
-
if (latest && latest.id !== lastTaskCheckRef.current && latest.completedAt) {
|
|
261
|
-
// Only notify if completed in the last 30s
|
|
262
|
-
const age = Date.now() - new Date(latest.completedAt).getTime();
|
|
263
|
-
if (age < 30_000) {
|
|
264
|
-
lastTaskCheckRef.current = latest.id;
|
|
265
|
-
setTaskNotification({
|
|
266
|
-
id: latest.id,
|
|
267
|
-
status: latest.status,
|
|
268
|
-
prompt: latest.prompt,
|
|
269
|
-
sessionId: latest.conversationId,
|
|
270
|
-
});
|
|
271
|
-
setTimeout(() => setTaskNotification(null), 15_000);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
} catch {}
|
|
275
|
-
};
|
|
276
|
-
const timer = setInterval(check, 5000);
|
|
277
|
-
return () => clearInterval(timer);
|
|
278
|
-
}, [currentDir]);
|
|
250
|
+
// Task completion is notified via hook stop — no polling needed
|
|
279
251
|
|
|
280
252
|
// Build git status map for tree coloring
|
|
281
253
|
const gitMap: GitStatusMap = new Map(gitChanges.map(g => [g.path, g.status]));
|
package/components/Dashboard.tsx
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useCallback, useRef, lazy, Suspense } from 'react';
|
|
4
4
|
import { signOut } from 'next-auth/react';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const TaskBoard = lazy(() => import('./TaskBoard'));
|
|
6
|
+
const TaskDetail = lazy(() => import('./TaskDetail'));
|
|
7
|
+
const TunnelToggle = lazy(() => import('./TunnelToggle'));
|
|
8
8
|
import type { Task } from '@/src/types';
|
|
9
9
|
import type { WebTerminalHandle } from './WebTerminal';
|
|
10
10
|
|
|
@@ -181,7 +181,7 @@ export default function Dashboard({ user }: { user: any }) {
|
|
|
181
181
|
return () => clearInterval(id);
|
|
182
182
|
}, []);
|
|
183
183
|
|
|
184
|
-
//
|
|
184
|
+
// Notifications: poll unread count at 30s, full fetch when panel opens
|
|
185
185
|
const fetchNotifications = useCallback(() => {
|
|
186
186
|
fetch('/api/notifications').then(r => r.json()).then(data => {
|
|
187
187
|
setNotifications(data.notifications || []);
|
|
@@ -191,10 +191,15 @@ export default function Dashboard({ user }: { user: any }) {
|
|
|
191
191
|
|
|
192
192
|
useEffect(() => {
|
|
193
193
|
fetchNotifications();
|
|
194
|
-
const id = setInterval(fetchNotifications,
|
|
194
|
+
const id = setInterval(fetchNotifications, 30000);
|
|
195
195
|
return () => clearInterval(id);
|
|
196
196
|
}, [fetchNotifications]);
|
|
197
197
|
|
|
198
|
+
// Refresh full list when notification panel opens
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (showNotifications) fetchNotifications();
|
|
201
|
+
}, [showNotifications, fetchNotifications]);
|
|
202
|
+
|
|
198
203
|
// Heartbeat for online user tracking
|
|
199
204
|
useEffect(() => {
|
|
200
205
|
const ping = () => {
|
|
@@ -204,26 +209,35 @@ export default function Dashboard({ user }: { user: any }) {
|
|
|
204
209
|
.catch(() => {});
|
|
205
210
|
};
|
|
206
211
|
ping();
|
|
207
|
-
const id = setInterval(ping,
|
|
212
|
+
const id = setInterval(ping, 60_000); // every 60s
|
|
208
213
|
return () => clearInterval(id);
|
|
209
214
|
}, []);
|
|
210
215
|
|
|
211
216
|
const fetchData = useCallback(async () => {
|
|
212
217
|
try {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
fetch('/api/
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
// Only fetch what's needed for current view
|
|
219
|
+
const fetches: Promise<void>[] = [
|
|
220
|
+
fetch('/api/projects').then(async r => { if (r.ok) setProjects(await r.json()); }),
|
|
221
|
+
];
|
|
222
|
+
// Tasks + status only when relevant tabs are active
|
|
223
|
+
if (viewMode === 'tasks' || viewMode === 'terminal') {
|
|
224
|
+
fetches.push(
|
|
225
|
+
fetch('/api/tasks').then(async r => { if (r.ok) setTasks(await r.json()); }),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
if (viewMode === 'usage') {
|
|
229
|
+
fetches.push(
|
|
230
|
+
fetch('/api/status').then(async r => { if (r.ok) { const s = await r.json(); setProviders(s.providers); setUsage(s.usage); } }),
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
await Promise.all(fetches);
|
|
221
234
|
} catch {}
|
|
222
|
-
}, []);
|
|
235
|
+
}, [viewMode]);
|
|
223
236
|
|
|
224
237
|
useEffect(() => {
|
|
225
238
|
fetchData();
|
|
226
|
-
|
|
239
|
+
// Poll less aggressively: 10s instead of 5s
|
|
240
|
+
const interval = setInterval(fetchData, 10000);
|
|
227
241
|
return () => clearInterval(interval);
|
|
228
242
|
}, [fetchData]);
|
|
229
243
|
|
|
@@ -421,7 +435,7 @@ export default function Dashboard({ user }: { user: any }) {
|
|
|
421
435
|
: 'border-[var(--border)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:border-[var(--text-secondary)]'
|
|
422
436
|
}`}
|
|
423
437
|
>Usage</button>
|
|
424
|
-
<TunnelToggle
|
|
438
|
+
<Suspense fallback={null}><TunnelToggle /></Suspense>
|
|
425
439
|
{onlineCount.total > 0 && (
|
|
426
440
|
<span className="text-[10px] text-[var(--text-secondary)] flex items-center gap-1" title={`${onlineCount.total} online${onlineCount.remote > 0 ? `, ${onlineCount.remote} remote` : ''}`}>
|
|
427
441
|
<span className="text-green-500">●</span>
|
|
@@ -596,13 +610,13 @@ export default function Dashboard({ user }: { user: any }) {
|
|
|
596
610
|
<>
|
|
597
611
|
{/* Left — Task list */}
|
|
598
612
|
<aside className="w-72 border-r border-[var(--border)] flex flex-col shrink-0">
|
|
599
|
-
<TaskBoard tasks={tasks} activeId={activeTaskId} onSelect={setActiveTaskId} onRefresh={fetchData}
|
|
613
|
+
<Suspense fallback={null}><TaskBoard tasks={tasks} activeId={activeTaskId} onSelect={setActiveTaskId} onRefresh={fetchData} /></Suspense>
|
|
600
614
|
</aside>
|
|
601
615
|
|
|
602
616
|
{/* Center — Task detail / empty state */}
|
|
603
617
|
<main className="flex-1 flex flex-col min-w-0">
|
|
604
618
|
{activeTask ? (
|
|
605
|
-
<TaskDetail
|
|
619
|
+
<Suspense fallback={null}><TaskDetail
|
|
606
620
|
task={activeTask}
|
|
607
621
|
onRefresh={fetchData}
|
|
608
622
|
onFollowUp={async (data) => {
|
|
@@ -615,7 +629,7 @@ export default function Dashboard({ user }: { user: any }) {
|
|
|
615
629
|
setActiveTaskId(newTask.id);
|
|
616
630
|
fetchData();
|
|
617
631
|
}}
|
|
618
|
-
|
|
632
|
+
/></Suspense>
|
|
619
633
|
) : (
|
|
620
634
|
<div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">
|
|
621
635
|
<div className="text-center space-y-2">
|
|
@@ -67,7 +67,9 @@ async function loadSharedState(): Promise<{ tabs: TabState[]; activeTabId: numbe
|
|
|
67
67
|
if (!res.ok) return null;
|
|
68
68
|
const d = await res.json();
|
|
69
69
|
if (d && Array.isArray(d.tabs) && d.tabs.length > 0 && typeof d.activeTabId === 'number') {
|
|
70
|
-
|
|
70
|
+
// Always start with bell disabled — user can enable manually per tab
|
|
71
|
+
const tabs = d.tabs.map((t: any) => ({ ...t, bellEnabled: false }));
|
|
72
|
+
return { tabs, activeTabId: d.activeTabId, sessionLabels: d.sessionLabels || {} };
|
|
71
73
|
}
|
|
72
74
|
return null;
|
|
73
75
|
} catch {
|
|
@@ -165,6 +167,38 @@ function collectAllSessionNames(tabs: TabState[]): string[] {
|
|
|
165
167
|
return tabs.flatMap(t => collectSessionNames(t.tree));
|
|
166
168
|
}
|
|
167
169
|
|
|
170
|
+
// ─── Mouse Toggle Button ─────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
function MouseToggle() {
|
|
173
|
+
const [mouseOn, setMouseOn] = useState(true);
|
|
174
|
+
|
|
175
|
+
const toggle = () => {
|
|
176
|
+
const next = !mouseOn;
|
|
177
|
+
try {
|
|
178
|
+
const wsUrl = getWsUrl();
|
|
179
|
+
const ws = new WebSocket(wsUrl);
|
|
180
|
+
ws.onopen = () => {
|
|
181
|
+
ws.send(JSON.stringify({ type: 'tmux-mouse', mouse: next }));
|
|
182
|
+
setTimeout(() => ws.close(), 300);
|
|
183
|
+
};
|
|
184
|
+
ws.onerror = () => ws.close();
|
|
185
|
+
} catch {}
|
|
186
|
+
setMouseOn(next);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<div className="flex items-center gap-1 mr-2">
|
|
191
|
+
<span className="text-[8px] text-gray-600">
|
|
192
|
+
{mouseOn ? 'scroll: trackpad · copy: Shift+drag' : 'scroll: Ctrl+B [ · copy: drag'}
|
|
193
|
+
</span>
|
|
194
|
+
<button onClick={toggle} title={mouseOn ? 'Click to disable mouse (easier text select)' : 'Click to enable mouse (trackpad scroll)'}
|
|
195
|
+
className={`text-[9px] px-1.5 py-0.5 rounded border transition-colors ${mouseOn ? 'border-green-600/40 text-green-400 bg-green-500/10' : 'border-gray-600 text-gray-500'}`}>
|
|
196
|
+
🖱️ {mouseOn ? 'ON' : 'OFF'}
|
|
197
|
+
</button>
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
168
202
|
// ─── Pending commands for new terminal panes ────────────────
|
|
169
203
|
|
|
170
204
|
const pendingCommands = new Map<number, string>();
|
|
@@ -736,7 +770,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
|
|
|
736
770
|
|
|
737
771
|
{/* Toolbar */}
|
|
738
772
|
<div className="flex items-center gap-1 px-2 ml-auto">
|
|
739
|
-
<
|
|
773
|
+
<MouseToggle />
|
|
740
774
|
<button onClick={() => onSplit('vertical')} className="text-[10px] px-2 py-0.5 text-gray-400 hover:text-white hover:bg-[var(--term-border)] rounded">
|
|
741
775
|
Split Right
|
|
742
776
|
</button>
|
|
@@ -271,9 +271,9 @@ wss.on('connection', (ws: WebSocket) => {
|
|
|
271
271
|
term = null;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
// Ensure mouse mode is
|
|
274
|
+
// Ensure mouse mode is on (enables trackpad scrolling) and scrollback is set
|
|
275
275
|
try {
|
|
276
|
-
execSync(`${TMUX} set-option -t ${name} mouse
|
|
276
|
+
execSync(`${TMUX} set-option -t ${name} mouse on 2>/dev/null`);
|
|
277
277
|
execSync(`${TMUX} set-option -t ${name} history-limit 50000 2>/dev/null`);
|
|
278
278
|
} catch {}
|
|
279
279
|
|
|
@@ -384,6 +384,23 @@ wss.on('connection', (ws: WebSocket) => {
|
|
|
384
384
|
break;
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
case 'tmux-mouse': {
|
|
388
|
+
// Toggle mouse for ALL tmux sessions (global + per-session)
|
|
389
|
+
const val = parsed.mouse ? 'on' : 'off';
|
|
390
|
+
try {
|
|
391
|
+
execSync(`${TMUX} set -g mouse ${val}`, { timeout: 3000 });
|
|
392
|
+
// Also apply to every existing session (overrides per-session setting)
|
|
393
|
+
const sessions = listTmuxSessions();
|
|
394
|
+
for (const s of sessions) {
|
|
395
|
+
try { execSync(`${TMUX} set-option -t "${s.name}" mouse ${val}`, { timeout: 1000 }); } catch {}
|
|
396
|
+
}
|
|
397
|
+
ws.send(JSON.stringify({ type: 'tmux-mouse-result', ok: true, mouse: parsed.mouse }));
|
|
398
|
+
} catch (e: any) {
|
|
399
|
+
ws.send(JSON.stringify({ type: 'tmux-mouse-result', ok: false, error: e.message }));
|
|
400
|
+
}
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
|
|
387
404
|
case 'load-state': {
|
|
388
405
|
const state = loadTerminalState();
|
|
389
406
|
ws.send(JSON.stringify({ type: 'terminal-state', data: state }));
|
package/package.json
CHANGED