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,379 @@
|
|
|
1
|
+
import { BaseRepository } from './BaseRepository.js';
|
|
2
|
+
import { databaseManager } from './DatabaseManager.js';
|
|
3
|
+
import { mapConversationRow, executeBranch } from './conversation-helpers.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Conversation repository class for managing conversation threads within sessions
|
|
7
|
+
*/
|
|
8
|
+
export class ConversationRepository extends BaseRepository {
|
|
9
|
+
constructor() {
|
|
10
|
+
super('conversations', mapConversationRow);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create a new conversation for a session
|
|
15
|
+
* @param {string} sessionId - The session ID
|
|
16
|
+
* @param {string} [name] - Optional name for the conversation
|
|
17
|
+
* @param {boolean} [isActive=true] - Whether this is the active conversation
|
|
18
|
+
* @returns {Object} The created conversation
|
|
19
|
+
*/
|
|
20
|
+
create(sessionId, name = null, isActive = true) {
|
|
21
|
+
const id = databaseManager.generateId();
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
|
|
24
|
+
// If this conversation should be active, deactivate others first
|
|
25
|
+
if (isActive) {
|
|
26
|
+
this.db
|
|
27
|
+
.prepare('UPDATE conversations SET is_active = 0, updated_at = ? WHERE session_id = ?')
|
|
28
|
+
.run(now, sessionId);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
this.db
|
|
32
|
+
.prepare(
|
|
33
|
+
`INSERT INTO conversations (id, session_id, name, is_active, created_at, updated_at)
|
|
34
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
35
|
+
)
|
|
36
|
+
.run(id, sessionId, name, isActive ? 1 : 0, now, now);
|
|
37
|
+
|
|
38
|
+
return this.getById(id);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get all conversations for a session
|
|
43
|
+
* @param {string} sessionId - The session ID
|
|
44
|
+
* @returns {Array} List of conversations ordered by creation time
|
|
45
|
+
*/
|
|
46
|
+
getBySessionId(sessionId) {
|
|
47
|
+
const rows = this.db
|
|
48
|
+
.prepare('SELECT * FROM conversations WHERE session_id = ? ORDER BY created_at ASC')
|
|
49
|
+
.all(sessionId);
|
|
50
|
+
return this.mapAll(rows);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get the active conversation for a session
|
|
55
|
+
* @param {string} sessionId - The session ID
|
|
56
|
+
* @returns {Object|null} The active conversation or null
|
|
57
|
+
*/
|
|
58
|
+
getActiveBySessionId(sessionId) {
|
|
59
|
+
const row = this.db
|
|
60
|
+
.prepare('SELECT * FROM conversations WHERE session_id = ? AND is_active = 1')
|
|
61
|
+
.get(sessionId);
|
|
62
|
+
return this.map(row);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get conversation with message count
|
|
67
|
+
* @param {string} sessionId - The session ID
|
|
68
|
+
* @returns {Array} Conversations with messageCount property
|
|
69
|
+
*/
|
|
70
|
+
getBySessionIdWithMessageCount(sessionId) {
|
|
71
|
+
const rows = this.db
|
|
72
|
+
.prepare(`
|
|
73
|
+
SELECT c.*, COUNT(m.id) as message_count
|
|
74
|
+
FROM conversations c
|
|
75
|
+
LEFT JOIN conversation_messages m ON m.conversation_id = c.id
|
|
76
|
+
WHERE c.session_id = ?
|
|
77
|
+
GROUP BY c.id
|
|
78
|
+
ORDER BY c.created_at ASC
|
|
79
|
+
`)
|
|
80
|
+
.all(sessionId);
|
|
81
|
+
|
|
82
|
+
return rows.map((row) => ({
|
|
83
|
+
...mapConversationRow(row),
|
|
84
|
+
messageCount: row.message_count,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Update a conversation
|
|
90
|
+
* @param {string} id - The conversation ID
|
|
91
|
+
* @param {Object} data - Fields to update
|
|
92
|
+
* @returns {Object} The updated conversation
|
|
93
|
+
*/
|
|
94
|
+
update(id, data) {
|
|
95
|
+
const updates = [];
|
|
96
|
+
const values = [];
|
|
97
|
+
|
|
98
|
+
if (data.name !== undefined) {
|
|
99
|
+
updates.push('name = ?');
|
|
100
|
+
values.push(data.name);
|
|
101
|
+
}
|
|
102
|
+
if (data.summary !== undefined) {
|
|
103
|
+
updates.push('summary = ?');
|
|
104
|
+
values.push(data.summary);
|
|
105
|
+
}
|
|
106
|
+
if (data.summaryGeneratedAt !== undefined) {
|
|
107
|
+
updates.push('summary_generated_at = ?');
|
|
108
|
+
values.push(data.summaryGeneratedAt);
|
|
109
|
+
}
|
|
110
|
+
if (data.claudeSessionId !== undefined) {
|
|
111
|
+
updates.push('claude_session_id = ?');
|
|
112
|
+
values.push(data.claudeSessionId);
|
|
113
|
+
}
|
|
114
|
+
if (data.isActive !== undefined) {
|
|
115
|
+
// If setting this conversation as active, deactivate others first
|
|
116
|
+
if (data.isActive) {
|
|
117
|
+
const conv = this.getById(id);
|
|
118
|
+
if (conv) {
|
|
119
|
+
this.db
|
|
120
|
+
.prepare('UPDATE conversations SET is_active = 0, updated_at = ? WHERE session_id = ? AND id != ?')
|
|
121
|
+
.run(Date.now(), conv.sessionId, id);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
updates.push('is_active = ?');
|
|
125
|
+
values.push(data.isActive ? 1 : 0);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (updates.length === 0) {
|
|
129
|
+
return this.getById(id);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
updates.push('updated_at = ?');
|
|
133
|
+
values.push(Date.now());
|
|
134
|
+
values.push(id);
|
|
135
|
+
|
|
136
|
+
this.db
|
|
137
|
+
.prepare(`UPDATE conversations SET ${updates.join(', ')} WHERE id = ?`)
|
|
138
|
+
.run(...values);
|
|
139
|
+
|
|
140
|
+
return this.getById(id);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Set a conversation as active (and deactivate others)
|
|
145
|
+
* @param {string} id - The conversation ID to activate
|
|
146
|
+
* @returns {Object} The updated conversation
|
|
147
|
+
*/
|
|
148
|
+
setActive(id) {
|
|
149
|
+
return this.update(id, { isActive: true });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Delete a conversation and handle edge cases
|
|
154
|
+
* @param {string} id - The conversation ID
|
|
155
|
+
* @returns {Object|null} The new active conversation if one was auto-created
|
|
156
|
+
*/
|
|
157
|
+
deleteAndHandleActive(id) {
|
|
158
|
+
const conv = this.getById(id);
|
|
159
|
+
if (!conv) return null;
|
|
160
|
+
|
|
161
|
+
const wasActive = conv.isActive;
|
|
162
|
+
const sessionId = conv.sessionId;
|
|
163
|
+
|
|
164
|
+
// Delete the conversation (messages will be cascade deleted)
|
|
165
|
+
this.delete(id);
|
|
166
|
+
|
|
167
|
+
// If we deleted the active conversation, activate another one
|
|
168
|
+
if (wasActive) {
|
|
169
|
+
const remaining = this.getBySessionId(sessionId);
|
|
170
|
+
if (remaining.length > 0) {
|
|
171
|
+
// Activate the most recent conversation
|
|
172
|
+
return this.setActive(remaining[remaining.length - 1].id);
|
|
173
|
+
} else {
|
|
174
|
+
// No conversations left - create a new empty one
|
|
175
|
+
return this.create(sessionId, 'New Conversation', true);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Ensure a session has at least one conversation, creating one if needed
|
|
184
|
+
* @param {string} sessionId - The session ID
|
|
185
|
+
* @returns {Object} The active conversation
|
|
186
|
+
*/
|
|
187
|
+
ensureActiveConversation(sessionId) {
|
|
188
|
+
let active = this.getActiveBySessionId(sessionId);
|
|
189
|
+
if (!active) {
|
|
190
|
+
// Check if there are any conversations at all
|
|
191
|
+
const conversations = this.getBySessionId(sessionId);
|
|
192
|
+
if (conversations.length > 0) {
|
|
193
|
+
// Activate the first one
|
|
194
|
+
active = this.setActive(conversations[0].id);
|
|
195
|
+
} else {
|
|
196
|
+
// Create a new conversation
|
|
197
|
+
active = this.create(sessionId, 'Initial', true);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return active;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Auto-generate a conversation name from the first user message
|
|
205
|
+
* @param {string} id - The conversation ID
|
|
206
|
+
* @param {string} firstMessage - The first user message content
|
|
207
|
+
* @returns {Object} The updated conversation
|
|
208
|
+
*/
|
|
209
|
+
autoName(id, firstMessage) {
|
|
210
|
+
// Take first 50 chars of the message, truncate at word boundary
|
|
211
|
+
let name = firstMessage.substring(0, 50);
|
|
212
|
+
const lastSpace = name.lastIndexOf(' ');
|
|
213
|
+
if (lastSpace > 30) {
|
|
214
|
+
name = name.substring(0, lastSpace);
|
|
215
|
+
}
|
|
216
|
+
if (firstMessage.length > 50) {
|
|
217
|
+
name += '...';
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return this.update(id, { name });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Update token usage statistics for a conversation
|
|
225
|
+
* @param {string} id - Conversation ID
|
|
226
|
+
* @param {Object} usage - Usage data
|
|
227
|
+
* @param {number} usage.inputTokens
|
|
228
|
+
* @param {number} usage.outputTokens
|
|
229
|
+
* @param {number} usage.cacheReadInputTokens
|
|
230
|
+
* @param {number} usage.cacheCreationInputTokens
|
|
231
|
+
* @param {number} usage.webSearchRequests
|
|
232
|
+
* @param {number} usage.contextWindow
|
|
233
|
+
* @returns {Object|null} Updated conversation or null if not found
|
|
234
|
+
*/
|
|
235
|
+
updateUsage(id, usage) {
|
|
236
|
+
const conversation = this.getById(id);
|
|
237
|
+
if (!conversation) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const now = Date.now();
|
|
242
|
+
this.db
|
|
243
|
+
.prepare(
|
|
244
|
+
`UPDATE conversations SET
|
|
245
|
+
input_tokens = ?,
|
|
246
|
+
output_tokens = ?,
|
|
247
|
+
cache_read_input_tokens = ?,
|
|
248
|
+
cache_creation_input_tokens = ?,
|
|
249
|
+
web_search_requests = ?,
|
|
250
|
+
context_window = ?,
|
|
251
|
+
updated_at = ?
|
|
252
|
+
WHERE id = ?`
|
|
253
|
+
)
|
|
254
|
+
.run(
|
|
255
|
+
usage.inputTokens,
|
|
256
|
+
usage.outputTokens,
|
|
257
|
+
usage.cacheReadInputTokens,
|
|
258
|
+
usage.cacheCreationInputTokens,
|
|
259
|
+
usage.webSearchRequests,
|
|
260
|
+
usage.contextWindow,
|
|
261
|
+
now,
|
|
262
|
+
id
|
|
263
|
+
);
|
|
264
|
+
return this.getById(id);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Duplicates all conversations from one session to another.
|
|
269
|
+
* @param {string} sourceSessionId - Source session ID
|
|
270
|
+
* @param {string} targetSessionId - Target session ID
|
|
271
|
+
* @returns {Map<string, string>} Mapping of old conversation IDs to new IDs
|
|
272
|
+
*/
|
|
273
|
+
duplicateForSession(sourceSessionId, targetSessionId) {
|
|
274
|
+
const sourceConversations = this.getBySessionId(sourceSessionId);
|
|
275
|
+
const idMapping = new Map();
|
|
276
|
+
|
|
277
|
+
for (const conv of sourceConversations) {
|
|
278
|
+
const id = databaseManager.generateId();
|
|
279
|
+
const now = Date.now();
|
|
280
|
+
|
|
281
|
+
this.db
|
|
282
|
+
.prepare(
|
|
283
|
+
`INSERT INTO conversations (id, session_id, name, summary, is_active, created_at, updated_at)
|
|
284
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
285
|
+
)
|
|
286
|
+
.run(
|
|
287
|
+
id,
|
|
288
|
+
targetSessionId,
|
|
289
|
+
conv.name,
|
|
290
|
+
conv.summary,
|
|
291
|
+
conv.isActive ? 1 : 0,
|
|
292
|
+
now,
|
|
293
|
+
now
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
idMapping.set(conv.id, id);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return idMapping;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Create a branch from an existing conversation at a specific message
|
|
304
|
+
* Copies all messages BEFORE the specified message to a new conversation,
|
|
305
|
+
* then adds the initialPrompt as a replacement for that message
|
|
306
|
+
* @param {string} conversationId - The source conversation ID
|
|
307
|
+
* @param {string} messageId - The message ID to branch from (messages before this are copied, this message is replaced)
|
|
308
|
+
* @param {string} [name] - Optional name for the new conversation (if not provided, auto-generated from prompt)
|
|
309
|
+
* @param {string} initialPrompt - Required: the new prompt that replaces the branch point message
|
|
310
|
+
* @returns {Object} The created branch conversation
|
|
311
|
+
*/
|
|
312
|
+
branch(conversationId, messageId, name = null, initialPrompt = null) {
|
|
313
|
+
return executeBranch(this, conversationId, messageId, { name, initialPrompt });
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Get conversations for a session with branch hierarchy info
|
|
318
|
+
* @param {string} sessionId - The session ID
|
|
319
|
+
* @returns {Array} Conversations with childCount property
|
|
320
|
+
*/
|
|
321
|
+
getBySessionIdWithBranchInfo(sessionId) {
|
|
322
|
+
const rows = this.db
|
|
323
|
+
.prepare(`
|
|
324
|
+
SELECT c.*,
|
|
325
|
+
(SELECT COUNT(*) FROM conversations child WHERE child.parent_conversation_id = c.id) as child_count,
|
|
326
|
+
(SELECT COUNT(*) FROM conversation_messages m WHERE m.conversation_id = c.id) as message_count
|
|
327
|
+
FROM conversations c
|
|
328
|
+
WHERE c.session_id = ?
|
|
329
|
+
ORDER BY c.created_at ASC
|
|
330
|
+
`)
|
|
331
|
+
.all(sessionId);
|
|
332
|
+
|
|
333
|
+
return rows.map((row) => ({
|
|
334
|
+
...mapConversationRow(row),
|
|
335
|
+
childCount: row.child_count || 0,
|
|
336
|
+
messageCount: row.message_count || 0,
|
|
337
|
+
}));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Get child conversations (branches) of a conversation
|
|
342
|
+
* @param {string} parentConversationId - The parent conversation ID
|
|
343
|
+
* @returns {Array} Child conversations
|
|
344
|
+
*/
|
|
345
|
+
getChildren(parentConversationId) {
|
|
346
|
+
const rows = this.db
|
|
347
|
+
.prepare('SELECT * FROM conversations WHERE parent_conversation_id = ? ORDER BY created_at ASC')
|
|
348
|
+
.all(parentConversationId);
|
|
349
|
+
return this.mapAll(rows);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Get the branch point message for a conversation
|
|
354
|
+
* @param {string} conversationId - The conversation ID
|
|
355
|
+
* @returns {Object|null} The branch point message or null if not a branch
|
|
356
|
+
*/
|
|
357
|
+
getBranchPointMessage(conversationId) {
|
|
358
|
+
const conv = this.getById(conversationId);
|
|
359
|
+
if (!conv || !conv.branchFromMessageId) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const row = this.db
|
|
364
|
+
.prepare('SELECT * FROM conversation_messages WHERE id = ?')
|
|
365
|
+
.get(conv.branchFromMessageId);
|
|
366
|
+
|
|
367
|
+
if (!row) return null;
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
id: row.id,
|
|
371
|
+
sessionId: row.session_id,
|
|
372
|
+
conversationId: row.conversation_id,
|
|
373
|
+
role: row.role,
|
|
374
|
+
content: row.content,
|
|
375
|
+
toolUse: row.tool_use ? JSON.parse(row.tool_use) : null,
|
|
376
|
+
timestamp: row.timestamp,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import crypto from 'crypto';
|
|
6
|
+
import { allMigrations } from './migrations/index.js';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Database manager class - singleton pattern for managing SQLite connection
|
|
13
|
+
*/
|
|
14
|
+
export class DatabaseManager {
|
|
15
|
+
#db = null;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialize database with WAL mode and foreign keys
|
|
19
|
+
* @param {string} dbPath - Path to database file (use ':memory:' for in-memory)
|
|
20
|
+
* @returns {Database.Database}
|
|
21
|
+
*/
|
|
22
|
+
init(dbPath = 'circuschief.db') {
|
|
23
|
+
this.#db = new Database(dbPath);
|
|
24
|
+
|
|
25
|
+
// Enable WAL mode and foreign keys
|
|
26
|
+
this.#db.pragma('journal_mode = WAL');
|
|
27
|
+
this.#db.pragma('foreign_keys = ON');
|
|
28
|
+
|
|
29
|
+
// Run schema
|
|
30
|
+
const schema = readFileSync(join(__dirname, '..', 'schema.sql'), 'utf-8');
|
|
31
|
+
this.#db.exec(schema);
|
|
32
|
+
|
|
33
|
+
// Run migrations for existing databases
|
|
34
|
+
this.#runMigrations();
|
|
35
|
+
|
|
36
|
+
return this.#db;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Run database migrations for existing databases.
|
|
41
|
+
* Iterates over the flat, ordered list of migrations exported from
|
|
42
|
+
* the migrations/ directory.
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
#runMigrations() {
|
|
46
|
+
for (const migration of allMigrations) {
|
|
47
|
+
migration.up(this.#db);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the database instance
|
|
53
|
+
* @returns {Database.Database}
|
|
54
|
+
*/
|
|
55
|
+
get() {
|
|
56
|
+
if (!this.#db) {
|
|
57
|
+
throw new Error('Database not initialized. Call init first.');
|
|
58
|
+
}
|
|
59
|
+
return this.#db;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Close the database connection
|
|
64
|
+
*/
|
|
65
|
+
close() {
|
|
66
|
+
if (this.#db) {
|
|
67
|
+
this.#db.close();
|
|
68
|
+
this.#db = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate a UUID
|
|
74
|
+
* @returns {string}
|
|
75
|
+
*/
|
|
76
|
+
generateId() {
|
|
77
|
+
return crypto.randomUUID();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Run a function inside a transaction
|
|
82
|
+
* @param {Function} fn - Function to run in transaction
|
|
83
|
+
* @returns {any}
|
|
84
|
+
*/
|
|
85
|
+
transaction(fn) {
|
|
86
|
+
return this.get().transaction(fn)();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Singleton instance
|
|
91
|
+
export const databaseManager = new DatabaseManager();
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { BaseRepository } from './BaseRepository.js';
|
|
2
|
+
import { databaseManager } from './DatabaseManager.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Kanban board repository class
|
|
6
|
+
*/
|
|
7
|
+
export class KanbanBoardRepository extends BaseRepository {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('kanban_boards', KanbanBoardRepository.#mapBoard);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static #mapBoard(row) {
|
|
13
|
+
return {
|
|
14
|
+
id: row.id,
|
|
15
|
+
projectId: row.project_id,
|
|
16
|
+
createdAt: row.created_at,
|
|
17
|
+
updatedAt: row.updated_at,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get board by project ID
|
|
23
|
+
* @param {string} projectId
|
|
24
|
+
* @returns {Object|null}
|
|
25
|
+
*/
|
|
26
|
+
getByProjectId(projectId) {
|
|
27
|
+
const row = this.db
|
|
28
|
+
.prepare('SELECT * FROM kanban_boards WHERE project_id = ?')
|
|
29
|
+
.get(projectId);
|
|
30
|
+
return this.map(row);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a new kanban board with default lanes
|
|
35
|
+
* @param {string} projectId
|
|
36
|
+
* @returns {Object}
|
|
37
|
+
*/
|
|
38
|
+
create(projectId) {
|
|
39
|
+
const id = databaseManager.generateId();
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
|
|
42
|
+
// Use a transaction to create board and default lanes together
|
|
43
|
+
return databaseManager.transaction(() => {
|
|
44
|
+
// Create the board
|
|
45
|
+
this.db
|
|
46
|
+
.prepare(
|
|
47
|
+
`INSERT INTO kanban_boards (id, project_id, created_at, updated_at)
|
|
48
|
+
VALUES (?, ?, ?, ?)`
|
|
49
|
+
)
|
|
50
|
+
.run(id, projectId, now, now);
|
|
51
|
+
|
|
52
|
+
// Create default lanes
|
|
53
|
+
const defaultLanes = ['To Do', 'In Progress', 'Review', 'Done'];
|
|
54
|
+
const insertLane = this.db.prepare(
|
|
55
|
+
`INSERT INTO kanban_lanes (id, board_id, name, sort_order, created_at, updated_at)
|
|
56
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
defaultLanes.forEach((name, index) => {
|
|
60
|
+
insertLane.run(databaseManager.generateId(), id, name, index, now, now);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return this.getById(id);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get or create a board for a project (lazy initialization)
|
|
69
|
+
* @param {string} projectId
|
|
70
|
+
* @returns {Object}
|
|
71
|
+
*/
|
|
72
|
+
getOrCreateForProject(projectId) {
|
|
73
|
+
const existing = this.getByProjectId(projectId);
|
|
74
|
+
if (existing) {
|
|
75
|
+
return existing;
|
|
76
|
+
}
|
|
77
|
+
return this.create(projectId);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Delete a board and all its lanes and cards (cascade handled by FK)
|
|
82
|
+
* @param {string} id
|
|
83
|
+
*/
|
|
84
|
+
delete(id) {
|
|
85
|
+
const now = Date.now();
|
|
86
|
+
// Update timestamp before delete for any listeners
|
|
87
|
+
this.db
|
|
88
|
+
.prepare('UPDATE kanban_boards SET updated_at = ? WHERE id = ?')
|
|
89
|
+
.run(now, id);
|
|
90
|
+
super.delete(id);
|
|
91
|
+
}
|
|
92
|
+
}
|