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,258 @@
|
|
|
1
|
+
import { readFile, readdir, access, constants } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import YAML from 'yaml';
|
|
5
|
+
import {
|
|
6
|
+
discoverPluginCommands,
|
|
7
|
+
discoverPluginSkills,
|
|
8
|
+
discoverMarketplaceCommands,
|
|
9
|
+
discoverMarketplaceSkills,
|
|
10
|
+
} from './slashCommandPluginDiscovery.js';
|
|
11
|
+
|
|
12
|
+
// Command source locations in priority order:
|
|
13
|
+
// 1. Project: .claude/commands/*.md
|
|
14
|
+
// 2. User: ~/.claude/commands/*.md
|
|
15
|
+
// 3. Plugins: ~/.claude/plugins/cache/.../{plugin}/.../commands/*.md
|
|
16
|
+
// 4. Built-in: hardcoded list below
|
|
17
|
+
const COMMANDS_DIR = '.claude/commands';
|
|
18
|
+
const SKILLS_DIR = '.claude/skills';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parse YAML frontmatter from a markdown command file
|
|
22
|
+
* Supports our extended schema with typed arguments
|
|
23
|
+
*
|
|
24
|
+
* @param {string} content - The file content
|
|
25
|
+
* @returns {{ description: string, arguments: Array, body: string }}
|
|
26
|
+
*/
|
|
27
|
+
export function parseCommandFile(content) {
|
|
28
|
+
// Check for frontmatter
|
|
29
|
+
if (!content.startsWith('---')) {
|
|
30
|
+
return { description: '', arguments: [], body: content.trim() };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Find end of frontmatter
|
|
34
|
+
const endIndex = content.indexOf('---', 3);
|
|
35
|
+
if (endIndex === -1) {
|
|
36
|
+
return { description: '', arguments: [], body: content.trim() };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const frontmatterStr = content.slice(3, endIndex).trim();
|
|
40
|
+
const body = content.slice(endIndex + 3).trim();
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const frontmatter = YAML.parse(frontmatterStr);
|
|
44
|
+
const args = Array.isArray(frontmatter.arguments)
|
|
45
|
+
? frontmatter.arguments.map(normalizeArgument)
|
|
46
|
+
: [];
|
|
47
|
+
return { description: frontmatter.description || '', arguments: args, body };
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.warn('Failed to parse command frontmatter:', err.message);
|
|
50
|
+
return { description: '', arguments: [], body: content.trim() };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Normalize an argument definition to ensure it has all required fields
|
|
56
|
+
* @param {Object} arg - Argument from frontmatter
|
|
57
|
+
* @returns {Object} Normalized argument
|
|
58
|
+
*/
|
|
59
|
+
function normalizeArgument(arg) {
|
|
60
|
+
if (!arg || typeof arg !== 'object') {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const normalized = {
|
|
65
|
+
name: arg.name || 'unnamed',
|
|
66
|
+
type: ['select', 'text', 'multiline'].includes(arg.type) ? arg.type : 'text',
|
|
67
|
+
label: arg.label || arg.name || 'Unnamed',
|
|
68
|
+
required: arg.required === true,
|
|
69
|
+
placeholder: arg.placeholder || '',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (normalized.type === 'select' && Array.isArray(arg.options)) {
|
|
73
|
+
normalized.options = arg.options.map(opt => {
|
|
74
|
+
if (typeof opt === 'string') return { value: opt, label: opt };
|
|
75
|
+
return {
|
|
76
|
+
value: opt.value || opt.label || '',
|
|
77
|
+
label: opt.label || opt.value || '',
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (arg.default !== undefined) {
|
|
83
|
+
normalized.default = arg.default;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return normalized;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Parse a SKILL.md file with its extended frontmatter
|
|
91
|
+
* @param {string} content - File content
|
|
92
|
+
* @param {string} directoryName - The skill directory name (used as fallback name)
|
|
93
|
+
* @returns {Object} Parsed skill object
|
|
94
|
+
*/
|
|
95
|
+
export function parseSkillFile(content, directoryName) {
|
|
96
|
+
const defaults = {
|
|
97
|
+
name: directoryName,
|
|
98
|
+
description: '',
|
|
99
|
+
argumentHint: null,
|
|
100
|
+
userInvocable: true,
|
|
101
|
+
disableModelInvocation: false,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
if (!content.startsWith('---')) {
|
|
105
|
+
return { ...defaults, body: content.trim() };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const endIndex = content.indexOf('---', 3);
|
|
109
|
+
if (endIndex === -1) {
|
|
110
|
+
return { ...defaults, body: content.trim() };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const frontmatterStr = content.slice(3, endIndex).trim();
|
|
114
|
+
const body = content.slice(endIndex + 3).trim();
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const fm = YAML.parse(frontmatterStr);
|
|
118
|
+
return {
|
|
119
|
+
name: fm.name || directoryName,
|
|
120
|
+
description: fm.description || '',
|
|
121
|
+
argumentHint: fm['argument-hint'] || null,
|
|
122
|
+
userInvocable: fm['user-invocable'] !== false,
|
|
123
|
+
disableModelInvocation: fm['disable-model-invocation'] === true,
|
|
124
|
+
body,
|
|
125
|
+
};
|
|
126
|
+
} catch (err) {
|
|
127
|
+
console.warn('Failed to parse skill frontmatter:', err.message);
|
|
128
|
+
return { ...defaults, body: content.trim() };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Discover commands from a directory
|
|
134
|
+
* @param {string} directory - Directory to scan
|
|
135
|
+
* @param {string} source - Source type ('project', 'user', or 'plugin')
|
|
136
|
+
* @param {string} [namespace] - Optional namespace prefix for plugin commands
|
|
137
|
+
* @returns {Promise<Array>} Array of command objects
|
|
138
|
+
*/
|
|
139
|
+
async function discoverCommandsFromDir(directory, source, namespace = null) {
|
|
140
|
+
const commands = [];
|
|
141
|
+
try {
|
|
142
|
+
await access(directory, constants.R_OK);
|
|
143
|
+
const files = await readdir(directory);
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
if (!file.endsWith('.md')) continue;
|
|
146
|
+
const baseName = file.replace(/\.md$/, '');
|
|
147
|
+
const name = namespace ? `${namespace}:${baseName}` : baseName;
|
|
148
|
+
const filePath = join(directory, file);
|
|
149
|
+
try {
|
|
150
|
+
const content = await readFile(filePath, 'utf-8');
|
|
151
|
+
const parsed = parseCommandFile(content);
|
|
152
|
+
commands.push({
|
|
153
|
+
name,
|
|
154
|
+
description: parsed.description,
|
|
155
|
+
arguments: parsed.arguments.filter(Boolean),
|
|
156
|
+
source,
|
|
157
|
+
filePath,
|
|
158
|
+
});
|
|
159
|
+
} catch (err) {
|
|
160
|
+
console.warn(`Failed to read command file ${filePath}:`, err.message);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch {
|
|
164
|
+
// Directory doesn't exist or isn't readable
|
|
165
|
+
}
|
|
166
|
+
return commands;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Discover skills from a directory
|
|
171
|
+
* Scans basePath/.claude/skills/SKILL.md
|
|
172
|
+
*/
|
|
173
|
+
async function readSkillEntry(skillsDir, entryName, source, namespace) {
|
|
174
|
+
const skillMdPath = join(skillsDir, entryName, 'SKILL.md');
|
|
175
|
+
try {
|
|
176
|
+
const content = await readFile(skillMdPath, 'utf-8');
|
|
177
|
+
const parsed = parseSkillFile(content, entryName);
|
|
178
|
+
if (!parsed.userInvocable) return null;
|
|
179
|
+
const name = namespace ? `${namespace}:${parsed.name}` : parsed.name;
|
|
180
|
+
return {
|
|
181
|
+
name,
|
|
182
|
+
description: parsed.description,
|
|
183
|
+
arguments: [],
|
|
184
|
+
argumentHint: parsed.argumentHint,
|
|
185
|
+
source,
|
|
186
|
+
filePath: skillMdPath,
|
|
187
|
+
isSkill: true,
|
|
188
|
+
disableModelInvocation: parsed.disableModelInvocation,
|
|
189
|
+
};
|
|
190
|
+
} catch {
|
|
191
|
+
// SKILL.md doesn't exist or isn't readable
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function discoverSkillsFromDir(basePath, source, namespace = null) {
|
|
197
|
+
const skillsDir = join(basePath, SKILLS_DIR);
|
|
198
|
+
const skills = [];
|
|
199
|
+
try {
|
|
200
|
+
await access(skillsDir, constants.R_OK);
|
|
201
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
202
|
+
for (const entry of entries) {
|
|
203
|
+
if (!entry.isDirectory()) continue;
|
|
204
|
+
const skill = await readSkillEntry(skillsDir, entry.name, source, namespace);
|
|
205
|
+
if (skill) skills.push(skill);
|
|
206
|
+
}
|
|
207
|
+
} catch {
|
|
208
|
+
// Skills directory doesn't exist
|
|
209
|
+
}
|
|
210
|
+
return skills;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Discover all commands and skills for a working directory from all sources.
|
|
215
|
+
* Returns deduplicated list with skills taking precedence over commands.
|
|
216
|
+
*
|
|
217
|
+
* @param {string} workingDirectory - The project working directory
|
|
218
|
+
* @returns {Promise<Array>} Array of command/skill objects
|
|
219
|
+
*/
|
|
220
|
+
export async function discoverAllCommands(workingDirectory) {
|
|
221
|
+
const projectCommands = await discoverCommandsFromDir(
|
|
222
|
+
join(workingDirectory, COMMANDS_DIR), 'project'
|
|
223
|
+
);
|
|
224
|
+
const userCommands = await discoverCommandsFromDir(
|
|
225
|
+
join(homedir(), COMMANDS_DIR), 'user'
|
|
226
|
+
);
|
|
227
|
+
const pluginCommands = await discoverPluginCommands(workingDirectory, discoverCommandsFromDir);
|
|
228
|
+
const marketplaceCommands = await discoverMarketplaceCommands(discoverCommandsFromDir);
|
|
229
|
+
const projectSkills = await discoverSkillsFromDir(workingDirectory, 'project-skill');
|
|
230
|
+
const userSkills = await discoverSkillsFromDir(homedir(), 'user-skill');
|
|
231
|
+
const pluginSkills = await discoverPluginSkills(workingDirectory);
|
|
232
|
+
const marketplaceSkills = await discoverMarketplaceSkills();
|
|
233
|
+
|
|
234
|
+
// Skills take precedence over commands with the same name (per Anthropic docs)
|
|
235
|
+
// Priority: project > user > installed-plugin > marketplace; then commands fill remaining slots
|
|
236
|
+
const seen = new Set();
|
|
237
|
+
const commands = [];
|
|
238
|
+
|
|
239
|
+
// Add all skills first (they take precedence)
|
|
240
|
+
// installed_plugins.json entries come before marketplace to win dedup
|
|
241
|
+
for (const skill of [...projectSkills, ...userSkills, ...pluginSkills, ...marketplaceSkills]) {
|
|
242
|
+
if (!seen.has(skill.name)) {
|
|
243
|
+
seen.add(skill.name);
|
|
244
|
+
commands.push(skill);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Then add commands (only if name not already taken by a skill)
|
|
249
|
+
// installed_plugins.json entries come before marketplace to win dedup
|
|
250
|
+
for (const cmd of [...projectCommands, ...userCommands, ...pluginCommands, ...marketplaceCommands]) {
|
|
251
|
+
if (!seen.has(cmd.name)) {
|
|
252
|
+
seen.add(cmd.name);
|
|
253
|
+
commands.push(cmd);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return commands;
|
|
258
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { readFile, readdir, access, constants } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { parseSkillFile } from './slashCommandDiscovery.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if a working directory matches a plugin's project path
|
|
8
|
+
* Handles git worktrees which are subdirectories of the main repo
|
|
9
|
+
*/
|
|
10
|
+
function isMatchingProject(workingDirectory, projectPath) {
|
|
11
|
+
if (workingDirectory === projectPath) return true;
|
|
12
|
+
if (workingDirectory.startsWith(`${projectPath}/.worktrees/`)) return true;
|
|
13
|
+
const worktreeMarker = '/.worktrees/';
|
|
14
|
+
const worktreeIndex = workingDirectory.indexOf(worktreeMarker);
|
|
15
|
+
if (worktreeIndex !== -1) {
|
|
16
|
+
const mainRepoPath = workingDirectory.substring(0, worktreeIndex);
|
|
17
|
+
if (mainRepoPath === projectPath) return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Find the relevant plugin installation for a given working directory
|
|
24
|
+
*/
|
|
25
|
+
function findRelevantInstall(installations, workingDirectory) {
|
|
26
|
+
return installations.find(
|
|
27
|
+
install => install.scope === 'global' || isMatchingProject(workingDirectory, install.projectPath)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Read and parse the installed_plugins.json file
|
|
33
|
+
* @returns {Promise<Object|null>} Parsed plugins object or null
|
|
34
|
+
*/
|
|
35
|
+
async function readInstalledPlugins() {
|
|
36
|
+
try {
|
|
37
|
+
const path = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
|
|
38
|
+
const content = await readFile(path, 'utf-8');
|
|
39
|
+
const data = JSON.parse(content);
|
|
40
|
+
return data.plugins || null;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Read and parse the known_marketplaces.json file
|
|
48
|
+
* @returns {Promise<Object|null>} Parsed marketplaces object or null
|
|
49
|
+
*/
|
|
50
|
+
async function readKnownMarketplaces() {
|
|
51
|
+
try {
|
|
52
|
+
const path = join(homedir(), '.claude', 'plugins', 'known_marketplaces.json');
|
|
53
|
+
const content = await readFile(path, 'utf-8');
|
|
54
|
+
return JSON.parse(content);
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parse a single SKILL.md file and return skill object if valid
|
|
62
|
+
*/
|
|
63
|
+
async function parseSkillFromPath(skillMdPath, namespace, directoryName) {
|
|
64
|
+
try {
|
|
65
|
+
const skillContent = await readFile(skillMdPath, 'utf-8');
|
|
66
|
+
const parsed = parseSkillFile(skillContent, directoryName);
|
|
67
|
+
if (!parsed.userInvocable) return null;
|
|
68
|
+
return {
|
|
69
|
+
name: `${namespace}:${parsed.name}`,
|
|
70
|
+
description: parsed.description,
|
|
71
|
+
arguments: [],
|
|
72
|
+
argumentHint: parsed.argumentHint,
|
|
73
|
+
source: 'plugin-skill',
|
|
74
|
+
filePath: skillMdPath,
|
|
75
|
+
isSkill: true,
|
|
76
|
+
disableModelInvocation: parsed.disableModelInvocation,
|
|
77
|
+
};
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Scan a skills directory and return all valid skills
|
|
85
|
+
*/
|
|
86
|
+
export async function scanSkillsDirectory(skillsDir, namespace) {
|
|
87
|
+
const skills = [];
|
|
88
|
+
try {
|
|
89
|
+
await access(skillsDir, constants.R_OK);
|
|
90
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
if (!entry.isDirectory()) continue;
|
|
93
|
+
const skillMdPath = join(skillsDir, entry.name, 'SKILL.md');
|
|
94
|
+
const skill = await parseSkillFromPath(skillMdPath, namespace, entry.name);
|
|
95
|
+
if (skill) skills.push(skill);
|
|
96
|
+
}
|
|
97
|
+
} catch { /* skills directory doesn't exist */ }
|
|
98
|
+
return skills;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Scan a plugins directory for skills
|
|
103
|
+
*/
|
|
104
|
+
async function scanPluginsDirForSkills(pluginsDir) {
|
|
105
|
+
const skills = [];
|
|
106
|
+
try {
|
|
107
|
+
await access(pluginsDir, constants.R_OK);
|
|
108
|
+
const pluginDirs = await readdir(pluginsDir, { withFileTypes: true });
|
|
109
|
+
for (const pluginEntry of pluginDirs) {
|
|
110
|
+
if (!pluginEntry.isDirectory()) continue;
|
|
111
|
+
const namespace = pluginEntry.name;
|
|
112
|
+
const skillsDir = join(pluginsDir, pluginEntry.name, 'skills');
|
|
113
|
+
const pluginSkills = await scanSkillsDirectory(skillsDir, namespace);
|
|
114
|
+
skills.push(...pluginSkills);
|
|
115
|
+
}
|
|
116
|
+
} catch { /* plugins directory doesn't exist */ }
|
|
117
|
+
return skills;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Scan a plugins directory for commands
|
|
122
|
+
* @param {string} pluginsDir - Path to plugins directory
|
|
123
|
+
* @param {Function} discoverCommandsFromDir - Function to discover commands from a directory
|
|
124
|
+
*/
|
|
125
|
+
export async function scanPluginsDirForCommands(pluginsDir, discoverCommandsFromDir) {
|
|
126
|
+
const commands = [];
|
|
127
|
+
try {
|
|
128
|
+
await access(pluginsDir, constants.R_OK);
|
|
129
|
+
const pluginDirs = await readdir(pluginsDir, { withFileTypes: true });
|
|
130
|
+
for (const pluginEntry of pluginDirs) {
|
|
131
|
+
if (!pluginEntry.isDirectory()) continue;
|
|
132
|
+
const namespace = pluginEntry.name;
|
|
133
|
+
const pluginCommandsDir = join(pluginsDir, pluginEntry.name, 'commands');
|
|
134
|
+
const pluginCommands = await discoverCommandsFromDir(pluginCommandsDir, 'plugin', namespace);
|
|
135
|
+
commands.push(...pluginCommands);
|
|
136
|
+
}
|
|
137
|
+
} catch { /* plugins directory doesn't exist */ }
|
|
138
|
+
return commands;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Discover commands from installed plugins
|
|
143
|
+
* @param {string} workingDirectory - The project working directory
|
|
144
|
+
* @param {Function} discoverCommandsFromDir - Function to discover commands from a directory
|
|
145
|
+
*/
|
|
146
|
+
export async function discoverPluginCommands(workingDirectory, discoverCommandsFromDir) {
|
|
147
|
+
const plugins = await readInstalledPlugins();
|
|
148
|
+
if (!plugins) return [];
|
|
149
|
+
const commands = [];
|
|
150
|
+
for (const [pluginId, installations] of Object.entries(plugins)) {
|
|
151
|
+
const relevantInstall = findRelevantInstall(installations, workingDirectory);
|
|
152
|
+
if (!relevantInstall) continue;
|
|
153
|
+
const namespace = pluginId.split('@')[0];
|
|
154
|
+
const pluginCommandsDir = join(relevantInstall.installPath, 'commands');
|
|
155
|
+
const pluginCommands = await discoverCommandsFromDir(pluginCommandsDir, 'plugin', namespace);
|
|
156
|
+
commands.push(...pluginCommands);
|
|
157
|
+
}
|
|
158
|
+
return commands;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Discover skills from installed plugins
|
|
163
|
+
*/
|
|
164
|
+
export async function discoverPluginSkills(workingDirectory) {
|
|
165
|
+
const plugins = await readInstalledPlugins();
|
|
166
|
+
if (!plugins) return [];
|
|
167
|
+
const skills = [];
|
|
168
|
+
for (const [pluginId, installations] of Object.entries(plugins)) {
|
|
169
|
+
const relevantInstall = findRelevantInstall(installations, workingDirectory);
|
|
170
|
+
if (!relevantInstall) continue;
|
|
171
|
+
const namespace = pluginId.split('@')[0];
|
|
172
|
+
const pluginSkillsDir = join(relevantInstall.installPath, 'skills');
|
|
173
|
+
const pluginSkills = await scanSkillsDirectory(pluginSkillsDir, namespace);
|
|
174
|
+
skills.push(...pluginSkills);
|
|
175
|
+
}
|
|
176
|
+
return skills;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Discover skills from marketplace plugins
|
|
181
|
+
*/
|
|
182
|
+
export async function discoverMarketplaceSkills() {
|
|
183
|
+
const marketplaces = await readKnownMarketplaces();
|
|
184
|
+
if (!marketplaces) return [];
|
|
185
|
+
const skills = [];
|
|
186
|
+
for (const [_marketplaceId, marketplace] of Object.entries(marketplaces)) {
|
|
187
|
+
const basePath = marketplace.installLocation;
|
|
188
|
+
if (!basePath) continue;
|
|
189
|
+
for (const subdir of ['plugins', 'external_plugins']) {
|
|
190
|
+
const pluginsDir = join(basePath, subdir);
|
|
191
|
+
const subdirSkills = await scanPluginsDirForSkills(pluginsDir);
|
|
192
|
+
skills.push(...subdirSkills);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return skills;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Discover commands from marketplace plugins
|
|
200
|
+
* @param {Function} discoverCommandsFromDir - Function to discover commands from a directory
|
|
201
|
+
*/
|
|
202
|
+
export async function discoverMarketplaceCommands(discoverCommandsFromDir) {
|
|
203
|
+
const marketplaces = await readKnownMarketplaces();
|
|
204
|
+
if (!marketplaces) return [];
|
|
205
|
+
const commands = [];
|
|
206
|
+
for (const [_marketplaceId, marketplace] of Object.entries(marketplaces)) {
|
|
207
|
+
const basePath = marketplace.installLocation;
|
|
208
|
+
if (!basePath) continue;
|
|
209
|
+
for (const subdir of ['plugins', 'external_plugins']) {
|
|
210
|
+
const pluginsDir = join(basePath, subdir);
|
|
211
|
+
const subdirCommands = await scanPluginsDirForCommands(pluginsDir, discoverCommandsFromDir);
|
|
212
|
+
commands.push(...subdirCommands);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return commands;
|
|
216
|
+
}
|