@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,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline Scheduler — manages project-pipeline bindings, scheduled execution,
|
|
3
|
+
* and issue scanning (replaces issue-scanner.ts).
|
|
4
|
+
*
|
|
5
|
+
* Each project can bind multiple workflows. Each binding has:
|
|
6
|
+
* - config: JSON with workflow-specific settings (interval, scanType, labels, baseBranch)
|
|
7
|
+
* - enabled: on/off toggle
|
|
8
|
+
* - scheduled execution via config.interval (minutes, 0 = manual only)
|
|
9
|
+
* - config.scanType: 'github-issues' enables automatic issue scanning + dedup
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { getDb } from '@/src/core/db/database';
|
|
13
|
+
import { getDbPath } from '@/src/config';
|
|
14
|
+
import { startPipeline, getPipeline } from './pipeline';
|
|
15
|
+
import { randomUUID } from 'node:crypto';
|
|
16
|
+
import { execSync } from 'node:child_process';
|
|
17
|
+
|
|
18
|
+
function db() { return getDb(getDbPath()); }
|
|
19
|
+
|
|
20
|
+
/** Normalize SQLite datetime('now') → ISO 8601 UTC string. */
|
|
21
|
+
function toIsoUTC(s: string | null): string | null {
|
|
22
|
+
if (!s) return null;
|
|
23
|
+
if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(s)) return s.replace(' ', 'T') + 'Z';
|
|
24
|
+
return s;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ProjectPipelineBinding {
|
|
28
|
+
id: number;
|
|
29
|
+
projectPath: string;
|
|
30
|
+
projectName: string;
|
|
31
|
+
workflowName: string;
|
|
32
|
+
enabled: boolean;
|
|
33
|
+
config: Record<string, any>; // interval, scanType, labels, baseBranch, etc.
|
|
34
|
+
lastRunAt: string | null;
|
|
35
|
+
createdAt: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface PipelineRun {
|
|
39
|
+
id: string;
|
|
40
|
+
projectPath: string;
|
|
41
|
+
workflowName: string;
|
|
42
|
+
pipelineId: string;
|
|
43
|
+
status: string;
|
|
44
|
+
summary: string;
|
|
45
|
+
dedupKey: string | null;
|
|
46
|
+
createdAt: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─── Bindings CRUD ───────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
export function getBindings(projectPath: string): ProjectPipelineBinding[] {
|
|
52
|
+
return (db().prepare('SELECT * FROM project_pipelines WHERE project_path = ? ORDER BY created_at ASC')
|
|
53
|
+
.all(projectPath) as any[]).map(r => ({
|
|
54
|
+
id: r.id,
|
|
55
|
+
projectPath: r.project_path,
|
|
56
|
+
projectName: r.project_name,
|
|
57
|
+
workflowName: r.workflow_name,
|
|
58
|
+
enabled: !!r.enabled,
|
|
59
|
+
config: JSON.parse(r.config || '{}'),
|
|
60
|
+
lastRunAt: toIsoUTC(r.last_run_at),
|
|
61
|
+
createdAt: toIsoUTC(r.created_at) ?? r.created_at,
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getAllScheduledBindings(): ProjectPipelineBinding[] {
|
|
66
|
+
return (db().prepare('SELECT * FROM project_pipelines WHERE enabled = 1')
|
|
67
|
+
.all() as any[]).map(r => ({
|
|
68
|
+
id: r.id,
|
|
69
|
+
projectPath: r.project_path,
|
|
70
|
+
projectName: r.project_name,
|
|
71
|
+
workflowName: r.workflow_name,
|
|
72
|
+
enabled: true,
|
|
73
|
+
config: JSON.parse(r.config || '{}'),
|
|
74
|
+
lastRunAt: toIsoUTC(r.last_run_at),
|
|
75
|
+
createdAt: toIsoUTC(r.created_at) ?? r.created_at,
|
|
76
|
+
})).filter(b => b.config.interval && b.config.interval > 0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function addBinding(projectPath: string, projectName: string, workflowName: string, config?: Record<string, any>): void {
|
|
80
|
+
db().prepare(`
|
|
81
|
+
INSERT OR REPLACE INTO project_pipelines (project_path, project_name, workflow_name, config)
|
|
82
|
+
VALUES (?, ?, ?, ?)
|
|
83
|
+
`).run(projectPath, projectName, workflowName, JSON.stringify(config || {}));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function removeBinding(projectPath: string, workflowName: string): void {
|
|
87
|
+
db().prepare('DELETE FROM project_pipelines WHERE project_path = ? AND workflow_name = ?')
|
|
88
|
+
.run(projectPath, workflowName);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function updateBinding(projectPath: string, workflowName: string, updates: { enabled?: boolean; config?: Record<string, any> }): void {
|
|
92
|
+
if (updates.enabled !== undefined) {
|
|
93
|
+
db().prepare('UPDATE project_pipelines SET enabled = ? WHERE project_path = ? AND workflow_name = ?')
|
|
94
|
+
.run(updates.enabled ? 1 : 0, projectPath, workflowName);
|
|
95
|
+
}
|
|
96
|
+
if (updates.config) {
|
|
97
|
+
db().prepare('UPDATE project_pipelines SET config = ? WHERE project_path = ? AND workflow_name = ?')
|
|
98
|
+
.run(JSON.stringify(updates.config), projectPath, workflowName);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function updateLastRunAt(projectPath: string, workflowName: string): void {
|
|
103
|
+
db().prepare('UPDATE project_pipelines SET last_run_at = ? WHERE project_path = ? AND workflow_name = ?')
|
|
104
|
+
.run(new Date().toISOString(), projectPath, workflowName);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ─── Runs ────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
export function recordRun(projectPath: string, workflowName: string, pipelineId: string, dedupKey?: string): string {
|
|
110
|
+
const id = randomUUID().slice(0, 8);
|
|
111
|
+
db().prepare(`
|
|
112
|
+
INSERT INTO pipeline_runs (id, project_path, workflow_name, pipeline_id, status, dedup_key)
|
|
113
|
+
VALUES (?, ?, ?, ?, 'running', ?)
|
|
114
|
+
`).run(id, projectPath, workflowName, pipelineId, dedupKey || null);
|
|
115
|
+
return id;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function updateRun(pipelineId: string, status: string, summary?: string): void {
|
|
119
|
+
if (summary) {
|
|
120
|
+
db().prepare('UPDATE pipeline_runs SET status = ?, summary = ? WHERE pipeline_id = ?')
|
|
121
|
+
.run(status, summary, pipelineId);
|
|
122
|
+
} else {
|
|
123
|
+
db().prepare('UPDATE pipeline_runs SET status = ? WHERE pipeline_id = ?')
|
|
124
|
+
.run(status, pipelineId);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function getRuns(projectPath: string, workflowName?: string, limit = 20): PipelineRun[] {
|
|
129
|
+
const query = workflowName
|
|
130
|
+
? 'SELECT * FROM pipeline_runs WHERE project_path = ? AND workflow_name = ? ORDER BY created_at DESC LIMIT ?'
|
|
131
|
+
: 'SELECT * FROM pipeline_runs WHERE project_path = ? ORDER BY created_at DESC LIMIT ?';
|
|
132
|
+
const params = workflowName ? [projectPath, workflowName, limit] : [projectPath, limit];
|
|
133
|
+
return (db().prepare(query).all(...params) as any[]).map(r => ({
|
|
134
|
+
id: r.id,
|
|
135
|
+
projectPath: r.project_path,
|
|
136
|
+
workflowName: r.workflow_name,
|
|
137
|
+
pipelineId: r.pipeline_id,
|
|
138
|
+
status: r.status,
|
|
139
|
+
summary: r.summary || '',
|
|
140
|
+
dedupKey: r.dedup_key || null,
|
|
141
|
+
createdAt: toIsoUTC(r.created_at) ?? r.created_at,
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function deleteRun(id: string): void {
|
|
146
|
+
db().prepare('DELETE FROM pipeline_runs WHERE id = ?').run(id);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ─── Dedup ──────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
function isDuplicate(projectPath: string, workflowName: string, dedupKey: string): boolean {
|
|
152
|
+
const row = db().prepare(
|
|
153
|
+
'SELECT status FROM pipeline_runs WHERE project_path = ? AND workflow_name = ? AND dedup_key = ? ORDER BY created_at DESC LIMIT 1'
|
|
154
|
+
).get(projectPath, workflowName, dedupKey) as { status: string } | undefined;
|
|
155
|
+
if (!row) return false;
|
|
156
|
+
// Failed runs are not duplicates — allow retry
|
|
157
|
+
return row.status !== 'failed';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function resetDedup(projectPath: string, workflowName: string, dedupKey: string): void {
|
|
161
|
+
db().prepare(
|
|
162
|
+
'DELETE FROM pipeline_runs WHERE project_path = ? AND workflow_name = ? AND dedup_key = ?'
|
|
163
|
+
).run(projectPath, workflowName, dedupKey);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ─── Trigger ─────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
export function triggerPipeline(
|
|
169
|
+
projectPath: string, projectName: string, workflowName: string,
|
|
170
|
+
extraInput?: Record<string, any>, dedupKey?: string
|
|
171
|
+
): { pipelineId: string; runId: string } {
|
|
172
|
+
const input: Record<string, string> = {
|
|
173
|
+
project: projectName,
|
|
174
|
+
...extraInput,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const pipeline = startPipeline(workflowName, input);
|
|
178
|
+
const runId = recordRun(projectPath, workflowName, pipeline.id, dedupKey);
|
|
179
|
+
updateLastRunAt(projectPath, workflowName);
|
|
180
|
+
console.log(`[pipeline-scheduler] Triggered ${workflowName} for ${projectName} (pipeline: ${pipeline.id}${dedupKey ? ', dedup: ' + dedupKey : ''})`);
|
|
181
|
+
return { pipelineId: pipeline.id, runId };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ─── Status sync (called from pipeline completion) ───────
|
|
185
|
+
|
|
186
|
+
export function syncRunStatus(pipelineId: string): void {
|
|
187
|
+
const pipeline = getPipeline(pipelineId);
|
|
188
|
+
if (!pipeline) return;
|
|
189
|
+
|
|
190
|
+
// Build summary from outputs
|
|
191
|
+
let summary = '';
|
|
192
|
+
for (const [nodeId, node] of Object.entries(pipeline.nodes)) {
|
|
193
|
+
if (node.outputs && Object.keys(node.outputs).length > 0) {
|
|
194
|
+
for (const [key, val] of Object.entries(node.outputs)) {
|
|
195
|
+
if (val && typeof val === 'string' && val.length < 500) {
|
|
196
|
+
summary += `${nodeId}.${key}: ${val.slice(0, 200)}\n`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
updateRun(pipelineId, pipeline.status, summary.trim());
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ─── GitHub Issue Scanning ──────────────────────────────
|
|
206
|
+
|
|
207
|
+
function getRepoFromProject(projectPath: string): string | null {
|
|
208
|
+
try {
|
|
209
|
+
return execSync('gh repo view --json nameWithOwner -q .nameWithOwner', {
|
|
210
|
+
cwd: projectPath, encoding: 'utf-8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'],
|
|
211
|
+
}).trim() || null;
|
|
212
|
+
} catch {
|
|
213
|
+
try {
|
|
214
|
+
const url = execSync('git remote get-url origin', {
|
|
215
|
+
cwd: projectPath, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'],
|
|
216
|
+
}).trim();
|
|
217
|
+
return url.replace(/.*github\.com[:/]/, '').replace(/\.git$/, '') || null;
|
|
218
|
+
} catch { return null; }
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function fetchOpenIssues(projectPath: string, labels: string[]): { number: number; title: string; error?: string }[] {
|
|
223
|
+
const repo = getRepoFromProject(projectPath);
|
|
224
|
+
if (!repo) return [{ number: -1, title: '', error: 'Could not detect GitHub repo. Run: gh auth login' }];
|
|
225
|
+
try {
|
|
226
|
+
const labelFilter = labels.length > 0 ? ` --label "${labels.join(',')}"` : '';
|
|
227
|
+
const out = execSync(`gh issue list --state open --limit 30 --json number,title${labelFilter} -R ${repo}`, {
|
|
228
|
+
cwd: projectPath, encoding: 'utf-8', timeout: 15000, stdio: ['pipe', 'pipe', 'pipe'],
|
|
229
|
+
});
|
|
230
|
+
return JSON.parse(out) || [];
|
|
231
|
+
} catch (e: any) {
|
|
232
|
+
const msg = e.stderr?.toString() || e.message || 'gh CLI failed';
|
|
233
|
+
return [{ number: -1, title: '', error: msg.includes('auth') ? 'GitHub CLI not authenticated. Run: gh auth login' : msg }];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function scanAndTriggerIssues(binding: ProjectPipelineBinding): { triggered: number; issues: number[]; total: number; pending: number; error?: string } {
|
|
238
|
+
const labels: string[] = binding.config.labels || [];
|
|
239
|
+
const issues = fetchOpenIssues(binding.projectPath, labels);
|
|
240
|
+
|
|
241
|
+
// Check for errors
|
|
242
|
+
if (issues.length === 1 && (issues[0] as any).error) {
|
|
243
|
+
return { triggered: 0, issues: [], total: 0, pending: 0, error: (issues[0] as any).error };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Check if there's already a running pipeline for this project+workflow — only one at a time
|
|
247
|
+
// to prevent concurrent git operations on the same repo
|
|
248
|
+
const recentRuns = getRuns(binding.projectPath, binding.workflowName, 5);
|
|
249
|
+
const hasRunning = recentRuns.some(r => r.status === 'running');
|
|
250
|
+
|
|
251
|
+
// Batch dedup check — one query instead of N
|
|
252
|
+
const processedKeys = new Set(
|
|
253
|
+
(db().prepare(
|
|
254
|
+
'SELECT dedup_key FROM pipeline_runs WHERE project_path = ? AND workflow_name = ? AND dedup_key IS NOT NULL AND status != ?'
|
|
255
|
+
).all(binding.projectPath, binding.workflowName, 'failed') as { dedup_key: string }[])
|
|
256
|
+
.map(r => r.dedup_key)
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
const newIssues: { number: number; title: string }[] = [];
|
|
260
|
+
for (const issue of issues) {
|
|
261
|
+
if (issue.number < 0) continue;
|
|
262
|
+
if (!processedKeys.has(`issue:${issue.number}`)) {
|
|
263
|
+
newIssues.push(issue);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (newIssues.length === 0) {
|
|
268
|
+
updateLastRunAt(binding.projectPath, binding.workflowName);
|
|
269
|
+
return { triggered: 0, issues: [], total: issues.length, pending: 0 };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Only trigger ONE issue at a time to avoid concurrent git conflicts
|
|
273
|
+
// Next issue will be triggered on the next scan cycle
|
|
274
|
+
if (hasRunning) {
|
|
275
|
+
console.log(`[pipeline-scheduler] Issue scan: ${newIssues.length} new issues for ${binding.projectName}, waiting for current pipeline to finish`);
|
|
276
|
+
return { triggered: 0, issues: [], total: issues.length, pending: newIssues.length };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const issue = newIssues[0];
|
|
280
|
+
const dedupKey = `issue:${issue.number}`;
|
|
281
|
+
// Remove old failed run so new dedup_key insert won't conflict
|
|
282
|
+
db().prepare('DELETE FROM pipeline_runs WHERE project_path = ? AND workflow_name = ? AND dedup_key = ? AND status = ?')
|
|
283
|
+
.run(binding.projectPath, binding.workflowName, dedupKey, 'failed');
|
|
284
|
+
try {
|
|
285
|
+
triggerPipeline(
|
|
286
|
+
binding.projectPath, binding.projectName, binding.workflowName,
|
|
287
|
+
{
|
|
288
|
+
issue_id: String(issue.number),
|
|
289
|
+
base_branch: binding.config.baseBranch || 'auto-detect',
|
|
290
|
+
},
|
|
291
|
+
dedupKey
|
|
292
|
+
);
|
|
293
|
+
console.log(`[pipeline-scheduler] Issue scan: triggered #${issue.number} "${issue.title}" for ${binding.projectName} (${newIssues.length - 1} more pending)`);
|
|
294
|
+
} catch (e: any) {
|
|
295
|
+
console.error(`[pipeline-scheduler] Issue scan: failed to trigger #${issue.number}:`, e.message);
|
|
296
|
+
return { triggered: 0, issues: [], total: issues.length, pending: newIssues.length, error: e.message };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
updateLastRunAt(binding.projectPath, binding.workflowName);
|
|
300
|
+
return { triggered: 1, issues: [issue.number], total: issues.length, pending: newIssues.length - 1 };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ─── Periodic Scheduler ─────────────────────────────────
|
|
304
|
+
|
|
305
|
+
const schedulerKey = Symbol.for('forge-pipeline-scheduler');
|
|
306
|
+
const gAny = globalThis as any;
|
|
307
|
+
if (!gAny[schedulerKey]) gAny[schedulerKey] = { started: false, timer: null as NodeJS.Timeout | null };
|
|
308
|
+
const schedulerState = gAny[schedulerKey] as { started: boolean; timer: NodeJS.Timeout | null };
|
|
309
|
+
|
|
310
|
+
const CHECK_INTERVAL_MS = 60 * 1000; // check every 60s
|
|
311
|
+
|
|
312
|
+
export function startScheduler(): void {
|
|
313
|
+
if (schedulerState.started) return;
|
|
314
|
+
schedulerState.started = true;
|
|
315
|
+
|
|
316
|
+
// Check on startup after a short delay
|
|
317
|
+
setTimeout(() => tickScheduler(), 5000);
|
|
318
|
+
|
|
319
|
+
// Then check periodically
|
|
320
|
+
schedulerState.timer = setInterval(() => tickScheduler(), CHECK_INTERVAL_MS);
|
|
321
|
+
console.log('[pipeline-scheduler] Scheduler started (checking every 60s)');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function stopScheduler(): void {
|
|
325
|
+
if (schedulerState.timer) {
|
|
326
|
+
clearInterval(schedulerState.timer);
|
|
327
|
+
schedulerState.timer = null;
|
|
328
|
+
}
|
|
329
|
+
schedulerState.started = false;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function tickScheduler(): void {
|
|
333
|
+
try {
|
|
334
|
+
const bindings = getAllScheduledBindings();
|
|
335
|
+
const now = Date.now();
|
|
336
|
+
|
|
337
|
+
for (const binding of bindings) {
|
|
338
|
+
const intervalMs = binding.config.interval * 60 * 1000;
|
|
339
|
+
const lastRun = binding.lastRunAt ? new Date(binding.lastRunAt).getTime() : 0;
|
|
340
|
+
const elapsed = now - lastRun;
|
|
341
|
+
|
|
342
|
+
if (elapsed < intervalMs) continue;
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
const isIssueWorkflow = binding.workflowName === 'issue-fix-and-review' || binding.workflowName === 'issue-auto-fix' || binding.config.scanType === 'github-issues';
|
|
346
|
+
if (isIssueWorkflow) {
|
|
347
|
+
// Issue scan mode: fetch issues → dedup → trigger per issue
|
|
348
|
+
console.log(`[pipeline-scheduler] Scheduled issue scan: ${binding.workflowName} for ${binding.projectName}`);
|
|
349
|
+
scanAndTriggerIssues(binding);
|
|
350
|
+
} else {
|
|
351
|
+
// Normal mode: single trigger (skip if still running)
|
|
352
|
+
const recentRuns = getRuns(binding.projectPath, binding.workflowName, 1);
|
|
353
|
+
if (recentRuns.length > 0 && recentRuns[0].status === 'running') continue;
|
|
354
|
+
|
|
355
|
+
console.log(`[pipeline-scheduler] Scheduled trigger: ${binding.workflowName} for ${binding.projectName}`);
|
|
356
|
+
triggerPipeline(binding.projectPath, binding.projectName, binding.workflowName, binding.config.input);
|
|
357
|
+
}
|
|
358
|
+
} catch (e: any) {
|
|
359
|
+
console.error(`[pipeline-scheduler] Scheduled trigger failed for ${binding.workflowName}:`, e.message);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
} catch (e: any) {
|
|
363
|
+
console.error('[pipeline-scheduler] Tick error:', e.message);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Get next scheduled run time for a binding */
|
|
368
|
+
export function getNextRunTime(binding: ProjectPipelineBinding): string | null {
|
|
369
|
+
if (!binding.enabled || !binding.config.interval || binding.config.interval <= 0) return null;
|
|
370
|
+
const intervalMs = binding.config.interval * 60 * 1000;
|
|
371
|
+
const lastRun = binding.lastRunAt ? new Date(binding.lastRunAt).getTime() : new Date(binding.createdAt).getTime();
|
|
372
|
+
return new Date(lastRun + intervalMs).toISOString();
|
|
373
|
+
}
|