@within-7/minto 0.3.10 → 0.4.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/dist/Tool.js.map +2 -2
- package/dist/commands/agents/AgentsCommand.js +2 -2
- package/dist/commands/agents/AgentsCommand.js.map +2 -2
- package/dist/commands/ctx_viz.js +1 -1
- package/dist/commands/effort.js +87 -0
- package/dist/commands/effort.js.map +7 -0
- package/dist/commands/export.js +19 -9
- package/dist/commands/export.js.map +2 -2
- package/dist/commands/ide.js +18 -0
- package/dist/commands/ide.js.map +7 -0
- package/dist/commands/mcp-interactive.js +14 -8
- package/dist/commands/mcp-interactive.js.map +2 -2
- package/dist/commands/memory.js +168 -0
- package/dist/commands/memory.js.map +7 -0
- package/dist/commands/model.js +45 -2
- package/dist/commands/model.js.map +2 -2
- package/dist/commands/outputStyle.js +64 -0
- package/dist/commands/outputStyle.js.map +7 -0
- package/dist/commands/plugin/utils.js +33 -1
- package/dist/commands/plugin/utils.js.map +2 -2
- package/dist/commands/plugin.js +10 -1
- package/dist/commands/plugin.js.map +2 -2
- package/dist/commands/refreshCommands.js +2 -0
- package/dist/commands/refreshCommands.js.map +2 -2
- package/dist/commands/review.js +51 -0
- package/dist/commands/review.js.map +7 -0
- package/dist/commands/terminalSetup.js +6 -0
- package/dist/commands/terminalSetup.js.map +2 -2
- package/dist/commands/undo.js +8 -0
- package/dist/commands/undo.js.map +2 -2
- package/dist/commands/vim.js +22 -0
- package/dist/commands/vim.js.map +7 -0
- package/dist/commands.js +12 -0
- package/dist/commands.js.map +2 -2
- package/dist/components/HighlightedCode.js +1 -0
- package/dist/components/HighlightedCode.js.map +2 -2
- package/dist/components/ModelSelector/ModelSelector.js +250 -143
- package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
- package/dist/components/PromptInput.js +21 -6
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/PulseLabel.js +44 -0
- package/dist/components/PulseLabel.js.map +7 -0
- package/dist/components/RequestStatusIndicator.js +1 -1
- package/dist/components/RequestStatusIndicator.js.map +1 -1
- package/dist/components/Spinner.js +12 -42
- package/dist/components/Spinner.js.map +3 -3
- package/dist/components/StartupStatus.js +57 -0
- package/dist/components/StartupStatus.js.map +7 -0
- package/dist/components/SubagentBlock.js +43 -6
- package/dist/components/SubagentBlock.js.map +2 -2
- package/dist/components/TabbedListView/TabBar.js +13 -8
- package/dist/components/TabbedListView/TabBar.js.map +2 -2
- package/dist/components/TabbedListView/TabbedListView.js +1 -1
- package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
- package/dist/components/TodoPanel.js +1 -1
- package/dist/components/TodoPanel.js.map +1 -1
- package/dist/components/ToolUseLoader.js +5 -0
- package/dist/components/ToolUseLoader.js.map +2 -2
- package/dist/components/TrustDialog.js +0 -2
- package/dist/components/TrustDialog.js.map +2 -2
- package/dist/components/messages/TaskInModuleView.js +1 -1
- package/dist/components/messages/TaskInModuleView.js.map +2 -2
- package/dist/components/messages/TaskToolMessage.js +1 -1
- package/dist/components/messages/TaskToolMessage.js.map +2 -2
- package/dist/components/messages/UserPromptMessage.js +6 -1
- package/dist/components/messages/UserPromptMessage.js.map +2 -2
- package/dist/constants/modelCapabilities.js +103 -18
- package/dist/constants/modelCapabilities.js.map +2 -2
- package/dist/constants/product.js +2 -0
- package/dist/constants/product.js.map +2 -2
- package/dist/constants/prompts/agentPrompt.js +30 -0
- package/dist/constants/prompts/agentPrompt.js.map +7 -0
- package/dist/constants/prompts/codeConventions.js +27 -0
- package/dist/constants/prompts/codeConventions.js.map +7 -0
- package/dist/constants/prompts/doingTasks.js +15 -0
- package/dist/constants/prompts/doingTasks.js.map +7 -0
- package/dist/constants/prompts/envInfo.js +17 -0
- package/dist/constants/prompts/envInfo.js.map +7 -0
- package/dist/constants/prompts/executingWithCare.js +17 -0
- package/dist/constants/prompts/executingWithCare.js.map +7 -0
- package/dist/constants/prompts/identity.js +10 -0
- package/dist/constants/prompts/identity.js.map +7 -0
- package/dist/constants/prompts/index.js +78 -0
- package/dist/constants/prompts/index.js.map +7 -0
- package/dist/constants/prompts/taskManagement.js +60 -0
- package/dist/constants/prompts/taskManagement.js.map +7 -0
- package/dist/constants/prompts/toneAndStyle.js +62 -0
- package/dist/constants/prompts/toneAndStyle.js.map +7 -0
- package/dist/constants/prompts/toolUsagePolicy.js +38 -0
- package/dist/constants/prompts/toolUsagePolicy.js.map +7 -0
- package/dist/constants/prompts.js +5 -176
- package/dist/constants/prompts.js.map +2 -2
- package/dist/constants/providerRegistry.js +235 -0
- package/dist/constants/providerRegistry.js.map +7 -0
- package/dist/constants/providers.js +35 -0
- package/dist/constants/providers.js.map +7 -0
- package/dist/context/PermissionContext.js +0 -1
- package/dist/context/PermissionContext.js.map +2 -2
- package/dist/context.js +87 -31
- package/dist/context.js.map +2 -2
- package/dist/core/backupHook.js +2 -2
- package/dist/core/backupHook.js.map +2 -2
- package/dist/core/config/defaults.js +4 -1
- package/dist/core/config/defaults.js.map +2 -2
- package/dist/core/config/schema.js +7 -1
- package/dist/core/config/schema.js.map +2 -2
- package/dist/core/costTracker.js +18 -0
- package/dist/core/costTracker.js.map +2 -2
- package/dist/core/index.js +0 -1
- package/dist/core/index.js.map +2 -2
- package/dist/core/tokenStatsManager.js +22 -4
- package/dist/core/tokenStatsManager.js.map +2 -2
- package/dist/entrypoints/cli.js +65 -84
- package/dist/entrypoints/cli.js.map +2 -2
- package/dist/hooks/useAgentTokenStats.js +1 -1
- package/dist/hooks/useAgentTokenStats.js.map +2 -2
- package/dist/hooks/useAgentTranscripts.js +2 -1
- package/dist/hooks/useAgentTranscripts.js.map +2 -2
- package/dist/hooks/useBackgroundShells.js +29 -0
- package/dist/hooks/useBackgroundShells.js.map +7 -0
- package/dist/hooks/useCanUseTool.js +1 -1
- package/dist/hooks/useCanUseTool.js.map +2 -2
- package/dist/hooks/useDeferredLoading.js +64 -0
- package/dist/hooks/useDeferredLoading.js.map +7 -0
- package/dist/hooks/useHookStatus.js +1 -1
- package/dist/hooks/useHookStatus.js.map +2 -2
- package/dist/hooks/useSessionTracking.js +55 -0
- package/dist/hooks/useSessionTracking.js.map +7 -0
- package/dist/hooks/useTerminalSize.js +21 -0
- package/dist/hooks/useTerminalSize.js.map +2 -2
- package/dist/hooks/useTextInput.js +1 -0
- package/dist/hooks/useTextInput.js.map +2 -2
- package/dist/hooks/useUnifiedCompletion.js +3 -2
- package/dist/hooks/useUnifiedCompletion.js.map +2 -2
- package/dist/i18n/locales/en.js +8 -9
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +8 -9
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/messages.js +41 -17
- package/dist/messages.js.map +2 -2
- package/dist/permissions.js +94 -1
- package/dist/permissions.js.map +2 -2
- package/dist/query.js +27 -19
- package/dist/query.js.map +2 -2
- package/dist/screens/REPL.js +83 -74
- package/dist/screens/REPL.js.map +2 -2
- package/dist/services/adapters/responsesAPI.js +6 -0
- package/dist/services/adapters/responsesAPI.js.map +2 -2
- package/dist/services/agentTeams/index.js +35 -0
- package/dist/services/agentTeams/index.js.map +7 -0
- package/dist/services/agentTeams/mailbox.js +114 -0
- package/dist/services/agentTeams/mailbox.js.map +7 -0
- package/dist/services/agentTeams/teamManager.js +149 -0
- package/dist/services/agentTeams/teamManager.js.map +7 -0
- package/dist/services/agentTeams/teamTaskStore.js +114 -0
- package/dist/services/agentTeams/teamTaskStore.js.map +7 -0
- package/dist/services/agentTeams/teammateSpawner.js +80 -0
- package/dist/services/agentTeams/teammateSpawner.js.map +7 -0
- package/dist/services/checkpointManager.js +16 -3
- package/dist/services/checkpointManager.js.map +2 -2
- package/dist/services/claude.js +19 -1728
- package/dist/services/claude.js.map +3 -3
- package/dist/services/gpt5ConnectionTest.js +4 -2
- package/dist/services/gpt5ConnectionTest.js.map +2 -2
- package/dist/services/hookExecutor.js +411 -127
- package/dist/services/hookExecutor.js.map +2 -2
- package/dist/services/llm/anthropicProvider.js +807 -0
- package/dist/services/llm/anthropicProvider.js.map +7 -0
- package/dist/services/llm/dispatch.js +218 -0
- package/dist/services/llm/dispatch.js.map +7 -0
- package/dist/services/llm/index.js +44 -0
- package/dist/services/llm/index.js.map +7 -0
- package/dist/services/llm/mintoContext.js +69 -0
- package/dist/services/llm/mintoContext.js.map +7 -0
- package/dist/services/llm/openaiProvider.js +622 -0
- package/dist/services/llm/openaiProvider.js.map +7 -0
- package/dist/services/llm/types.js +157 -0
- package/dist/services/llm/types.js.map +7 -0
- package/dist/services/mcpClient.js +183 -33
- package/dist/services/mcpClient.js.map +2 -2
- package/dist/services/notifier.js +14 -0
- package/dist/services/notifier.js.map +2 -2
- package/dist/services/oauth.js +4 -2
- package/dist/services/oauth.js.map +2 -2
- package/dist/services/openai.js +66 -56
- package/dist/services/openai.js.map +3 -3
- package/dist/services/outputStyles.js +102 -21
- package/dist/services/outputStyles.js.map +2 -2
- package/dist/services/plugins/skillMarketplace.js +4 -1
- package/dist/services/plugins/skillMarketplace.js.map +2 -2
- package/dist/services/sentry.js +1 -1
- package/dist/services/sentry.js.map +2 -2
- package/dist/services/sessionMemory.js +16 -3
- package/dist/services/sessionMemory.js.map +2 -2
- package/dist/services/systemReminder.js +350 -3
- package/dist/services/systemReminder.js.map +2 -2
- package/dist/services/taskStore.js +19 -0
- package/dist/services/taskStore.js.map +2 -2
- package/dist/tools/ArchitectTool/ArchitectTool.js.map +1 -1
- package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +1 -1
- package/dist/tools/BashOutputTool/BashOutputTool.js.map +1 -1
- package/dist/tools/BashTool/BashTool.js +28 -0
- package/dist/tools/BashTool/BashTool.js.map +2 -2
- package/dist/tools/FileEditTool/FileEditTool.js +1 -1
- package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
- package/dist/tools/FileReadTool/FileReadTool.js +14 -0
- package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
- package/dist/tools/FileWriteTool/FileWriteTool.js +3 -1
- package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
- package/dist/tools/GlobTool/GlobTool.js.map +1 -1
- package/dist/tools/GrepTool/GrepTool.js.map +1 -1
- package/dist/tools/KillShellTool/KillShellTool.js.map +1 -1
- package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +2 -2
- package/dist/tools/LspTool/LspTool.js +11 -2
- package/dist/tools/LspTool/LspTool.js.map +2 -2
- package/dist/tools/MCPTool/MCPTool.js.map +1 -1
- package/dist/tools/MemoryReadTool/MemoryReadTool.js +2 -1
- package/dist/tools/MemoryReadTool/MemoryReadTool.js.map +2 -2
- package/dist/tools/MemoryWriteTool/MemoryWriteTool.js +2 -1
- package/dist/tools/MemoryWriteTool/MemoryWriteTool.js.map +2 -2
- package/dist/tools/MultiEditTool/MultiEditTool.js.map +1 -1
- package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +1 -1
- package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +1 -1
- package/dist/tools/PlanModeTool/EnterPlanModeTool.js +8 -2
- package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
- package/dist/tools/PlanModeTool/ExitPlanModeTool.js +2 -0
- package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
- package/dist/tools/ReadMcpResourceTool/ReadMcpResourceTool.js.map +1 -1
- package/dist/tools/SlashCommandTool/SlashCommandTool.js +174 -18
- package/dist/tools/SlashCommandTool/SlashCommandTool.js.map +3 -3
- package/dist/tools/TaskCreateTool/TaskCreateTool.js.map +1 -1
- package/dist/tools/TaskGetTool/TaskGetTool.js.map +1 -1
- package/dist/tools/TaskListTool/TaskListTool.js.map +1 -1
- package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +1 -1
- package/dist/tools/TaskStopTool/TaskStopTool.js.map +1 -1
- package/dist/tools/TaskTool/TaskTool.js +75 -5
- package/dist/tools/TaskTool/TaskTool.js.map +2 -2
- package/dist/tools/TaskTool/prompt.js +12 -6
- package/dist/tools/TaskTool/prompt.js.map +2 -2
- package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +1 -1
- package/dist/tools/ThinkTool/ThinkTool.js.map +1 -1
- package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +1 -1
- package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +1 -1
- package/dist/tools/WebSearchTool/WebSearchTool.js.map +1 -1
- package/dist/tools/WebSearchTool/searchProviders.js +2 -1
- package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
- package/dist/tools/lsTool/lsTool.js.map +2 -2
- package/dist/tools/lsTool/prompt.js.map +1 -1
- package/dist/tools.js +14 -3
- package/dist/tools.js.map +2 -2
- package/dist/types/PermissionMode.js +21 -1
- package/dist/types/PermissionMode.js.map +2 -2
- package/dist/types/agentTeams.js +1 -0
- package/dist/types/agentTeams.js.map +7 -0
- package/dist/types/hooks.js +8 -2
- package/dist/types/hooks.js.map +2 -2
- package/dist/types/plugin.js +1 -1
- package/dist/types/plugin.js.map +2 -2
- package/dist/utils/agentLoader.js +25 -3
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/animationManager.js +1 -1
- package/dist/utils/animationManager.js.map +2 -2
- package/dist/utils/ask.js +1 -1
- package/dist/utils/async.js +5 -1
- package/dist/utils/async.js.map +2 -2
- package/dist/utils/autoCompactCore.js +60 -0
- package/dist/utils/autoCompactCore.js.map +2 -2
- package/dist/utils/config.js +26 -128
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/configSchema.js +227 -0
- package/dist/utils/configSchema.js.map +7 -0
- package/dist/utils/debugLogger.js.map +2 -2
- package/dist/utils/env.js +4 -3
- package/dist/utils/env.js.map +2 -2
- package/dist/utils/envConfig.js +34 -0
- package/dist/utils/envConfig.js.map +3 -3
- package/dist/utils/gpt5.js +146 -0
- package/dist/utils/gpt5.js.map +7 -0
- package/dist/utils/hookManager.js +374 -140
- package/dist/utils/hookManager.js.map +2 -2
- package/dist/utils/markdown.js +47 -0
- package/dist/utils/markdown.js.map +2 -2
- package/dist/utils/memoizeWithTTL.js +25 -0
- package/dist/utils/memoizeWithTTL.js.map +7 -0
- package/dist/utils/model.js +34 -9
- package/dist/utils/model.js.map +2 -2
- package/dist/utils/pluginInstaller.js +34 -5
- package/dist/utils/pluginInstaller.js.map +2 -2
- package/dist/utils/pluginLoader.js +201 -32
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/safeFetch.js +45 -0
- package/dist/utils/safeFetch.js.map +7 -0
- package/dist/utils/skillLoader.js +59 -6
- package/dist/utils/skillLoader.js.map +2 -2
- package/dist/utils/streamingState.js +52 -0
- package/dist/utils/streamingState.js.map +7 -0
- package/dist/utils/style.js +6 -3
- package/dist/utils/style.js.map +2 -2
- package/dist/utils/teamConfig.js +9 -3
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/toolRiskClassification.js +0 -6
- package/dist/utils/toolRiskClassification.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/commands/mcp-interactive.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * Interactive MCP Server Management UI\n *\n * Uses TabbedListView for consistent UI matching /agents and /plugins.\n *\n * Phase flow:\n * main \u2192 (select server) \u2192 server-actions \u2192 (toggle|delete|back)\n * \u2193\n * confirm-delete \u2192 main\n * main \u2192 (Add Server) \u2192 add-name \u2192 add-command \u2192 main\n * main \u2192 (Refresh All) \u2192 main\n */\n\nimport React, { useState, useEffect, useCallback, useMemo } from 'react'\nimport { Command } from '@commands'\nimport {\n listMCPServers,\n getClients,\n refreshMCPConnections,\n getMcpServer,\n addMcpServer,\n removeMcpServer,\n type ScopedMcpServerConfig,\n} from '@services/mcpClient'\nimport {\n getCurrentProjectConfig,\n saveCurrentProjectConfig,\n} from '@utils/config'\nimport { TabbedListView } from '@components/TabbedListView/TabbedListView'\nimport type {\n ListItem,\n TabDefinition,\n StatusOverlay,\n} from '@components/TabbedListView/types'\nimport { t } from '@i18n'\n\n// =============================================================================\n// Types\n// =============================================================================\n\ntype ServerStatus = {\n name: string\n status: 'connected' | 'failed'\n config: ScopedMcpServerConfig\n}\n\ntype MCPPhase =\n | { type: 'main' }\n | { type: 'server-actions'; serverName: string; scope: string }\n | { type: 'confirm-delete'; serverName: string; scope: string }\n | { type: 'add-name' }\n | { type: 'add-command'; name: string }\n\n// Special item IDs for action entries in the list\nconst ID_ADD_SERVER = '__add__'\nconst ID_REFRESH_ALL = '__refresh__'\nconst ID_TOGGLE = '__toggle__'\nconst ID_DELETE = '__delete__'\nconst ID_BACK = '__back__'\nconst ID_CONFIRM_YES = '__yes__'\nconst ID_CONFIRM_NO = '__no__'\n\n// =============================================================================\n// Main Interactive Component\n// =============================================================================\n\nconst MCPInteractive: React.FC<{\n onDone: (result?: string) => void\n}> = ({ onDone }) => {\n const [phase, setPhase] = useState<MCPPhase>({ type: 'main' })\n const [activeTab, setActiveTab] = useState('all')\n const [searchQuery, setSearchQuery] = useState('')\n const [statusOverlay, setStatusOverlay] = useState<StatusOverlay | null>(null)\n const [servers, setServers] = useState<ServerStatus[]>([])\n const [loading, setLoading] = useState(true)\n\n // Load servers\n const loadServers = useCallback(async () => {\n try {\n const serverConfigs = listMCPServers()\n const clients = await getClients()\n\n const serverStatuses: ServerStatus[] = Object.entries(serverConfigs).map(\n ([name, _]) => {\n const client = clients.find(c => c.name === name)\n const config = getMcpServer(name)!\n return {\n name,\n status: client?.type === 'connected' ? 'connected' : 'failed',\n config,\n }\n },\n )\n\n setServers(serverStatuses)\n } catch {\n setServers([])\n }\n }, [])\n\n // Initial load\n useEffect(() => {\n setLoading(true)\n loadServers().finally(() => setLoading(false))\n }, [loadServers])\n\n // Reload on return to main\n useEffect(() => {\n if (phase.type === 'main') {\n loadServers()\n setSearchQuery('')\n }\n }, [phase, loadServers])\n\n // \u2500\u2500\u2500 Actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const toggleServer = useCallback(\n async (serverName: string) => {\n setStatusOverlay({\n type: 'loading',\n message: t('commands.mcp.toggling'),\n })\n try {\n const projectConfig = getCurrentProjectConfig()\n const currentServer = projectConfig.mcpServers?.[serverName]\n\n if (currentServer) {\n const newEnabled = !(currentServer.enabled ?? true)\n await saveCurrentProjectConfig({\n ...projectConfig,\n mcpServers: {\n ...projectConfig.mcpServers,\n [serverName]: { ...currentServer, enabled: newEnabled },\n },\n })\n } else {\n const serverConfig = getMcpServer(serverName)\n if (serverConfig) {\n await saveCurrentProjectConfig({\n ...projectConfig,\n mcpServers: {\n ...projectConfig.mcpServers,\n [serverName]: { ...serverConfig, enabled: false },\n },\n })\n }\n }\n\n refreshMCPConnections()\n await loadServers()\n setStatusOverlay({\n type: 'success',\n message: t('commands.mcp.toggleSuccess'),\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n setStatusOverlay({ type: 'error', message: msg })\n }\n },\n [loadServers],\n )\n\n const deleteServer = useCallback(\n async (serverName: string, scope: string) => {\n setStatusOverlay({\n type: 'loading',\n message: t('commands.mcp.deleting'),\n })\n try {\n removeMcpServer(serverName, scope as 'project' | 'global')\n refreshMCPConnections()\n await loadServers()\n setStatusOverlay({\n type: 'success',\n message: t('commands.mcp.deleteSuccess'),\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n setStatusOverlay({ type: 'error', message: msg })\n }\n },\n [loadServers],\n )\n\n const addNewServer = useCallback(\n async (name: string, commandOrUrl: string) => {\n setStatusOverlay({\n type: 'loading',\n message: t('commands.mcp.adding'),\n })\n try {\n if (\n commandOrUrl.startsWith('http://') ||\n commandOrUrl.startsWith('https://')\n ) {\n addMcpServer(name, { type: 'sse', url: commandOrUrl }, 'project')\n } else {\n const parts = commandOrUrl.split(/\\s+/)\n const command = parts[0]\n const args = parts.slice(1)\n addMcpServer(\n name,\n { type: 'stdio', command, args: args.length > 0 ? args : [] },\n 'project',\n )\n }\n refreshMCPConnections()\n await loadServers()\n setStatusOverlay({\n type: 'success',\n message: t('commands.mcp.addSuccess'),\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n setStatusOverlay({\n type: 'error',\n message: `${t('commands.mcp.addFailed')}: ${msg}`,\n })\n }\n },\n [loadServers],\n )\n\n const refreshAll = useCallback(async () => {\n setStatusOverlay({\n type: 'loading',\n message: t('commands.mcp.refreshing'),\n })\n try {\n refreshMCPConnections()\n await loadServers()\n setStatusOverlay({\n type: 'success',\n message: t('commands.mcp.refreshSuccess'),\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n setStatusOverlay({ type: 'error', message: msg })\n }\n }, [loadServers])\n\n // \u2500\u2500\u2500 Build tabs, items, title based on phase \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const { tabs, items, title, footerHint, searchEnabled, searchPlaceholder } =\n useMemo(() => {\n switch (phase.type) {\n case 'main': {\n const projectCount = servers.filter(\n s => s.config.scope === 'project',\n ).length\n const globalCount = servers.filter(\n s => s.config.scope === 'global',\n ).length\n\n const mainTabs: TabDefinition[] = [\n {\n id: 'all',\n label: t('commands.mcp.tabAll'),\n badge: servers.length,\n },\n {\n id: 'project',\n label: t('commands.mcp.tabProject'),\n badge: projectCount,\n },\n {\n id: 'global',\n label: t('commands.mcp.tabGlobal'),\n badge: globalCount,\n },\n ]\n\n // Filter servers by active tab\n const filtered =\n activeTab === 'all'\n ? servers\n : servers.filter(s => s.config.scope === activeTab)\n\n const mainItems: ListItem[] = filtered.map(server => {\n const isEnabled =\n 'enabled' in server.config ? server.config.enabled : true\n return {\n id: server.name,\n label: server.name,\n description: `${server.config.scope} \u00B7 ${server.config.type}`,\n status: !isEnabled\n ? 'disabled'\n : server.status === 'connected'\n ? 'enabled'\n : 'error',\n data: { type: 'server', server },\n }\n })\n\n // Action entries at the bottom\n mainItems.push({\n id: ID_ADD_SERVER,\n label: `+ ${t('commands.mcp.addServer')}`,\n data: { type: 'action' },\n })\n mainItems.push({\n id: ID_REFRESH_ALL,\n label: `\u21BB ${t('commands.mcp.refreshAll')}`,\n data: { type: 'action' },\n })\n\n return {\n tabs: mainTabs,\n items: mainItems,\n title: t('commands.mcp.title'),\n footerHint:\n '\u2191\u2193 Navigate \u00B7 Enter Select \u00B7 Tab Switch \u00B7 / Search \u00B7 Esc Exit',\n searchEnabled: true,\n searchPlaceholder: undefined,\n }\n }\n\n case 'server-actions': {\n const server = servers.find(s => s.name === phase.serverName)\n const isEnabled = server\n ? 'enabled' in server.config\n ? server.config.enabled\n : true\n : true\n\n const actionItems: ListItem[] = [\n {\n id: ID_TOGGLE,\n label: isEnabled\n ? t('commands.mcp.disable')\n : t('commands.mcp.enable'),\n },\n { id: ID_DELETE, label: t('commands.mcp.deleteServer') },\n { id: ID_BACK, label: `\u2190 ${t('commands.mcp.back')}` },\n ]\n\n return {\n tabs: [\n { id: 'actions', label: t('commands.mcp.actions') },\n ] as TabDefinition[],\n items: actionItems,\n title: phase.serverName,\n footerHint: '\u2191\u2193 Navigate \u00B7 Enter Select \u00B7 Esc Back',\n searchEnabled: false,\n searchPlaceholder: undefined,\n }\n }\n\n case 'confirm-delete': {\n const confirmItems: ListItem[] = [\n { id: ID_CONFIRM_YES, label: t('commands.mcp.confirmYes') },\n { id: ID_CONFIRM_NO, label: t('commands.mcp.confirmNo') },\n ]\n\n return {\n tabs: [\n { id: 'actions', label: t('commands.mcp.actions') },\n ] as TabDefinition[],\n items: confirmItems,\n title: t('commands.mcp.confirmDeleteTitle').replace(\n '{name}',\n phase.serverName,\n ),\n footerHint: '\u2191\u2193 Navigate \u00B7 Enter Select \u00B7 Esc Back',\n searchEnabled: false,\n searchPlaceholder: undefined,\n }\n }\n\n case 'add-name': {\n return {\n tabs: [\n { id: 'add', label: t('commands.mcp.addServer') },\n ] as TabDefinition[],\n items: [] as ListItem[],\n title: t('commands.mcp.addServer'),\n footerHint: t('commands.mcp.addFooterHint'),\n searchEnabled: true,\n searchPlaceholder: t('commands.mcp.addNamePlaceholder'),\n }\n }\n\n case 'add-command': {\n const exampleItems: ListItem[] = [\n {\n id: 'example-stdio',\n label: 'npx -y @modelcontextprotocol/server-xxx',\n description: 'stdio command',\n },\n {\n id: 'example-sse',\n label: 'https://example.com/mcp/sse',\n description: 'SSE endpoint',\n },\n ]\n\n return {\n tabs: [\n { id: 'add', label: t('commands.mcp.addServer') },\n ] as TabDefinition[],\n items: exampleItems,\n title: `${t('commands.mcp.addServer')}: ${phase.name}`,\n footerHint: t('commands.mcp.addCommandFooterHint'),\n searchEnabled: true,\n searchPlaceholder: t('commands.mcp.addCommandPlaceholder'),\n }\n }\n }\n }, [phase, activeTab, servers])\n\n // \u2500\u2500\u2500 Handle tab change \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleTabChange = useCallback((tabId: string) => {\n setActiveTab(tabId)\n setSearchQuery('')\n }, [])\n\n // \u2500\u2500\u2500 Handle status overlay dismiss \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleStatusDismiss = useCallback(() => {\n setStatusOverlay(null)\n setPhase({ type: 'main' })\n }, [])\n\n // \u2500\u2500\u2500 Handle ESC / back navigation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleBack = useCallback(() => {\n if (statusOverlay && statusOverlay.type !== 'loading') {\n handleStatusDismiss()\n return\n }\n switch (phase.type) {\n case 'main':\n onDone()\n break\n case 'server-actions':\n setPhase({ type: 'main' })\n setSearchQuery('')\n break\n case 'confirm-delete':\n setPhase({\n type: 'server-actions',\n serverName: phase.serverName,\n scope: phase.scope,\n })\n break\n case 'add-name':\n case 'add-command':\n setPhase({ type: 'main' })\n setSearchQuery('')\n break\n }\n }, [phase, statusOverlay, onDone, handleStatusDismiss])\n\n const handleClose = useCallback(() => {\n if (statusOverlay && statusOverlay.type !== 'loading') {\n handleStatusDismiss()\n return\n }\n if (phase.type === 'main') {\n onDone()\n } else {\n setPhase({ type: 'main' })\n setSearchQuery('')\n }\n }, [phase, statusOverlay, onDone, handleStatusDismiss])\n\n // \u2500\u2500\u2500 Handle search submit (for add-name / add-command) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleSearchSubmit = useCallback(\n async (query: string) => {\n if (!query.trim()) return\n\n if (phase.type === 'add-name') {\n setPhase({ type: 'add-command', name: query.trim() })\n setSearchQuery('')\n return\n }\n\n if (phase.type === 'add-command') {\n await addNewServer(phase.name, query.trim())\n setSearchQuery('')\n }\n },\n [phase, addNewServer],\n )\n\n // \u2500\u2500\u2500 Handle item selection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleSelect = useCallback(\n async (item: ListItem) => {\n switch (phase.type) {\n case 'main': {\n if (item.id === ID_ADD_SERVER) {\n setPhase({ type: 'add-name' })\n setSearchQuery('')\n return\n }\n if (item.id === ID_REFRESH_ALL) {\n await refreshAll()\n return\n }\n // Server selected \u2014 go to actions\n const data = item.data as { type: string; server?: ServerStatus }\n if (data.type === 'server' && data.server) {\n setPhase({\n type: 'server-actions',\n serverName: data.server.name,\n scope: data.server.config.scope,\n })\n }\n break\n }\n\n case 'server-actions': {\n if (item.id === ID_BACK) {\n setPhase({ type: 'main' })\n return\n }\n if (item.id === ID_TOGGLE) {\n await toggleServer(phase.serverName)\n return\n }\n if (item.id === ID_DELETE) {\n setPhase({\n type: 'confirm-delete',\n serverName: phase.serverName,\n scope: phase.scope,\n })\n }\n break\n }\n\n case 'confirm-delete': {\n if (item.id === ID_CONFIRM_YES) {\n await deleteServer(phase.serverName, phase.scope)\n } else {\n // No \u2014 back to actions\n setPhase({\n type: 'server-actions',\n serverName: phase.serverName,\n scope: phase.scope,\n })\n }\n break\n }\n\n case 'add-command': {\n // Clicking an example copies label to search\n setSearchQuery(item.label)\n break\n }\n }\n },\n [phase, refreshAll, toggleServer, deleteServer],\n )\n\n // \u2500\u2500\u2500 Determine current tab for rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const currentTab =\n phase.type === 'main'\n ? activeTab\n : phase.type === 'server-actions' || phase.type === 'confirm-delete'\n ? 'actions'\n : 'add'\n\n // \u2500\u2500\u2500 Determine if search submit is needed \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const needsSearchSubmit =\n phase.type === 'add-name' || phase.type === 'add-command'\n\n return (\n <TabbedListView\n title={title}\n tabs={tabs}\n activeTab={currentTab}\n onTabChange={handleTabChange}\n items={items}\n searchEnabled={searchEnabled}\n searchPlaceholder={searchPlaceholder}\n searchQuery={searchQuery}\n onSearchChange={setSearchQuery}\n onSearchSubmit={needsSearchSubmit ? handleSearchSubmit : undefined}\n onSelect={handleSelect}\n onClose={handleClose}\n onBack={handleBack}\n statusOverlay={statusOverlay}\n footerHint={footerHint}\n groupByCategory={false}\n isLoading={loading}\n emptyText={t('commands.mcp.noServers')}\n statusDismissHint=\"Enter Continue \u00B7 Esc Back\"\n />\n )\n}\n\n// =============================================================================\n// Command Definition\n// =============================================================================\n\nconst mcpInteractive = {\n type: 'local-jsx',\n name: 'mcp',\n description: t('commands.mcp.description'),\n isEnabled: true,\n isHidden: false,\n hidePromptInput: true,\n async call(onDone: (result?: string) => void) {\n return <MCPInteractive onDone={onDone} />\n },\n userFacingName() {\n return 'mcp'\n },\n} satisfies Command\n\nexport default mcpInteractive\n"],
|
|
5
|
-
"mappings": "AAaA,OAAO,SAAS,UAAU,WAAW,aAAa,eAAe;AAEjE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,sBAAsB;AAM/B,SAAS,SAAS;AAoBlB,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AACvB,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,UAAU;AAChB,MAAM,iBAAiB;AACvB,MAAM,gBAAgB;AAMtB,MAAM,iBAED,CAAC,EAAE,OAAO,MAAM;AACnB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAmB,EAAE,MAAM,OAAO,CAAC;AAC7D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,EAAE;AACjD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAA+B,IAAI;AAC7E,QAAM,CAAC,SAAS,UAAU,IAAI,SAAyB,CAAC,CAAC;AACzD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAG3C,QAAM,cAAc,YAAY,YAAY;AAC1C,QAAI;AACF,YAAM,gBAAgB,eAAe;AACrC,YAAM,UAAU,MAAM,WAAW;AAEjC,YAAM,iBAAiC,OAAO,QAAQ,aAAa,EAAE;AAAA,QACnE,CAAC,CAAC,MAAM,CAAC,MAAM;AACb,gBAAM,SAAS,QAAQ,KAAK,OAAK,EAAE,SAAS,IAAI;AAChD,gBAAM,SAAS,aAAa,IAAI;AAChC,iBAAO;AAAA,YACL;AAAA,YACA,QAAQ,QAAQ,SAAS,cAAc,cAAc;AAAA,YACrD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,iBAAW,cAAc;AAAA,IAC3B,QAAQ;AACN,iBAAW,CAAC,CAAC;AAAA,IACf;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,YAAU,MAAM;AACd,eAAW,IAAI;AACf,gBAAY,EAAE,QAAQ,MAAM,WAAW,KAAK,CAAC;AAAA,EAC/C,GAAG,CAAC,WAAW,CAAC;AAGhB,YAAU,MAAM;AACd,QAAI,MAAM,SAAS,QAAQ;AACzB,kBAAY;AACZ,qBAAe,EAAE;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,OAAO,WAAW,CAAC;AAIvB,QAAM,eAAe;AAAA,IACnB,OAAO,eAAuB;AAC5B,uBAAiB;AAAA,QACf,MAAM;AAAA,QACN,SAAS,EAAE,uBAAuB;AAAA,MACpC,CAAC;AACD,UAAI;AACF,cAAM,gBAAgB,wBAAwB;AAC9C,cAAM,gBAAgB,cAAc,aAAa,UAAU;AAE3D,YAAI,eAAe;AACjB,gBAAM,aAAa,EAAE,cAAc,WAAW;AAC9C,gBAAM,yBAAyB;AAAA,YAC7B,GAAG;AAAA,YACH,YAAY;AAAA,cACV,GAAG,cAAc;AAAA,cACjB,CAAC,UAAU,GAAG,EAAE,GAAG,eAAe,SAAS,WAAW;AAAA,YACxD;AAAA,UACF,CAAC;AAAA,QACH,OAAO;AACL,gBAAM,eAAe,aAAa,UAAU;AAC5C,cAAI,cAAc;AAChB,kBAAM,yBAAyB;AAAA,cAC7B,GAAG;AAAA,cACH,YAAY;AAAA,gBACV,GAAG,cAAc;AAAA,gBACjB,CAAC,UAAU,GAAG,EAAE,GAAG,cAAc,SAAS,MAAM;AAAA,cAClD;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAEA,
|
|
4
|
+
"sourcesContent": ["/**\n * Interactive MCP Server Management UI\n *\n * Uses TabbedListView for consistent UI matching /agents and /plugins.\n *\n * Phase flow:\n * main \u2192 (select server) \u2192 server-actions \u2192 (toggle|delete|back)\n * \u2193\n * confirm-delete \u2192 main\n * main \u2192 (Add Server) \u2192 add-name \u2192 add-command \u2192 main\n * main \u2192 (Refresh All) \u2192 main\n */\n\nimport React, { useState, useEffect, useCallback, useMemo } from 'react'\nimport { Command } from '@commands'\nimport {\n listMCPServers,\n getClients,\n refreshMCPConnections,\n getMcpServer,\n addMcpServer,\n removeMcpServer,\n type ScopedMcpServerConfig,\n} from '@services/mcpClient'\nimport {\n getCurrentProjectConfig,\n saveCurrentProjectConfig,\n} from '@utils/config'\nimport { TabbedListView } from '@components/TabbedListView/TabbedListView'\nimport type {\n ListItem,\n TabDefinition,\n StatusOverlay,\n} from '@components/TabbedListView/types'\nimport { t } from '@i18n'\n\n// =============================================================================\n// Types\n// =============================================================================\n\ntype ServerStatus = {\n name: string\n status: 'connected' | 'failed'\n config: ScopedMcpServerConfig\n}\n\ntype MCPPhase =\n | { type: 'main' }\n | { type: 'server-actions'; serverName: string; scope: string }\n | { type: 'confirm-delete'; serverName: string; scope: string }\n | { type: 'add-name' }\n | { type: 'add-command'; name: string }\n\n// Special item IDs for action entries in the list\nconst ID_ADD_SERVER = '__add__'\nconst ID_REFRESH_ALL = '__refresh__'\nconst ID_TOGGLE = '__toggle__'\nconst ID_DELETE = '__delete__'\nconst ID_BACK = '__back__'\nconst ID_CONFIRM_YES = '__yes__'\nconst ID_CONFIRM_NO = '__no__'\n\n// =============================================================================\n// Main Interactive Component\n// =============================================================================\n\nconst MCPInteractive: React.FC<{\n onDone: (result?: string) => void\n}> = ({ onDone }) => {\n const [phase, setPhase] = useState<MCPPhase>({ type: 'main' })\n const [activeTab, setActiveTab] = useState('all')\n const [searchQuery, setSearchQuery] = useState('')\n const [statusOverlay, setStatusOverlay] = useState<StatusOverlay | null>(null)\n const [servers, setServers] = useState<ServerStatus[]>([])\n const [loading, setLoading] = useState(true)\n\n // Load servers\n const loadServers = useCallback(async () => {\n try {\n const serverConfigs = listMCPServers()\n const clients = await getClients()\n\n const serverStatuses: ServerStatus[] = Object.entries(serverConfigs).map(\n ([name, _]) => {\n const client = clients.find(c => c.name === name)\n const config = getMcpServer(name)!\n return {\n name,\n status: client?.type === 'connected' ? 'connected' : 'failed',\n config,\n }\n },\n )\n\n setServers(serverStatuses)\n } catch {\n setServers([])\n }\n }, [])\n\n // Initial load\n useEffect(() => {\n setLoading(true)\n loadServers().finally(() => setLoading(false))\n }, [loadServers])\n\n // Reload on return to main\n useEffect(() => {\n if (phase.type === 'main') {\n loadServers()\n setSearchQuery('')\n }\n }, [phase, loadServers])\n\n // \u2500\u2500\u2500 Actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const toggleServer = useCallback(\n async (serverName: string) => {\n setStatusOverlay({\n type: 'loading',\n message: t('commands.mcp.toggling'),\n })\n try {\n const projectConfig = getCurrentProjectConfig()\n const currentServer = projectConfig.mcpServers?.[serverName]\n\n if (currentServer) {\n const newEnabled = !(currentServer.enabled ?? true)\n await saveCurrentProjectConfig({\n ...projectConfig,\n mcpServers: {\n ...projectConfig.mcpServers,\n [serverName]: { ...currentServer, enabled: newEnabled },\n },\n })\n } else {\n const serverConfig = getMcpServer(serverName)\n if (serverConfig) {\n await saveCurrentProjectConfig({\n ...projectConfig,\n mcpServers: {\n ...projectConfig.mcpServers,\n [serverName]: { ...serverConfig, enabled: false },\n },\n })\n }\n }\n\n await refreshMCPConnections()\n await loadServers()\n setStatusOverlay({\n type: 'success',\n message: t('commands.mcp.toggleSuccess'),\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n setStatusOverlay({ type: 'error', message: msg })\n }\n },\n [loadServers],\n )\n\n const deleteServer = useCallback(\n async (serverName: string, scope: string) => {\n setStatusOverlay({\n type: 'loading',\n message: t('commands.mcp.deleting'),\n })\n try {\n removeMcpServer(serverName, scope as 'project' | 'global')\n await refreshMCPConnections()\n await loadServers()\n setStatusOverlay({\n type: 'success',\n message: t('commands.mcp.deleteSuccess'),\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n setStatusOverlay({ type: 'error', message: msg })\n }\n },\n [loadServers],\n )\n\n const addNewServer = useCallback(\n async (name: string, commandOrUrl: string) => {\n setStatusOverlay({\n type: 'loading',\n message: t('commands.mcp.adding'),\n })\n try {\n if (\n commandOrUrl.startsWith('http://') ||\n commandOrUrl.startsWith('https://')\n ) {\n addMcpServer(name, { type: 'sse', url: commandOrUrl }, 'project')\n } else {\n const parts = commandOrUrl.split(/\\s+/)\n const command = parts[0]\n const args = parts.slice(1)\n addMcpServer(\n name,\n { type: 'stdio', command, args: args.length > 0 ? args : [] },\n 'project',\n )\n }\n await refreshMCPConnections()\n await loadServers()\n setStatusOverlay({\n type: 'success',\n message: t('commands.mcp.addSuccess'),\n })\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n setStatusOverlay({\n type: 'error',\n message: `${t('commands.mcp.addFailed')}: ${msg}`,\n })\n }\n },\n [loadServers],\n )\n\n const refreshAll = useCallback(async () => {\n setStatusOverlay({\n type: 'loading',\n message: t('commands.mcp.refreshing'),\n })\n try {\n await refreshMCPConnections()\n await loadServers()\n // Clear overlay directly \u2014 no success screen needed for refresh\n setStatusOverlay(null)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n setStatusOverlay({ type: 'error', message: msg })\n }\n }, [loadServers])\n\n // \u2500\u2500\u2500 Build tabs, items, title based on phase \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const { tabs, items, title, footerHint, searchEnabled, searchPlaceholder } =\n useMemo(() => {\n switch (phase.type) {\n case 'main': {\n const projectCount = servers.filter(\n s => s.config.scope === 'project',\n ).length\n const globalCount = servers.filter(\n s => s.config.scope === 'global',\n ).length\n\n const mainTabs: TabDefinition[] = [\n {\n id: 'all',\n label: t('commands.mcp.tabAll'),\n badge: servers.length,\n },\n {\n id: 'project',\n label: t('commands.mcp.tabProject'),\n badge: projectCount,\n },\n {\n id: 'global',\n label: t('commands.mcp.tabGlobal'),\n badge: globalCount,\n },\n ]\n\n // Filter servers by active tab\n const filtered =\n activeTab === 'all'\n ? servers\n : servers.filter(s => s.config.scope === activeTab)\n\n const mainItems: ListItem[] = filtered.map(server => {\n const isEnabled =\n 'enabled' in server.config ? server.config.enabled : true\n return {\n id: server.name,\n label: server.name,\n description: `${server.config.scope} \u00B7 ${server.config.type}`,\n status: !isEnabled\n ? 'disabled'\n : server.status === 'connected'\n ? 'enabled'\n : 'error',\n data: { type: 'server', server },\n }\n })\n\n // Action entries at the bottom\n mainItems.push({\n id: ID_ADD_SERVER,\n label: `+ ${t('commands.mcp.addServer')}`,\n data: { type: 'action' },\n })\n mainItems.push({\n id: ID_REFRESH_ALL,\n label: `\u21BB ${t('commands.mcp.refreshAll')}`,\n data: { type: 'action' },\n })\n\n return {\n tabs: mainTabs,\n items: mainItems,\n title: t('commands.mcp.title'),\n footerHint:\n '\u2191\u2193 Navigate \u00B7 Enter Select \u00B7 Tab Switch \u00B7 / Search \u00B7 Esc Exit',\n searchEnabled: true,\n searchPlaceholder: undefined,\n }\n }\n\n case 'server-actions': {\n const server = servers.find(s => s.name === phase.serverName)\n const isEnabled = server\n ? 'enabled' in server.config\n ? server.config.enabled\n : true\n : true\n\n const actionItems: ListItem[] = [\n {\n id: ID_TOGGLE,\n label: isEnabled\n ? t('commands.mcp.disable')\n : t('commands.mcp.enable'),\n },\n { id: ID_DELETE, label: t('commands.mcp.deleteServer') },\n { id: ID_BACK, label: `\u2190 ${t('commands.mcp.back')}` },\n ]\n\n return {\n tabs: [\n { id: 'actions', label: t('commands.mcp.actions') },\n ] as TabDefinition[],\n items: actionItems,\n title: phase.serverName,\n footerHint: '\u2191\u2193 Navigate \u00B7 Enter Select \u00B7 Esc Back',\n searchEnabled: false,\n searchPlaceholder: undefined,\n }\n }\n\n case 'confirm-delete': {\n const confirmItems: ListItem[] = [\n { id: ID_CONFIRM_YES, label: t('commands.mcp.confirmYes') },\n { id: ID_CONFIRM_NO, label: t('commands.mcp.confirmNo') },\n ]\n\n return {\n tabs: [\n { id: 'actions', label: t('commands.mcp.actions') },\n ] as TabDefinition[],\n items: confirmItems,\n title: t('commands.mcp.confirmDeleteTitle').replace(\n '{name}',\n phase.serverName,\n ),\n footerHint: '\u2191\u2193 Navigate \u00B7 Enter Select \u00B7 Esc Back',\n searchEnabled: false,\n searchPlaceholder: undefined,\n }\n }\n\n case 'add-name': {\n return {\n tabs: [\n { id: 'add', label: t('commands.mcp.addServer') },\n ] as TabDefinition[],\n items: [] as ListItem[],\n title: t('commands.mcp.addServer'),\n footerHint: t('commands.mcp.addFooterHint'),\n searchEnabled: true,\n searchPlaceholder: t('commands.mcp.addNamePlaceholder'),\n }\n }\n\n case 'add-command': {\n const exampleItems: ListItem[] = [\n {\n id: 'example-stdio',\n label: 'npx -y @modelcontextprotocol/server-xxx',\n description: 'stdio command',\n },\n {\n id: 'example-sse',\n label: 'https://example.com/mcp/sse',\n description: 'SSE endpoint',\n },\n ]\n\n return {\n tabs: [\n { id: 'add', label: t('commands.mcp.addServer') },\n ] as TabDefinition[],\n items: exampleItems,\n title: `${t('commands.mcp.addServer')}: ${phase.name}`,\n footerHint: t('commands.mcp.addCommandFooterHint'),\n searchEnabled: true,\n searchPlaceholder: t('commands.mcp.addCommandPlaceholder'),\n }\n }\n default:\n return {\n tabs: [],\n items: [],\n title: '',\n footerHint: '',\n searchEnabled: false,\n searchPlaceholder: undefined,\n }\n }\n }, [phase, activeTab, servers])\n\n // \u2500\u2500\u2500 Handle tab change \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleTabChange = useCallback((tabId: string) => {\n setActiveTab(tabId)\n setSearchQuery('')\n }, [])\n\n // \u2500\u2500\u2500 Handle status overlay dismiss \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleStatusDismiss = useCallback(() => {\n setStatusOverlay(null)\n setPhase({ type: 'main' })\n }, [])\n\n // \u2500\u2500\u2500 Handle ESC / back navigation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleBack = useCallback(() => {\n if (statusOverlay && statusOverlay.type !== 'loading') {\n handleStatusDismiss()\n return\n }\n switch (phase.type) {\n case 'main':\n onDone()\n break\n case 'server-actions':\n setPhase({ type: 'main' })\n setSearchQuery('')\n break\n case 'confirm-delete':\n setPhase({\n type: 'server-actions',\n serverName: phase.serverName,\n scope: phase.scope,\n })\n break\n case 'add-name':\n case 'add-command':\n setPhase({ type: 'main' })\n setSearchQuery('')\n break\n }\n }, [phase, statusOverlay, onDone, handleStatusDismiss])\n\n const handleClose = useCallback(() => {\n if (statusOverlay && statusOverlay.type !== 'loading') {\n handleStatusDismiss()\n return\n }\n if (phase.type === 'main') {\n onDone()\n } else {\n setPhase({ type: 'main' })\n setSearchQuery('')\n }\n }, [phase, statusOverlay, onDone, handleStatusDismiss])\n\n // \u2500\u2500\u2500 Handle search submit (for add-name / add-command) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleSearchSubmit = useCallback(\n async (query: string) => {\n if (!query.trim()) return\n\n if (phase.type === 'add-name') {\n setPhase({ type: 'add-command', name: query.trim() })\n setSearchQuery('')\n return\n }\n\n if (phase.type === 'add-command') {\n await addNewServer(phase.name, query.trim())\n setSearchQuery('')\n }\n },\n [phase, addNewServer],\n )\n\n // \u2500\u2500\u2500 Handle item selection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleSelect = useCallback(\n async (item: ListItem) => {\n switch (phase.type) {\n case 'main': {\n if (item.id === ID_ADD_SERVER) {\n setPhase({ type: 'add-name' })\n setSearchQuery('')\n return\n }\n if (item.id === ID_REFRESH_ALL) {\n await refreshAll()\n return\n }\n // Server selected \u2014 go to actions\n const data = item.data as { type: string; server?: ServerStatus }\n if (data.type === 'server' && data.server) {\n setPhase({\n type: 'server-actions',\n serverName: data.server.name,\n scope: data.server.config.scope,\n })\n }\n break\n }\n\n case 'server-actions': {\n if (item.id === ID_BACK) {\n setPhase({ type: 'main' })\n return\n }\n if (item.id === ID_TOGGLE) {\n await toggleServer(phase.serverName)\n return\n }\n if (item.id === ID_DELETE) {\n setPhase({\n type: 'confirm-delete',\n serverName: phase.serverName,\n scope: phase.scope,\n })\n }\n break\n }\n\n case 'confirm-delete': {\n if (item.id === ID_CONFIRM_YES) {\n await deleteServer(phase.serverName, phase.scope)\n } else {\n // No \u2014 back to actions\n setPhase({\n type: 'server-actions',\n serverName: phase.serverName,\n scope: phase.scope,\n })\n }\n break\n }\n\n case 'add-command': {\n // Clicking an example copies label to search\n setSearchQuery(item.label)\n break\n }\n }\n },\n [phase, refreshAll, toggleServer, deleteServer],\n )\n\n // \u2500\u2500\u2500 Determine current tab for rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const currentTab =\n phase.type === 'main'\n ? activeTab\n : phase.type === 'server-actions' || phase.type === 'confirm-delete'\n ? 'actions'\n : 'add'\n\n // \u2500\u2500\u2500 Determine if search submit is needed \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const needsSearchSubmit =\n phase.type === 'add-name' || phase.type === 'add-command'\n\n return (\n <TabbedListView\n title={title}\n tabs={tabs}\n activeTab={currentTab}\n onTabChange={handleTabChange}\n items={items}\n searchEnabled={searchEnabled}\n searchPlaceholder={searchPlaceholder}\n searchQuery={searchQuery}\n onSearchChange={setSearchQuery}\n onSearchSubmit={needsSearchSubmit ? handleSearchSubmit : undefined}\n onSelect={handleSelect}\n onClose={handleClose}\n onBack={handleBack}\n statusOverlay={statusOverlay}\n footerHint={footerHint}\n groupByCategory={false}\n isLoading={loading}\n emptyText={t('commands.mcp.noServers')}\n statusDismissHint=\"Enter Continue \u00B7 Esc Back\"\n />\n )\n}\n\n// =============================================================================\n// Command Definition\n// =============================================================================\n\nconst mcpInteractive = {\n type: 'local-jsx',\n name: 'mcp',\n description: t('commands.mcp.description'),\n isEnabled: true,\n isHidden: false,\n hidePromptInput: true,\n async call(onDone: (result?: string) => void) {\n return <MCPInteractive onDone={onDone} />\n },\n userFacingName() {\n return 'mcp'\n },\n} satisfies Command\n\nexport default mcpInteractive\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { existsSync, writeFileSync, mkdirSync, openSync, closeSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { execSync, spawnSync } from "child_process";
|
|
4
|
+
import { platform as osPlatform, userInfo } from "os";
|
|
5
|
+
import * as net from "net";
|
|
6
|
+
import { getCwd } from "../utils/state.js";
|
|
7
|
+
import { PRODUCT_NAME } from "../constants/product.js";
|
|
8
|
+
const TEMPLATE = `# ${PRODUCT_NAME} Project Instructions
|
|
9
|
+
|
|
10
|
+
<!-- Add project-specific instructions here. These will be included in every conversation. -->
|
|
11
|
+
|
|
12
|
+
## Project Overview
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## Coding Conventions
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Important Notes
|
|
19
|
+
|
|
20
|
+
`;
|
|
21
|
+
function getAiTerSocketPath() {
|
|
22
|
+
if (osPlatform() === "win32") {
|
|
23
|
+
const username = userInfo().username;
|
|
24
|
+
return `\\\\.\\pipe\\aiter-${username}`;
|
|
25
|
+
}
|
|
26
|
+
const uid = process.getuid?.() ?? userInfo().uid;
|
|
27
|
+
return `/tmp/aiter-${uid}.sock`;
|
|
28
|
+
}
|
|
29
|
+
function isAiTerRunning() {
|
|
30
|
+
const socketPath = getAiTerSocketPath();
|
|
31
|
+
if (osPlatform() !== "win32" && !existsSync(socketPath)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
function openInAiTer(filePath) {
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
const socketPath = getAiTerSocketPath();
|
|
39
|
+
const request = JSON.stringify({
|
|
40
|
+
command: "editor",
|
|
41
|
+
action: "open",
|
|
42
|
+
params: { filePath, mode: "edit" }
|
|
43
|
+
});
|
|
44
|
+
const socket = net.createConnection(socketPath);
|
|
45
|
+
let buffer = "";
|
|
46
|
+
const timeout = setTimeout(() => {
|
|
47
|
+
socket.destroy();
|
|
48
|
+
resolve(false);
|
|
49
|
+
}, 3e3);
|
|
50
|
+
socket.on("connect", () => {
|
|
51
|
+
socket.write(request + "\n");
|
|
52
|
+
});
|
|
53
|
+
socket.on("data", (chunk) => {
|
|
54
|
+
buffer += chunk.toString();
|
|
55
|
+
try {
|
|
56
|
+
const response = JSON.parse(buffer);
|
|
57
|
+
clearTimeout(timeout);
|
|
58
|
+
socket.end();
|
|
59
|
+
resolve(response.success === true);
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
socket.on("error", () => {
|
|
64
|
+
clearTimeout(timeout);
|
|
65
|
+
resolve(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const TERMINAL_EDITORS = /^(vi|vim|nvim|nano|emacs|pico|ed|micro|helix|hx|kak)$/i;
|
|
70
|
+
function openInTerminalEditor(editor, filePath) {
|
|
71
|
+
let ttyFd;
|
|
72
|
+
try {
|
|
73
|
+
ttyFd = openSync("/dev/tty", "r");
|
|
74
|
+
} catch {
|
|
75
|
+
ttyFd = void 0;
|
|
76
|
+
}
|
|
77
|
+
if (typeof process.stdin.setRawMode === "function") {
|
|
78
|
+
process.stdin.setRawMode(false);
|
|
79
|
+
}
|
|
80
|
+
process.stdin.pause();
|
|
81
|
+
try {
|
|
82
|
+
const result = spawnSync(editor, [filePath], {
|
|
83
|
+
stdio: [ttyFd ?? "inherit", "inherit", "inherit"],
|
|
84
|
+
shell: false
|
|
85
|
+
});
|
|
86
|
+
if (result.error) {
|
|
87
|
+
throw new Error(`Failed to open editor: ${result.error.message}`);
|
|
88
|
+
}
|
|
89
|
+
} finally {
|
|
90
|
+
if (ttyFd !== void 0) {
|
|
91
|
+
try {
|
|
92
|
+
closeSync(ttyFd);
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
process.stdin.resume();
|
|
97
|
+
if (typeof process.stdin.setRawMode === "function") {
|
|
98
|
+
process.stdin.setRawMode(true);
|
|
99
|
+
}
|
|
100
|
+
process.stdout.emit("resize");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function getPlatformDefaultEditor() {
|
|
104
|
+
if (osPlatform() === "win32") return "notepad";
|
|
105
|
+
return "vi";
|
|
106
|
+
}
|
|
107
|
+
const memory = {
|
|
108
|
+
name: "memory",
|
|
109
|
+
description: "Open project instructions file (MINTO.md) in your editor",
|
|
110
|
+
isEnabled: true,
|
|
111
|
+
isHidden: false,
|
|
112
|
+
type: "local",
|
|
113
|
+
userFacingName() {
|
|
114
|
+
return "memory";
|
|
115
|
+
},
|
|
116
|
+
async call() {
|
|
117
|
+
const cwd = getCwd();
|
|
118
|
+
let filePath = join(cwd, "MINTO.md");
|
|
119
|
+
if (!existsSync(filePath)) {
|
|
120
|
+
const agentsPath = join(cwd, "AGENTS.md");
|
|
121
|
+
if (existsSync(agentsPath)) {
|
|
122
|
+
filePath = agentsPath;
|
|
123
|
+
} else {
|
|
124
|
+
const claudePath = join(cwd, "CLAUDE.md");
|
|
125
|
+
if (existsSync(claudePath)) {
|
|
126
|
+
filePath = claudePath;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (!existsSync(filePath)) {
|
|
131
|
+
const dir = dirname(filePath);
|
|
132
|
+
if (!existsSync(dir)) {
|
|
133
|
+
mkdirSync(dir, { recursive: true });
|
|
134
|
+
}
|
|
135
|
+
writeFileSync(filePath, TEMPLATE, "utf-8");
|
|
136
|
+
}
|
|
137
|
+
if (isAiTerRunning()) {
|
|
138
|
+
try {
|
|
139
|
+
const success = await openInAiTer(filePath);
|
|
140
|
+
if (success) {
|
|
141
|
+
return `Opened ${filePath} in AiTer editor. Changes will take effect in the next conversation.`;
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const envEditor = process.env.VISUAL || process.env.EDITOR;
|
|
147
|
+
const editor = envEditor || getPlatformDefaultEditor();
|
|
148
|
+
const editorBin = editor.split("/").pop() || editor;
|
|
149
|
+
try {
|
|
150
|
+
if (TERMINAL_EDITORS.test(editorBin)) {
|
|
151
|
+
openInTerminalEditor(editor, filePath);
|
|
152
|
+
} else {
|
|
153
|
+
execSync(`${editor} ${JSON.stringify(filePath)}`, {
|
|
154
|
+
stdio: "ignore",
|
|
155
|
+
timeout: 5e3
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return `Opened ${filePath} in ${editorBin}. Changes will take effect in the next conversation.`;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return `Failed to open editor (${editorBin}): ${error instanceof Error ? error.message : String(error)}`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
var memory_default = memory;
|
|
165
|
+
export {
|
|
166
|
+
memory_default as default
|
|
167
|
+
};
|
|
168
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/commands/memory.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * /memory command\n *\n * Opens MINTO.md (or CLAUDE.md) for editing.\n *\n * Editor selection priority:\n * 1. AiTer (if running) \u2014 best experience, uses AiTer's built-in editor\n * 2. $VISUAL / $EDITOR (if set) \u2014 respects user preference\n * 3. Platform default \u2014 vi (macOS/Linux), notepad (Windows)\n *\n * For terminal editors (vi/vim/nano/emacs), properly releases Ink's TTY\n * and restores it after the editor exits.\n */\n\nimport { existsSync, writeFileSync, mkdirSync, openSync, closeSync } from 'fs'\nimport { join, dirname } from 'path'\nimport { execSync, spawnSync } from 'child_process'\nimport { platform as osPlatform, userInfo } from 'os'\nimport * as net from 'net'\nimport { getCwd } from '@utils/state'\nimport { PRODUCT_NAME } from '@constants/product'\nimport type { Command } from '../commands'\n\nconst TEMPLATE = `# ${PRODUCT_NAME} Project Instructions\n\n<!-- Add project-specific instructions here. These will be included in every conversation. -->\n\n## Project Overview\n\n\n## Coding Conventions\n\n\n## Important Notes\n\n`\n\n// \u2500\u2500\u2500 AiTer Integration \u2500\u2500\u2500\n\n/**\n * Get AiTer CLI socket path (must match AiTer's getSocketPath()).\n */\nfunction getAiTerSocketPath(): string {\n if (osPlatform() === 'win32') {\n const username = userInfo().username\n return `\\\\\\\\.\\\\pipe\\\\aiter-${username}`\n }\n const uid = process.getuid?.() ?? userInfo().uid\n return `/tmp/aiter-${uid}.sock`\n}\n\n/**\n * Check if AiTer is running by testing the socket.\n */\nfunction isAiTerRunning(): boolean {\n const socketPath = getAiTerSocketPath()\n if (osPlatform() !== 'win32' && !existsSync(socketPath)) {\n return false\n }\n return true\n}\n\n/**\n * Open a file in AiTer's editor via CLI socket.\n * Returns true on success, false on failure.\n */\nfunction openInAiTer(filePath: string): Promise<boolean> {\n return new Promise(resolve => {\n const socketPath = getAiTerSocketPath()\n const request = JSON.stringify({\n command: 'editor',\n action: 'open',\n params: { filePath, mode: 'edit' },\n })\n\n const socket = net.createConnection(socketPath)\n let buffer = ''\n\n const timeout = setTimeout(() => {\n socket.destroy()\n resolve(false)\n }, 3000)\n\n socket.on('connect', () => {\n socket.write(request + '\\n')\n })\n\n socket.on('data', chunk => {\n buffer += chunk.toString()\n try {\n const response = JSON.parse(buffer)\n clearTimeout(timeout)\n socket.end()\n resolve(response.success === true)\n } catch {\n // Incomplete JSON, wait for more data\n }\n })\n\n socket.on('error', () => {\n clearTimeout(timeout)\n resolve(false)\n })\n })\n}\n\n// \u2500\u2500\u2500 Terminal Editor (TTY-safe) \u2500\u2500\u2500\n\nconst TERMINAL_EDITORS = /^(vi|vim|nvim|nano|emacs|pico|ed|micro|helix|hx|kak)$/i\n\n/**\n * Open file in a terminal editor with proper TTY management.\n * Releases Ink's raw mode, blocks until editor exits, restores state.\n */\nfunction openInTerminalEditor(editor: string, filePath: string): void {\n // Open /dev/tty for keyboard input (bypasses Ink's stdin)\n let ttyFd: number | undefined\n try {\n ttyFd = openSync('/dev/tty', 'r')\n } catch {\n ttyFd = undefined\n }\n\n // Release Ink's raw mode\n if (typeof process.stdin.setRawMode === 'function') {\n process.stdin.setRawMode(false)\n }\n process.stdin.pause()\n\n try {\n const result = spawnSync(editor, [filePath], {\n stdio: [ttyFd ?? 'inherit', 'inherit', 'inherit'],\n shell: false,\n })\n if (result.error) {\n throw new Error(`Failed to open editor: ${result.error.message}`)\n }\n } finally {\n if (ttyFd !== undefined) {\n try {\n closeSync(ttyFd)\n } catch {\n // Ignore\n }\n }\n // Restore Ink state\n process.stdin.resume()\n if (typeof process.stdin.setRawMode === 'function') {\n process.stdin.setRawMode(true)\n }\n process.stdout.emit('resize')\n }\n}\n\n// \u2500\u2500\u2500 Platform Default Editors \u2500\u2500\u2500\n\nfunction getPlatformDefaultEditor(): string {\n if (osPlatform() === 'win32') return 'notepad'\n return 'vi' // macOS and Linux always have vi\n}\n\n// \u2500\u2500\u2500 Command Implementation \u2500\u2500\u2500\n\nconst memory: Command = {\n name: 'memory',\n description: 'Open project instructions file (MINTO.md) in your editor',\n isEnabled: true,\n isHidden: false,\n type: 'local',\n userFacingName() {\n return 'memory'\n },\n async call() {\n const cwd = getCwd()\n\n // Find existing file: MINTO.md > AGENTS.md > CLAUDE.md\n let filePath = join(cwd, 'MINTO.md')\n if (!existsSync(filePath)) {\n const agentsPath = join(cwd, 'AGENTS.md')\n if (existsSync(agentsPath)) {\n filePath = agentsPath\n } else {\n const claudePath = join(cwd, 'CLAUDE.md')\n if (existsSync(claudePath)) {\n filePath = claudePath\n }\n }\n }\n\n // Create MINTO.md with template if neither exists\n if (!existsSync(filePath)) {\n const dir = dirname(filePath)\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n writeFileSync(filePath, TEMPLATE, 'utf-8')\n }\n\n // Strategy 1: Try AiTer first\n if (isAiTerRunning()) {\n try {\n const success = await openInAiTer(filePath)\n if (success) {\n return `Opened ${filePath} in AiTer editor. Changes will take effect in the next conversation.`\n }\n } catch {\n // AiTer failed, fall through\n }\n }\n\n // Strategy 2: Use $VISUAL or $EDITOR\n const envEditor = process.env.VISUAL || process.env.EDITOR\n const editor = envEditor || getPlatformDefaultEditor()\n const editorBin = editor.split('/').pop() || editor\n\n try {\n if (TERMINAL_EDITORS.test(editorBin)) {\n // Terminal editor \u2014 needs TTY management\n openInTerminalEditor(editor, filePath)\n } else {\n // GUI editor \u2014 non-blocking exec\n execSync(`${editor} ${JSON.stringify(filePath)}`, {\n stdio: 'ignore',\n timeout: 5000,\n })\n }\n return `Opened ${filePath} in ${editorBin}. Changes will take effect in the next conversation.`\n } catch (error) {\n return `Failed to open editor (${editorBin}): ${error instanceof Error ? error.message : String(error)}`\n }\n },\n}\n\nexport default memory\n"],
|
|
5
|
+
"mappings": "AAcA,SAAS,YAAY,eAAe,WAAW,UAAU,iBAAiB;AAC1E,SAAS,MAAM,eAAe;AAC9B,SAAS,UAAU,iBAAiB;AACpC,SAAS,YAAY,YAAY,gBAAgB;AACjD,YAAY,SAAS;AACrB,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAG7B,MAAM,WAAW,KAAK,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBlC,SAAS,qBAA6B;AACpC,MAAI,WAAW,MAAM,SAAS;AAC5B,UAAM,WAAW,SAAS,EAAE;AAC5B,WAAO,sBAAsB,QAAQ;AAAA,EACvC;AACA,QAAM,MAAM,QAAQ,SAAS,KAAK,SAAS,EAAE;AAC7C,SAAO,cAAc,GAAG;AAC1B;AAKA,SAAS,iBAA0B;AACjC,QAAM,aAAa,mBAAmB;AACtC,MAAI,WAAW,MAAM,WAAW,CAAC,WAAW,UAAU,GAAG;AACvD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAMA,SAAS,YAAY,UAAoC;AACvD,SAAO,IAAI,QAAQ,aAAW;AAC5B,UAAM,aAAa,mBAAmB;AACtC,UAAM,UAAU,KAAK,UAAU;AAAA,MAC7B,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ,EAAE,UAAU,MAAM,OAAO;AAAA,IACnC,CAAC;AAED,UAAM,SAAS,IAAI,iBAAiB,UAAU;AAC9C,QAAI,SAAS;AAEb,UAAM,UAAU,WAAW,MAAM;AAC/B,aAAO,QAAQ;AACf,cAAQ,KAAK;AAAA,IACf,GAAG,GAAI;AAEP,WAAO,GAAG,WAAW,MAAM;AACzB,aAAO,MAAM,UAAU,IAAI;AAAA,IAC7B,CAAC;AAED,WAAO,GAAG,QAAQ,WAAS;AACzB,gBAAU,MAAM,SAAS;AACzB,UAAI;AACF,cAAM,WAAW,KAAK,MAAM,MAAM;AAClC,qBAAa,OAAO;AACpB,eAAO,IAAI;AACX,gBAAQ,SAAS,YAAY,IAAI;AAAA,MACnC,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAED,WAAO,GAAG,SAAS,MAAM;AACvB,mBAAa,OAAO;AACpB,cAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EACH,CAAC;AACH;AAIA,MAAM,mBAAmB;AAMzB,SAAS,qBAAqB,QAAgB,UAAwB;AAEpE,MAAI;AACJ,MAAI;AACF,YAAQ,SAAS,YAAY,GAAG;AAAA,EAClC,QAAQ;AACN,YAAQ;AAAA,EACV;AAGA,MAAI,OAAO,QAAQ,MAAM,eAAe,YAAY;AAClD,YAAQ,MAAM,WAAW,KAAK;AAAA,EAChC;AACA,UAAQ,MAAM,MAAM;AAEpB,MAAI;AACF,UAAM,SAAS,UAAU,QAAQ,CAAC,QAAQ,GAAG;AAAA,MAC3C,OAAO,CAAC,SAAS,WAAW,WAAW,SAAS;AAAA,MAChD,OAAO;AAAA,IACT,CAAC;AACD,QAAI,OAAO,OAAO;AAChB,YAAM,IAAI,MAAM,0BAA0B,OAAO,MAAM,OAAO,EAAE;AAAA,IAClE;AAAA,EACF,UAAE;AACA,QAAI,UAAU,QAAW;AACvB,UAAI;AACF,kBAAU,KAAK;AAAA,MACjB,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,YAAQ,MAAM,OAAO;AACrB,QAAI,OAAO,QAAQ,MAAM,eAAe,YAAY;AAClD,cAAQ,MAAM,WAAW,IAAI;AAAA,IAC/B;AACA,YAAQ,OAAO,KAAK,QAAQ;AAAA,EAC9B;AACF;AAIA,SAAS,2BAAmC;AAC1C,MAAI,WAAW,MAAM,QAAS,QAAO;AACrC,SAAO;AACT;AAIA,MAAM,SAAkB;AAAA,EACtB,MAAM;AAAA,EACN,aAAa;AAAA,EACb,WAAW;AAAA,EACX,UAAU;AAAA,EACV,MAAM;AAAA,EACN,iBAAiB;AACf,WAAO;AAAA,EACT;AAAA,EACA,MAAM,OAAO;AACX,UAAM,MAAM,OAAO;AAGnB,QAAI,WAAW,KAAK,KAAK,UAAU;AACnC,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,YAAM,aAAa,KAAK,KAAK,WAAW;AACxC,UAAI,WAAW,UAAU,GAAG;AAC1B,mBAAW;AAAA,MACb,OAAO;AACL,cAAM,aAAa,KAAK,KAAK,WAAW;AACxC,YAAI,WAAW,UAAU,GAAG;AAC1B,qBAAW;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAGA,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,YAAM,MAAM,QAAQ,QAAQ;AAC5B,UAAI,CAAC,WAAW,GAAG,GAAG;AACpB,kBAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,MACpC;AACA,oBAAc,UAAU,UAAU,OAAO;AAAA,IAC3C;AAGA,QAAI,eAAe,GAAG;AACpB,UAAI;AACF,cAAM,UAAU,MAAM,YAAY,QAAQ;AAC1C,YAAI,SAAS;AACX,iBAAO,UAAU,QAAQ;AAAA,QAC3B;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,UAAM,YAAY,QAAQ,IAAI,UAAU,QAAQ,IAAI;AACpD,UAAM,SAAS,aAAa,yBAAyB;AACrD,UAAM,YAAY,OAAO,MAAM,GAAG,EAAE,IAAI,KAAK;AAE7C,QAAI;AACF,UAAI,iBAAiB,KAAK,SAAS,GAAG;AAEpC,6BAAqB,QAAQ,QAAQ;AAAA,MACvC,OAAO;AAEL,iBAAS,GAAG,MAAM,IAAI,KAAK,UAAU,QAAQ,CAAC,IAAI;AAAA,UAChD,OAAO;AAAA,UACP,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AACA,aAAO,UAAU,QAAQ,OAAO,SAAS;AAAA,IAC3C,SAAS,OAAO;AACd,aAAO,0BAA0B,SAAS,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IACxG;AAAA,EACF;AACF;AAEA,IAAO,iBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/commands/model.js
CHANGED
|
@@ -12,6 +12,7 @@ import { t } from "../i18n/index.js";
|
|
|
12
12
|
const ID_MANAGE_LIBRARY = "__manage_library__";
|
|
13
13
|
const ID_ADD_NEW = "__add_new__";
|
|
14
14
|
const ID_CLEAR = "__clear__";
|
|
15
|
+
const ID_EDIT = "__edit__";
|
|
15
16
|
const ID_DELETE = "__delete__";
|
|
16
17
|
const ID_BACK = "__back__";
|
|
17
18
|
const ID_CONFIRM_YES = "__yes__";
|
|
@@ -120,6 +121,15 @@ function ModelCommand({ onClose, abortController }) {
|
|
|
120
121
|
{
|
|
121
122
|
modelName: phase.modelName,
|
|
122
123
|
modelLabel: phase.modelLabel,
|
|
124
|
+
onEdit: () => {
|
|
125
|
+
enableConfigs();
|
|
126
|
+
abortController?.abort?.();
|
|
127
|
+
setPhase({
|
|
128
|
+
type: "edit-model",
|
|
129
|
+
modelName: phase.modelName,
|
|
130
|
+
modelLabel: phase.modelLabel
|
|
131
|
+
});
|
|
132
|
+
},
|
|
123
133
|
onDelete: () => setPhase({
|
|
124
134
|
type: "confirm-delete",
|
|
125
135
|
modelName: phase.modelName,
|
|
@@ -182,6 +192,32 @@ function ModelCommand({ onClose, abortController }) {
|
|
|
182
192
|
}
|
|
183
193
|
);
|
|
184
194
|
}
|
|
195
|
+
if (phase.type === "edit-model") {
|
|
196
|
+
const modelManager = getModelManager();
|
|
197
|
+
const profile = modelManager.getAvailableModels().find((m) => m.modelName === phase.modelName);
|
|
198
|
+
if (!profile) {
|
|
199
|
+
setPhase({ type: "library" });
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
return /* @__PURE__ */ React.createElement(
|
|
203
|
+
ModelSelector,
|
|
204
|
+
{
|
|
205
|
+
onDone: () => {
|
|
206
|
+
refreshModels();
|
|
207
|
+
setPhase({ type: "library" });
|
|
208
|
+
},
|
|
209
|
+
onCancel: () => setPhase({
|
|
210
|
+
type: "model-actions",
|
|
211
|
+
modelName: phase.modelName,
|
|
212
|
+
modelLabel: phase.modelLabel
|
|
213
|
+
}),
|
|
214
|
+
skipModelType: true,
|
|
215
|
+
isOnboarding: false,
|
|
216
|
+
abortController: new AbortController(),
|
|
217
|
+
editingModel: profile
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
}
|
|
185
221
|
return null;
|
|
186
222
|
}
|
|
187
223
|
function MainPhase({
|
|
@@ -353,10 +389,15 @@ function LibraryPhase({
|
|
|
353
389
|
function ModelActionsPhase({
|
|
354
390
|
modelName,
|
|
355
391
|
modelLabel,
|
|
392
|
+
onEdit,
|
|
356
393
|
onDelete,
|
|
357
394
|
onClose
|
|
358
395
|
}) {
|
|
359
396
|
const items = [
|
|
397
|
+
{
|
|
398
|
+
id: ID_EDIT,
|
|
399
|
+
label: t("commands.model.editModel")
|
|
400
|
+
},
|
|
360
401
|
{
|
|
361
402
|
id: ID_DELETE,
|
|
362
403
|
label: t("common.delete"),
|
|
@@ -370,13 +411,15 @@ function ModelActionsPhase({
|
|
|
370
411
|
];
|
|
371
412
|
const handleSelect = useCallback(
|
|
372
413
|
(item) => {
|
|
373
|
-
if (item.id ===
|
|
414
|
+
if (item.id === ID_EDIT) {
|
|
415
|
+
onEdit();
|
|
416
|
+
} else if (item.id === ID_DELETE) {
|
|
374
417
|
onDelete();
|
|
375
418
|
} else {
|
|
376
419
|
onClose();
|
|
377
420
|
}
|
|
378
421
|
},
|
|
379
|
-
[onDelete, onClose]
|
|
422
|
+
[onEdit, onDelete, onClose]
|
|
380
423
|
);
|
|
381
424
|
return /* @__PURE__ */ React.createElement(
|
|
382
425
|
SimpleSelector,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/commands/model.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * Model Command\n *\n * Interactive model configuration using SimpleSelector phase state machine.\n *\n * Phase flow:\n * main \u2192 (select pointer) \u2192 assign \u2192 (select model) \u2192 main\n * main \u2192 (Manage Library) \u2192 library \u2192 (select model) \u2192 model-actions\n * \u2193\n * confirm-delete \u2192 library\n * library \u2192 (+ Add New) \u2192 add-model \u2192 library\n */\n\nimport React, { useState, useMemo, useCallback } from 'react'\nimport { SimpleSelector } from '@components/SimpleSelector'\nimport type { SelectorItem, StatusOverlay } from '@components/SimpleSelector'\nimport { ModelSelector } from '@components/ModelSelector'\nimport { enableConfigs } from '@utils/config'\nimport {\n ModelPointerType,\n setModelPointer,\n getGlobalConfig,\n} from '@utils/config'\nimport { getModelManager, reloadModelManager } from '@utils/model'\nimport { triggerModelConfigChange } from '@messages'\nimport { t } from '@i18n'\nimport type { Command } from '@commands'\n\n// =============================================================================\n// Types\n// =============================================================================\n\ntype ModelPhase =\n | { type: 'main' }\n | { type: 'assign'; pointer: ModelPointerType }\n | { type: 'library' }\n | { type: 'model-actions'; modelName: string; modelLabel: string }\n | { type: 'confirm-delete'; modelName: string; modelLabel: string }\n | { type: 'add-model' }\n\n// Special item IDs\nconst ID_MANAGE_LIBRARY = '__manage_library__'\nconst ID_ADD_NEW = '__add_new__'\nconst ID_CLEAR = '__clear__'\nconst ID_DELETE = '__delete__'\nconst ID_BACK = '__back__'\nconst ID_CONFIRM_YES = '__yes__'\nconst ID_CONFIRM_NO = '__no__'\n\nconst POINTER_TYPES: ModelPointerType[] = [\n 'main',\n 'task',\n 'reasoning',\n 'quick',\n 'compact',\n]\n\nconst POINTER_LABELS: Record<ModelPointerType, string> = {\n main: 'Main',\n task: 'Task',\n reasoning: 'Reasoning',\n quick: 'Quick',\n compact: 'Compact',\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction getPointerDescription(pointer: ModelPointerType): string {\n const key = `ui.modelConfig.${pointer}Description` as const\n return t(key)\n}\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\ntype Props = {\n onClose: () => void\n abortController?: AbortController\n}\n\nfunction ModelCommand({ onClose, abortController }: Props): React.ReactNode {\n const [phase, setPhase] = useState<ModelPhase>({ type: 'main' })\n const [statusOverlay, setStatusOverlay] = useState<StatusOverlay | null>(null)\n const [refreshKey, setRefreshKey] = useState(0)\n\n // Reload models and trigger config change\n const refreshModels = useCallback(() => {\n reloadModelManager()\n triggerModelConfigChange()\n setRefreshKey(prev => prev + 1)\n }, [])\n\n // Show a temporary success overlay then transition\n const showSuccessAndGo = useCallback(\n (message: string, nextPhase: ModelPhase) => {\n setStatusOverlay({ type: 'success', message })\n setTimeout(() => {\n setStatusOverlay(null)\n setPhase(nextPhase)\n }, 800)\n },\n [],\n )\n\n // \u2500\u2500\u2500 Phase: main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'main') {\n return (\n <MainPhase\n refreshKey={refreshKey}\n statusOverlay={statusOverlay}\n onSelectPointer={pointer => setPhase({ type: 'assign', pointer })}\n onManageLibrary={() => setPhase({ type: 'library' })}\n onClose={onClose}\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: assign \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'assign') {\n return (\n <AssignPhase\n pointer={phase.pointer}\n refreshKey={refreshKey}\n statusOverlay={statusOverlay}\n onAssign={(pointer, modelName) => {\n setModelPointer(pointer, modelName)\n refreshModels()\n showSuccessAndGo(\n t('commands.model.pointerUpdated').replace(\n '{pointer}',\n POINTER_LABELS[pointer],\n ),\n { type: 'main' },\n )\n }}\n onClear={pointer => {\n setModelPointer(pointer, '')\n refreshModels()\n showSuccessAndGo(\n t('commands.model.pointerCleared').replace(\n '{pointer}',\n POINTER_LABELS[pointer],\n ),\n { type: 'main' },\n )\n }}\n onClose={() => setPhase({ type: 'main' })}\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: library \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'library') {\n return (\n <LibraryPhase\n refreshKey={refreshKey}\n statusOverlay={statusOverlay}\n onSelectModel={(modelName, modelLabel) =>\n setPhase({ type: 'model-actions', modelName, modelLabel })\n }\n onAddNew={() => {\n enableConfigs()\n abortController?.abort?.()\n setPhase({ type: 'add-model' })\n }}\n onClose={() => setPhase({ type: 'main' })}\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: model-actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'model-actions') {\n return (\n <ModelActionsPhase\n modelName={phase.modelName}\n modelLabel={phase.modelLabel}\n onDelete={() =>\n setPhase({\n type: 'confirm-delete',\n modelName: phase.modelName,\n modelLabel: phase.modelLabel,\n })\n }\n onClose={() => setPhase({ type: 'library' })}\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: confirm-delete \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'confirm-delete') {\n return (\n <ConfirmDeletePhase\n modelName={phase.modelName}\n modelLabel={phase.modelLabel}\n statusOverlay={statusOverlay}\n onConfirm={() => {\n try {\n const modelManager = getModelManager()\n modelManager.removeModel(phase.modelName)\n refreshModels()\n showSuccessAndGo(t('commands.model.modelDeleted'), {\n type: 'library',\n })\n } catch {\n setStatusOverlay({\n type: 'error',\n message: t('commands.model.deleteFailed'),\n })\n setTimeout(() => {\n setStatusOverlay(null)\n setPhase({\n type: 'model-actions',\n modelName: phase.modelName,\n modelLabel: phase.modelLabel,\n })\n }, 1500)\n }\n }}\n onCancel={() =>\n setPhase({\n type: 'model-actions',\n modelName: phase.modelName,\n modelLabel: phase.modelLabel,\n })\n }\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: add-model \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'add-model') {\n return (\n <ModelSelector\n onDone={() => {\n refreshModels()\n setPhase({ type: 'library' })\n }}\n onCancel={() => setPhase({ type: 'library' })}\n skipModelType={true}\n isOnboarding={false}\n abortController={new AbortController()}\n />\n )\n }\n\n // Fallback (shouldn't happen)\n return null\n}\n\n// =============================================================================\n// Phase Components\n// =============================================================================\n\nfunction MainPhase({\n refreshKey,\n statusOverlay,\n onSelectPointer,\n onManageLibrary,\n onClose,\n}: {\n refreshKey: number\n statusOverlay: StatusOverlay | null\n onSelectPointer: (pointer: ModelPointerType) => void\n onManageLibrary: () => void\n onClose: () => void\n}) {\n const items = useMemo((): SelectorItem[] => {\n const config = getGlobalConfig()\n const modelManager = getModelManager()\n const models = modelManager.getAvailableModels()\n\n const pointerItems: SelectorItem[] = POINTER_TYPES.map(pointer => {\n const assignedModelName = config.modelPointers?.[pointer] || ''\n const model = models.find(m => m.modelName === assignedModelName)\n const description = model\n ? `${model.name} (${model.provider})`\n : getPointerDescription(pointer)\n\n return {\n id: pointer,\n label: POINTER_LABELS[pointer],\n description,\n category: t('commands.model.pointersCategory'),\n statusIcon: model ? '\\u2713' : undefined,\n statusColor: model ? '#4EBA65' : undefined,\n }\n })\n\n const actionItems: SelectorItem[] = [\n {\n id: ID_MANAGE_LIBRARY,\n label: t('commands.model.modelLibrary'),\n description: t('commands.model.modelLibrarySubtitle'),\n category: t('commands.model.actionsCategory'),\n },\n ]\n\n return [...pointerItems, ...actionItems]\n }, [refreshKey])\n\n const handleSelect = useCallback(\n (item: SelectorItem) => {\n if (item.id === ID_MANAGE_LIBRARY) {\n onManageLibrary()\n } else {\n onSelectPointer(item.id as ModelPointerType)\n }\n },\n [onSelectPointer, onManageLibrary],\n )\n\n return (\n <SimpleSelector\n title={t('commands.model.title')}\n items={items}\n onSelect={handleSelect}\n onClose={onClose}\n groupByCategory={true}\n statusOverlay={statusOverlay}\n />\n )\n}\n\nfunction AssignPhase({\n pointer,\n refreshKey,\n statusOverlay,\n onAssign,\n onClear,\n onClose,\n}: {\n pointer: ModelPointerType\n refreshKey: number\n statusOverlay: StatusOverlay | null\n onAssign: (pointer: ModelPointerType, modelName: string) => void\n onClear: (pointer: ModelPointerType) => void\n onClose: () => void\n}) {\n const items = useMemo((): SelectorItem[] => {\n const config = getGlobalConfig()\n const modelManager = getModelManager()\n const models = modelManager.getAvailableModels()\n const currentAssignment = config.modelPointers?.[pointer] || ''\n\n const modelItems: SelectorItem[] = models.map(model => ({\n id: model.modelName,\n label: model.name,\n description: `${model.provider}: ${model.modelName}`,\n isCurrent: model.modelName === currentAssignment,\n }))\n\n // Add \"Clear Assignment\" option if there's a current assignment\n if (currentAssignment) {\n modelItems.push({\n id: ID_CLEAR,\n label: t('commands.model.clearAssignment'),\n statusIcon: '\\u2715',\n statusColor: '#FF6B80',\n })\n }\n\n return modelItems\n }, [pointer, refreshKey])\n\n const handleSelect = useCallback(\n (item: SelectorItem) => {\n if (item.id === ID_CLEAR) {\n onClear(pointer)\n } else {\n onAssign(pointer, item.id)\n }\n },\n [pointer, onAssign, onClear],\n )\n\n return (\n <SimpleSelector\n title={t('commands.model.selectModel').replace(\n '{pointer}',\n POINTER_LABELS[pointer],\n )}\n items={items}\n onSelect={handleSelect}\n onClose={onClose}\n statusOverlay={statusOverlay}\n />\n )\n}\n\nfunction LibraryPhase({\n refreshKey,\n statusOverlay,\n onSelectModel,\n onAddNew,\n onClose,\n}: {\n refreshKey: number\n statusOverlay: StatusOverlay | null\n onSelectModel: (modelName: string, modelLabel: string) => void\n onAddNew: () => void\n onClose: () => void\n}) {\n const items = useMemo((): SelectorItem[] => {\n const config = getGlobalConfig()\n const modelManager = getModelManager()\n const models = modelManager.getAvailableModels()\n\n const modelItems: SelectorItem[] = models.map(model => {\n // Find which pointers use this model\n const usedBy: string[] = []\n POINTER_TYPES.forEach(p => {\n if (config.modelPointers?.[p] === model.modelName) {\n usedBy.push(POINTER_LABELS[p])\n }\n })\n const usageDesc = usedBy.length > 0 ? `[${usedBy.join(', ')}]` : ''\n\n return {\n id: model.modelName,\n label: model.name,\n description: `${model.provider}${usageDesc ? ' ' + usageDesc : ''}`,\n statusIcon: usedBy.length > 0 ? '\\u2713' : undefined,\n statusColor: usedBy.length > 0 ? '#4EBA65' : undefined,\n data: { modelLabel: model.name },\n }\n })\n\n modelItems.push({\n id: ID_ADD_NEW,\n label: t('commands.model.addNewModel'),\n })\n\n return modelItems\n }, [refreshKey])\n\n const handleSelect = useCallback(\n (item: SelectorItem) => {\n if (item.id === ID_ADD_NEW) {\n onAddNew()\n } else {\n const data = item.data as { modelLabel: string } | undefined\n onSelectModel(item.id, data?.modelLabel || item.label)\n }\n },\n [onSelectModel, onAddNew],\n )\n\n return (\n <SimpleSelector\n title={t('commands.model.modelLibrary')}\n subtitle={t('commands.model.modelLibrarySubtitle')}\n items={items}\n onSelect={handleSelect}\n onClose={onClose}\n statusOverlay={statusOverlay}\n />\n )\n}\n\nfunction ModelActionsPhase({\n modelName,\n modelLabel,\n onDelete,\n onClose,\n}: {\n modelName: string\n modelLabel: string\n onDelete: () => void\n onClose: () => void\n}) {\n const items: SelectorItem[] = [\n {\n id: ID_DELETE,\n label: t('common.delete'),\n statusIcon: '\\u2715',\n statusColor: '#FF6B80',\n },\n {\n id: ID_BACK,\n label: t('common.back'),\n },\n ]\n\n const handleSelect = useCallback(\n (item: SelectorItem) => {\n if (item.id === ID_DELETE) {\n onDelete()\n } else {\n onClose()\n }\n },\n [onDelete, onClose],\n )\n\n return (\n <SimpleSelector\n title={t('commands.model.modelActions').replace('{model}', modelLabel)}\n items={items}\n onSelect={handleSelect}\n onClose={onClose}\n />\n )\n}\n\nfunction ConfirmDeletePhase({\n modelName,\n modelLabel,\n statusOverlay,\n onConfirm,\n onCancel,\n}: {\n modelName: string\n modelLabel: string\n statusOverlay: StatusOverlay | null\n onConfirm: () => void\n onCancel: () => void\n}) {\n const items: SelectorItem[] = [\n {\n id: ID_CONFIRM_YES,\n label: t('commands.model.confirmDeleteYes'),\n statusIcon: '\\u2715',\n statusColor: '#FF6B80',\n },\n {\n id: ID_CONFIRM_NO,\n label: t('commands.model.confirmDeleteNo'),\n },\n ]\n\n const handleSelect = useCallback(\n (item: SelectorItem) => {\n if (item.id === ID_CONFIRM_YES) {\n onConfirm()\n } else {\n onCancel()\n }\n },\n [onConfirm, onCancel],\n )\n\n return (\n <SimpleSelector\n title={t('commands.model.confirmDeleteTitle').replace(\n '{model}',\n modelLabel,\n )}\n items={items}\n onSelect={handleSelect}\n onClose={onCancel}\n statusOverlay={statusOverlay}\n />\n )\n}\n\n// =============================================================================\n// Command Export\n// =============================================================================\n\nconst model: Command = {\n name: 'model',\n description: 'View model status and configure AI provider settings',\n aliases: ['ms', 'modelstatus', 'model-status'],\n isEnabled: true,\n isHidden: false,\n hidePromptInput: true,\n type: 'local-jsx',\n userFacingName() {\n return 'model'\n },\n async call(onDone, context) {\n const { abortController } = context || {}\n return <ModelCommand onClose={onDone} abortController={abortController} />\n },\n}\n\nexport default model\n"],
|
|
5
|
-
"mappings": "AAaA,OAAO,SAAS,UAAU,SAAS,mBAAmB;AACtD,SAAS,sBAAsB;AAE/B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B;AAAA,EAEE;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB,0BAA0B;AACpD,SAAS,gCAAgC;AACzC,SAAS,SAAS;
|
|
4
|
+
"sourcesContent": ["/**\n * Model Command\n *\n * Interactive model configuration using SimpleSelector phase state machine.\n *\n * Phase flow:\n * main \u2192 (select pointer) \u2192 assign \u2192 (select model) \u2192 main\n * main \u2192 (Manage Library) \u2192 library \u2192 (select model) \u2192 model-actions\n * \u2193\n * confirm-delete \u2192 library\n * library \u2192 (+ Add New) \u2192 add-model \u2192 library\n */\n\nimport React, { useState, useMemo, useCallback } from 'react'\nimport { SimpleSelector } from '@components/SimpleSelector'\nimport type { SelectorItem, StatusOverlay } from '@components/SimpleSelector'\nimport { ModelSelector } from '@components/ModelSelector'\nimport { enableConfigs } from '@utils/config'\nimport {\n ModelPointerType,\n setModelPointer,\n getGlobalConfig,\n} from '@utils/config'\nimport { getModelManager, reloadModelManager } from '@utils/model'\nimport { triggerModelConfigChange } from '@messages'\nimport { t } from '@i18n'\nimport type { Command } from '@commands'\n\n// =============================================================================\n// Types\n// =============================================================================\n\ntype ModelPhase =\n | { type: 'main' }\n | { type: 'assign'; pointer: ModelPointerType }\n | { type: 'library' }\n | { type: 'model-actions'; modelName: string; modelLabel: string }\n | { type: 'confirm-delete'; modelName: string; modelLabel: string }\n | { type: 'add-model' }\n | { type: 'edit-model'; modelName: string; modelLabel: string }\n\n// Special item IDs\nconst ID_MANAGE_LIBRARY = '__manage_library__'\nconst ID_ADD_NEW = '__add_new__'\nconst ID_CLEAR = '__clear__'\nconst ID_EDIT = '__edit__'\nconst ID_DELETE = '__delete__'\nconst ID_BACK = '__back__'\nconst ID_CONFIRM_YES = '__yes__'\nconst ID_CONFIRM_NO = '__no__'\n\nconst POINTER_TYPES: ModelPointerType[] = [\n 'main',\n 'task',\n 'reasoning',\n 'quick',\n 'compact',\n]\n\nconst POINTER_LABELS: Record<ModelPointerType, string> = {\n main: 'Main',\n task: 'Task',\n reasoning: 'Reasoning',\n quick: 'Quick',\n compact: 'Compact',\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction getPointerDescription(pointer: ModelPointerType): string {\n const key = `ui.modelConfig.${pointer}Description` as const\n return t(key)\n}\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\ntype Props = {\n onClose: () => void\n abortController?: AbortController\n}\n\nfunction ModelCommand({ onClose, abortController }: Props): React.ReactNode {\n const [phase, setPhase] = useState<ModelPhase>({ type: 'main' })\n const [statusOverlay, setStatusOverlay] = useState<StatusOverlay | null>(null)\n const [refreshKey, setRefreshKey] = useState(0)\n\n // Reload models and trigger config change\n const refreshModels = useCallback(() => {\n reloadModelManager()\n triggerModelConfigChange()\n setRefreshKey(prev => prev + 1)\n }, [])\n\n // Show a temporary success overlay then transition\n const showSuccessAndGo = useCallback(\n (message: string, nextPhase: ModelPhase) => {\n setStatusOverlay({ type: 'success', message })\n setTimeout(() => {\n setStatusOverlay(null)\n setPhase(nextPhase)\n }, 800)\n },\n [],\n )\n\n // \u2500\u2500\u2500 Phase: main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'main') {\n return (\n <MainPhase\n refreshKey={refreshKey}\n statusOverlay={statusOverlay}\n onSelectPointer={pointer => setPhase({ type: 'assign', pointer })}\n onManageLibrary={() => setPhase({ type: 'library' })}\n onClose={onClose}\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: assign \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'assign') {\n return (\n <AssignPhase\n pointer={phase.pointer}\n refreshKey={refreshKey}\n statusOverlay={statusOverlay}\n onAssign={(pointer, modelName) => {\n setModelPointer(pointer, modelName)\n refreshModels()\n showSuccessAndGo(\n t('commands.model.pointerUpdated').replace(\n '{pointer}',\n POINTER_LABELS[pointer],\n ),\n { type: 'main' },\n )\n }}\n onClear={pointer => {\n setModelPointer(pointer, '')\n refreshModels()\n showSuccessAndGo(\n t('commands.model.pointerCleared').replace(\n '{pointer}',\n POINTER_LABELS[pointer],\n ),\n { type: 'main' },\n )\n }}\n onClose={() => setPhase({ type: 'main' })}\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: library \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'library') {\n return (\n <LibraryPhase\n refreshKey={refreshKey}\n statusOverlay={statusOverlay}\n onSelectModel={(modelName, modelLabel) =>\n setPhase({ type: 'model-actions', modelName, modelLabel })\n }\n onAddNew={() => {\n enableConfigs()\n abortController?.abort?.()\n setPhase({ type: 'add-model' })\n }}\n onClose={() => setPhase({ type: 'main' })}\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: model-actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'model-actions') {\n return (\n <ModelActionsPhase\n modelName={phase.modelName}\n modelLabel={phase.modelLabel}\n onEdit={() => {\n enableConfigs()\n abortController?.abort?.()\n setPhase({\n type: 'edit-model',\n modelName: phase.modelName,\n modelLabel: phase.modelLabel,\n })\n }}\n onDelete={() =>\n setPhase({\n type: 'confirm-delete',\n modelName: phase.modelName,\n modelLabel: phase.modelLabel,\n })\n }\n onClose={() => setPhase({ type: 'library' })}\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: confirm-delete \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'confirm-delete') {\n return (\n <ConfirmDeletePhase\n modelName={phase.modelName}\n modelLabel={phase.modelLabel}\n statusOverlay={statusOverlay}\n onConfirm={() => {\n try {\n const modelManager = getModelManager()\n modelManager.removeModel(phase.modelName)\n refreshModels()\n showSuccessAndGo(t('commands.model.modelDeleted'), {\n type: 'library',\n })\n } catch {\n setStatusOverlay({\n type: 'error',\n message: t('commands.model.deleteFailed'),\n })\n setTimeout(() => {\n setStatusOverlay(null)\n setPhase({\n type: 'model-actions',\n modelName: phase.modelName,\n modelLabel: phase.modelLabel,\n })\n }, 1500)\n }\n }}\n onCancel={() =>\n setPhase({\n type: 'model-actions',\n modelName: phase.modelName,\n modelLabel: phase.modelLabel,\n })\n }\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: add-model \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'add-model') {\n return (\n <ModelSelector\n onDone={() => {\n refreshModels()\n setPhase({ type: 'library' })\n }}\n onCancel={() => setPhase({ type: 'library' })}\n skipModelType={true}\n isOnboarding={false}\n abortController={new AbortController()}\n />\n )\n }\n\n // \u2500\u2500\u2500 Phase: edit-model \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (phase.type === 'edit-model') {\n const modelManager = getModelManager()\n const profile = modelManager\n .getAvailableModels()\n .find(m => m.modelName === phase.modelName)\n if (!profile) {\n // Model not found, go back to library\n setPhase({ type: 'library' })\n return null\n }\n return (\n <ModelSelector\n onDone={() => {\n refreshModels()\n setPhase({ type: 'library' })\n }}\n onCancel={() =>\n setPhase({\n type: 'model-actions',\n modelName: phase.modelName,\n modelLabel: phase.modelLabel,\n })\n }\n skipModelType={true}\n isOnboarding={false}\n abortController={new AbortController()}\n editingModel={profile}\n />\n )\n }\n\n // Fallback (shouldn't happen)\n return null\n}\n\n// =============================================================================\n// Phase Components\n// =============================================================================\n\nfunction MainPhase({\n refreshKey,\n statusOverlay,\n onSelectPointer,\n onManageLibrary,\n onClose,\n}: {\n refreshKey: number\n statusOverlay: StatusOverlay | null\n onSelectPointer: (pointer: ModelPointerType) => void\n onManageLibrary: () => void\n onClose: () => void\n}) {\n const items = useMemo((): SelectorItem[] => {\n const config = getGlobalConfig()\n const modelManager = getModelManager()\n const models = modelManager.getAvailableModels()\n\n const pointerItems: SelectorItem[] = POINTER_TYPES.map(pointer => {\n const assignedModelName = config.modelPointers?.[pointer] || ''\n const model = models.find(m => m.modelName === assignedModelName)\n const description = model\n ? `${model.name} (${model.provider})`\n : getPointerDescription(pointer)\n\n return {\n id: pointer,\n label: POINTER_LABELS[pointer],\n description,\n category: t('commands.model.pointersCategory'),\n statusIcon: model ? '\\u2713' : undefined,\n statusColor: model ? '#4EBA65' : undefined,\n }\n })\n\n const actionItems: SelectorItem[] = [\n {\n id: ID_MANAGE_LIBRARY,\n label: t('commands.model.modelLibrary'),\n description: t('commands.model.modelLibrarySubtitle'),\n category: t('commands.model.actionsCategory'),\n },\n ]\n\n return [...pointerItems, ...actionItems]\n }, [refreshKey])\n\n const handleSelect = useCallback(\n (item: SelectorItem) => {\n if (item.id === ID_MANAGE_LIBRARY) {\n onManageLibrary()\n } else {\n onSelectPointer(item.id as ModelPointerType)\n }\n },\n [onSelectPointer, onManageLibrary],\n )\n\n return (\n <SimpleSelector\n title={t('commands.model.title')}\n items={items}\n onSelect={handleSelect}\n onClose={onClose}\n groupByCategory={true}\n statusOverlay={statusOverlay}\n />\n )\n}\n\nfunction AssignPhase({\n pointer,\n refreshKey,\n statusOverlay,\n onAssign,\n onClear,\n onClose,\n}: {\n pointer: ModelPointerType\n refreshKey: number\n statusOverlay: StatusOverlay | null\n onAssign: (pointer: ModelPointerType, modelName: string) => void\n onClear: (pointer: ModelPointerType) => void\n onClose: () => void\n}) {\n const items = useMemo((): SelectorItem[] => {\n const config = getGlobalConfig()\n const modelManager = getModelManager()\n const models = modelManager.getAvailableModels()\n const currentAssignment = config.modelPointers?.[pointer] || ''\n\n const modelItems: SelectorItem[] = models.map(model => ({\n id: model.modelName,\n label: model.name,\n description: `${model.provider}: ${model.modelName}`,\n isCurrent: model.modelName === currentAssignment,\n }))\n\n // Add \"Clear Assignment\" option if there's a current assignment\n if (currentAssignment) {\n modelItems.push({\n id: ID_CLEAR,\n label: t('commands.model.clearAssignment'),\n statusIcon: '\\u2715',\n statusColor: '#FF6B80',\n })\n }\n\n return modelItems\n }, [pointer, refreshKey])\n\n const handleSelect = useCallback(\n (item: SelectorItem) => {\n if (item.id === ID_CLEAR) {\n onClear(pointer)\n } else {\n onAssign(pointer, item.id)\n }\n },\n [pointer, onAssign, onClear],\n )\n\n return (\n <SimpleSelector\n title={t('commands.model.selectModel').replace(\n '{pointer}',\n POINTER_LABELS[pointer],\n )}\n items={items}\n onSelect={handleSelect}\n onClose={onClose}\n statusOverlay={statusOverlay}\n />\n )\n}\n\nfunction LibraryPhase({\n refreshKey,\n statusOverlay,\n onSelectModel,\n onAddNew,\n onClose,\n}: {\n refreshKey: number\n statusOverlay: StatusOverlay | null\n onSelectModel: (modelName: string, modelLabel: string) => void\n onAddNew: () => void\n onClose: () => void\n}) {\n const items = useMemo((): SelectorItem[] => {\n const config = getGlobalConfig()\n const modelManager = getModelManager()\n const models = modelManager.getAvailableModels()\n\n const modelItems: SelectorItem[] = models.map(model => {\n // Find which pointers use this model\n const usedBy: string[] = []\n POINTER_TYPES.forEach(p => {\n if (config.modelPointers?.[p] === model.modelName) {\n usedBy.push(POINTER_LABELS[p])\n }\n })\n const usageDesc = usedBy.length > 0 ? `[${usedBy.join(', ')}]` : ''\n\n return {\n id: model.modelName,\n label: model.name,\n description: `${model.provider}${usageDesc ? ' ' + usageDesc : ''}`,\n statusIcon: usedBy.length > 0 ? '\\u2713' : undefined,\n statusColor: usedBy.length > 0 ? '#4EBA65' : undefined,\n data: { modelLabel: model.name },\n }\n })\n\n modelItems.push({\n id: ID_ADD_NEW,\n label: t('commands.model.addNewModel'),\n })\n\n return modelItems\n }, [refreshKey])\n\n const handleSelect = useCallback(\n (item: SelectorItem) => {\n if (item.id === ID_ADD_NEW) {\n onAddNew()\n } else {\n const data = item.data as { modelLabel: string } | undefined\n onSelectModel(item.id, data?.modelLabel || item.label)\n }\n },\n [onSelectModel, onAddNew],\n )\n\n return (\n <SimpleSelector\n title={t('commands.model.modelLibrary')}\n subtitle={t('commands.model.modelLibrarySubtitle')}\n items={items}\n onSelect={handleSelect}\n onClose={onClose}\n statusOverlay={statusOverlay}\n />\n )\n}\n\nfunction ModelActionsPhase({\n modelName,\n modelLabel,\n onEdit,\n onDelete,\n onClose,\n}: {\n modelName: string\n modelLabel: string\n onEdit: () => void\n onDelete: () => void\n onClose: () => void\n}) {\n const items: SelectorItem[] = [\n {\n id: ID_EDIT,\n label: t('commands.model.editModel'),\n },\n {\n id: ID_DELETE,\n label: t('common.delete'),\n statusIcon: '\\u2715',\n statusColor: '#FF6B80',\n },\n {\n id: ID_BACK,\n label: t('common.back'),\n },\n ]\n\n const handleSelect = useCallback(\n (item: SelectorItem) => {\n if (item.id === ID_EDIT) {\n onEdit()\n } else if (item.id === ID_DELETE) {\n onDelete()\n } else {\n onClose()\n }\n },\n [onEdit, onDelete, onClose],\n )\n\n return (\n <SimpleSelector\n title={t('commands.model.modelActions').replace('{model}', modelLabel)}\n items={items}\n onSelect={handleSelect}\n onClose={onClose}\n />\n )\n}\n\nfunction ConfirmDeletePhase({\n modelName,\n modelLabel,\n statusOverlay,\n onConfirm,\n onCancel,\n}: {\n modelName: string\n modelLabel: string\n statusOverlay: StatusOverlay | null\n onConfirm: () => void\n onCancel: () => void\n}) {\n const items: SelectorItem[] = [\n {\n id: ID_CONFIRM_YES,\n label: t('commands.model.confirmDeleteYes'),\n statusIcon: '\\u2715',\n statusColor: '#FF6B80',\n },\n {\n id: ID_CONFIRM_NO,\n label: t('commands.model.confirmDeleteNo'),\n },\n ]\n\n const handleSelect = useCallback(\n (item: SelectorItem) => {\n if (item.id === ID_CONFIRM_YES) {\n onConfirm()\n } else {\n onCancel()\n }\n },\n [onConfirm, onCancel],\n )\n\n return (\n <SimpleSelector\n title={t('commands.model.confirmDeleteTitle').replace(\n '{model}',\n modelLabel,\n )}\n items={items}\n onSelect={handleSelect}\n onClose={onCancel}\n statusOverlay={statusOverlay}\n />\n )\n}\n\n// =============================================================================\n// Command Export\n// =============================================================================\n\nconst model: Command = {\n name: 'model',\n description: 'View model status and configure AI provider settings',\n aliases: ['ms', 'modelstatus', 'model-status'],\n isEnabled: true,\n isHidden: false,\n hidePromptInput: true,\n type: 'local-jsx',\n userFacingName() {\n return 'model'\n },\n async call(onDone, context) {\n const { abortController } = context || {}\n return <ModelCommand onClose={onDone} abortController={abortController} />\n },\n}\n\nexport default model\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["model"]
|
|
7
7
|
}
|