mulmoclaude 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/README.md +44 -0
- package/bin/mulmoclaude.js +202 -0
- package/bin/prepare-dist.js +93 -0
- package/client/assets/chunk-vKJrgz-R-C_I3GbVV.js +1 -0
- package/client/assets/html2canvas-Cx501zZr-BF5dYYkY.js +5 -0
- package/client/assets/index-D8rhwXLq.js +4906 -0
- package/client/assets/index-KNLBjwuh.css +1 -0
- package/client/assets/index.es-D4YyL_Dg-BfRHLTZV.js +5 -0
- package/client/assets/material-icons-Dr0goTwe.woff +0 -0
- package/client/assets/material-icons-kAwBdRge.woff2 +0 -0
- package/client/assets/material-icons-outlined-BpWbwl2n.woff +0 -0
- package/client/assets/material-icons-outlined-DZhiGvEA.woff2 +0 -0
- package/client/assets/material-icons-round-BDlwx-sv.woff +0 -0
- package/client/assets/material-icons-round-DrirKXBx.woff2 +0 -0
- package/client/assets/material-icons-sharp-CH1KkVu7.woff +0 -0
- package/client/assets/material-icons-sharp-gidztirS.woff2 +0 -0
- package/client/assets/material-icons-two-tone-B7wz7mED.woff +0 -0
- package/client/assets/material-icons-two-tone-DuNIpaEj.woff2 +0 -0
- package/client/assets/mulmo_bw-ERmkSv0a.png +0 -0
- package/client/assets/purify.es-Fx1Nqyry-PeS5RUhs.js +2 -0
- package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +1 -0
- package/client/index.html +28 -0
- package/package.json +66 -0
- package/server/agent/attachmentConverter.ts +270 -0
- package/server/agent/config.ts +414 -0
- package/server/agent/index.ts +260 -0
- package/server/agent/mcp-server.ts +412 -0
- package/server/agent/mcp-tools/index.ts +63 -0
- package/server/agent/mcp-tools/x.ts +188 -0
- package/server/agent/plugin-names.ts +75 -0
- package/server/agent/prompt.ts +349 -0
- package/server/agent/resumeFailover.ts +129 -0
- package/server/agent/sandboxMounts.ts +329 -0
- package/server/agent/stream.ts +194 -0
- package/server/api/auth/bearerAuth.ts +61 -0
- package/server/api/auth/token.ts +98 -0
- package/server/api/csrfGuard.ts +85 -0
- package/server/api/routes/agent.ts +478 -0
- package/server/api/routes/chart.ts +98 -0
- package/server/api/routes/chat-index.ts +46 -0
- package/server/api/routes/config.ts +258 -0
- package/server/api/routes/dispatchResponse.ts +79 -0
- package/server/api/routes/files.ts +812 -0
- package/server/api/routes/html.ts +101 -0
- package/server/api/routes/image.ts +169 -0
- package/server/api/routes/mulmo-script.ts +712 -0
- package/server/api/routes/mulmoScriptValidate.ts +101 -0
- package/server/api/routes/notifications.ts +69 -0
- package/server/api/routes/pdf.ts +163 -0
- package/server/api/routes/plugins.ts +276 -0
- package/server/api/routes/presentHtml.ts +48 -0
- package/server/api/routes/roles.ts +125 -0
- package/server/api/routes/scheduler.ts +153 -0
- package/server/api/routes/schedulerHandlers.ts +151 -0
- package/server/api/routes/schedulerTasks.ts +163 -0
- package/server/api/routes/sessions.ts +294 -0
- package/server/api/routes/sessionsCursor.ts +59 -0
- package/server/api/routes/skills.ts +195 -0
- package/server/api/routes/sources.ts +540 -0
- package/server/api/routes/todos.ts +263 -0
- package/server/api/routes/todosColumnsHandlers.ts +347 -0
- package/server/api/routes/todosHandlers.ts +274 -0
- package/server/api/routes/todosItemsHandlers.ts +386 -0
- package/server/api/routes/wiki/pageIndex.ts +53 -0
- package/server/api/routes/wiki.ts +363 -0
- package/server/api/sandboxStatus.ts +64 -0
- package/server/events/notifications.ts +160 -0
- package/server/events/pub-sub/index.ts +45 -0
- package/server/events/relay-client.ts +288 -0
- package/server/events/scheduler-adapter.ts +302 -0
- package/server/events/session-store/index.ts +492 -0
- package/server/events/task-manager/index.ts +181 -0
- package/server/index.ts +572 -0
- package/server/system/config.ts +243 -0
- package/server/system/credentials.ts +220 -0
- package/server/system/docker.ts +97 -0
- package/server/system/env.ts +109 -0
- package/server/system/logger/config.ts +112 -0
- package/server/system/logger/formatters.ts +40 -0
- package/server/system/logger/index.ts +53 -0
- package/server/system/logger/rotation.ts +37 -0
- package/server/system/logger/sinks.ts +101 -0
- package/server/system/logger/types.ts +29 -0
- package/server/utils/date.ts +57 -0
- package/server/utils/errors.ts +7 -0
- package/server/utils/fetch.ts +27 -0
- package/server/utils/files/atomic.ts +125 -0
- package/server/utils/files/html-io.ts +20 -0
- package/server/utils/files/image-store.ts +66 -0
- package/server/utils/files/index.ts +45 -0
- package/server/utils/files/journal-io.ts +213 -0
- package/server/utils/files/json.ts +69 -0
- package/server/utils/files/markdown-store.ts +33 -0
- package/server/utils/files/naming.ts +50 -0
- package/server/utils/files/reference-dirs-io.ts +45 -0
- package/server/utils/files/roles-io.ts +45 -0
- package/server/utils/files/safe.ts +106 -0
- package/server/utils/files/scheduler-io.ts +20 -0
- package/server/utils/files/scheduler-overrides-io.ts +64 -0
- package/server/utils/files/session-io.ts +136 -0
- package/server/utils/files/spreadsheet-store.ts +63 -0
- package/server/utils/files/todos-io.ts +29 -0
- package/server/utils/files/user-tasks-io.ts +25 -0
- package/server/utils/files/workspace-io.ts +221 -0
- package/server/utils/gemini.ts +59 -0
- package/server/utils/gitignore.ts +69 -0
- package/server/utils/http.ts +15 -0
- package/server/utils/httpError.ts +61 -0
- package/server/utils/id.ts +16 -0
- package/server/utils/json.ts +83 -0
- package/server/utils/logBackgroundError.ts +22 -0
- package/server/utils/markdown.ts +82 -0
- package/server/utils/request.ts +29 -0
- package/server/utils/slug.ts +50 -0
- package/server/utils/spawn.ts +62 -0
- package/server/utils/time.ts +34 -0
- package/server/utils/types.ts +47 -0
- package/server/workspace/chat-index/index.ts +153 -0
- package/server/workspace/chat-index/indexer.ts +209 -0
- package/server/workspace/chat-index/paths.ts +34 -0
- package/server/workspace/chat-index/summarizer.ts +247 -0
- package/server/workspace/chat-index/types.ts +38 -0
- package/server/workspace/custom-dirs.ts +220 -0
- package/server/workspace/helps/business.md +104 -0
- package/server/workspace/helps/github.md +23 -0
- package/server/workspace/helps/index.md +60 -0
- package/server/workspace/helps/mulmoscript.md +249 -0
- package/server/workspace/helps/sandbox.md +90 -0
- package/server/workspace/helps/spreadsheet.md +43 -0
- package/server/workspace/helps/telegram.md +135 -0
- package/server/workspace/helps/wiki.md +131 -0
- package/server/workspace/journal/archivist.ts +386 -0
- package/server/workspace/journal/dailyPass.ts +743 -0
- package/server/workspace/journal/diff.ts +71 -0
- package/server/workspace/journal/index.ts +185 -0
- package/server/workspace/journal/indexFile.ts +136 -0
- package/server/workspace/journal/linkRewrite.ts +4 -0
- package/server/workspace/journal/memoryExtractor.ts +130 -0
- package/server/workspace/journal/optimizationPass.ts +160 -0
- package/server/workspace/journal/paths.ts +76 -0
- package/server/workspace/journal/state.ts +125 -0
- package/server/workspace/paths.ts +158 -0
- package/server/workspace/reference-dirs.ts +252 -0
- package/server/workspace/roles.ts +37 -0
- package/server/workspace/skills/discovery.ts +125 -0
- package/server/workspace/skills/index.ts +10 -0
- package/server/workspace/skills/parser.ts +144 -0
- package/server/workspace/skills/paths.ts +41 -0
- package/server/workspace/skills/scheduler.ts +149 -0
- package/server/workspace/skills/types.ts +30 -0
- package/server/workspace/skills/user-tasks.ts +257 -0
- package/server/workspace/skills/writer.ts +189 -0
- package/server/workspace/sources/arxivDiscovery.ts +182 -0
- package/server/workspace/sources/classifier.ts +268 -0
- package/server/workspace/sources/fetchers/arxiv.ts +170 -0
- package/server/workspace/sources/fetchers/github.ts +106 -0
- package/server/workspace/sources/fetchers/githubIssues.ts +208 -0
- package/server/workspace/sources/fetchers/githubReleases.ts +186 -0
- package/server/workspace/sources/fetchers/index.ts +71 -0
- package/server/workspace/sources/fetchers/registerAll.ts +15 -0
- package/server/workspace/sources/fetchers/rss.ts +141 -0
- package/server/workspace/sources/fetchers/rssParser.ts +295 -0
- package/server/workspace/sources/httpFetcher.ts +230 -0
- package/server/workspace/sources/interests.ts +120 -0
- package/server/workspace/sources/paths.ts +110 -0
- package/server/workspace/sources/pipeline/dedup.ts +60 -0
- package/server/workspace/sources/pipeline/fetch.ts +136 -0
- package/server/workspace/sources/pipeline/index.ts +249 -0
- package/server/workspace/sources/pipeline/notify.ts +72 -0
- package/server/workspace/sources/pipeline/plan.ts +66 -0
- package/server/workspace/sources/pipeline/summarize.ts +189 -0
- package/server/workspace/sources/pipeline/write.ts +185 -0
- package/server/workspace/sources/rateLimiter.ts +148 -0
- package/server/workspace/sources/registry.ts +326 -0
- package/server/workspace/sources/robots.ts +271 -0
- package/server/workspace/sources/sourceState.ts +135 -0
- package/server/workspace/sources/taxonomy.ts +74 -0
- package/server/workspace/sources/types.ts +144 -0
- package/server/workspace/sources/urls.ts +112 -0
- package/server/workspace/tool-trace/classify.ts +114 -0
- package/server/workspace/tool-trace/index.ts +250 -0
- package/server/workspace/tool-trace/writeSearch.ts +98 -0
- package/server/workspace/wiki-backlinks/index.ts +107 -0
- package/server/workspace/wiki-backlinks/sessionBacklinks.ts +144 -0
- package/server/workspace/workspace.ts +66 -0
- package/src/App.vue +720 -0
- package/src/assets/mulmo_bw.png +0 -0
- package/src/components/CanvasViewToggle.vue +27 -0
- package/src/components/ChatAttachmentPreview.vue +45 -0
- package/src/components/ChatImagePreview.vue +17 -0
- package/src/components/ChatInput.vue +208 -0
- package/src/components/FileContentHeader.vue +49 -0
- package/src/components/FileContentRenderer.vue +162 -0
- package/src/components/FileTree.vue +115 -0
- package/src/components/FileTreePane.vue +85 -0
- package/src/components/FilesView.vue +206 -0
- package/src/components/LockStatusPopup.vue +111 -0
- package/src/components/NotificationBell.vue +131 -0
- package/src/components/NotificationToast.vue +72 -0
- package/src/components/PluginLauncher.vue +138 -0
- package/src/components/RightSidebar.vue +113 -0
- package/src/components/RoleSelector.vue +64 -0
- package/src/components/SessionHistoryPanel.vue +176 -0
- package/src/components/SessionTabBar.vue +81 -0
- package/src/components/SettingsMcpTab.vue +350 -0
- package/src/components/SettingsModal.vue +275 -0
- package/src/components/SettingsReferenceDirsTab.vue +173 -0
- package/src/components/SettingsWorkspaceDirsTab.vue +174 -0
- package/src/components/SidebarHeader.vue +69 -0
- package/src/components/StackView.vue +360 -0
- package/src/components/SuggestionsPanel.vue +65 -0
- package/src/components/TodoExplorer.vue +358 -0
- package/src/components/ToolResultsPanel.vue +77 -0
- package/src/components/todo/TodoAddDialog.vue +131 -0
- package/src/components/todo/TodoEditDialog.vue +47 -0
- package/src/components/todo/TodoEditPanel.vue +113 -0
- package/src/components/todo/TodoKanbanView.vue +249 -0
- package/src/components/todo/TodoListView.vue +79 -0
- package/src/components/todo/TodoTableView.vue +177 -0
- package/src/composables/useActiveSession.ts +40 -0
- package/src/composables/useAppApi.ts +45 -0
- package/src/composables/useCanvasViewMode.ts +121 -0
- package/src/composables/useChatScroll.ts +47 -0
- package/src/composables/useClickOutside.ts +26 -0
- package/src/composables/useClipboardCopy.ts +44 -0
- package/src/composables/useContentDisplay.ts +52 -0
- package/src/composables/useDebugBeat.ts +23 -0
- package/src/composables/useDynamicFavicon.ts +115 -0
- package/src/composables/useEventListeners.ts +42 -0
- package/src/composables/useExpandedDirs.ts +64 -0
- package/src/composables/useFaviconState.ts +30 -0
- package/src/composables/useFileSelection.ts +115 -0
- package/src/composables/useFileSortMode.ts +24 -0
- package/src/composables/useFileTree.ts +85 -0
- package/src/composables/useFreshPluginData.ts +89 -0
- package/src/composables/useHealth.ts +38 -0
- package/src/composables/useImeAwareEnter.ts +57 -0
- package/src/composables/useKeyNavigation.ts +60 -0
- package/src/composables/useMarkdownLinkHandler.ts +46 -0
- package/src/composables/useMarkdownMode.ts +17 -0
- package/src/composables/useMcpTools.ts +71 -0
- package/src/composables/useMergedSessions.ts +27 -0
- package/src/composables/useNotifications.ts +90 -0
- package/src/composables/usePdfDownload.ts +60 -0
- package/src/composables/usePendingCalls.ts +77 -0
- package/src/composables/usePubSub.ts +85 -0
- package/src/composables/useRightSidebar.ts +23 -0
- package/src/composables/useRoles.ts +34 -0
- package/src/composables/useSandboxStatus.ts +67 -0
- package/src/composables/useSelectedResult.ts +49 -0
- package/src/composables/useSessionDerived.ts +51 -0
- package/src/composables/useSessionHistory.ts +81 -0
- package/src/composables/useSessionSync.ts +57 -0
- package/src/composables/useViewLayout.ts +55 -0
- package/src/config/apiRoutes.ts +173 -0
- package/src/config/pubsubChannels.ts +45 -0
- package/src/config/roles.ts +335 -0
- package/src/config/schedulerActions.ts +25 -0
- package/src/config/toolNames.ts +71 -0
- package/src/config/workspacePaths.ts +24 -0
- package/src/index.css +107 -0
- package/src/main.ts +25 -0
- package/src/plugins/canvas/Preview.vue +13 -0
- package/src/plugins/canvas/View.vue +333 -0
- package/src/plugins/canvas/definition.ts +38 -0
- package/src/plugins/canvas/index.ts +36 -0
- package/src/plugins/chart/Preview.vue +49 -0
- package/src/plugins/chart/View.vue +143 -0
- package/src/plugins/chart/definition.ts +58 -0
- package/src/plugins/chart/index.ts +52 -0
- package/src/plugins/editImage/Preview.vue +13 -0
- package/src/plugins/editImage/View.vue +13 -0
- package/src/plugins/editImage/definition.ts +27 -0
- package/src/plugins/editImage/index.ts +36 -0
- package/src/plugins/generateImage/Preview.vue +13 -0
- package/src/plugins/generateImage/View.vue +33 -0
- package/src/plugins/generateImage/definition.ts +32 -0
- package/src/plugins/generateImage/index.ts +56 -0
- package/src/plugins/manageRoles/Preview.vue +49 -0
- package/src/plugins/manageRoles/View.vue +525 -0
- package/src/plugins/manageRoles/definition.ts +43 -0
- package/src/plugins/manageRoles/index.ts +47 -0
- package/src/plugins/manageSkills/Preview.vue +21 -0
- package/src/plugins/manageSkills/View.vue +321 -0
- package/src/plugins/manageSkills/definition.ts +49 -0
- package/src/plugins/manageSkills/index.ts +49 -0
- package/src/plugins/manageSource/Preview.vue +33 -0
- package/src/plugins/manageSource/View.vue +697 -0
- package/src/plugins/manageSource/definition.ts +63 -0
- package/src/plugins/manageSource/index.ts +66 -0
- package/src/plugins/markdown/Preview.vue +77 -0
- package/src/plugins/markdown/View.vue +476 -0
- package/src/plugins/markdown/definition.ts +50 -0
- package/src/plugins/markdown/index.ts +36 -0
- package/src/plugins/presentHtml/Preview.vue +25 -0
- package/src/plugins/presentHtml/View.vue +52 -0
- package/src/plugins/presentHtml/definition.ts +27 -0
- package/src/plugins/presentHtml/helpers.ts +72 -0
- package/src/plugins/presentHtml/index.ts +41 -0
- package/src/plugins/presentMulmoScript/Preview.vue +23 -0
- package/src/plugins/presentMulmoScript/View.vue +1166 -0
- package/src/plugins/presentMulmoScript/definition.ts +95 -0
- package/src/plugins/presentMulmoScript/helpers.ts +162 -0
- package/src/plugins/presentMulmoScript/index.ts +40 -0
- package/src/plugins/scheduler/Preview.vue +67 -0
- package/src/plugins/scheduler/TasksTab.vue +205 -0
- package/src/plugins/scheduler/View.vue +565 -0
- package/src/plugins/scheduler/definition.ts +57 -0
- package/src/plugins/scheduler/index.ts +45 -0
- package/src/plugins/scheduler/viewModes.ts +26 -0
- package/src/plugins/spreadsheet/Preview.vue +29 -0
- package/src/plugins/spreadsheet/View.vue +997 -0
- package/src/plugins/spreadsheet/cellHighlights.ts +79 -0
- package/src/plugins/spreadsheet/definition.ts +121 -0
- package/src/plugins/spreadsheet/engine/calculator.ts +459 -0
- package/src/plugins/spreadsheet/engine/cellBuilder.ts +81 -0
- package/src/plugins/spreadsheet/engine/date-parser.ts +220 -0
- package/src/plugins/spreadsheet/engine/date-utils.ts +56 -0
- package/src/plugins/spreadsheet/engine/engine.ts +176 -0
- package/src/plugins/spreadsheet/engine/evaluator.ts +390 -0
- package/src/plugins/spreadsheet/engine/formatter.ts +172 -0
- package/src/plugins/spreadsheet/engine/formulaRefs.ts +101 -0
- package/src/plugins/spreadsheet/engine/functions/date.ts +299 -0
- package/src/plugins/spreadsheet/engine/functions/financial.ts +387 -0
- package/src/plugins/spreadsheet/engine/functions/index.ts +16 -0
- package/src/plugins/spreadsheet/engine/functions/logical.ts +262 -0
- package/src/plugins/spreadsheet/engine/functions/lookup.ts +400 -0
- package/src/plugins/spreadsheet/engine/functions/mathematical.ts +297 -0
- package/src/plugins/spreadsheet/engine/functions/statistical.ts +338 -0
- package/src/plugins/spreadsheet/engine/functions/text.ts +389 -0
- package/src/plugins/spreadsheet/engine/index.ts +27 -0
- package/src/plugins/spreadsheet/engine/jsonCellLocator.ts +111 -0
- package/src/plugins/spreadsheet/engine/parser.ts +143 -0
- package/src/plugins/spreadsheet/engine/registry.ts +150 -0
- package/src/plugins/spreadsheet/engine/responseDecoder.ts +67 -0
- package/src/plugins/spreadsheet/engine/types.ts +64 -0
- package/src/plugins/spreadsheet/index.ts +36 -0
- package/src/plugins/textResponse/Preview.vue +94 -0
- package/src/plugins/textResponse/View.vue +503 -0
- package/src/plugins/textResponse/definition.ts +34 -0
- package/src/plugins/textResponse/index.ts +27 -0
- package/src/plugins/textResponse/plugin.ts +29 -0
- package/src/plugins/textResponse/samples.ts +97 -0
- package/src/plugins/textResponse/types.ts +11 -0
- package/src/plugins/todo/Preview.vue +63 -0
- package/src/plugins/todo/View.vue +364 -0
- package/src/plugins/todo/composables/useTodos.ts +177 -0
- package/src/plugins/todo/definition.ts +45 -0
- package/src/plugins/todo/index.ts +61 -0
- package/src/plugins/todo/labels.ts +163 -0
- package/src/plugins/todo/priority.ts +98 -0
- package/src/plugins/todo/viewModes.ts +19 -0
- package/src/plugins/ui-image/ImagePreview.vue +23 -0
- package/src/plugins/ui-image/ImageView.vue +34 -0
- package/src/plugins/ui-image/index.ts +3 -0
- package/src/plugins/ui-image/types.ts +4 -0
- package/src/plugins/wiki/Preview.vue +65 -0
- package/src/plugins/wiki/View.vue +342 -0
- package/src/plugins/wiki/definition.ts +25 -0
- package/src/plugins/wiki/helpers.ts +59 -0
- package/src/plugins/wiki/index.ts +52 -0
- package/src/router/guards.ts +61 -0
- package/src/router/index.ts +50 -0
- package/src/tools/index.ts +52 -0
- package/src/tools/types.ts +27 -0
- package/src/types/events.ts +16 -0
- package/src/types/fileTree.ts +13 -0
- package/src/types/notification.ts +67 -0
- package/src/types/session.ts +116 -0
- package/src/types/sse.ts +90 -0
- package/src/types/toolCallHistory.ts +13 -0
- package/src/utils/agent/eventDispatch.ts +74 -0
- package/src/utils/agent/request.ts +55 -0
- package/src/utils/agent/toolCalls.ts +62 -0
- package/src/utils/api.ts +218 -0
- package/src/utils/canvas/viewMode.ts +46 -0
- package/src/utils/dom/authTokenMeta.ts +20 -0
- package/src/utils/dom/clickOutside.ts +11 -0
- package/src/utils/dom/externalLink.ts +57 -0
- package/src/utils/dom/scrollable.ts +24 -0
- package/src/utils/errors.ts +11 -0
- package/src/utils/files/expandedDirs.ts +25 -0
- package/src/utils/files/filename.ts +12 -0
- package/src/utils/files/sortChildren.ts +20 -0
- package/src/utils/filesPreview/schedulerPreview.ts +38 -0
- package/src/utils/filesPreview/todoPreview.ts +40 -0
- package/src/utils/format/date.ts +85 -0
- package/src/utils/format/frontmatter.ts +80 -0
- package/src/utils/format/jsonSyntax.ts +109 -0
- package/src/utils/html/previewCsp.ts +65 -0
- package/src/utils/image/resolve.ts +8 -0
- package/src/utils/image/rewriteMarkdownImageRefs.ts +182 -0
- package/src/utils/markdown/extractFirstH1.ts +39 -0
- package/src/utils/notification/dispatch.ts +22 -0
- package/src/utils/path/relativeLink.ts +130 -0
- package/src/utils/role/icon.ts +20 -0
- package/src/utils/role/merge.ts +10 -0
- package/src/utils/role/plugins.ts +12 -0
- package/src/utils/session/mergeSessions.ts +103 -0
- package/src/utils/session/seedRoleDefault.ts +35 -0
- package/src/utils/session/sessionEntries.ts +121 -0
- package/src/utils/session/sessionFactory.ts +22 -0
- package/src/utils/session/sessionHelpers.ts +99 -0
- package/src/utils/tools/dedup.ts +17 -0
- package/src/utils/tools/mcp.ts +33 -0
- package/src/utils/tools/pendingCalls.ts +16 -0
- package/src/utils/tools/result.ts +40 -0
- package/src/utils/types.ts +44 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="h-full bg-white flex flex-col overflow-hidden">
|
|
3
|
+
<!-- Header -->
|
|
4
|
+
<div class="flex items-center justify-between px-6 py-4 border-b border-gray-100 shrink-0">
|
|
5
|
+
<div>
|
|
6
|
+
<h2 class="text-lg font-semibold text-gray-800">Skills</h2>
|
|
7
|
+
<p class="text-xs text-gray-400 mt-0.5">{{ skills.length }} available · click one to view · "Run" invokes it as /<name></p>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<!-- List load error (standalone mode) -->
|
|
12
|
+
<div v-if="listError" class="px-6 py-3 text-sm text-red-600 bg-red-50 border-b border-red-100">
|
|
13
|
+
{{ listError }}
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="flex-1 min-h-0 flex overflow-hidden">
|
|
17
|
+
<!-- Left: skill list -->
|
|
18
|
+
<div class="w-64 shrink-0 border-r border-gray-100 overflow-y-auto bg-gray-50">
|
|
19
|
+
<div
|
|
20
|
+
v-for="skill in skills"
|
|
21
|
+
:key="skill.name"
|
|
22
|
+
:data-testid="`skill-item-${skill.name}`"
|
|
23
|
+
class="cursor-pointer px-4 py-3 border-b border-gray-100 text-sm hover:bg-white transition-colors"
|
|
24
|
+
:class="selectedName === skill.name ? 'bg-white border-l-2 border-l-blue-500' : ''"
|
|
25
|
+
@click="selectedName = skill.name"
|
|
26
|
+
>
|
|
27
|
+
<div class="font-medium text-gray-800 truncate">{{ skill.name }}</div>
|
|
28
|
+
<div class="text-xs text-gray-500 truncate mt-0.5">
|
|
29
|
+
{{ skill.description }}
|
|
30
|
+
</div>
|
|
31
|
+
<div class="text-[10px] text-gray-400 uppercase mt-0.5">
|
|
32
|
+
{{ skill.source }}
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<p v-if="skills.length === 0" class="p-4 text-sm text-gray-400 italic">
|
|
36
|
+
No skills found. Add skill folders under
|
|
37
|
+
<code class="text-[11px]">~/.claude/skills/</code>.
|
|
38
|
+
</p>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<!-- Right: detail pane -->
|
|
42
|
+
<div class="flex-1 min-w-0 overflow-y-auto">
|
|
43
|
+
<div v-if="!selected" class="p-6 text-sm text-gray-400 italic">Select a skill on the left to view its SKILL.md.</div>
|
|
44
|
+
<div v-else class="p-6">
|
|
45
|
+
<div class="flex items-start justify-between gap-4 mb-4">
|
|
46
|
+
<div class="min-w-0">
|
|
47
|
+
<h3 class="text-xl font-semibold text-gray-800 truncate">
|
|
48
|
+
{{ selected.name }}
|
|
49
|
+
</h3>
|
|
50
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
51
|
+
{{ selected.description }}
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="flex items-center gap-2 shrink-0">
|
|
55
|
+
<template v-if="editing">
|
|
56
|
+
<button
|
|
57
|
+
class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50 flex items-center gap-1"
|
|
58
|
+
data-testid="skill-cancel-btn"
|
|
59
|
+
@click="cancelEdit"
|
|
60
|
+
>
|
|
61
|
+
Cancel
|
|
62
|
+
</button>
|
|
63
|
+
<button
|
|
64
|
+
class="px-3 py-1.5 text-sm rounded bg-green-600 hover:bg-green-700 text-white disabled:opacity-40 flex items-center gap-1"
|
|
65
|
+
:disabled="saving"
|
|
66
|
+
data-testid="skill-save-btn"
|
|
67
|
+
@click="saveEdit"
|
|
68
|
+
>
|
|
69
|
+
<span class="material-icons text-base">save</span>
|
|
70
|
+
Save
|
|
71
|
+
</button>
|
|
72
|
+
</template>
|
|
73
|
+
<template v-else>
|
|
74
|
+
<button
|
|
75
|
+
v-if="detail && detail.source === 'project'"
|
|
76
|
+
class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50 disabled:opacity-40 flex items-center gap-1"
|
|
77
|
+
:disabled="detailLoading"
|
|
78
|
+
data-testid="skill-edit-btn"
|
|
79
|
+
@click="startEdit"
|
|
80
|
+
>
|
|
81
|
+
<span class="material-icons text-base">edit</span>
|
|
82
|
+
Edit
|
|
83
|
+
</button>
|
|
84
|
+
<button
|
|
85
|
+
v-if="detail && detail.source === 'project'"
|
|
86
|
+
class="px-3 py-1.5 text-sm rounded border border-red-300 text-red-600 hover:bg-red-50 disabled:opacity-40 flex items-center gap-1"
|
|
87
|
+
:disabled="detailLoading || deleting"
|
|
88
|
+
data-testid="skill-delete-btn"
|
|
89
|
+
title="Delete this project-scope skill"
|
|
90
|
+
@click="deleteSkill"
|
|
91
|
+
>
|
|
92
|
+
<span class="material-icons text-base">delete</span>
|
|
93
|
+
Delete
|
|
94
|
+
</button>
|
|
95
|
+
<button
|
|
96
|
+
class="px-3 py-1.5 text-sm rounded bg-blue-600 hover:bg-blue-700 text-white disabled:opacity-40 flex items-center gap-1"
|
|
97
|
+
:disabled="detailLoading || !detail"
|
|
98
|
+
data-testid="skill-run-btn"
|
|
99
|
+
@click="runSkill"
|
|
100
|
+
>
|
|
101
|
+
<span class="material-icons text-base">play_arrow</span>
|
|
102
|
+
Run
|
|
103
|
+
</button>
|
|
104
|
+
</template>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
<div v-if="detailLoading" class="text-sm text-gray-400 italic">Loading…</div>
|
|
108
|
+
<div v-else-if="detailError" class="text-sm text-red-600">
|
|
109
|
+
{{ detailError }}
|
|
110
|
+
</div>
|
|
111
|
+
<!-- Edit mode -->
|
|
112
|
+
<div v-else-if="editing && detail" class="space-y-4">
|
|
113
|
+
<div>
|
|
114
|
+
<label class="block text-xs font-medium text-gray-500 mb-1"> Description </label>
|
|
115
|
+
<input
|
|
116
|
+
v-model="editDescription"
|
|
117
|
+
data-testid="skill-edit-description"
|
|
118
|
+
class="w-full px-3 py-2 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500 text-gray-800"
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="flex-1">
|
|
122
|
+
<label class="block text-xs font-medium text-gray-500 mb-1"> Body (Markdown) </label>
|
|
123
|
+
<textarea
|
|
124
|
+
v-model="editBody"
|
|
125
|
+
data-testid="skill-edit-body"
|
|
126
|
+
class="w-full h-96 px-3 py-2 text-sm font-mono border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500 text-gray-800 resize-y"
|
|
127
|
+
></textarea>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
<!-- View mode -->
|
|
131
|
+
<!-- eslint-disable-next-line vue/no-v-html -- sanitized via DOMPurify -->
|
|
132
|
+
<div v-else-if="detail && renderedBody" class="markdown-content text-gray-700" data-testid="skill-body-rendered" v-html="renderedBody"></div>
|
|
133
|
+
<p v-else-if="detail" class="text-sm text-gray-400 italic">(empty body)</p>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</template>
|
|
139
|
+
|
|
140
|
+
<script setup lang="ts">
|
|
141
|
+
import { computed, onMounted, ref, watch } from "vue";
|
|
142
|
+
import { marked } from "marked";
|
|
143
|
+
import DOMPurify from "dompurify";
|
|
144
|
+
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
145
|
+
import type { ManageSkillsData, SkillSummary } from "./index";
|
|
146
|
+
import { useAppApi } from "../../composables/useAppApi";
|
|
147
|
+
import { apiGet, apiPut, apiDelete } from "../../utils/api";
|
|
148
|
+
import { API_ROUTES } from "../../config/apiRoutes";
|
|
149
|
+
|
|
150
|
+
interface SkillDetail {
|
|
151
|
+
name: string;
|
|
152
|
+
description: string;
|
|
153
|
+
body: string;
|
|
154
|
+
source: "user" | "project";
|
|
155
|
+
path: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const props = defineProps<{
|
|
159
|
+
selectedResult?: ToolResultComplete<ManageSkillsData>;
|
|
160
|
+
}>();
|
|
161
|
+
|
|
162
|
+
// Local mutable copy of the skill list so the Delete button can
|
|
163
|
+
// remove rows without waiting for a fresh tool_result push.
|
|
164
|
+
// Re-seeded whenever the underlying tool result changes.
|
|
165
|
+
const skills = ref<SkillSummary[]>(props.selectedResult?.data?.skills ?? []);
|
|
166
|
+
const selectedName = ref<string | null>(skills.value[0]?.name ?? null);
|
|
167
|
+
const detail = ref<SkillDetail | null>(null);
|
|
168
|
+
const detailLoading = ref(false);
|
|
169
|
+
const detailError = ref<string | null>(null);
|
|
170
|
+
const deleting = ref(false);
|
|
171
|
+
const editing = ref(false);
|
|
172
|
+
const saving = ref(false);
|
|
173
|
+
const editDescription = ref("");
|
|
174
|
+
const editBody = ref("");
|
|
175
|
+
|
|
176
|
+
const selected = computed(() => skills.value.find((s) => s.name === selectedName.value) ?? null);
|
|
177
|
+
|
|
178
|
+
const renderedBody = computed(() => {
|
|
179
|
+
const body = detail.value?.body;
|
|
180
|
+
if (!body) return "";
|
|
181
|
+
return DOMPurify.sanitize(marked(body) as string);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Reset the selection when the tool result is replaced (e.g. the
|
|
185
|
+
// user opens a newer `manageSkills` invocation from the sidebar).
|
|
186
|
+
watch(
|
|
187
|
+
() => props.selectedResult?.uuid,
|
|
188
|
+
() => {
|
|
189
|
+
skills.value = props.selectedResult?.data?.skills ?? [];
|
|
190
|
+
selectedName.value = skills.value[0]?.name ?? null;
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const listError = ref<string | null>(null);
|
|
195
|
+
|
|
196
|
+
// Standalone mode: if no selectedResult was passed, fetch the skill
|
|
197
|
+
// list from the API on mount so the view is populated.
|
|
198
|
+
onMounted(async () => {
|
|
199
|
+
if (props.selectedResult || skills.value.length > 0) return;
|
|
200
|
+
const response = await apiGet<{ skills: SkillSummary[] }>(API_ROUTES.skills.list);
|
|
201
|
+
if (!response.ok) {
|
|
202
|
+
listError.value = `Failed to load skills: ${response.error}`;
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (Array.isArray(response.data.skills)) {
|
|
206
|
+
skills.value = response.data.skills;
|
|
207
|
+
selectedName.value = skills.value[0]?.name ?? null;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Fetch detail when the selection changes. Failures surface inline
|
|
212
|
+
// so the Run button stays disabled and the user sees why. Each request
|
|
213
|
+
// captures the `name` it was issued for — if the user clicks another
|
|
214
|
+
// skill while the first fetch is in flight, the slower response is
|
|
215
|
+
// discarded (otherwise stale detail can land under the new selection
|
|
216
|
+
// and break deleteSkill(), which reads `detail.value.name`).
|
|
217
|
+
watch(
|
|
218
|
+
selectedName,
|
|
219
|
+
async (name) => {
|
|
220
|
+
if (!name) {
|
|
221
|
+
detail.value = null;
|
|
222
|
+
editing.value = false;
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
editing.value = false;
|
|
226
|
+
detailLoading.value = true;
|
|
227
|
+
detailError.value = null;
|
|
228
|
+
const response = await apiGet<{ skill: SkillDetail }>(API_ROUTES.skills.detail.replace(":name", encodeURIComponent(name)));
|
|
229
|
+
if (selectedName.value !== name) {
|
|
230
|
+
// Selection changed while this request was in flight — drop it.
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (!response.ok) {
|
|
234
|
+
detailError.value = `Failed to load skill: ${response.error}`;
|
|
235
|
+
detail.value = null;
|
|
236
|
+
} else {
|
|
237
|
+
detail.value = response.data.skill;
|
|
238
|
+
}
|
|
239
|
+
detailLoading.value = false;
|
|
240
|
+
},
|
|
241
|
+
{ immediate: true },
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
function startEdit(): void {
|
|
245
|
+
if (!detail.value) return;
|
|
246
|
+
editDescription.value = detail.value.description;
|
|
247
|
+
editBody.value = detail.value.body;
|
|
248
|
+
editing.value = true;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function cancelEdit(): void {
|
|
252
|
+
editing.value = false;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function saveEdit(): Promise<void> {
|
|
256
|
+
if (!detail.value) return;
|
|
257
|
+
const name = detail.value.name;
|
|
258
|
+
saving.value = true;
|
|
259
|
+
detailError.value = null;
|
|
260
|
+
const result = await apiPut<{ updated: boolean; path: string }>(API_ROUTES.skills.update.replace(":name", encodeURIComponent(name)), {
|
|
261
|
+
description: editDescription.value,
|
|
262
|
+
body: editBody.value,
|
|
263
|
+
});
|
|
264
|
+
saving.value = false;
|
|
265
|
+
if (!result.ok) {
|
|
266
|
+
detailError.value = `Save failed: ${result.error}`;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
detail.value = {
|
|
270
|
+
...detail.value,
|
|
271
|
+
description: editDescription.value,
|
|
272
|
+
body: editBody.value,
|
|
273
|
+
};
|
|
274
|
+
// Update the sidebar summary too.
|
|
275
|
+
const idx = skills.value.findIndex((s) => s.name === name);
|
|
276
|
+
if (idx >= 0) {
|
|
277
|
+
skills.value[idx] = {
|
|
278
|
+
...skills.value[idx],
|
|
279
|
+
description: editDescription.value,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
editing.value = false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Run = send the skill invocation as a Claude Code slash command.
|
|
286
|
+
// Claude CLI already knows about every ~/.claude/skills/<name>/SKILL.md
|
|
287
|
+
// at spawn, so sending `/<name>` is enough — no need to ship the body.
|
|
288
|
+
// Routes through App.vue's sendMessage via provide/inject (#227).
|
|
289
|
+
const appApi = useAppApi();
|
|
290
|
+
|
|
291
|
+
function runSkill(): void {
|
|
292
|
+
if (!selectedName.value) return;
|
|
293
|
+
appApi.sendMessage(`/${selectedName.value}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Delete is project-scope only — see saveProjectSkill / deleteProjectSkill
|
|
297
|
+
// in server/skills/writer.ts. The button is hidden in the template
|
|
298
|
+
// when source !== "project". A native confirm() is enough for phase 1
|
|
299
|
+
// since the action is reversible by re-saving via the conversation.
|
|
300
|
+
async function deleteSkill(): Promise<void> {
|
|
301
|
+
if (!detail.value || detail.value.source !== "project") return;
|
|
302
|
+
const name = detail.value.name;
|
|
303
|
+
if (!window.confirm(`Delete skill "${name}"? This removes ~/mulmoclaude/.claude/skills/${name}/SKILL.md.`)) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
deleting.value = true;
|
|
307
|
+
const result = await apiDelete<unknown>(API_ROUTES.skills.remove.replace(":name", encodeURIComponent(name)));
|
|
308
|
+
deleting.value = false;
|
|
309
|
+
if (!result.ok) {
|
|
310
|
+
detailError.value = result.error || "Failed to delete";
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
// Remove from the local list, advance selection, clear detail.
|
|
314
|
+
const idx = skills.value.findIndex((s) => s.name === name);
|
|
315
|
+
if (idx >= 0) {
|
|
316
|
+
skills.value.splice(idx, 1);
|
|
317
|
+
}
|
|
318
|
+
selectedName.value = skills.value[0]?.name ?? null;
|
|
319
|
+
detail.value = null;
|
|
320
|
+
}
|
|
321
|
+
</script>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ToolDefinition } from "gui-chat-protocol";
|
|
2
|
+
|
|
3
|
+
export const TOOL_NAME = "manageSkills";
|
|
4
|
+
|
|
5
|
+
const toolDefinition: ToolDefinition = {
|
|
6
|
+
type: "function",
|
|
7
|
+
name: TOOL_NAME,
|
|
8
|
+
description:
|
|
9
|
+
"List, save, update, or delete Claude Code skills. Discovery merges ~/.claude/skills/ (user, read-only) and <workspace>/.claude/skills/ (project, writable). Save, update, and delete only affect the project scope.",
|
|
10
|
+
prompt:
|
|
11
|
+
`Use the ${TOOL_NAME} tool for the user's skill library:\n\n` +
|
|
12
|
+
`- **list** (default, no args): show every available skill in the canvas.\n` +
|
|
13
|
+
`- **save**: when the user asks to turn the current conversation into a skill (e.g. "skill 化して" / "save this as a skill"), do these steps:\n` +
|
|
14
|
+
` 1. Read the current chat transcript via the Read tool. The path is \`chat/<session-id>.jsonl\` under the workspace; if you don't know the session id, list \`chat/\` first.\n` +
|
|
15
|
+
` 2. Distill the conversation into a short markdown body explaining the steps you took, in second person ("First, do X. Then, do Y."). Keep it focused on the reusable workflow, not the one-off details.\n` +
|
|
16
|
+
` 3. Pick a kebab-case slug (lowercase letters, digits, hyphens; no leading/trailing hyphen; 1-64 chars). If the user gave a name in the request, use it.\n` +
|
|
17
|
+
` 4. Write a one-line description for the YAML frontmatter.\n` +
|
|
18
|
+
` 5. Call ${TOOL_NAME} with \`{action: "save", name, description, body}\`. Saves go to <workspace>/.claude/skills/<slug>/SKILL.md.\n` +
|
|
19
|
+
` 6. If the response says the name already exists, ask the user for a different one.\n` +
|
|
20
|
+
`- **update**: when the user asks to modify an existing skill (e.g. "〇〇のスキルを更新して" / "change the description of X"), call \`{action: "update", name, description, body}\`. Only project-scope skills can be updated; user-scope skills are read-only. You must provide both description and body (read the current skill first via list or the Read tool if you only need to change part of it).\n` +
|
|
21
|
+
`- **delete**: when the user asks to remove a named skill, call \`{action: "delete", name}\`. Only project-scope skills can be deleted; user-scope skills are protected.\n\n` +
|
|
22
|
+
`**IMPORTANT**: NEVER edit SKILL.md files directly with the Edit or Write tools. Always use this ${TOOL_NAME} tool with the appropriate action. Direct file edits bypass validation and won't refresh the skills UI.`,
|
|
23
|
+
parameters: {
|
|
24
|
+
type: "object",
|
|
25
|
+
properties: {
|
|
26
|
+
action: {
|
|
27
|
+
type: "string",
|
|
28
|
+
enum: ["list", "save", "update", "delete"],
|
|
29
|
+
description: "The operation to perform. Default: list (show all skills).",
|
|
30
|
+
},
|
|
31
|
+
name: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description:
|
|
34
|
+
"Skill slug. Required for save and delete. Lowercase letters, digits, and hyphens only; 1-64 chars; no leading/trailing or consecutive hyphens.",
|
|
35
|
+
},
|
|
36
|
+
description: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "One-line summary for the YAML frontmatter. Required for save.",
|
|
39
|
+
},
|
|
40
|
+
body: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Markdown body of the skill (instructions for Claude to follow). Required for save. May be multi-line.",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
required: ["action"],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default toolDefinition;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ToolPlugin } from "../../tools/types";
|
|
2
|
+
import toolDefinition, { TOOL_NAME } from "./definition";
|
|
3
|
+
import View from "./View.vue";
|
|
4
|
+
import Preview from "./Preview.vue";
|
|
5
|
+
import { apiGet } from "../../utils/api";
|
|
6
|
+
import { API_ROUTES } from "../../config/apiRoutes";
|
|
7
|
+
|
|
8
|
+
export interface SkillSummary {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
source: "user" | "project";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ManageSkillsData {
|
|
15
|
+
skills: SkillSummary[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const manageSkillsPlugin: ToolPlugin<ManageSkillsData> = {
|
|
19
|
+
toolDefinition,
|
|
20
|
+
async execute() {
|
|
21
|
+
// Claude invokes this tool to show the user their skills list.
|
|
22
|
+
// The server exposes GET /api/skills (discovery + merge); we just
|
|
23
|
+
// shape it for the View component.
|
|
24
|
+
const result = await apiGet<{ skills: SkillSummary[] }>(API_ROUTES.skills.list);
|
|
25
|
+
if (!result.ok) {
|
|
26
|
+
return {
|
|
27
|
+
toolName: TOOL_NAME,
|
|
28
|
+
uuid: crypto.randomUUID(),
|
|
29
|
+
message: `Failed to load skills: ${result.error}`,
|
|
30
|
+
error: `Failed to load skills: ${result.error}`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const skills = result.data.skills;
|
|
34
|
+
return {
|
|
35
|
+
toolName: TOOL_NAME,
|
|
36
|
+
uuid: crypto.randomUUID(),
|
|
37
|
+
title: "Skills",
|
|
38
|
+
message: `Found ${skills.length} skill${skills.length === 1 ? "" : "s"}.`,
|
|
39
|
+
data: { skills },
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
isEnabled: () => true,
|
|
43
|
+
generatingMessage: "Loading skills…",
|
|
44
|
+
viewComponent: View,
|
|
45
|
+
previewComponent: Preview,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default manageSkillsPlugin;
|
|
49
|
+
export { TOOL_NAME };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="text-sm">
|
|
3
|
+
<div class="font-medium text-gray-700 truncate mb-1">
|
|
4
|
+
{{ title }}
|
|
5
|
+
</div>
|
|
6
|
+
<div v-if="hint" class="text-xs text-gray-500 leading-relaxed truncate">
|
|
7
|
+
{{ hint }}
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup lang="ts">
|
|
13
|
+
import { computed } from "vue";
|
|
14
|
+
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
15
|
+
import type { ManageSourceData, Source } from "./index";
|
|
16
|
+
|
|
17
|
+
const props = defineProps<{ result: ToolResultComplete<ManageSourceData> }>();
|
|
18
|
+
|
|
19
|
+
const data = computed(() => props.result.data);
|
|
20
|
+
const title = computed(() => "Information sources");
|
|
21
|
+
|
|
22
|
+
const hint = computed(() => {
|
|
23
|
+
const sources = data.value?.sources ?? [];
|
|
24
|
+
if (sources.length === 0) return "No sources registered yet.";
|
|
25
|
+
const names = sources
|
|
26
|
+
.slice(0, 3)
|
|
27
|
+
.map((s: Source) => s.slug)
|
|
28
|
+
.join(", ");
|
|
29
|
+
const tail = sources.length > 3 ? ", …" : "";
|
|
30
|
+
const plural = sources.length === 1 ? "" : "s";
|
|
31
|
+
return `${sources.length} source${plural}: ${names}${tail}`;
|
|
32
|
+
});
|
|
33
|
+
</script>
|