circuschief 0.1.0
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/package.json +33 -0
- package/packages/server/bin/cli.js +4 -0
- package/packages/server/src/agents/AgentGateway.js +64 -0
- package/packages/server/src/agents/BaseAgent.js +41 -0
- package/packages/server/src/agents/LoggingAgentWrapper.js +73 -0
- package/packages/server/src/agents/adapters/ClaudeCodeAdapter.js +33 -0
- package/packages/server/src/agents/adapters/CodexAdapter.js +26 -0
- package/packages/server/src/agents/types.js +43 -0
- package/packages/server/src/agents/vcr/CassetteStore.js +111 -0
- package/packages/server/src/agents/vcr/VCRAgentAdapter.js +126 -0
- package/packages/server/src/agents/vcr/VCRSummaryWrapper.js +71 -0
- package/packages/server/src/api/canvas-helpers.js +249 -0
- package/packages/server/src/api/canvas-trash-routes.js +205 -0
- package/packages/server/src/api/canvas.js +331 -0
- package/packages/server/src/api/commandButtons.js +312 -0
- package/packages/server/src/api/commands.js +169 -0
- package/packages/server/src/api/filesystem.js +62 -0
- package/packages/server/src/api/git.js +85 -0
- package/packages/server/src/api/index.js +44 -0
- package/packages/server/src/api/kanban.js +342 -0
- package/packages/server/src/api/metrics.js +194 -0
- package/packages/server/src/api/projects-helpers.js +43 -0
- package/packages/server/src/api/projects-session-helpers.js +295 -0
- package/packages/server/src/api/projects.js +384 -0
- package/packages/server/src/api/providers.js +249 -0
- package/packages/server/src/api/quickResponses.js +129 -0
- package/packages/server/src/api/sessions-archive.js +69 -0
- package/packages/server/src/api/sessions-commands.js +220 -0
- package/packages/server/src/api/sessions-conversations.js +168 -0
- package/packages/server/src/api/sessions-draft.js +72 -0
- package/packages/server/src/api/sessions-lifecycle.js +190 -0
- package/packages/server/src/api/sessions-messages.js +141 -0
- package/packages/server/src/api/sessions-notes.js +51 -0
- package/packages/server/src/api/sessions-patch.js +252 -0
- package/packages/server/src/api/sessions-streaming.js +86 -0
- package/packages/server/src/api/sessions.js +269 -0
- package/packages/server/src/api/settings.js +194 -0
- package/packages/server/src/api/templates.js +63 -0
- package/packages/server/src/app.js +51 -0
- package/packages/server/src/database.js +58 -0
- package/packages/server/src/db/AgentCallLogRepository.js +322 -0
- package/packages/server/src/db/AttachmentRepository.js +191 -0
- package/packages/server/src/db/BaseRepository.js +39 -0
- package/packages/server/src/db/CanvasItemRepository.js +315 -0
- package/packages/server/src/db/CommandButtonRepository.js +75 -0
- package/packages/server/src/db/CommandRunRepository.js +219 -0
- package/packages/server/src/db/ConversationRepository.js +379 -0
- package/packages/server/src/db/DatabaseManager.js +91 -0
- package/packages/server/src/db/KanbanBoardRepository.js +92 -0
- package/packages/server/src/db/KanbanCardRepository.js +286 -0
- package/packages/server/src/db/KanbanLaneRepository.js +279 -0
- package/packages/server/src/db/MessageRepository.js +156 -0
- package/packages/server/src/db/ProjectDefaultsRepository.js +173 -0
- package/packages/server/src/db/ProjectRepository.js +110 -0
- package/packages/server/src/db/ProviderRepository.js +307 -0
- package/packages/server/src/db/QuickResponseRepository.js +186 -0
- package/packages/server/src/db/SessionNoteRepository.js +60 -0
- package/packages/server/src/db/SessionRepository.js +314 -0
- package/packages/server/src/db/SessionSummaryRepository.js +200 -0
- package/packages/server/src/db/SessionTemplateRepository.js +171 -0
- package/packages/server/src/db/SettingsRepository.js +211 -0
- package/packages/server/src/db/TodoRepository.js +132 -0
- package/packages/server/src/db/WorkLogRepository.js +122 -0
- package/packages/server/src/db/conversation-helpers.js +119 -0
- package/packages/server/src/db/index.js +100 -0
- package/packages/server/src/db/migrations/canvasItemsMigrations.js +109 -0
- package/packages/server/src/db/migrations/conversationsMigrations.js +183 -0
- package/packages/server/src/db/migrations/index.js +199 -0
- package/packages/server/src/db/migrations/kanbanMigrations.js +99 -0
- package/packages/server/src/db/migrations/migrationUtils.js +55 -0
- package/packages/server/src/db/migrations/miscMigrations.js +242 -0
- package/packages/server/src/db/migrations/projectsMigrations.js +95 -0
- package/packages/server/src/db/migrations/sessionsMigrations.js +282 -0
- package/packages/server/src/db/session-helpers.js +150 -0
- package/packages/server/src/index.js +106 -0
- package/packages/server/src/logger.js +22 -0
- package/packages/server/src/middleware/sessionLookup.js +57 -0
- package/packages/server/src/middleware/upload.js +94 -0
- package/packages/server/src/schema.sql +363 -0
- package/packages/server/src/services/agentCallLogger.js +116 -0
- package/packages/server/src/services/canvasStore.js +56 -0
- package/packages/server/src/services/childSessionContext.js +61 -0
- package/packages/server/src/services/commandRunner.js +422 -0
- package/packages/server/src/services/conversationContext.js +72 -0
- package/packages/server/src/services/diffService.js +172 -0
- package/packages/server/src/services/draftSessionService.js +181 -0
- package/packages/server/src/services/encryption.js +134 -0
- package/packages/server/src/services/ghService.js +169 -0
- package/packages/server/src/services/gitService.js +520 -0
- package/packages/server/src/services/gitSessionSetup.js +48 -0
- package/packages/server/src/services/hookService.js +60 -0
- package/packages/server/src/services/kanbanService.js +262 -0
- package/packages/server/src/services/kanbanTriggers.js +273 -0
- package/packages/server/src/services/nodeSpawnHelper.js +63 -0
- package/packages/server/src/services/prStatusService.js +204 -0
- package/packages/server/src/services/prUrlService.js +224 -0
- package/packages/server/src/services/providerTestService.js +81 -0
- package/packages/server/src/services/scheduleService.js +110 -0
- package/packages/server/src/services/schedulerService.js +281 -0
- package/packages/server/src/services/sessionDuplicator.js +63 -0
- package/packages/server/src/services/sessionErrors.js +173 -0
- package/packages/server/src/services/sessionExecution.js +378 -0
- package/packages/server/src/services/sessionManager.js +356 -0
- package/packages/server/src/services/sessionPrompts.js +427 -0
- package/packages/server/src/services/sessionProvider.js +107 -0
- package/packages/server/src/services/slashCommandDiscovery.js +258 -0
- package/packages/server/src/services/slashCommandPluginDiscovery.js +216 -0
- package/packages/server/src/services/slashCommandService.js +306 -0
- package/packages/server/src/services/streamEventCallbacks.js +170 -0
- package/packages/server/src/services/streamEventHandler.js +488 -0
- package/packages/server/src/services/streamUsageHandler.js +228 -0
- package/packages/server/src/services/summaryBroadcast.js +61 -0
- package/packages/server/src/services/summaryClaudeClient.js +180 -0
- package/packages/server/src/services/summaryPrompts.js +169 -0
- package/packages/server/src/services/summaryService.js +552 -0
- package/packages/server/src/services/summaryStaleCheck.js +35 -0
- package/packages/server/src/services/systemMonitor.js +281 -0
- package/packages/server/src/services/templateTriggerService.js +197 -0
- package/packages/server/src/services/terminalOutput.js +160 -0
- package/packages/server/src/services/todoStore.js +58 -0
- package/packages/server/src/services/usageTracker.js +69 -0
- package/packages/server/src/services/withConcurrencyGuard.js +110 -0
- package/packages/server/src/websocket.js +10 -0
- package/packages/server/src/ws/WebSocketManager.js +240 -0
- package/packages/server/src/ws/index.js +50 -0
- package/packages/shared/package.json +27 -0
- package/packages/shared/src/constants.js +44 -0
- package/packages/shared/src/contracts/canvas.js +25 -0
- package/packages/shared/src/contracts/commandButtons.js +36 -0
- package/packages/shared/src/contracts/kanban.js +142 -0
- package/packages/shared/src/contracts/projects.js +63 -0
- package/packages/shared/src/contracts/providers.js +81 -0
- package/packages/shared/src/contracts/quickResponses.js +44 -0
- package/packages/shared/src/contracts/sessions.js +112 -0
- package/packages/shared/src/contracts/templates.js +51 -0
- package/packages/shared/src/index.js +5 -0
- package/packages/shared/src/protocol.js +76 -0
- package/packages/shared/src/routeParams.js +36 -0
- package/packages/shared/src/types.js +167 -0
- package/packages/shared/src/utils.js +101 -0
- package/packages/web/dist/assets/ActiveSessionsView-BQc76Jc8.js +1 -0
- package/packages/web/dist/assets/ActiveSessionsView-ofSvx-K1.css +1 -0
- package/packages/web/dist/assets/AgentLogsView-CTCjHjsu.js +2 -0
- package/packages/web/dist/assets/AgentLogsView-D90PnQVk.css +1 -0
- package/packages/web/dist/assets/ApiClient-Dbs1H78V.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-CCxSZ52u.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-CQZeuYBz.css +1 -0
- package/packages/web/dist/assets/CommandButtonDetailView-CF_-LXpU.js +1 -0
- package/packages/web/dist/assets/CommandButtonDetailView-DBm3rzhw.css +1 -0
- package/packages/web/dist/assets/EffortLevelSelector-BQaQmU2d.css +1 -0
- package/packages/web/dist/assets/EffortLevelSelector-DPofLvm-.js +1 -0
- package/packages/web/dist/assets/GeneralSettingsView-BCf53fpC.css +1 -0
- package/packages/web/dist/assets/GeneralSettingsView-BY1G-Kv8.js +1 -0
- package/packages/web/dist/assets/InterpolationHelp-CgdbNcJB.js +1 -0
- package/packages/web/dist/assets/InterpolationHelp-iNxTxmhs.css +1 -0
- package/packages/web/dist/assets/MarkdownEditor-CqT1U8lo.js +2 -0
- package/packages/web/dist/assets/MarkdownEditor-enuH2yvP.css +1 -0
- package/packages/web/dist/assets/ModelSelector-BBn_Ve0D.js +1 -0
- package/packages/web/dist/assets/ModelSelector-DPPD-92R.css +1 -0
- package/packages/web/dist/assets/NewSessionView-Bo5l49nu.js +3 -0
- package/packages/web/dist/assets/NewSessionView-Byoi1XdQ.css +1 -0
- package/packages/web/dist/assets/PathChooser-BoMGzeg2.css +1 -0
- package/packages/web/dist/assets/PathChooser-Cx9gQ-Qt.js +1 -0
- package/packages/web/dist/assets/ProjectEditView-BFuscj-V.js +1 -0
- package/packages/web/dist/assets/ProjectEditView-DNwBUNRk.css +1 -0
- package/packages/web/dist/assets/ProjectListView-C55H1JHQ.css +1 -0
- package/packages/web/dist/assets/ProjectListView-Dj0jBZ46.js +1 -0
- package/packages/web/dist/assets/ProjectNewView-Brdp-xUu.js +1 -0
- package/packages/web/dist/assets/ProjectNewView-CpgE4R-l.css +1 -0
- package/packages/web/dist/assets/ProvidersView-B_QQF3RM.css +1 -0
- package/packages/web/dist/assets/ProvidersView-Cxc-1skq.js +1 -0
- package/packages/web/dist/assets/QuickResponseSettings-B2eVAtHW.js +1 -0
- package/packages/web/dist/assets/QuickResponseSettings-B8188A1D.css +1 -0
- package/packages/web/dist/assets/QuickResponsesPanel-DIBQFj0W.css +1 -0
- package/packages/web/dist/assets/QuickResponsesPanel-lU8pW2B0.js +1 -0
- package/packages/web/dist/assets/ResizableTextarea-B5nAA0RV.css +1 -0
- package/packages/web/dist/assets/ResizableTextarea-DSy1mWGY.js +1 -0
- package/packages/web/dist/assets/SessionCard-BvjLwVYg.js +1 -0
- package/packages/web/dist/assets/SessionCard-D20G3bX8.css +1 -0
- package/packages/web/dist/assets/SessionDetailView-BQbPg-RJ.js +36 -0
- package/packages/web/dist/assets/SessionDetailView-BrMG4p2-.css +1 -0
- package/packages/web/dist/assets/SessionFormOptions-BgqFR-5f.js +1 -0
- package/packages/web/dist/assets/SessionFormOptions-BuLlDF-7.css +1 -0
- package/packages/web/dist/assets/SessionListView-BAIBtJF7.css +1 -0
- package/packages/web/dist/assets/SessionListView-CYIHI8qF.js +1 -0
- package/packages/web/dist/assets/SessionLogStream-B-FwUMJQ.js +18 -0
- package/packages/web/dist/assets/SessionLogStream-zPUTiGbe.css +1 -0
- package/packages/web/dist/assets/SettingsView-DC8-hTQ-.css +1 -0
- package/packages/web/dist/assets/SettingsView-fZxpiGp7.js +1 -0
- package/packages/web/dist/assets/SlashCommandWizard-BB30cSvo.css +1 -0
- package/packages/web/dist/assets/SlashCommandWizard-BgaOw9W3.js +1 -0
- package/packages/web/dist/assets/SummarySettingsView-DcsmSVJI.css +1 -0
- package/packages/web/dist/assets/SummarySettingsView-eeu1Xq86.js +1 -0
- package/packages/web/dist/assets/TemplateDetailView-DEPKSwDo.js +1 -0
- package/packages/web/dist/assets/TemplateDetailView-DT2m06W7.css +1 -0
- package/packages/web/dist/assets/apl-B4CMkyY2.js +1 -0
- package/packages/web/dist/assets/asciiarmor-Df11BRmG.js +1 -0
- package/packages/web/dist/assets/asn1-EdZsLKOL.js +1 -0
- package/packages/web/dist/assets/asterisk-B-8jnY81.js +1 -0
- package/packages/web/dist/assets/brainfuck-C4LP7Hcl.js +1 -0
- package/packages/web/dist/assets/clike-B9uivgTg.js +1 -0
- package/packages/web/dist/assets/clojure-BMjYHr_A.js +1 -0
- package/packages/web/dist/assets/cmake-BQqOBYOt.js +1 -0
- package/packages/web/dist/assets/cobol-CWcv1MsR.js +1 -0
- package/packages/web/dist/assets/coffeescript-S37ZYGWr.js +1 -0
- package/packages/web/dist/assets/commandButtons-DNSHH8IA.js +4 -0
- package/packages/web/dist/assets/commonlisp-DBKNyK5s.js +1 -0
- package/packages/web/dist/assets/crystal-SjHAIU92.js +1 -0
- package/packages/web/dist/assets/css-BnMrqG3P.js +1 -0
- package/packages/web/dist/assets/cypher-C_CwsFkJ.js +1 -0
- package/packages/web/dist/assets/d-pRatUO7H.js +1 -0
- package/packages/web/dist/assets/diff-DbItnlRl.js +1 -0
- package/packages/web/dist/assets/dockerfile-BKs6k2Af.js +1 -0
- package/packages/web/dist/assets/dtd-DF_7sFjM.js +1 -0
- package/packages/web/dist/assets/dylan-DwRh75JA.js +1 -0
- package/packages/web/dist/assets/ebnf-CDyGwa7X.js +1 -0
- package/packages/web/dist/assets/ecl-Cabwm37j.js +1 -0
- package/packages/web/dist/assets/eiffel-CnydiIhH.js +1 -0
- package/packages/web/dist/assets/elm-vLlmbW-K.js +1 -0
- package/packages/web/dist/assets/erlang-BNw1qcRV.js +1 -0
- package/packages/web/dist/assets/factor-kuTfRLto.js +1 -0
- package/packages/web/dist/assets/fcl-Kvtd6kyn.js +1 -0
- package/packages/web/dist/assets/forth-Ffai-XNe.js +1 -0
- package/packages/web/dist/assets/fortran-DYz_wnZ1.js +1 -0
- package/packages/web/dist/assets/gas-Bneqetm1.js +1 -0
- package/packages/web/dist/assets/gherkin-heZmZLOM.js +1 -0
- package/packages/web/dist/assets/groovy-D9Dt4D0W.js +1 -0
- package/packages/web/dist/assets/haskell-BWDZoCOh.js +1 -0
- package/packages/web/dist/assets/haxe-H-WmDvRZ.js +1 -0
- package/packages/web/dist/assets/http-DBlCnlav.js +1 -0
- package/packages/web/dist/assets/idl-BEugSyMb.js +1 -0
- package/packages/web/dist/assets/index-BZlHgDSz.js +1 -0
- package/packages/web/dist/assets/index-BhWX8AfE.js +2 -0
- package/packages/web/dist/assets/index-Bi3XvF_f.js +1 -0
- package/packages/web/dist/assets/index-BqXoPf_D.js +1 -0
- package/packages/web/dist/assets/index-CAuTOZSD.js +1 -0
- package/packages/web/dist/assets/index-CKYk-fkb.js +1 -0
- package/packages/web/dist/assets/index-CTumW_tV.js +318 -0
- package/packages/web/dist/assets/index-CVOJVSsC.js +82 -0
- package/packages/web/dist/assets/index-CXK2Z3_z.js +1 -0
- package/packages/web/dist/assets/index-CYllQ3Vd.js +1 -0
- package/packages/web/dist/assets/index-CpsfI08O.js +1 -0
- package/packages/web/dist/assets/index-DQkhDeTA.js +3 -0
- package/packages/web/dist/assets/index-DWP8iCBp.js +1 -0
- package/packages/web/dist/assets/index-DkVb9W_J.js +1 -0
- package/packages/web/dist/assets/index-DmKHPbIa.js +1 -0
- package/packages/web/dist/assets/index-DrlQi03X.js +1 -0
- package/packages/web/dist/assets/index-gmCCsCQ1.css +1 -0
- package/packages/web/dist/assets/index-prTEzzgO.js +1 -0
- package/packages/web/dist/assets/index-wqgejMCM.js +1 -0
- package/packages/web/dist/assets/index-yh0ZHIWw.js +7 -0
- package/packages/web/dist/assets/javascript-qCveANmP.js +1 -0
- package/packages/web/dist/assets/julia-DuME0IfC.js +1 -0
- package/packages/web/dist/assets/livescript-BwQOo05w.js +1 -0
- package/packages/web/dist/assets/lua-BgMRiT3U.js +1 -0
- package/packages/web/dist/assets/mathematica-DTrFuWx2.js +1 -0
- package/packages/web/dist/assets/mbox-CNhZ1qSd.js +1 -0
- package/packages/web/dist/assets/mirc-CjQqDB4T.js +1 -0
- package/packages/web/dist/assets/mllike-CXdrOF99.js +1 -0
- package/packages/web/dist/assets/modelica-Dc1JOy9r.js +1 -0
- package/packages/web/dist/assets/mscgen-BA5vi2Kp.js +1 -0
- package/packages/web/dist/assets/mumps-BT43cFF4.js +1 -0
- package/packages/web/dist/assets/nginx-DdIZxoE0.js +1 -0
- package/packages/web/dist/assets/nsis-LdVXkNf5.js +1 -0
- package/packages/web/dist/assets/ntriples-BfvgReVJ.js +1 -0
- package/packages/web/dist/assets/octave-Ck1zUtKM.js +1 -0
- package/packages/web/dist/assets/oz-BzwKVEFT.js +1 -0
- package/packages/web/dist/assets/pascal--L3eBynH.js +1 -0
- package/packages/web/dist/assets/perl-CdXCOZ3F.js +1 -0
- package/packages/web/dist/assets/pig-CevX1Tat.js +1 -0
- package/packages/web/dist/assets/powershell-CFHJl5sT.js +1 -0
- package/packages/web/dist/assets/projects-DbBQQH-V.js +1 -0
- package/packages/web/dist/assets/properties-C78fOPTZ.js +1 -0
- package/packages/web/dist/assets/protobuf-ChK-085T.js +1 -0
- package/packages/web/dist/assets/providers-ceCc4xRU.js +1 -0
- package/packages/web/dist/assets/pug-DukmZTjD.js +1 -0
- package/packages/web/dist/assets/puppet-DMA9R1ak.js +1 -0
- package/packages/web/dist/assets/python-BuPzkPfP.js +1 -0
- package/packages/web/dist/assets/q-pXgVlZs6.js +1 -0
- package/packages/web/dist/assets/r-DUYO_cvP.js +1 -0
- package/packages/web/dist/assets/rpm-CTu-6PCP.js +1 -0
- package/packages/web/dist/assets/ruby-B2Rjki9n.js +1 -0
- package/packages/web/dist/assets/sas-B4kiWyti.js +1 -0
- package/packages/web/dist/assets/scheme-C41bIUwD.js +1 -0
- package/packages/web/dist/assets/sessions-D681M81k.js +1 -0
- package/packages/web/dist/assets/settings-D0evez2V.js +1 -0
- package/packages/web/dist/assets/shell-CjFT_Tl9.js +1 -0
- package/packages/web/dist/assets/sieve-C3Gn_uJK.js +1 -0
- package/packages/web/dist/assets/simple-mode-GW_nhZxv.js +1 -0
- package/packages/web/dist/assets/smalltalk-CnHTOXQT.js +1 -0
- package/packages/web/dist/assets/solr-DehyRSwq.js +1 -0
- package/packages/web/dist/assets/sparql-DkYu6x3z.js +1 -0
- package/packages/web/dist/assets/spreadsheet-BCZA_wO0.js +1 -0
- package/packages/web/dist/assets/sql-D0XecflT.js +1 -0
- package/packages/web/dist/assets/stex-C3f8Ysf7.js +1 -0
- package/packages/web/dist/assets/style-BTin-zR_.css +1 -0
- package/packages/web/dist/assets/stylus-B533Al4x.js +1 -0
- package/packages/web/dist/assets/swift-BzpIVaGY.js +1 -0
- package/packages/web/dist/assets/tcl-DVfN8rqt.js +1 -0
- package/packages/web/dist/assets/textile-CnDTJFAw.js +1 -0
- package/packages/web/dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
- package/packages/web/dist/assets/tiki-DGYXhP31.js +1 -0
- package/packages/web/dist/assets/toml-Bm5Em-hy.js +1 -0
- package/packages/web/dist/assets/troff-wAsdV37c.js +1 -0
- package/packages/web/dist/assets/ttcn-CfJYG6tj.js +1 -0
- package/packages/web/dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
- package/packages/web/dist/assets/turtle-B1tBg_DP.js +1 -0
- package/packages/web/dist/assets/vb-CmGdzxic.js +1 -0
- package/packages/web/dist/assets/vbscript-BuJXcnF6.js +1 -0
- package/packages/web/dist/assets/velocity-D8B20fx6.js +1 -0
- package/packages/web/dist/assets/verilog-C6RDOZhf.js +1 -0
- package/packages/web/dist/assets/vhdl-lSbBsy5d.js +1 -0
- package/packages/web/dist/assets/webidl-ZXfAyPTL.js +1 -0
- package/packages/web/dist/assets/xquery-CQfU5ijd.js +1 -0
- package/packages/web/dist/assets/yacas-BJ4BC0dw.js +1 -0
- package/packages/web/dist/assets/z80-Hz9HOZM7.js +1 -0
- package/packages/web/dist/favicon.png +0 -0
- package/packages/web/dist/index.html +17 -0
- package/packages/web/dist/logo.png +0 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import {
|
|
2
|
+
kanbanBoards,
|
|
3
|
+
kanbanLanes,
|
|
4
|
+
kanbanCards,
|
|
5
|
+
sessions,
|
|
6
|
+
sessionTemplates,
|
|
7
|
+
projects,
|
|
8
|
+
} from '../database.js';
|
|
9
|
+
import { broadcastToProject } from '../websocket.js';
|
|
10
|
+
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
11
|
+
import { triggerOnEnterTemplate, triggerOnEnterPrompt } from './kanbanTriggers.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Helper to build full board response with lanes and cards
|
|
15
|
+
*/
|
|
16
|
+
function buildFullBoardResponse(board) {
|
|
17
|
+
if (!board) return null;
|
|
18
|
+
|
|
19
|
+
const lanes = kanbanLanes.getByBoardId(board.id);
|
|
20
|
+
const allCards = kanbanCards.getByBoardId(board.id);
|
|
21
|
+
|
|
22
|
+
// Group cards by lane
|
|
23
|
+
const cardsByLane = {};
|
|
24
|
+
for (const lane of lanes) {
|
|
25
|
+
cardsByLane[lane.id] = [];
|
|
26
|
+
}
|
|
27
|
+
for (const card of allCards) {
|
|
28
|
+
if (cardsByLane[card.laneId]) {
|
|
29
|
+
cardsByLane[card.laneId].push(card);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
id: board.id,
|
|
35
|
+
projectId: board.projectId,
|
|
36
|
+
lanes: lanes.map((lane) => ({
|
|
37
|
+
...lane,
|
|
38
|
+
cards: cardsByLane[lane.id] || [],
|
|
39
|
+
})),
|
|
40
|
+
createdAt: board.createdAt,
|
|
41
|
+
updatedAt: board.updatedAt,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the full board with all lanes and cards for a project.
|
|
47
|
+
* Lazy-creates the board with default lanes if it doesn't exist.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} projectId - The project ID
|
|
50
|
+
* @returns {Object|null} Full board with lanes and cards, or null if kanban is disabled
|
|
51
|
+
*/
|
|
52
|
+
export function getFullBoard(projectId) {
|
|
53
|
+
const project = projects.getById(projectId);
|
|
54
|
+
if (!project) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// If kanban is disabled, return null
|
|
59
|
+
if (!project.kanbanEnabled) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const board = kanbanBoards.getOrCreateForProject(projectId);
|
|
64
|
+
return buildFullBoardResponse(board);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Add a session to the kanban board.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} sessionId - The session ID
|
|
71
|
+
* @param {string} laneId - The lane to add the session to
|
|
72
|
+
* @param {Object} [options] - Options
|
|
73
|
+
* @param {number} [options.sortOrder] - Optional sort order
|
|
74
|
+
* @returns {Object} The created card
|
|
75
|
+
* @throws {Error} If session already has a card on the board
|
|
76
|
+
*/
|
|
77
|
+
export function addSessionToBoard(sessionId, laneId, options = {}) {
|
|
78
|
+
// Check if session already has a card
|
|
79
|
+
const existingCard = kanbanCards.getBySessionId(sessionId);
|
|
80
|
+
if (existingCard) {
|
|
81
|
+
throw new Error('Session already has a card on the board');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const card = kanbanCards.create(laneId, sessionId, options);
|
|
85
|
+
|
|
86
|
+
// Get session to find project ID for broadcast
|
|
87
|
+
const session = sessions.getById(sessionId);
|
|
88
|
+
if (session) {
|
|
89
|
+
broadcastToProject(session.projectId, WS_MESSAGE_TYPES.KANBAN_CARD_ADDED, {
|
|
90
|
+
projectId: session.projectId,
|
|
91
|
+
card,
|
|
92
|
+
laneId,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return card;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Move a card to a different lane, optionally triggering the on-enter template.
|
|
101
|
+
*
|
|
102
|
+
* @param {string} cardId - The card ID
|
|
103
|
+
* @param {string} targetLaneId - The target lane ID
|
|
104
|
+
* @param {Object} [options] - Options
|
|
105
|
+
* @param {number} [options.sortOrder] - Optional sort order in target lane
|
|
106
|
+
* @param {boolean} [options.runOnEnterTemplate=true] - Whether to run the on-enter template
|
|
107
|
+
* @param {number} [options.depth=0] - Current recursion depth for template triggers
|
|
108
|
+
* @returns {Promise<Object>} The moved card
|
|
109
|
+
*/
|
|
110
|
+
export async function moveCard(cardId, targetLaneId, options = {}) {
|
|
111
|
+
const { sortOrder, runOnEnterTemplate = true, depth = 0 } = options;
|
|
112
|
+
|
|
113
|
+
const card = kanbanCards.getByIdWithLane(cardId);
|
|
114
|
+
if (!card) {
|
|
115
|
+
throw new Error('Card not found');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const fromLaneId = card.laneId;
|
|
119
|
+
|
|
120
|
+
// Move the card
|
|
121
|
+
const movedCard = kanbanCards.moveToLane(cardId, targetLaneId, sortOrder);
|
|
122
|
+
|
|
123
|
+
// Get session for project ID and broadcast
|
|
124
|
+
const sessionId = card.sessions?.[0]?.id;
|
|
125
|
+
const session = sessionId ? sessions.getById(sessionId) : null;
|
|
126
|
+
|
|
127
|
+
if (session) {
|
|
128
|
+
broadcastToProject(session.projectId, WS_MESSAGE_TYPES.KANBAN_CARD_MOVED, {
|
|
129
|
+
projectId: session.projectId,
|
|
130
|
+
cardId,
|
|
131
|
+
fromLaneId,
|
|
132
|
+
toLaneId: targetLaneId,
|
|
133
|
+
card: movedCard,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Trigger on-enter automation if configured (template or custom prompt)
|
|
137
|
+
if (runOnEnterTemplate) {
|
|
138
|
+
const targetLane = kanbanLanes.getByIdWithTemplate(targetLaneId);
|
|
139
|
+
if (targetLane?.onEnterTemplateId) {
|
|
140
|
+
await triggerOnEnterTemplate(sessionId, targetLane, { depth });
|
|
141
|
+
} else if (targetLane?.onEnterPrompt) {
|
|
142
|
+
await triggerOnEnterPrompt(sessionId, targetLane, { depth });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return movedCard;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Handle turn completion for a session.
|
|
152
|
+
* If the session has a target_lane_id set, move its card to that lane.
|
|
153
|
+
*
|
|
154
|
+
* @param {string} sessionId - The session that just completed its turn
|
|
155
|
+
*/
|
|
156
|
+
export async function handleTurnCompletion(sessionId) {
|
|
157
|
+
const session = sessions.getById(sessionId);
|
|
158
|
+
if (!session) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check if session has a target lane
|
|
163
|
+
if (!session.targetLaneId) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(
|
|
168
|
+
`Kanban: Handling turn completion for session ${sessionId}, target lane: ${session.targetLaneId}`
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// Find or create the card for this session
|
|
172
|
+
let card = kanbanCards.getBySessionId(sessionId);
|
|
173
|
+
|
|
174
|
+
if (!card) {
|
|
175
|
+
// Session doesn't have a card yet, create one in the target lane
|
|
176
|
+
const lane = kanbanLanes.getById(session.targetLaneId);
|
|
177
|
+
if (!lane) {
|
|
178
|
+
console.warn(`Kanban: Target lane ${session.targetLaneId} not found for session ${sessionId}`);
|
|
179
|
+
// Clear the invalid target lane
|
|
180
|
+
sessions.update(sessionId, { targetLaneId: null });
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
card = kanbanCards.create(session.targetLaneId, sessionId);
|
|
185
|
+
|
|
186
|
+
broadcastToProject(session.projectId, WS_MESSAGE_TYPES.KANBAN_CARD_ADDED, {
|
|
187
|
+
projectId: session.projectId,
|
|
188
|
+
card,
|
|
189
|
+
laneId: session.targetLaneId,
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
// Move existing card to target lane
|
|
193
|
+
const fromLaneId = card.laneId;
|
|
194
|
+
|
|
195
|
+
if (fromLaneId !== session.targetLaneId) {
|
|
196
|
+
await moveCard(card.id, session.targetLaneId, {
|
|
197
|
+
runOnEnterTemplate: true,
|
|
198
|
+
depth: session.laneTriggerDepth || 0,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Clear the target lane now that we've processed it
|
|
204
|
+
sessions.update(sessionId, { targetLaneId: null });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Remove a session from the board (called when session is deleted).
|
|
209
|
+
*
|
|
210
|
+
* @param {string} sessionId - The session ID
|
|
211
|
+
*/
|
|
212
|
+
export function removeSessionFromBoard(sessionId) {
|
|
213
|
+
const card = kanbanCards.getBySessionId(sessionId);
|
|
214
|
+
if (!card) {
|
|
215
|
+
return; // Session wasn't on the board
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const laneId = card.laneId;
|
|
219
|
+
const session = sessions.getById(sessionId);
|
|
220
|
+
const projectId = session?.projectId;
|
|
221
|
+
|
|
222
|
+
kanbanCards.delete(card.id);
|
|
223
|
+
|
|
224
|
+
if (projectId) {
|
|
225
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.KANBAN_CARD_REMOVED, {
|
|
226
|
+
projectId,
|
|
227
|
+
cardId: card.id,
|
|
228
|
+
laneId,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Add a session to its template's target lane if configured.
|
|
235
|
+
* Called after session creation from a template.
|
|
236
|
+
*
|
|
237
|
+
* @param {string} sessionId - The newly created session ID
|
|
238
|
+
* @param {string} templateId - The template ID used to create the session
|
|
239
|
+
*/
|
|
240
|
+
export function addSessionToTemplateTargetLane(sessionId, templateId) {
|
|
241
|
+
const template = sessionTemplates.getById(templateId);
|
|
242
|
+
if (!template?.targetLaneId) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const lane = kanbanLanes.getById(template.targetLaneId);
|
|
247
|
+
if (!lane) {
|
|
248
|
+
console.warn(
|
|
249
|
+
`Kanban: Template target lane ${template.targetLaneId} not found for session ${sessionId}`
|
|
250
|
+
);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
addSessionToBoard(sessionId, template.targetLaneId);
|
|
256
|
+
console.log(
|
|
257
|
+
`Kanban: Added session ${sessionId} to lane "${lane.name}" based on template target`
|
|
258
|
+
);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.warn(`Kanban: Failed to add session ${sessionId} to board:`, error.message);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sessions,
|
|
3
|
+
sessionTemplates,
|
|
4
|
+
sessionSummaries,
|
|
5
|
+
projects,
|
|
6
|
+
} from '../database.js';
|
|
7
|
+
import { broadcastToProject } from '../websocket.js';
|
|
8
|
+
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
9
|
+
import { renderTemplatePrompt, getRootSession } from './templateTriggerService.js';
|
|
10
|
+
import { setupGitForSession } from './gitSessionSetup.js';
|
|
11
|
+
import { runSession } from './sessionManager.js';
|
|
12
|
+
|
|
13
|
+
// Maximum depth for recursive lane-entry template triggers
|
|
14
|
+
export const MAX_LANE_TRIGGER_DEPTH = 5;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get session and project for lane trigger, returning null if not found.
|
|
18
|
+
* @param {string} sessionId
|
|
19
|
+
* @returns {{session: Object, project: Object}|null}
|
|
20
|
+
*/
|
|
21
|
+
export function getSessionAndProjectForTrigger(sessionId) {
|
|
22
|
+
const session = sessions.getById(sessionId);
|
|
23
|
+
if (!session) {
|
|
24
|
+
console.warn(`Kanban: Session ${sessionId} not found for on-enter trigger`);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const project = projects.getById(session.projectId);
|
|
28
|
+
if (!project) {
|
|
29
|
+
console.warn(`Kanban: Project ${session.projectId} not found for session ${sessionId}`);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return { session, project };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Determine working directory for child session, inheriting parent's worktree if present.
|
|
37
|
+
* @param {Object} parentSession
|
|
38
|
+
* @param {Object} project
|
|
39
|
+
* @param {Object} [gitOptions]
|
|
40
|
+
* @param {string} [gitOptions.gitMode]
|
|
41
|
+
* @param {string} [gitOptions.gitBranch]
|
|
42
|
+
* @param {string} [gitOptions.sessionId]
|
|
43
|
+
* @returns {Promise<{workingDirectory: string, gitWorktree: string|null}>}
|
|
44
|
+
*/
|
|
45
|
+
export async function determineWorkingDirectory(parentSession, project, gitOptions = {}) {
|
|
46
|
+
if (parentSession.gitWorktree) {
|
|
47
|
+
console.log(`Kanban: Inheriting parent worktree: ${parentSession.gitWorktree}`);
|
|
48
|
+
return { workingDirectory: parentSession.gitWorktree, gitWorktree: parentSession.gitWorktree };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (gitOptions.sessionId) {
|
|
52
|
+
const gitSetup = await setupGitForSession({
|
|
53
|
+
projectDir: project.workingDirectory,
|
|
54
|
+
gitMode: gitOptions.gitMode || null,
|
|
55
|
+
gitBranch: gitOptions.gitBranch || null,
|
|
56
|
+
sessionId: gitOptions.sessionId,
|
|
57
|
+
});
|
|
58
|
+
return { workingDirectory: gitSetup.workingDirectory, gitWorktree: gitSetup.gitWorktree };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { workingDirectory: project.workingDirectory, gitWorktree: null };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Start a child session and handle errors via broadcast.
|
|
66
|
+
* @param {Object} newSession
|
|
67
|
+
* @param {string} prompt
|
|
68
|
+
* @param {string} workingDirectory
|
|
69
|
+
* @param {Object} options
|
|
70
|
+
*/
|
|
71
|
+
export function startChildSession(newSession, prompt, workingDirectory, options) {
|
|
72
|
+
runSession(newSession.id, prompt, workingDirectory, options).catch((error) => {
|
|
73
|
+
console.error(`Kanban: Error running on-enter session ${newSession.id}:`, error);
|
|
74
|
+
const errorSession = sessions.update(newSession.id, { status: 'error', error: error.message });
|
|
75
|
+
broadcastToProject(newSession.projectId, WS_MESSAGE_TYPES.SESSION_UPDATED, {
|
|
76
|
+
projectId: newSession.projectId,
|
|
77
|
+
sessionId: newSession.id,
|
|
78
|
+
session: errorSession,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get lane session settings from lane or inherit from parent session.
|
|
85
|
+
* @param {Object} lane
|
|
86
|
+
* @param {Object} session
|
|
87
|
+
* @returns {Object}
|
|
88
|
+
*/
|
|
89
|
+
export function getLaneSessionSettings(lane, session) {
|
|
90
|
+
return {
|
|
91
|
+
thinkingEnabled: lane.onEnterThinkingEnabled ?? session.thinkingEnabled,
|
|
92
|
+
model: lane.onEnterModel || session.model,
|
|
93
|
+
mode: lane.onEnterMode || session.mode,
|
|
94
|
+
effortLevel: lane.onEnterEffortLevel || session.effortLevel || null,
|
|
95
|
+
gitBranch: session.gitBranch,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get template session settings from template or inherit from parent session.
|
|
101
|
+
* @param {Object} template
|
|
102
|
+
* @param {Object} session
|
|
103
|
+
* @returns {Object}
|
|
104
|
+
*/
|
|
105
|
+
export function getTemplateSessionSettings(template, session) {
|
|
106
|
+
return {
|
|
107
|
+
thinkingEnabled: template.thinkingEnabled !== null ? template.thinkingEnabled : session.thinkingEnabled,
|
|
108
|
+
model: template.model || session.model,
|
|
109
|
+
mode: template.mode || session.mode,
|
|
110
|
+
gitBranch: template.gitBranch || session.gitBranch,
|
|
111
|
+
gitMode: template.gitMode || null,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Trigger the on-enter template for a lane.
|
|
117
|
+
*
|
|
118
|
+
* @param {string} sessionId - The session that entered the lane
|
|
119
|
+
* @param {Object} lane - The lane with template info
|
|
120
|
+
* @param {Object} [options] - Options
|
|
121
|
+
* @param {number} [options.depth=0] - Current recursion depth
|
|
122
|
+
*/
|
|
123
|
+
export async function triggerOnEnterTemplate(sessionId, lane, options = {}) {
|
|
124
|
+
const { depth = 0 } = options;
|
|
125
|
+
|
|
126
|
+
if (depth >= MAX_LANE_TRIGGER_DEPTH) {
|
|
127
|
+
console.warn(`Lane trigger depth limit reached for session ${sessionId} in lane ${lane.id}`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const template = sessionTemplates.getById(lane.onEnterTemplateId);
|
|
132
|
+
if (!template) {
|
|
133
|
+
console.warn(`Kanban: On-enter template ${lane.onEnterTemplateId} not found for lane ${lane.id}`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const context = getSessionAndProjectForTrigger(sessionId);
|
|
138
|
+
if (!context) return;
|
|
139
|
+
const { session, project } = context;
|
|
140
|
+
|
|
141
|
+
console.log(`Kanban: Triggering on-enter template "${template.name}" for session "${session.name}" entering lane "${lane.name}"`);
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
// Render prompt with session context
|
|
145
|
+
const parentSummary = sessionSummaries.getBySessionId(sessionId);
|
|
146
|
+
const rootSession = getRootSession(session);
|
|
147
|
+
const rootSummary = sessionSummaries.getBySessionId(rootSession.id);
|
|
148
|
+
const renderedPrompt = await renderTemplatePrompt(
|
|
149
|
+
template.prompt,
|
|
150
|
+
{ parentSession: session, parentSummary, rootSession, rootSummary }
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// Get settings and create session
|
|
154
|
+
const settings = getTemplateSessionSettings(template, session);
|
|
155
|
+
const newSession = sessions.create(session.projectId, `${template.name} (lane: ${lane.name})`, renderedPrompt, {
|
|
156
|
+
mode: settings.mode,
|
|
157
|
+
thinkingEnabled: settings.thinkingEnabled,
|
|
158
|
+
gitBranch: settings.gitBranch,
|
|
159
|
+
status: 'starting',
|
|
160
|
+
model: settings.model,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Configure session
|
|
164
|
+
sessions.update(newSession.id, {
|
|
165
|
+
parentSessionId: session.id,
|
|
166
|
+
nextTemplateId: template.nextTemplateId || null,
|
|
167
|
+
targetLaneId: template.targetLaneId || null,
|
|
168
|
+
laneTriggerDepth: depth + 1,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Determine working directory
|
|
172
|
+
const { workingDirectory, gitWorktree } = await determineWorkingDirectory(session, project, {
|
|
173
|
+
gitMode: settings.gitMode,
|
|
174
|
+
gitBranch: settings.gitBranch,
|
|
175
|
+
sessionId: newSession.id,
|
|
176
|
+
});
|
|
177
|
+
if (gitWorktree) {
|
|
178
|
+
sessions.update(newSession.id, { gitWorktree });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Broadcast and start
|
|
182
|
+
broadcastToProject(session.projectId, WS_MESSAGE_TYPES.SESSION_CREATED, {
|
|
183
|
+
projectId: session.projectId,
|
|
184
|
+
session: sessions.getById(newSession.id),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
startChildSession(newSession, renderedPrompt, workingDirectory, {
|
|
188
|
+
systemPrompt: project.systemPrompt,
|
|
189
|
+
model: settings.model,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
console.log(`Kanban: Created and started on-enter session ${newSession.id}`);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error(`Kanban: Failed to trigger on-enter template for session ${sessionId}:`, error);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Trigger a custom prompt for a lane when a session enters it.
|
|
200
|
+
*
|
|
201
|
+
* @param {string} sessionId - The session that entered the lane
|
|
202
|
+
* @param {Object} lane - The lane with prompt info
|
|
203
|
+
* @param {Object} [options] - Options
|
|
204
|
+
* @param {number} [options.depth=0] - Current recursion depth
|
|
205
|
+
*/
|
|
206
|
+
export async function triggerOnEnterPrompt(sessionId, lane, options = {}) {
|
|
207
|
+
const { depth = 0 } = options;
|
|
208
|
+
|
|
209
|
+
if (depth >= MAX_LANE_TRIGGER_DEPTH) {
|
|
210
|
+
console.warn(`Lane trigger depth limit reached for session ${sessionId} in lane ${lane.id}`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const context = getSessionAndProjectForTrigger(sessionId);
|
|
215
|
+
if (!context) return;
|
|
216
|
+
const { session, project } = context;
|
|
217
|
+
|
|
218
|
+
console.log(`Kanban: Triggering on-enter prompt for session "${session.name}" entering lane "${lane.name}"`);
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
// Render prompt with session context
|
|
222
|
+
const parentSummary = sessionSummaries.getBySessionId(sessionId);
|
|
223
|
+
const rootSession = getRootSession(session);
|
|
224
|
+
const rootSummary = sessionSummaries.getBySessionId(rootSession.id);
|
|
225
|
+
const renderedPrompt = await renderTemplatePrompt(
|
|
226
|
+
lane.onEnterPrompt,
|
|
227
|
+
{ parentSession: session, parentSummary, rootSession, rootSummary }
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Get settings and create session
|
|
231
|
+
const settings = getLaneSessionSettings(lane, session);
|
|
232
|
+
const newSession = sessions.create(session.projectId, `Lane prompt (lane: ${lane.name})`, renderedPrompt, {
|
|
233
|
+
...settings,
|
|
234
|
+
status: 'starting',
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Configure session
|
|
238
|
+
const sessionUpdates = { parentSessionId: session.id, laneTriggerDepth: depth + 1 };
|
|
239
|
+
if (lane.onEnterAutoRescheduleEnabled) {
|
|
240
|
+
Object.assign(sessionUpdates, {
|
|
241
|
+
autoRescheduleEnabled: true,
|
|
242
|
+
rescheduleDelayMinutes: lane.onEnterRescheduleDelayMinutes || 15,
|
|
243
|
+
rescheduleOnTokenLimit: lane.onEnterRescheduleOnTokenLimit ?? true,
|
|
244
|
+
rescheduleOnServiceError: lane.onEnterRescheduleOnServiceError ?? true,
|
|
245
|
+
maxRescheduleCount: lane.onEnterMaxRescheduleCount || null,
|
|
246
|
+
maxTotalTokens: lane.onEnterMaxTotalTokens || null,
|
|
247
|
+
rescheduleAtTokenCount: lane.onEnterRescheduleAtTokenCount || null,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
sessions.update(newSession.id, sessionUpdates);
|
|
251
|
+
|
|
252
|
+
// Determine working directory
|
|
253
|
+
const { workingDirectory, gitWorktree } = await determineWorkingDirectory(session, project);
|
|
254
|
+
if (gitWorktree) {
|
|
255
|
+
sessions.update(newSession.id, { gitWorktree });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Broadcast and start
|
|
259
|
+
broadcastToProject(session.projectId, WS_MESSAGE_TYPES.SESSION_CREATED, {
|
|
260
|
+
projectId: session.projectId,
|
|
261
|
+
session: sessions.getById(newSession.id),
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
startChildSession(newSession, renderedPrompt, workingDirectory, {
|
|
265
|
+
systemPrompt: project.systemPrompt,
|
|
266
|
+
model: settings.model,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
console.log(`Kanban: Created and started on-enter prompt session ${newSession.id}`);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error(`Kanban: Failed to trigger on-enter prompt for session ${sessionId}:`, error);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get the directory containing the current Node.js executable.
|
|
6
|
+
* Used to ensure child processes can find node even when using version managers.
|
|
7
|
+
* @returns {string} Path to the directory containing the Node binary
|
|
8
|
+
*/
|
|
9
|
+
export function getNodeBinDir() {
|
|
10
|
+
return path.dirname(process.execPath);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create environment with guaranteed Node.js in PATH.
|
|
15
|
+
* Prepends the Node binary directory to PATH to ensure child processes can find node.
|
|
16
|
+
* This is critical for npx users with nvm/fnm/volta where 'node' may not be in system PATH.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} [baseEnv=process.env] - Base environment to extend
|
|
19
|
+
* @returns {Object} Environment object with robust PATH
|
|
20
|
+
*/
|
|
21
|
+
export function createRobustEnv(baseEnv = process.env) {
|
|
22
|
+
const nodeBinDir = getNodeBinDir();
|
|
23
|
+
const pathSeparator = process.platform === 'win32' ? ';' : ':';
|
|
24
|
+
const currentPath = baseEnv.PATH || baseEnv.Path || '';
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
...baseEnv,
|
|
28
|
+
PATH: `${nodeBinDir}${pathSeparator}${currentPath}`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create a custom spawn function for the Claude Agent SDK.
|
|
34
|
+
* Replaces 'node' command with process.execPath and ensures PATH is correct.
|
|
35
|
+
*
|
|
36
|
+
* This solves the "spawn node ENOENT" error that occurs when:
|
|
37
|
+
* - Users run the app via npx with Node version managers (nvm, fnm, volta)
|
|
38
|
+
* - The system PATH doesn't include the Node binary directory
|
|
39
|
+
*
|
|
40
|
+
* @returns {Function} Spawn function compatible with SDK's spawnClaudeCodeProcess option
|
|
41
|
+
*/
|
|
42
|
+
export function createClaudeCodeSpawner() {
|
|
43
|
+
return (options) => {
|
|
44
|
+
const { command, args, cwd, env, signal } = options;
|
|
45
|
+
|
|
46
|
+
// Replace 'node' with the absolute path to the current Node executable
|
|
47
|
+
// This ensures we use the same Node that's running our app
|
|
48
|
+
const actualCommand = command === 'node' ? process.execPath : command;
|
|
49
|
+
|
|
50
|
+
// Ensure PATH includes the directory containing Node
|
|
51
|
+
const robustEnv = createRobustEnv(env);
|
|
52
|
+
|
|
53
|
+
const stderrMode = robustEnv.DEBUG_CLAUDE_AGENT_SDK ? 'pipe' : 'ignore';
|
|
54
|
+
|
|
55
|
+
return spawn(actualCommand, args, {
|
|
56
|
+
cwd,
|
|
57
|
+
stdio: ['pipe', 'pipe', stderrMode],
|
|
58
|
+
signal,
|
|
59
|
+
env: robustEnv,
|
|
60
|
+
windowsHide: true,
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
}
|