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,169 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { sessions, projects } from '../database.js';
|
|
3
|
+
import * as slashCommandService from '../services/slashCommandService.js';
|
|
4
|
+
import { continueSession } from '../services/sessionManager.js';
|
|
5
|
+
|
|
6
|
+
const router = Router();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* GET /api/commands
|
|
10
|
+
* List all available slash commands for a directory
|
|
11
|
+
*
|
|
12
|
+
* Discovers commands from:
|
|
13
|
+
* - Project .claude/commands/ folder
|
|
14
|
+
* - User ~/.claude/commands/ folder
|
|
15
|
+
* - Installed plugins
|
|
16
|
+
* - Built-in Claude Code commands
|
|
17
|
+
*
|
|
18
|
+
* Query params:
|
|
19
|
+
* - directory: Working directory to discover commands from (required)
|
|
20
|
+
*/
|
|
21
|
+
router.get('/', async (req, res) => {
|
|
22
|
+
const { directory } = req.query;
|
|
23
|
+
|
|
24
|
+
if (!directory) {
|
|
25
|
+
return res.status(400).json({ error: 'directory query parameter is required' });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const commands = await slashCommandService.getCommands(directory);
|
|
30
|
+
res.json(commands);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.error('Error getting commands:', err);
|
|
33
|
+
res.status(500).json({ error: err.message });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* GET /api/commands/:name
|
|
39
|
+
* Get a single command's details
|
|
40
|
+
*
|
|
41
|
+
* Query params:
|
|
42
|
+
* - directory: Working directory to discover commands from (required)
|
|
43
|
+
*/
|
|
44
|
+
router.get('/:name', async (req, res) => {
|
|
45
|
+
const { directory } = req.query;
|
|
46
|
+
const { name } = req.params;
|
|
47
|
+
|
|
48
|
+
if (!directory) {
|
|
49
|
+
return res.status(400).json({ error: 'directory query parameter is required' });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const command = await slashCommandService.getCommand(directory, name);
|
|
54
|
+
|
|
55
|
+
if (!command) {
|
|
56
|
+
return res.status(404).json({ error: `Command not found: ${name}` });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
res.json(command);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.error('Error getting command:', err);
|
|
62
|
+
res.status(500).json({ error: err.message });
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* POST /api/commands/:name/execute
|
|
68
|
+
* Execute a slash command in a session
|
|
69
|
+
*
|
|
70
|
+
* Body:
|
|
71
|
+
* - sessionId: Session to execute command in (required)
|
|
72
|
+
* - args: Object with argument values keyed by argument name (optional)
|
|
73
|
+
*/
|
|
74
|
+
router.post('/:name/execute', async (req, res) => {
|
|
75
|
+
const { name } = req.params;
|
|
76
|
+
const { sessionId, args = {} } = req.body;
|
|
77
|
+
|
|
78
|
+
if (!sessionId) {
|
|
79
|
+
return res.status(400).json({ error: 'sessionId is required' });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get the session
|
|
83
|
+
const session = sessions.getById(sessionId);
|
|
84
|
+
if (!session) {
|
|
85
|
+
return res.status(404).json({ error: 'Session not found' });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Get the project to find the working directory
|
|
89
|
+
const project = projects.getById(session.projectId);
|
|
90
|
+
if (!project) {
|
|
91
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Determine working directory (may be a worktree)
|
|
95
|
+
const workingDirectory = session.gitWorktree || project.workingDirectory;
|
|
96
|
+
|
|
97
|
+
// The session must be in a state that accepts messages (waiting, stopped, error)
|
|
98
|
+
const validStatuses = ['waiting', 'stopped', 'error'];
|
|
99
|
+
if (!validStatuses.includes(session.status)) {
|
|
100
|
+
return res.status(400).json({
|
|
101
|
+
error: `Session is not ready for commands. Current status: ${session.status}`,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
// Check if this is a skill — skills need special handling:
|
|
107
|
+
// skill body → system prompt, user args → user message
|
|
108
|
+
const skillInvocation = await slashCommandService.buildSkillInvocation(
|
|
109
|
+
workingDirectory,
|
|
110
|
+
name,
|
|
111
|
+
args
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (skillInvocation) {
|
|
115
|
+
// Skill: inject body as system prompt context, args as user message
|
|
116
|
+
const skillSystemPrompt = slashCommandService.buildSkillSystemPrompt(
|
|
117
|
+
project.systemPrompt || null,
|
|
118
|
+
skillInvocation
|
|
119
|
+
);
|
|
120
|
+
const userMessage = slashCommandService.buildSkillUserMessage(skillInvocation);
|
|
121
|
+
|
|
122
|
+
continueSession(
|
|
123
|
+
sessionId,
|
|
124
|
+
userMessage,
|
|
125
|
+
workingDirectory,
|
|
126
|
+
{ systemPrompt: skillSystemPrompt } // No file attachments for slash commands
|
|
127
|
+
).catch(err => {
|
|
128
|
+
console.error('Error executing skill:', err);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
res.json({
|
|
132
|
+
success: true,
|
|
133
|
+
command: name,
|
|
134
|
+
message: userMessage,
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Non-skill command: build command string and send as user message
|
|
140
|
+
const commandString = await slashCommandService.buildCommandString(
|
|
141
|
+
workingDirectory,
|
|
142
|
+
name,
|
|
143
|
+
args
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Use continueSession to send the command
|
|
147
|
+
// This handles the full message flow including broadcasting to WebSocket
|
|
148
|
+
continueSession(
|
|
149
|
+
sessionId,
|
|
150
|
+
commandString,
|
|
151
|
+
workingDirectory,
|
|
152
|
+
{ systemPrompt: project.systemPrompt || null } // No file attachments for slash commands
|
|
153
|
+
).catch(err => {
|
|
154
|
+
// Log but don't throw - continueSession runs asynchronously
|
|
155
|
+
console.error('Error executing command:', err);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
res.json({
|
|
159
|
+
success: true,
|
|
160
|
+
command: name,
|
|
161
|
+
message: commandString,
|
|
162
|
+
});
|
|
163
|
+
} catch (err) {
|
|
164
|
+
console.error('Error executing command:', err);
|
|
165
|
+
res.status(500).json({ error: err.message });
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
export default router;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { readdir } from 'fs/promises';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { resolve, dirname, normalize } from 'path';
|
|
5
|
+
|
|
6
|
+
const router = Router();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* GET /api/filesystem/browse - Browse directory contents
|
|
10
|
+
* Query params:
|
|
11
|
+
* - path: Directory path to browse (defaults to home directory)
|
|
12
|
+
* Returns only directories, sorted alphabetically
|
|
13
|
+
*/
|
|
14
|
+
router.get('/browse', async (req, res) => {
|
|
15
|
+
try {
|
|
16
|
+
// Use provided path or default to home directory
|
|
17
|
+
const requestedPath = req.query.path ? String(req.query.path) : homedir();
|
|
18
|
+
const normalizedPath = normalize(resolve(requestedPath));
|
|
19
|
+
|
|
20
|
+
// Get parent directory (null if at root)
|
|
21
|
+
const parent = normalizedPath === '/' ? null : dirname(normalizedPath);
|
|
22
|
+
|
|
23
|
+
let entries = [];
|
|
24
|
+
let error = null;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const dirents = await readdir(normalizedPath, { withFileTypes: true });
|
|
28
|
+
|
|
29
|
+
// Filter to only directories and sort alphabetically
|
|
30
|
+
entries = dirents
|
|
31
|
+
.filter((dirent) => dirent.isDirectory())
|
|
32
|
+
.map((dirent) => ({
|
|
33
|
+
name: dirent.name,
|
|
34
|
+
type: 'directory',
|
|
35
|
+
}))
|
|
36
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
37
|
+
} catch (err) {
|
|
38
|
+
// Handle permission errors or non-existent directories gracefully
|
|
39
|
+
if (err.code === 'EACCES') {
|
|
40
|
+
error = 'Permission denied';
|
|
41
|
+
} else if (err.code === 'ENOENT') {
|
|
42
|
+
error = 'Directory not found';
|
|
43
|
+
} else if (err.code === 'ENOTDIR') {
|
|
44
|
+
error = 'Not a directory';
|
|
45
|
+
} else {
|
|
46
|
+
error = 'Unable to read directory';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
res.json({
|
|
51
|
+
path: normalizedPath,
|
|
52
|
+
parent,
|
|
53
|
+
entries,
|
|
54
|
+
error,
|
|
55
|
+
});
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.error('Filesystem browse error:', err);
|
|
58
|
+
res.status(500).json({ error: 'Failed to browse directory' });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export default router;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { projects } from '../database.js';
|
|
3
|
+
import * as gitService from '../services/gitService.js';
|
|
4
|
+
|
|
5
|
+
const router = Router();
|
|
6
|
+
|
|
7
|
+
// GET /api/projects/:id/git/status - Check if git repo, get branches
|
|
8
|
+
router.get('/projects/:id/status', async (req, res) => {
|
|
9
|
+
const project = projects.getById(req.params.id);
|
|
10
|
+
if (!project) {
|
|
11
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const isRepo = await gitService.isGitRepo(project.workingDirectory);
|
|
16
|
+
if (!isRepo) {
|
|
17
|
+
return res.json({ isGitRepo: false });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const [branches, currentBranch] = await Promise.all([
|
|
21
|
+
gitService.getBranches(project.workingDirectory),
|
|
22
|
+
gitService.getCurrentBranch(project.workingDirectory),
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
res.json({
|
|
26
|
+
isGitRepo: true,
|
|
27
|
+
currentBranch,
|
|
28
|
+
branches,
|
|
29
|
+
});
|
|
30
|
+
} catch (error) {
|
|
31
|
+
res.status(500).json({ error: error.message });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// GET /api/projects/:id/git/worktrees - List worktrees
|
|
36
|
+
router.get('/projects/:id/worktrees', async (req, res) => {
|
|
37
|
+
const project = projects.getById(req.params.id);
|
|
38
|
+
if (!project) {
|
|
39
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const worktrees = await gitService.getWorktrees(project.workingDirectory);
|
|
44
|
+
res.json(worktrees);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
res.status(500).json({ error: error.message });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// POST /api/projects/:id/git/worktrees - Create worktree
|
|
51
|
+
router.post('/projects/:id/worktrees', async (req, res) => {
|
|
52
|
+
const project = projects.getById(req.params.id);
|
|
53
|
+
if (!project) {
|
|
54
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const { branch, path } = req.body;
|
|
58
|
+
if (!branch || !path) {
|
|
59
|
+
return res.status(400).json({ error: 'Branch and path are required' });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const worktree = await gitService.createWorktree(project.workingDirectory, branch, path);
|
|
64
|
+
res.status(201).json(worktree);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
res.status(500).json({ error: error.message });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// DELETE /api/projects/:id/git/worktrees/:path - Remove worktree
|
|
71
|
+
router.delete('/projects/:id/worktrees/:path(*)', async (req, res) => {
|
|
72
|
+
const project = projects.getById(req.params.id);
|
|
73
|
+
if (!project) {
|
|
74
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
await gitService.removeWorktree(project.workingDirectory, req.params.path);
|
|
79
|
+
res.status(204).send();
|
|
80
|
+
} catch (error) {
|
|
81
|
+
res.status(500).json({ error: error.message });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export default router;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import projectsRouter from './projects.js';
|
|
3
|
+
import sessionsRouter from './sessions.js';
|
|
4
|
+
import templatesRouter from './templates.js';
|
|
5
|
+
import canvasRouter from './canvas.js';
|
|
6
|
+
import gitRouter from './git.js';
|
|
7
|
+
import filesystemRouter from './filesystem.js';
|
|
8
|
+
import quickResponsesRouter from './quickResponses.js';
|
|
9
|
+
import settingsRouter from './settings.js';
|
|
10
|
+
import providersRouter from './providers.js';
|
|
11
|
+
import commandsRouter from './commands.js';
|
|
12
|
+
import metricsRouter from './metrics.js';
|
|
13
|
+
import kanbanRouter from './kanban.js';
|
|
14
|
+
|
|
15
|
+
const router = Router();
|
|
16
|
+
|
|
17
|
+
// Lightweight identity endpoint — lets tools (e.g. pw.sh) verify which
|
|
18
|
+
// worktree / working directory this server instance belongs to.
|
|
19
|
+
router.get('/server-info', (_req, res) => {
|
|
20
|
+
res.json({ cwd: process.cwd() });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
router.use('/projects', projectsRouter);
|
|
24
|
+
router.use('/sessions', sessionsRouter);
|
|
25
|
+
router.use('/templates', templatesRouter);
|
|
26
|
+
router.use('/git', gitRouter);
|
|
27
|
+
router.use('/filesystem', filesystemRouter);
|
|
28
|
+
router.use('/settings', settingsRouter);
|
|
29
|
+
router.use('/providers', providersRouter);
|
|
30
|
+
router.use('/commands', commandsRouter)
|
|
31
|
+
|
|
32
|
+
// Canvas routes are nested under sessions
|
|
33
|
+
router.use('/sessions', canvasRouter);
|
|
34
|
+
|
|
35
|
+
// Kanban routes are nested under projects
|
|
36
|
+
router.use('/projects/:projectId/kanban', kanbanRouter);
|
|
37
|
+
|
|
38
|
+
// Metrics routes (agent call stats)
|
|
39
|
+
router.use('/', metricsRouter);
|
|
40
|
+
|
|
41
|
+
// Quick responses routes (nested under both projects and standalone)
|
|
42
|
+
router.use('/', quickResponsesRouter);
|
|
43
|
+
|
|
44
|
+
export default router;
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { kanbanBoards, kanbanLanes, kanbanCards, projects } from '../database.js';
|
|
3
|
+
import { broadcastToProject } from '../websocket.js';
|
|
4
|
+
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
5
|
+
import {
|
|
6
|
+
CreateKanbanLaneRequest,
|
|
7
|
+
UpdateKanbanLaneRequest,
|
|
8
|
+
ReorderKanbanLanesRequest,
|
|
9
|
+
CreateKanbanCardRequest,
|
|
10
|
+
MoveKanbanCardRequest,
|
|
11
|
+
ReorderKanbanCardsRequest,
|
|
12
|
+
} from '../../../shared/src/contracts/kanban.js';
|
|
13
|
+
import { moveCard as moveCardService } from '../services/kanbanService.js';
|
|
14
|
+
|
|
15
|
+
const router = Router({ mergeParams: true });
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Helper to build full board response with lanes and cards
|
|
19
|
+
*/
|
|
20
|
+
function buildFullBoardResponse(board) {
|
|
21
|
+
if (!board) return null;
|
|
22
|
+
|
|
23
|
+
const lanes = kanbanLanes.getByBoardId(board.id);
|
|
24
|
+
const allCards = kanbanCards.getByBoardId(board.id);
|
|
25
|
+
|
|
26
|
+
// Group cards by lane
|
|
27
|
+
const cardsByLane = {};
|
|
28
|
+
for (const lane of lanes) {
|
|
29
|
+
cardsByLane[lane.id] = [];
|
|
30
|
+
}
|
|
31
|
+
for (const card of allCards) {
|
|
32
|
+
if (cardsByLane[card.laneId]) {
|
|
33
|
+
cardsByLane[card.laneId].push(card);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
id: board.id,
|
|
39
|
+
projectId: board.projectId,
|
|
40
|
+
lanes: lanes.map(lane => ({
|
|
41
|
+
...lane,
|
|
42
|
+
cards: cardsByLane[lane.id] || [],
|
|
43
|
+
})),
|
|
44
|
+
createdAt: board.createdAt,
|
|
45
|
+
updatedAt: board.updatedAt,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ============== Board Endpoints ==============
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* GET /api/projects/:projectId/kanban
|
|
53
|
+
* Get board with all lanes and cards. Auto-creates if kanban_enabled.
|
|
54
|
+
*/
|
|
55
|
+
router.get('/', (req, res) => {
|
|
56
|
+
const { projectId } = req.params;
|
|
57
|
+
|
|
58
|
+
const project = projects.getById(projectId);
|
|
59
|
+
if (!project) {
|
|
60
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// If kanban is disabled, return null
|
|
64
|
+
if (!project.kanbanEnabled) {
|
|
65
|
+
return res.json(null);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Get or create board
|
|
69
|
+
const board = kanbanBoards.getOrCreateForProject(projectId);
|
|
70
|
+
const fullBoard = buildFullBoardResponse(board);
|
|
71
|
+
|
|
72
|
+
res.json(fullBoard);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* DELETE /api/projects/:projectId/kanban
|
|
77
|
+
* Delete board (resets all kanban data)
|
|
78
|
+
*/
|
|
79
|
+
router.delete('/', (req, res) => {
|
|
80
|
+
const { projectId } = req.params;
|
|
81
|
+
|
|
82
|
+
const board = kanbanBoards.getByProjectId(projectId);
|
|
83
|
+
if (!board) {
|
|
84
|
+
return res.status(404).json({ error: 'Board not found' });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
kanbanBoards.delete(board.id);
|
|
88
|
+
|
|
89
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.KANBAN_BOARD_UPDATED, {
|
|
90
|
+
projectId,
|
|
91
|
+
board: null,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
res.status(204).send();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ============== Lane Endpoints ==============
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* POST /api/projects/:projectId/kanban/lanes
|
|
101
|
+
* Create a new lane
|
|
102
|
+
*/
|
|
103
|
+
router.post('/lanes', (req, res) => {
|
|
104
|
+
const { projectId } = req.params;
|
|
105
|
+
|
|
106
|
+
const result = CreateKanbanLaneRequest.safeParse(req.body);
|
|
107
|
+
if (!result.success) {
|
|
108
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const board = kanbanBoards.getByProjectId(projectId);
|
|
112
|
+
if (!board) {
|
|
113
|
+
return res.status(404).json({ error: 'Board not found' });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const lane = kanbanLanes.create(board.id, result.data);
|
|
117
|
+
|
|
118
|
+
// Broadcast updated board
|
|
119
|
+
const fullBoard = buildFullBoardResponse(board);
|
|
120
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.KANBAN_BOARD_UPDATED, {
|
|
121
|
+
projectId,
|
|
122
|
+
board: fullBoard,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
res.status(201).json(lane);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* PATCH /api/projects/:projectId/kanban/lanes/:laneId
|
|
130
|
+
* Update a lane
|
|
131
|
+
*/
|
|
132
|
+
router.patch('/lanes/:laneId', (req, res) => {
|
|
133
|
+
const { projectId, laneId } = req.params;
|
|
134
|
+
|
|
135
|
+
const result = UpdateKanbanLaneRequest.safeParse(req.body);
|
|
136
|
+
if (!result.success) {
|
|
137
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const lane = kanbanLanes.getById(laneId);
|
|
141
|
+
if (!lane) {
|
|
142
|
+
return res.status(404).json({ error: 'Lane not found' });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const updated = kanbanLanes.update(laneId, result.data);
|
|
146
|
+
|
|
147
|
+
// Broadcast updated board
|
|
148
|
+
const board = kanbanBoards.getByProjectId(projectId);
|
|
149
|
+
const fullBoard = buildFullBoardResponse(board);
|
|
150
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.KANBAN_BOARD_UPDATED, {
|
|
151
|
+
projectId,
|
|
152
|
+
board: fullBoard,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
res.json(updated);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* DELETE /api/projects/:projectId/kanban/lanes/:laneId
|
|
160
|
+
* Delete a lane
|
|
161
|
+
*/
|
|
162
|
+
router.delete('/lanes/:laneId', (req, res) => {
|
|
163
|
+
const { projectId, laneId } = req.params;
|
|
164
|
+
|
|
165
|
+
const lane = kanbanLanes.getById(laneId);
|
|
166
|
+
if (!lane) {
|
|
167
|
+
return res.status(404).json({ error: 'Lane not found' });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
kanbanLanes.delete(laneId);
|
|
171
|
+
|
|
172
|
+
// Broadcast updated board
|
|
173
|
+
const board = kanbanBoards.getByProjectId(projectId);
|
|
174
|
+
const fullBoard = buildFullBoardResponse(board);
|
|
175
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.KANBAN_BOARD_UPDATED, {
|
|
176
|
+
projectId,
|
|
177
|
+
board: fullBoard,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
res.status(204).send();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* PUT /api/projects/:projectId/kanban/lanes/reorder
|
|
185
|
+
* Reorder all lanes
|
|
186
|
+
*/
|
|
187
|
+
router.put('/lanes/reorder', (req, res) => {
|
|
188
|
+
const { projectId } = req.params;
|
|
189
|
+
|
|
190
|
+
const result = ReorderKanbanLanesRequest.safeParse(req.body);
|
|
191
|
+
if (!result.success) {
|
|
192
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const board = kanbanBoards.getByProjectId(projectId);
|
|
196
|
+
if (!board) {
|
|
197
|
+
return res.status(404).json({ error: 'Board not found' });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
kanbanLanes.reorder(board.id, result.data);
|
|
201
|
+
|
|
202
|
+
// Broadcast updated board
|
|
203
|
+
const fullBoard = buildFullBoardResponse(board);
|
|
204
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.KANBAN_BOARD_UPDATED, {
|
|
205
|
+
projectId,
|
|
206
|
+
board: fullBoard,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
res.json(fullBoard);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// ============== Card Endpoints ==============
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* POST /api/projects/:projectId/kanban/cards
|
|
216
|
+
* Add a session to the board (create card in a lane)
|
|
217
|
+
*/
|
|
218
|
+
router.post('/cards', (req, res) => {
|
|
219
|
+
const { projectId } = req.params;
|
|
220
|
+
|
|
221
|
+
const result = CreateKanbanCardRequest.safeParse(req.body);
|
|
222
|
+
if (!result.success) {
|
|
223
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const { sessionId, laneId } = result.data;
|
|
227
|
+
|
|
228
|
+
// Check if session already has a card
|
|
229
|
+
const existingCard = kanbanCards.getBySessionId(sessionId);
|
|
230
|
+
if (existingCard) {
|
|
231
|
+
return res.status(409).json({ error: 'Session already has a card on the board' });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Verify lane exists
|
|
235
|
+
const lane = kanbanLanes.getById(laneId);
|
|
236
|
+
if (!lane) {
|
|
237
|
+
return res.status(404).json({ error: 'Lane not found' });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const card = kanbanCards.create(laneId, sessionId);
|
|
241
|
+
|
|
242
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.KANBAN_CARD_ADDED, {
|
|
243
|
+
projectId,
|
|
244
|
+
card,
|
|
245
|
+
laneId,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
res.status(201).json(card);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* PATCH /api/projects/:projectId/kanban/cards/:cardId/move
|
|
253
|
+
* Move card to a different lane
|
|
254
|
+
*/
|
|
255
|
+
router.patch('/cards/:cardId/move', async (req, res) => {
|
|
256
|
+
const { cardId } = req.params;
|
|
257
|
+
|
|
258
|
+
const result = MoveKanbanCardRequest.safeParse(req.body);
|
|
259
|
+
if (!result.success) {
|
|
260
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const card = kanbanCards.getByIdWithLane(cardId);
|
|
264
|
+
if (!card) {
|
|
265
|
+
return res.status(404).json({ error: 'Card not found' });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const { targetLaneId, sortOrder, runOnEnterTemplate } = result.data;
|
|
269
|
+
|
|
270
|
+
// Verify target lane exists
|
|
271
|
+
const targetLane = kanbanLanes.getById(targetLaneId);
|
|
272
|
+
if (!targetLane) {
|
|
273
|
+
return res.status(404).json({ error: 'Target lane not found' });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
const movedCard = await moveCardService(cardId, targetLaneId, {
|
|
278
|
+
sortOrder,
|
|
279
|
+
runOnEnterTemplate,
|
|
280
|
+
});
|
|
281
|
+
res.json(movedCard);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
console.error('Failed to move kanban card:', error);
|
|
284
|
+
res.status(500).json({ error: error.message });
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* DELETE /api/projects/:projectId/kanban/cards/:cardId
|
|
290
|
+
* Remove card from board
|
|
291
|
+
*/
|
|
292
|
+
router.delete('/cards/:cardId', (req, res) => {
|
|
293
|
+
const { projectId, cardId } = req.params;
|
|
294
|
+
|
|
295
|
+
const card = kanbanCards.getByIdWithLane(cardId);
|
|
296
|
+
if (!card) {
|
|
297
|
+
return res.status(404).json({ error: 'Card not found' });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const laneId = card.laneId;
|
|
301
|
+
kanbanCards.delete(cardId);
|
|
302
|
+
|
|
303
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.KANBAN_CARD_REMOVED, {
|
|
304
|
+
projectId,
|
|
305
|
+
cardId,
|
|
306
|
+
laneId,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
res.status(204).send();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* PUT /api/projects/:projectId/kanban/lanes/:laneId/cards/reorder
|
|
314
|
+
* Reorder cards within a lane
|
|
315
|
+
*/
|
|
316
|
+
router.put('/lanes/:laneId/cards/reorder', (req, res) => {
|
|
317
|
+
const { projectId, laneId } = req.params;
|
|
318
|
+
|
|
319
|
+
const result = ReorderKanbanCardsRequest.safeParse(req.body);
|
|
320
|
+
if (!result.success) {
|
|
321
|
+
return res.status(400).json({ error: result.error.issues[0].message });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const lane = kanbanLanes.getById(laneId);
|
|
325
|
+
if (!lane) {
|
|
326
|
+
return res.status(404).json({ error: 'Lane not found' });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
kanbanCards.reorder(laneId, result.data);
|
|
330
|
+
|
|
331
|
+
// Broadcast updated board
|
|
332
|
+
const board = kanbanBoards.getByProjectId(projectId);
|
|
333
|
+
const fullBoard = buildFullBoardResponse(board);
|
|
334
|
+
broadcastToProject(projectId, WS_MESSAGE_TYPES.KANBAN_BOARD_UPDATED, {
|
|
335
|
+
projectId,
|
|
336
|
+
board: fullBoard,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
res.json({ success: true });
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
export default router;
|