@within-7/minto 0.3.9 → 0.3.10
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/commands/agents/AgentsCommand.js +459 -655
- package/dist/commands/agents/AgentsCommand.js.map +2 -2
- package/dist/commands/agents/types.js +1 -0
- package/dist/commands/agents/types.js.map +2 -2
- package/dist/commands/agents/utils/fileOperations.js +96 -36
- package/dist/commands/agents/utils/fileOperations.js.map +3 -3
- package/dist/commands/agents/utils/index.js +3 -1
- package/dist/commands/agents/utils/index.js.map +2 -2
- package/dist/commands/context.js +54 -23
- package/dist/commands/context.js.map +2 -2
- package/dist/commands/export.js +673 -93
- package/dist/commands/export.js.map +2 -2
- package/dist/commands/language.js +19 -46
- package/dist/commands/language.js.map +2 -2
- package/dist/commands/mcp-interactive.js +419 -217
- package/dist/commands/mcp-interactive.js.map +2 -2
- package/dist/commands/model.js +415 -66
- package/dist/commands/model.js.map +2 -2
- package/dist/commands/permissions.js +75 -49
- package/dist/commands/permissions.js.map +2 -2
- package/dist/commands/plugin.js +882 -185
- package/dist/commands/plugin.js.map +3 -3
- package/dist/commands/resume.js +1 -1
- package/dist/commands/resume.js.map +1 -1
- package/dist/commands/sandbox.js +168 -70
- package/dist/commands/sandbox.js.map +2 -2
- package/dist/commands/setup.js +593 -107
- package/dist/commands/setup.js.map +2 -2
- package/dist/commands/stats.js +188 -131
- package/dist/commands/stats.js.map +2 -2
- package/dist/commands/status.js +75 -13
- package/dist/commands/status.js.map +2 -2
- package/dist/commands/undo.js +138 -174
- package/dist/commands/undo.js.map +2 -2
- package/dist/commands.js.map +1 -1
- package/dist/components/Help.js +165 -32
- package/dist/components/Help.js.map +2 -2
- package/dist/components/InfoPanel/InfoPanel.js +123 -0
- package/dist/components/InfoPanel/InfoPanel.js.map +7 -0
- package/dist/components/InfoPanel/index.js +5 -0
- package/dist/components/InfoPanel/index.js.map +7 -0
- package/dist/components/InfoPanel/types.js +1 -0
- package/dist/components/InfoPanel/types.js.map +7 -0
- package/dist/components/ModelSelector/BrandTextInput.js +43 -0
- package/dist/components/ModelSelector/BrandTextInput.js.map +7 -0
- package/dist/components/ModelSelector/ModelSelector.js +419 -501
- package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
- package/dist/components/ModelSelector/WizardContainer.js +45 -0
- package/dist/components/ModelSelector/WizardContainer.js.map +7 -0
- package/dist/components/ModelSelector/index.js +1 -3
- package/dist/components/ModelSelector/index.js.map +2 -2
- package/dist/components/PromptInput.js +5 -5
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/SimpleSelector/SimpleSelector.js +154 -0
- package/dist/components/SimpleSelector/SimpleSelector.js.map +7 -0
- package/dist/components/SimpleSelector/index.js +5 -0
- package/dist/components/SimpleSelector/index.js.map +7 -0
- package/dist/components/SimpleSelector/types.js +1 -0
- package/dist/components/SimpleSelector/types.js.map +7 -0
- package/dist/components/StatusOverlayContent.js +21 -0
- package/dist/components/StatusOverlayContent.js.map +7 -0
- package/dist/components/TabbedListView/ScrollableList.js +31 -5
- package/dist/components/TabbedListView/ScrollableList.js.map +2 -2
- package/dist/components/TabbedListView/TabbedListView.js +122 -47
- package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
- package/dist/core/backupHook.js +29 -0
- package/dist/core/backupHook.js.map +7 -0
- package/dist/core/config/defaults.js +8 -2
- package/dist/core/config/defaults.js.map +2 -2
- package/dist/core/config/schema.js +14 -2
- package/dist/core/config/schema.js.map +2 -2
- package/dist/core/costTracker.js +0 -16
- package/dist/core/costTracker.js.map +2 -2
- package/dist/cost-tracker.js +0 -16
- package/dist/cost-tracker.js.map +2 -2
- package/dist/entrypoints/bootstrap.js +3 -1
- package/dist/entrypoints/bootstrap.js.map +2 -2
- package/dist/entrypoints/cli.js +32 -0
- package/dist/entrypoints/cli.js.map +2 -2
- package/dist/i18n/locales/en.js +300 -1
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +301 -2
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/services/customCommands.js +30 -8
- package/dist/services/customCommands.js.map +2 -2
- package/dist/services/plugins/lspServers.js +1 -1
- package/dist/services/plugins/lspServers.js.map +2 -2
- package/dist/services/plugins/pluginRuntime.js +2 -1
- package/dist/services/plugins/pluginRuntime.js.map +2 -2
- package/dist/services/plugins/pluginValidation.js +10 -3
- package/dist/services/plugins/pluginValidation.js.map +2 -2
- package/dist/services/plugins/skillMarketplace.js +16 -8
- package/dist/services/plugins/skillMarketplace.js.map +2 -2
- package/dist/services/systemReminder.js +17 -6
- package/dist/services/systemReminder.js.map +2 -2
- package/dist/tools/FileEditTool/FileEditTool.js +7 -0
- package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
- package/dist/tools/FileWriteTool/FileWriteTool.js +7 -0
- package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
- package/dist/tools/MultiEditTool/MultiEditTool.js +7 -0
- package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
- package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -0
- package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +9 -6
- package/dist/tools/TaskTool/TaskTool.js.map +2 -2
- package/dist/types/PermissionMode.js.map +1 -1
- package/dist/types/plugin.js +2 -4
- package/dist/types/plugin.js.map +2 -2
- package/dist/utils/agentHookExecutor.js +1 -4
- package/dist/utils/agentHookExecutor.js.map +2 -2
- package/dist/utils/agentLoader.js +67 -13
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/agentMemory.js.map +2 -2
- package/dist/utils/claudeCodeSync.js +439 -0
- package/dist/utils/claudeCodeSync.js.map +7 -0
- package/dist/utils/config.js +1 -23
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/execFileNoThrow.js +2 -1
- package/dist/utils/execFileNoThrow.js.map +2 -2
- package/dist/utils/marketplaceManager.js +80 -43
- package/dist/utils/marketplaceManager.js.map +2 -2
- package/dist/utils/messages.js +2 -2
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/pluginInstaller.js +34 -24
- package/dist/utils/pluginInstaller.js.map +2 -2
- package/dist/utils/pluginLoader.js +48 -25
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/repoFetcher.js +110 -0
- package/dist/utils/repoFetcher.js.map +7 -0
- package/dist/utils/skillLoader.js +18 -6
- package/dist/utils/skillLoader.js.map +2 -2
- package/dist/utils/stringSubstitution.js +4 -5
- package/dist/utils/stringSubstitution.js.map +2 -2
- package/dist/utils/teamConfig.js +153 -13
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/terminal.js +1 -1
- package/dist/utils/terminal.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +6 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/TabbedListView/TabbedListView.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * TabbedListView Component\n *\n * A reusable component matching the /command suggestion UI pattern:\n * - Tab navigation (\u2190/\u2192 or Tab key)\n * - Search input (/ to focus)\n * - Scrollable list with \u25C6 selection indicator\n * - Brand gradient colors for selection\n * - Consistent height control (MAX_VISIBLE = 8)\n *\n * Based on Claude Code CLI's /plugins UI pattern.\n */\n\nimport React, { useState, useCallback, useMemo } from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\nimport { TabBar } from './TabBar'\nimport { SearchInput } from './SearchInput'\nimport { ScrollableList } from './ScrollableList'\nimport { SimpleSpinner } from '@components/Spinner'\nimport type { TabbedListViewProps, ListItem } from './types'\n\ntype FocusArea = 'tabs' | 'search' | 'list'\n\n// Match command suggestion visible count\nconst MAX_VISIBLE_ITEMS = 8\n\nexport function TabbedListView({\n title,\n tabs,\n activeTab,\n onTabChange,\n items,\n searchEnabled = true,\n searchPlaceholder = 'Search...',\n searchQuery = '',\n onSearchChange,\n onSelect,\n onClose,\n footerHint,\n isLoading = false,\n loadingText = 'Loading...',\n emptyText = 'No items found',\n groupByCategory = false,\n renderItem,\n}: TabbedListViewProps) {\n // Focus state: which area is currently active\n const [focusArea, setFocusArea] = useState<FocusArea>(\n searchEnabled ? 'search' : 'list',\n )\n\n // Index of focused item in the list\n const [focusedIndex, setFocusedIndex] = useState(0)\n\n // Filter items based on search query\n const filteredItems = useMemo(() => {\n if (!searchQuery.trim()) return items\n\n const query = searchQuery.toLowerCase()\n return items.filter(\n item =>\n item.label.toLowerCase().includes(query) ||\n item.description?.toLowerCase().includes(query) ||\n item.category?.toLowerCase().includes(query),\n )\n }, [items, searchQuery])\n\n // Reset focus index when items change\n React.useEffect(() => {\n setFocusedIndex(0)\n }, [filteredItems.length, activeTab])\n\n // Handle tab cycling\n const cycleTab = useCallback(\n (direction: 'left' | 'right') => {\n const currentIndex = tabs.findIndex(t => t.id === activeTab)\n let newIndex: number\n\n if (direction === 'right') {\n newIndex = (currentIndex + 1) % tabs.length\n } else {\n newIndex = currentIndex - 1 < 0 ? tabs.length - 1 : currentIndex - 1\n }\n\n onTabChange(tabs[newIndex].id)\n },\n [tabs, activeTab, onTabChange],\n )\n\n // Handle item selection\n const handleSelect = useCallback(\n (item: ListItem) => {\n if (onSelect) {\n onSelect(item)\n }\n },\n [onSelect],\n )\n\n // Keyboard input handling\n useInput((input, key) => {\n // Escape to close\n if (key.escape) {\n onClose()\n return\n }\n\n // Tab key cycles through tabs\n if (key.tab) {\n cycleTab(key.shift ? 'left' : 'right')\n return\n }\n\n // Left/Right arrows for tab navigation when in tabs or search area\n if (focusArea === 'tabs' || focusArea === 'search') {\n if (key.leftArrow) {\n cycleTab('left')\n return\n }\n if (key.rightArrow) {\n cycleTab('right')\n return\n }\n }\n\n // Handle focus area navigation\n if (key.downArrow) {\n if (focusArea === 'search') {\n setFocusArea('list')\n return\n }\n if (focusArea === 'list' && filteredItems.length > 0) {\n setFocusedIndex(prev =>\n prev < filteredItems.length - 1 ? prev + 1 : prev,\n )\n return\n }\n }\n\n if (key.upArrow) {\n if (focusArea === 'list') {\n if (focusedIndex > 0) {\n setFocusedIndex(prev => prev - 1)\n } else if (searchEnabled) {\n setFocusArea('search')\n }\n return\n }\n }\n\n // Enter to select\n if (key.return && focusArea === 'list' && filteredItems.length > 0) {\n handleSelect(filteredItems[focusedIndex])\n return\n }\n\n // '/' to focus search\n if (input === '/' && searchEnabled && focusArea !== 'search') {\n setFocusArea('search')\n return\n }\n })\n\n // Build default footer hint\n const defaultHint = '\u2191\u2193 Navigate \u00B7 Enter Select \u00B7 Tab/\u2190\u2192 Switch \u00B7 Esc Close'\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n {/* Header bar with top border - mimics PromptInput separator */}\n <Box\n borderTop={true}\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n borderColor={BRAND_GRADIENT.START}\n borderStyle=\"single\"\n width=\"100%\"\n paddingX={1}\n flexDirection=\"column\"\n >\n {/* Title with brand color */}\n <Box>\n <Text color={BRAND_GRADIENT.START}>\u25C6</Text>\n <Text bold color={SEMANTIC_COLORS.secondary}>\n {' '}\n {title}\n </Text>\n {filteredItems.length > 0 && (\n <Text color={SEMANTIC_COLORS.dim}> ({filteredItems.length})</Text>\n )}\n </Box>\n\n {/* Tab Bar - left aligned */}\n <Box marginTop={1}>\n <TabBar tabs={tabs} activeTab={activeTab} onTabChange={onTabChange} />\n </Box>\n </Box>\n\n {/* Content container with bottom border */}\n <Box\n flexDirection=\"column\"\n borderTop={false}\n borderBottom={true}\n borderLeft={false}\n borderRight={false}\n borderColor={BRAND_GRADIENT.START}\n borderStyle=\"single\"\n width=\"100%\"\n paddingX={1}\n paddingY={1}\n >\n {/* Search Input */}\n {searchEnabled && (\n <Box marginBottom={1}>\n <SearchInput\n value={searchQuery}\n onChange={onSearchChange || (() => {})}\n placeholder={searchPlaceholder}\n isActive={focusArea === 'search'}\n />\n </Box>\n )}\n\n {/* Content Area - key forces re-render on tab change */}\n <Box key={`content-${activeTab}`} flexDirection=\"column\">\n {isLoading ? (\n <Box>\n <SimpleSpinner />\n <Text color={SEMANTIC_COLORS.dim}> {loadingText}</Text>\n </Box>\n ) : filteredItems.length === 0 ? (\n <Box>\n <Text color={SEMANTIC_COLORS.dim}>{emptyText}</Text>\n </Box>\n ) : (\n <ScrollableList\n items={filteredItems}\n focusedIndex={focusedIndex}\n onFocusChange={setFocusedIndex}\n onSelect={handleSelect}\n visibleCount={MAX_VISIBLE_ITEMS}\n groupByCategory={groupByCategory}\n renderItem={renderItem}\n />\n )}\n </Box>\n\n {/* Footer Hint - compact style */}\n <Box marginTop={1}>\n <Text color={SEMANTIC_COLORS.muted}>{footerHint || defaultHint}</Text>\n </Box>\n </Box>\n </Box>\n )\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * TabbedListView Component\n *\n * A reusable component matching the /command suggestion UI pattern:\n * - Tab navigation (\u2190/\u2192 or Tab key)\n * - Search input (/ to focus)\n * - Scrollable list with \u25C6 selection indicator\n * - Brand gradient colors for selection\n * - Consistent height control (MAX_VISIBLE = 8)\n * - Status overlay for loading/success/error states (maintains frame)\n * - Back navigation support (onBack)\n *\n * Based on Claude Code CLI's /plugins UI pattern.\n */\n\nimport React, { useState, useCallback, useMemo } from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\nimport { TabBar } from './TabBar'\nimport { SearchInput } from './SearchInput'\nimport { ScrollableList } from './ScrollableList'\nimport { SimpleSpinner } from '@components/Spinner'\nimport { StatusOverlayContent } from '@components/StatusOverlayContent'\nimport { useTerminalSize } from '@hooks/useTerminalSize'\nimport type { TabbedListViewProps, ListItem } from './types'\n\ntype FocusArea = 'tabs' | 'search' | 'list'\n\n// Heights for dynamic visible count calculation\nconst PROMPT_HEIGHT = 5 // PromptInput borders + input + hints\nconst CHROME_HEIGHT = 6 // title + tabs + search + footer + borders\n\nexport function TabbedListView({\n title,\n tabs,\n activeTab,\n onTabChange,\n items,\n searchEnabled = true,\n searchPlaceholder = 'Search...',\n searchQuery = '',\n onSearchChange,\n onSearchSubmit,\n onSelect,\n onClose,\n onBack,\n footerHint,\n isLoading = false,\n loadingText = 'Loading...',\n emptyText = 'No items found',\n groupByCategory = false,\n renderItem,\n statusOverlay,\n isActive = true,\n showItemCount = true,\n statusDismissHint,\n multiSelect,\n selectedIds,\n onSelectionChange,\n onMultiSelect,\n multiSelectActionLabel,\n}: TabbedListViewProps) {\n // Dynamic visible count based on terminal height\n const { rows } = useTerminalSize()\n const visibleCount = Math.min(\n 8,\n Math.max(3, rows - PROMPT_HEIGHT - CHROME_HEIGHT),\n )\n\n // Focus state: which area is currently active\n const [focusArea, setFocusArea] = useState<FocusArea>(\n searchEnabled ? 'search' : 'list',\n )\n\n // Index of focused item in the list\n const [focusedIndex, setFocusedIndex] = useState(0)\n\n // Filter items based on search query\n const filteredItems = useMemo(() => {\n if (!searchQuery.trim()) return items\n\n const query = searchQuery.toLowerCase()\n return items.filter(\n item =>\n item.label.toLowerCase().includes(query) ||\n item.description?.toLowerCase().includes(query) ||\n item.category?.toLowerCase().includes(query),\n )\n }, [items, searchQuery])\n\n // Reset focus index when items change\n React.useEffect(() => {\n setFocusedIndex(0)\n }, [filteredItems.length, activeTab])\n\n // Handle tab cycling\n const cycleTab = useCallback(\n (direction: 'left' | 'right') => {\n const currentIndex = tabs.findIndex(t => t.id === activeTab)\n let newIndex: number\n\n if (direction === 'right') {\n newIndex = (currentIndex + 1) % tabs.length\n } else {\n newIndex = currentIndex - 1 < 0 ? tabs.length - 1 : currentIndex - 1\n }\n\n onTabChange(tabs[newIndex].id)\n },\n [tabs, activeTab, onTabChange],\n )\n\n // Handle item selection\n const handleSelect = useCallback(\n (item: ListItem) => {\n if (onSelect) {\n onSelect(item)\n }\n },\n [onSelect],\n )\n\n // Keyboard input handling\n useInput(\n (input, key) => {\n // Status overlay: success/error \u2192 ESC/Enter closes\n if (statusOverlay && statusOverlay.type !== 'loading') {\n if (key.escape || key.return) {\n onClose()\n }\n return\n }\n\n // Status overlay: loading \u2192 ignore all input\n if (statusOverlay) return\n\n // Escape: onBack if provided, else onClose\n if (key.escape) {\n if (onBack) {\n onBack()\n } else {\n onClose()\n }\n return\n }\n\n // Tab key cycles through tabs\n if (key.tab) {\n cycleTab(key.shift ? 'left' : 'right')\n return\n }\n\n // Left/Right arrows for tab navigation when in tabs or search area\n if (focusArea === 'tabs' || focusArea === 'search') {\n if (key.leftArrow) {\n cycleTab('left')\n return\n }\n if (key.rightArrow) {\n cycleTab('right')\n return\n }\n }\n\n // Handle focus area navigation\n if (key.downArrow) {\n if (focusArea === 'search') {\n setFocusArea('list')\n return\n }\n if (focusArea === 'list' && filteredItems.length > 0) {\n setFocusedIndex(prev =>\n prev < filteredItems.length - 1 ? prev + 1 : prev,\n )\n return\n }\n }\n\n if (key.upArrow) {\n if (focusArea === 'list') {\n if (focusedIndex > 0) {\n setFocusedIndex(prev => prev - 1)\n } else if (searchEnabled) {\n setFocusArea('search')\n }\n return\n }\n }\n\n // Enter in search: submit if onSearchSubmit provided\n if (key.return && focusArea === 'search' && onSearchSubmit) {\n onSearchSubmit(searchQuery)\n return\n }\n\n // Space: toggle selection in multi-select mode\n if (\n input === ' ' &&\n multiSelect &&\n focusArea === 'list' &&\n filteredItems.length > 0\n ) {\n const item = filteredItems[focusedIndex]\n if (item && onSelectionChange) {\n const isCurrentlySelected = selectedIds?.has(item.id) ?? false\n onSelectionChange(item.id, !isCurrentlySelected)\n }\n return\n }\n\n // 'a': toggle select-all / deselect-all in multi-select mode\n if (\n input === 'a' &&\n multiSelect &&\n focusArea === 'list' &&\n filteredItems.length > 0\n ) {\n if (onSelectionChange) {\n const allSelected = filteredItems.every(item =>\n selectedIds?.has(item.id),\n )\n for (const item of filteredItems) {\n onSelectionChange(item.id, !allSelected)\n }\n }\n return\n }\n\n // Enter: batch action if multi-select has selections, otherwise single select\n if (key.return && focusArea === 'list' && filteredItems.length > 0) {\n if (\n multiSelect &&\n selectedIds &&\n selectedIds.size > 0 &&\n onMultiSelect\n ) {\n const selectedItems = filteredItems.filter(item =>\n selectedIds.has(item.id),\n )\n if (selectedItems.length > 0) {\n onMultiSelect(selectedItems)\n return\n }\n }\n handleSelect(filteredItems[focusedIndex])\n return\n }\n\n // '/' to focus search\n if (input === '/' && searchEnabled && focusArea !== 'search') {\n setFocusArea('search')\n return\n }\n },\n { isActive },\n )\n\n // Build footer hint based on current state\n const defaultHint = '\u2191\u2193 Navigate \u00B7 Enter Select \u00B7 Tab/\u2190\u2192 Switch \u00B7 Esc Close'\n const multiSelectHint =\n '\u2191\u2193 Navigate \u00B7 Space Select \u00B7 a All \u00B7 Enter Action \u00B7 Tab Switch \u00B7 Esc Close'\n const displayHint = statusOverlay\n ? statusOverlay.type === 'loading'\n ? ''\n : statusDismissHint || 'Esc Close'\n : multiSelect\n ? selectedIds && selectedIds.size > 0 && multiSelectActionLabel\n ? `${multiSelectActionLabel} \u00B7 ${footerHint || multiSelectHint}`\n : footerHint || multiSelectHint\n : footerHint || defaultHint\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n {/* Header bar with top border - mimics PromptInput separator */}\n <Box\n borderTop={true}\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n borderColor={BRAND_GRADIENT.START}\n borderStyle=\"single\"\n width=\"100%\"\n paddingX={1}\n flexDirection=\"column\"\n >\n {/* Title with brand color */}\n <Box>\n <Text color={BRAND_GRADIENT.START}>\u25C6</Text>\n <Text bold color={SEMANTIC_COLORS.secondary}>\n {' '}\n {title}\n </Text>\n {showItemCount && !statusOverlay && filteredItems.length > 0 && (\n <Text color={SEMANTIC_COLORS.dim}> ({filteredItems.length})</Text>\n )}\n </Box>\n\n {/* Tab Bar - left aligned */}\n <Box marginTop={1}>\n <TabBar tabs={tabs} activeTab={activeTab} onTabChange={onTabChange} />\n </Box>\n </Box>\n\n {/* Content container with bottom border */}\n <Box\n flexDirection=\"column\"\n borderTop={false}\n borderBottom={true}\n borderLeft={false}\n borderRight={false}\n borderColor={BRAND_GRADIENT.START}\n borderStyle=\"single\"\n width=\"100%\"\n paddingX={1}\n paddingY={1}\n >\n {/* Search Input - hidden during status overlay */}\n {searchEnabled && !statusOverlay && (\n <Box marginBottom={1}>\n <SearchInput\n value={searchQuery}\n onChange={onSearchChange || (() => {})}\n placeholder={searchPlaceholder}\n isActive={focusArea === 'search'}\n />\n </Box>\n )}\n\n {/* Content Area - key forces re-render on tab change */}\n <Box key={`content-${activeTab}`} flexDirection=\"column\">\n {statusOverlay ? (\n <StatusOverlayContent\n type={statusOverlay.type}\n message={statusOverlay.message}\n />\n ) : isLoading ? (\n <Box>\n <SimpleSpinner />\n <Text color={SEMANTIC_COLORS.dim}> {loadingText}</Text>\n </Box>\n ) : filteredItems.length === 0 ? (\n <Box>\n <Text color={SEMANTIC_COLORS.dim}>{emptyText}</Text>\n </Box>\n ) : (\n <ScrollableList\n items={filteredItems}\n focusedIndex={focusedIndex}\n onFocusChange={setFocusedIndex}\n onSelect={handleSelect}\n visibleCount={visibleCount}\n groupByCategory={groupByCategory}\n renderItem={renderItem}\n multiSelect={multiSelect}\n selectedIds={selectedIds}\n />\n )}\n </Box>\n\n {/* Footer Hint - compact style */}\n {displayHint && (\n <Box marginTop={1}>\n <Text color={SEMANTIC_COLORS.muted}>{displayHint}</Text>\n </Box>\n )}\n </Box>\n </Box>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createBackup } from "./backupManager.js";
|
|
2
|
+
import { loadGlobalConfig } from "./config/loader.js";
|
|
3
|
+
const EXCLUDED_PATTERNS = [
|
|
4
|
+
"/node_modules/",
|
|
5
|
+
"/.git/",
|
|
6
|
+
"/.minto/backups/",
|
|
7
|
+
"/.claude/backups/"
|
|
8
|
+
];
|
|
9
|
+
function isExcludedPath(filePath) {
|
|
10
|
+
return EXCLUDED_PATTERNS.some((pattern) => filePath.includes(pattern));
|
|
11
|
+
}
|
|
12
|
+
function triggerBackup(filePath, oldContent, newContent, operation) {
|
|
13
|
+
if (isExcludedPath(filePath)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const config = loadGlobalConfig();
|
|
18
|
+
if (config.backupEnabled === false) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
createBackup(filePath, oldContent, newContent, operation).catch(() => {
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
triggerBackup
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=backupHook.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/backupHook.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Backup Hook\n *\n * Thin wrapper around createBackup() for use in file-writing tools.\n * Fire-and-forget: sync signature, internally async with silent failure.\n */\n\nimport { createBackup, type BackupOperation } from './backupManager'\nimport { loadGlobalConfig } from './config/loader'\n\n/** Paths that should never be backed up */\nconst EXCLUDED_PATTERNS = [\n '/node_modules/',\n '/.git/',\n '/.minto/backups/',\n '/.claude/backups/',\n]\n\n/**\n * Check if a file path should be excluded from backup\n */\nfunction isExcludedPath(filePath: string): boolean {\n return EXCLUDED_PATTERNS.some(pattern => filePath.includes(pattern))\n}\n\n/**\n * Trigger a backup for a file before modification.\n *\n * Fire-and-forget: returns void synchronously, runs createBackup async\n * in the background. Checks config and path exclusions before triggering.\n *\n * @param filePath - Absolute path to the file being modified\n * @param oldContent - Previous content (null for new files)\n * @param newContent - New content being written\n * @param operation - Type of operation (create, update, delete)\n */\nexport function triggerBackup(\n filePath: string,\n oldContent: string | null,\n newContent: string | null,\n operation: BackupOperation,\n): void {\n // Skip excluded paths\n if (isExcludedPath(filePath)) {\n return\n }\n\n // Check config (sync read, cached by loader)\n try {\n const config = loadGlobalConfig()\n if (config.backupEnabled === false) {\n return\n }\n } catch {\n // Config read failure \u2014 default to backing up\n }\n\n // Fire and forget \u2014 createBackup handles its own errors\n createBackup(filePath, oldContent, newContent, operation).catch(() => {\n // Silently ignore \u2014 createBackup already logs internally\n })\n}\n"],
|
|
5
|
+
"mappings": "AAOA,SAAS,oBAA0C;AACnD,SAAS,wBAAwB;AAGjC,MAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKA,SAAS,eAAe,UAA2B;AACjD,SAAO,kBAAkB,KAAK,aAAW,SAAS,SAAS,OAAO,CAAC;AACrE;AAaO,SAAS,cACd,UACA,YACA,YACA,WACM;AAEN,MAAI,eAAe,QAAQ,GAAG;AAC5B;AAAA,EACF;AAGA,MAAI;AACF,UAAM,SAAS,iBAAiB;AAChC,QAAI,OAAO,kBAAkB,OAAO;AAClC;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,eAAa,UAAU,YAAY,YAAY,SAAS,EAAE,MAAM,MAAM;AAAA,EAEtE,CAAC;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -14,7 +14,8 @@ const DEFAULT_MODEL_POINTERS = {
|
|
|
14
14
|
main: "",
|
|
15
15
|
task: "",
|
|
16
16
|
reasoning: "",
|
|
17
|
-
quick: ""
|
|
17
|
+
quick: "",
|
|
18
|
+
compact: ""
|
|
18
19
|
};
|
|
19
20
|
const DEFAULT_GLOBAL_CONFIG = {
|
|
20
21
|
numStartups: 0,
|
|
@@ -33,6 +34,7 @@ const DEFAULT_GLOBAL_CONFIG = {
|
|
|
33
34
|
lastDismissedUpdateVersion: void 0,
|
|
34
35
|
compressionMode: "business",
|
|
35
36
|
thinking: false,
|
|
37
|
+
backupEnabled: true,
|
|
36
38
|
configVersion: 1
|
|
37
39
|
// Current config version
|
|
38
40
|
};
|
|
@@ -56,7 +58,11 @@ const GLOBAL_CONFIG_KEYS = [
|
|
|
56
58
|
"preferredNotifChannel",
|
|
57
59
|
"shiftEnterKeyBindingInstalled",
|
|
58
60
|
"maxTokens",
|
|
59
|
-
"compressionMode"
|
|
61
|
+
"compressionMode",
|
|
62
|
+
"language",
|
|
63
|
+
"safetyMode",
|
|
64
|
+
"autoSyncClaudeCode",
|
|
65
|
+
"backupEnabled"
|
|
60
66
|
];
|
|
61
67
|
const PROJECT_CONFIG_KEYS = [
|
|
62
68
|
"dontCrawlDirectory",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/config/defaults.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Configuration Default Values\n *\n * Centralized default values for all configuration types.\n */\n\nimport type { GlobalConfig, ProjectConfig, ModelPointers } from './schema'\n\n/**\n * Default project configuration\n */\nexport const DEFAULT_PROJECT_CONFIG: ProjectConfig = {\n allowedTools: [],\n context: {},\n history: [],\n dontCrawlDirectory: false,\n enableArchitectTool: false,\n mcpContextUris: [],\n mcpServers: {},\n approvedMcprcServers: [],\n rejectedMcprcServers: [],\n hasTrustDialogAccepted: false,\n}\n\n/**\n * Default model pointers\n */\nexport const DEFAULT_MODEL_POINTERS: ModelPointers = {\n main: '',\n task: '',\n reasoning: '',\n quick: '',\n}\n\n/**\n * Default global configuration\n */\nexport const DEFAULT_GLOBAL_CONFIG: GlobalConfig = {\n numStartups: 0,\n autoUpdaterStatus: 'not_configured',\n theme: 'dark',\n preferredNotifChannel: 'iterm2',\n verbose: false,\n primaryProvider: 'anthropic',\n customApiKeyResponses: {\n approved: [],\n rejected: [],\n },\n stream: true,\n modelProfiles: [],\n modelPointers: DEFAULT_MODEL_POINTERS,\n lastDismissedUpdateVersion: undefined,\n compressionMode: 'business',\n thinking: false,\n configVersion: 1, // Current config version\n}\n\n/**\n * Get default project config, optionally adjusted for home directory\n */\nexport function defaultConfigForProject(\n projectPath: string,\n homeDir: string,\n): ProjectConfig {\n const config = { ...DEFAULT_PROJECT_CONFIG }\n if (projectPath === homeDir) {\n config.dontCrawlDirectory = true\n }\n return config\n}\n\n/**\n * Current configuration version\n * Increment this when making breaking changes to config structure\n */\nexport const CURRENT_CONFIG_VERSION = 1\n\n/**\n * Modifiable global config keys (exposed to CLI)\n */\nexport const GLOBAL_CONFIG_KEYS = [\n 'autoUpdaterStatus',\n 'theme',\n 'hasCompletedOnboarding',\n 'lastOnboardingVersion',\n 'lastReleaseNotesSeen',\n 'verbose',\n 'customApiKeyResponses',\n 'primaryProvider',\n 'preferredNotifChannel',\n 'shiftEnterKeyBindingInstalled',\n 'maxTokens',\n 'compressionMode',\n] as const\n\nexport type GlobalConfigKey = (typeof GLOBAL_CONFIG_KEYS)[number]\n\n/**\n * Modifiable project config keys (exposed to CLI)\n */\nexport const PROJECT_CONFIG_KEYS = [\n 'dontCrawlDirectory',\n 'enableArchitectTool',\n 'hasTrustDialogAccepted',\n 'hasCompletedProjectOnboarding',\n] as const\n\nexport type ProjectConfigKey = (typeof PROJECT_CONFIG_KEYS)[number]\n\n/**\n * Type guards for config keys\n */\nexport function isGlobalConfigKey(key: string): key is GlobalConfigKey {\n return GLOBAL_CONFIG_KEYS.includes(key as GlobalConfigKey)\n}\n\nexport function isProjectConfigKey(key: string): key is ProjectConfigKey {\n return PROJECT_CONFIG_KEYS.includes(key as ProjectConfigKey)\n}\n"],
|
|
5
|
-
"mappings": "AAWO,MAAM,yBAAwC;AAAA,EACnD,cAAc,CAAC;AAAA,EACf,SAAS,CAAC;AAAA,EACV,SAAS,CAAC;AAAA,EACV,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB,CAAC;AAAA,EACjB,YAAY,CAAC;AAAA,EACb,sBAAsB,CAAC;AAAA,EACvB,sBAAsB,CAAC;AAAA,EACvB,wBAAwB;AAC1B;AAKO,MAAM,yBAAwC;AAAA,EACnD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,WAAW;AAAA,EACX,OAAO;
|
|
4
|
+
"sourcesContent": ["/**\n * Configuration Default Values\n *\n * Centralized default values for all configuration types.\n */\n\nimport type { GlobalConfig, ProjectConfig, ModelPointers } from './schema'\n\n/**\n * Default project configuration\n */\nexport const DEFAULT_PROJECT_CONFIG: ProjectConfig = {\n allowedTools: [],\n context: {},\n history: [],\n dontCrawlDirectory: false,\n enableArchitectTool: false,\n mcpContextUris: [],\n mcpServers: {},\n approvedMcprcServers: [],\n rejectedMcprcServers: [],\n hasTrustDialogAccepted: false,\n}\n\n/**\n * Default model pointers\n */\nexport const DEFAULT_MODEL_POINTERS: ModelPointers = {\n main: '',\n task: '',\n reasoning: '',\n quick: '',\n compact: '',\n}\n\n/**\n * Default global configuration\n */\nexport const DEFAULT_GLOBAL_CONFIG: GlobalConfig = {\n numStartups: 0,\n autoUpdaterStatus: 'not_configured',\n theme: 'dark',\n preferredNotifChannel: 'iterm2',\n verbose: false,\n primaryProvider: 'anthropic',\n customApiKeyResponses: {\n approved: [],\n rejected: [],\n },\n stream: true,\n modelProfiles: [],\n modelPointers: DEFAULT_MODEL_POINTERS,\n lastDismissedUpdateVersion: undefined,\n compressionMode: 'business',\n thinking: false,\n backupEnabled: true,\n configVersion: 1, // Current config version\n}\n\n/**\n * Get default project config, optionally adjusted for home directory\n */\nexport function defaultConfigForProject(\n projectPath: string,\n homeDir: string,\n): ProjectConfig {\n const config = { ...DEFAULT_PROJECT_CONFIG }\n if (projectPath === homeDir) {\n config.dontCrawlDirectory = true\n }\n return config\n}\n\n/**\n * Current configuration version\n * Increment this when making breaking changes to config structure\n */\nexport const CURRENT_CONFIG_VERSION = 1\n\n/**\n * Modifiable global config keys (exposed to CLI)\n */\nexport const GLOBAL_CONFIG_KEYS = [\n 'autoUpdaterStatus',\n 'theme',\n 'hasCompletedOnboarding',\n 'lastOnboardingVersion',\n 'lastReleaseNotesSeen',\n 'verbose',\n 'customApiKeyResponses',\n 'primaryProvider',\n 'preferredNotifChannel',\n 'shiftEnterKeyBindingInstalled',\n 'maxTokens',\n 'compressionMode',\n 'language',\n 'safetyMode',\n 'autoSyncClaudeCode',\n 'backupEnabled',\n] as const\n\nexport type GlobalConfigKey = (typeof GLOBAL_CONFIG_KEYS)[number]\n\n/**\n * Modifiable project config keys (exposed to CLI)\n */\nexport const PROJECT_CONFIG_KEYS = [\n 'dontCrawlDirectory',\n 'enableArchitectTool',\n 'hasTrustDialogAccepted',\n 'hasCompletedProjectOnboarding',\n] as const\n\nexport type ProjectConfigKey = (typeof PROJECT_CONFIG_KEYS)[number]\n\n/**\n * Type guards for config keys\n */\nexport function isGlobalConfigKey(key: string): key is GlobalConfigKey {\n return GLOBAL_CONFIG_KEYS.includes(key as GlobalConfigKey)\n}\n\nexport function isProjectConfigKey(key: string): key is ProjectConfigKey {\n return PROJECT_CONFIG_KEYS.includes(key as ProjectConfigKey)\n}\n"],
|
|
5
|
+
"mappings": "AAWO,MAAM,yBAAwC;AAAA,EACnD,cAAc,CAAC;AAAA,EACf,SAAS,CAAC;AAAA,EACV,SAAS,CAAC;AAAA,EACV,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB,CAAC;AAAA,EACjB,YAAY,CAAC;AAAA,EACb,sBAAsB,CAAC;AAAA,EACvB,sBAAsB,CAAC;AAAA,EACvB,wBAAwB;AAC1B;AAKO,MAAM,yBAAwC;AAAA,EACnD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,WAAW;AAAA,EACX,OAAO;AAAA,EACP,SAAS;AACX;AAKO,MAAM,wBAAsC;AAAA,EACjD,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,OAAO;AAAA,EACP,uBAAuB;AAAA,EACvB,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,IACrB,UAAU,CAAC;AAAA,IACX,UAAU,CAAC;AAAA,EACb;AAAA,EACA,QAAQ;AAAA,EACR,eAAe,CAAC;AAAA,EAChB,eAAe;AAAA,EACf,4BAA4B;AAAA,EAC5B,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,eAAe;AAAA,EACf,eAAe;AAAA;AACjB;AAKO,SAAS,wBACd,aACA,SACe;AACf,QAAM,SAAS,EAAE,GAAG,uBAAuB;AAC3C,MAAI,gBAAgB,SAAS;AAC3B,WAAO,qBAAqB;AAAA,EAC9B;AACA,SAAO;AACT;AAMO,MAAM,yBAAyB;AAK/B,MAAM,qBAAqB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,MAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,SAAS,kBAAkB,KAAqC;AACrE,SAAO,mBAAmB,SAAS,GAAsB;AAC3D;AAEO,SAAS,mBAAmB,KAAsC;AACvE,SAAO,oBAAoB,SAAS,GAAuB;AAC7D;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -49,6 +49,8 @@ const ProviderTypeSchema = z.enum([
|
|
|
49
49
|
"custom-openai"
|
|
50
50
|
]);
|
|
51
51
|
const CompressionModeSchema = z.enum(["business", "code"]);
|
|
52
|
+
const UILanguageSchema = z.enum(["en", "zh-CN"]);
|
|
53
|
+
const SafetyModeSchema = z.enum(["yolo", "smart", "strict", "free"]);
|
|
52
54
|
const ReasoningEffortSchema = z.enum([
|
|
53
55
|
"minimal",
|
|
54
56
|
"low",
|
|
@@ -75,13 +77,15 @@ const ModelPointerTypeSchema = z.enum([
|
|
|
75
77
|
"main",
|
|
76
78
|
"task",
|
|
77
79
|
"reasoning",
|
|
78
|
-
"quick"
|
|
80
|
+
"quick",
|
|
81
|
+
"compact"
|
|
79
82
|
]);
|
|
80
83
|
const ModelPointersSchema = z.object({
|
|
81
84
|
main: z.string(),
|
|
82
85
|
task: z.string(),
|
|
83
86
|
reasoning: z.string(),
|
|
84
|
-
quick: z.string()
|
|
87
|
+
quick: z.string(),
|
|
88
|
+
compact: z.string()
|
|
85
89
|
});
|
|
86
90
|
const AccountInfoSchema = z.object({
|
|
87
91
|
accountUuid: z.string(),
|
|
@@ -138,6 +142,12 @@ const GlobalConfigSchema = z.object({
|
|
|
138
142
|
lastDismissedUpdateVersion: z.string().optional(),
|
|
139
143
|
compressionMode: CompressionModeSchema.optional(),
|
|
140
144
|
thinking: z.boolean().optional(),
|
|
145
|
+
language: UILanguageSchema.optional(),
|
|
146
|
+
safetyMode: SafetyModeSchema.optional(),
|
|
147
|
+
// Claude Code sync
|
|
148
|
+
autoSyncClaudeCode: z.boolean().optional(),
|
|
149
|
+
// File backup for /undo
|
|
150
|
+
backupEnabled: z.boolean().optional(),
|
|
141
151
|
// Config version for migrations
|
|
142
152
|
configVersion: z.number().optional()
|
|
143
153
|
});
|
|
@@ -170,6 +180,8 @@ export {
|
|
|
170
180
|
ProjectConfigSchema,
|
|
171
181
|
ProviderTypeSchema,
|
|
172
182
|
ReasoningEffortSchema,
|
|
183
|
+
SafetyModeSchema,
|
|
184
|
+
UILanguageSchema,
|
|
173
185
|
safeValidateGlobalConfig,
|
|
174
186
|
safeValidateProjectConfig,
|
|
175
187
|
validateGlobalConfig,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/config/schema.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Configuration Schema Definitions\n *\n * Defines all configuration types for Minto with Zod validation.\n */\n\nimport { z } from 'zod'\n\n// MCP Server Configurations\nexport const McpStdioServerConfigSchema = z.object({\n type: z.literal('stdio').optional().default('stdio'),\n command: z.string(),\n args: z.array(z.string()),\n env: z.record(z.string()).optional(),\n enabled: z.boolean().optional(),\n})\n\nexport const McpSSEServerConfigSchema = z.object({\n type: z.literal('sse'),\n url: z.string(),\n enabled: z.boolean().optional(),\n})\n\nexport const McpServerConfigSchema = z.union([\n McpStdioServerConfigSchema,\n McpSSEServerConfigSchema,\n])\n\n// Auto Updater Status\nexport const AutoUpdaterStatusSchema = z.enum([\n 'disabled',\n 'enabled',\n 'no_permissions',\n 'not_configured',\n])\n\n// Notification Channel\nexport const NotificationChannelSchema = z.enum([\n 'iterm2',\n 'terminal_bell',\n 'iterm2_with_bell',\n 'notifications_disabled',\n])\n\n// Provider Types\nexport const ProviderTypeSchema = z.enum([\n 'anthropic',\n 'openai',\n 'mistral',\n 'deepseek',\n 'kimi',\n 'qwen',\n 'glm',\n 'minimax',\n 'baidu-qianfan',\n 'siliconflow',\n 'bigdream',\n 'opendev',\n 'xai',\n 'groq',\n 'gemini',\n 'ollama',\n 'azure',\n 'custom',\n 'custom-openai',\n])\n\n// Compression Mode\nexport const CompressionModeSchema = z.enum(['business', 'code'])\n\n// Reasoning Effort\nexport const ReasoningEffortSchema = z.enum([\n 'minimal',\n 'low',\n 'medium',\n 'high',\n])\n\n// Model Profile\nexport const ModelProfileSchema = z.object({\n name: z.string(),\n provider: ProviderTypeSchema,\n modelName: z.string(),\n baseURL: z.string().optional(),\n apiKey: z.string(),\n maxTokens: z.number(),\n contextLength: z.number(),\n reasoningEffort: ReasoningEffortSchema.optional(),\n isActive: z.boolean(),\n createdAt: z.number(),\n lastUsed: z.number().optional(),\n isGPT5: z.boolean().optional(),\n validationStatus: z\n .enum(['valid', 'needs_repair', 'auto_repaired'])\n .optional(),\n lastValidation: z.number().optional(),\n})\n\n// Model Pointer Type\nexport const ModelPointerTypeSchema = z.enum([\n 'main',\n 'task',\n 'reasoning',\n 'quick',\n])\n\n// Model Pointers\nexport const ModelPointersSchema = z.object({\n main: z.string(),\n task: z.string(),\n reasoning: z.string(),\n quick: z.string(),\n})\n\n// Account Info\nexport const AccountInfoSchema = z.object({\n accountUuid: z.string(),\n emailAddress: z.string(),\n organizationUuid: z.string().optional(),\n})\n\n// Project Configuration\nexport const ProjectConfigSchema = z.object({\n allowedTools: z.array(z.string()),\n context: z.record(z.string()),\n contextFiles: z.array(z.string()).optional(),\n history: z.array(z.string()),\n dontCrawlDirectory: z.boolean().optional(),\n enableArchitectTool: z.boolean().optional(),\n mcpContextUris: z.array(z.string()),\n mcpServers: z.record(McpServerConfigSchema).optional(),\n approvedMcprcServers: z.array(z.string()).optional(),\n rejectedMcprcServers: z.array(z.string()).optional(),\n lastAPIDuration: z.number().optional(),\n lastCost: z.number().optional(),\n lastDuration: z.number().optional(),\n lastSessionId: z.string().optional(),\n exampleFiles: z.array(z.string()).optional(),\n exampleFilesGeneratedAt: z.number().optional(),\n hasTrustDialogAccepted: z.boolean().optional(),\n hasCompletedProjectOnboarding: z.boolean().optional(),\n})\n\n// Global Configuration\nexport const GlobalConfigSchema = z.object({\n projects: z.record(ProjectConfigSchema).optional(),\n numStartups: z.number(),\n autoUpdaterStatus: AutoUpdaterStatusSchema.optional(),\n userID: z.string().optional(),\n theme: z.string(),\n hasCompletedOnboarding: z.boolean().optional(),\n lastOnboardingVersion: z.string().optional(),\n lastReleaseNotesSeen: z.string().optional(),\n mcpServers: z.record(McpServerConfigSchema).optional(),\n preferredNotifChannel: NotificationChannelSchema,\n verbose: z.boolean(),\n customApiKeyResponses: z\n .object({\n approved: z.array(z.string()).optional(),\n rejected: z.array(z.string()).optional(),\n })\n .optional(),\n primaryProvider: ProviderTypeSchema.optional(),\n maxTokens: z.number().optional(),\n hasAcknowledgedCostThreshold: z.boolean().optional(),\n oauthAccount: AccountInfoSchema.optional(),\n iterm2KeyBindingInstalled: z.boolean().optional(),\n shiftEnterKeyBindingInstalled: z.boolean().optional(),\n proxy: z.string().optional(),\n stream: z.boolean().optional(),\n modelProfiles: z.array(ModelProfileSchema).optional(),\n modelPointers: ModelPointersSchema.optional(),\n defaultModelName: z.string().optional(),\n lastDismissedUpdateVersion: z.string().optional(),\n compressionMode: CompressionModeSchema.optional(),\n thinking: z.boolean().optional(),\n // Config version for migrations\n configVersion: z.number().optional(),\n})\n\n// Type exports for TypeScript usage\nexport type McpStdioServerConfig = z.infer<typeof McpStdioServerConfigSchema>\nexport type McpSSEServerConfig = z.infer<typeof McpSSEServerConfigSchema>\nexport type McpServerConfig = z.infer<typeof McpServerConfigSchema>\nexport type AutoUpdaterStatus = z.infer<typeof AutoUpdaterStatusSchema>\nexport type NotificationChannel = z.infer<typeof NotificationChannelSchema>\nexport type ProviderType = z.infer<typeof ProviderTypeSchema>\nexport type CompressionMode = z.infer<typeof CompressionModeSchema>\nexport type ReasoningEffort = z.infer<typeof ReasoningEffortSchema>\nexport type ModelProfile = z.infer<typeof ModelProfileSchema>\nexport type ModelPointerType = z.infer<typeof ModelPointerTypeSchema>\nexport type ModelPointers = z.infer<typeof ModelPointersSchema>\nexport type AccountInfo = z.infer<typeof AccountInfoSchema>\nexport type ProjectConfig = z.infer<typeof ProjectConfigSchema>\nexport type GlobalConfig = z.infer<typeof GlobalConfigSchema>\n\n// Validation utilities\nexport function validateGlobalConfig(config: unknown): GlobalConfig {\n return GlobalConfigSchema.parse(config)\n}\n\nexport function validateProjectConfig(config: unknown): ProjectConfig {\n return ProjectConfigSchema.parse(config)\n}\n\nexport function safeValidateGlobalConfig(config: unknown): {\n success: boolean\n data?: GlobalConfig\n error?: z.ZodError\n} {\n const result = GlobalConfigSchema.safeParse(config)\n return result.success\n ? { success: true, data: result.data }\n : { success: false, error: result.error }\n}\n\nexport function safeValidateProjectConfig(config: unknown): {\n success: boolean\n data?: ProjectConfig\n error?: z.ZodError\n} {\n const result = ProjectConfigSchema.safeParse(config)\n return result.success\n ? { success: true, data: result.data }\n : { success: false, error: result.error }\n}\n"],
|
|
5
|
-
"mappings": "AAMA,SAAS,SAAS;AAGX,MAAM,6BAA6B,EAAE,OAAO;AAAA,EACjD,MAAM,EAAE,QAAQ,OAAO,EAAE,SAAS,EAAE,QAAQ,OAAO;AAAA,EACnD,SAAS,EAAE,OAAO;AAAA,EAClB,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EACxB,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnC,SAAS,EAAE,QAAQ,EAAE,SAAS;AAChC,CAAC;AAEM,MAAM,2BAA2B,EAAE,OAAO;AAAA,EAC/C,MAAM,EAAE,QAAQ,KAAK;AAAA,EACrB,KAAK,EAAE,OAAO;AAAA,EACd,SAAS,EAAE,QAAQ,EAAE,SAAS;AAChC,CAAC;AAEM,MAAM,wBAAwB,EAAE,MAAM;AAAA,EAC3C;AAAA,EACA;AACF,CAAC;AAGM,MAAM,0BAA0B,EAAE,KAAK;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,4BAA4B,EAAE,KAAK;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,qBAAqB,EAAE,KAAK;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,wBAAwB,EAAE,KAAK,CAAC,YAAY,MAAM,CAAC;AAGzD,MAAM,wBAAwB,EAAE,KAAK;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,MAAM,EAAE,OAAO;AAAA,EACf,UAAU;AAAA,EACV,WAAW,EAAE,OAAO;AAAA,EACpB,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,QAAQ,EAAE,OAAO;AAAA,EACjB,WAAW,EAAE,OAAO;AAAA,EACpB,eAAe,EAAE,OAAO;AAAA,EACxB,iBAAiB,sBAAsB,SAAS;AAAA,EAChD,UAAU,EAAE,QAAQ;AAAA,EACpB,WAAW,EAAE,OAAO;AAAA,EACpB,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,QAAQ,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC7B,kBAAkB,EACf,KAAK,CAAC,SAAS,gBAAgB,eAAe,CAAC,EAC/C,SAAS;AAAA,EACZ,gBAAgB,EAAE,OAAO,EAAE,SAAS;AACtC,CAAC;AAGM,MAAM,yBAAyB,EAAE,KAAK;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,MAAM,EAAE,OAAO;AAAA,EACf,MAAM,EAAE,OAAO;AAAA,EACf,WAAW,EAAE,OAAO;AAAA,EACpB,OAAO,EAAE,OAAO;
|
|
4
|
+
"sourcesContent": ["/**\n * Configuration Schema Definitions\n *\n * Defines all configuration types for Minto with Zod validation.\n */\n\nimport { z } from 'zod'\n\n// MCP Server Configurations\nexport const McpStdioServerConfigSchema = z.object({\n type: z.literal('stdio').optional().default('stdio'),\n command: z.string(),\n args: z.array(z.string()),\n env: z.record(z.string()).optional(),\n enabled: z.boolean().optional(),\n})\n\nexport const McpSSEServerConfigSchema = z.object({\n type: z.literal('sse'),\n url: z.string(),\n enabled: z.boolean().optional(),\n})\n\nexport const McpServerConfigSchema = z.union([\n McpStdioServerConfigSchema,\n McpSSEServerConfigSchema,\n])\n\n// Auto Updater Status\nexport const AutoUpdaterStatusSchema = z.enum([\n 'disabled',\n 'enabled',\n 'no_permissions',\n 'not_configured',\n])\n\n// Notification Channel\nexport const NotificationChannelSchema = z.enum([\n 'iterm2',\n 'terminal_bell',\n 'iterm2_with_bell',\n 'notifications_disabled',\n])\n\n// Provider Types\nexport const ProviderTypeSchema = z.enum([\n 'anthropic',\n 'openai',\n 'mistral',\n 'deepseek',\n 'kimi',\n 'qwen',\n 'glm',\n 'minimax',\n 'baidu-qianfan',\n 'siliconflow',\n 'bigdream',\n 'opendev',\n 'xai',\n 'groq',\n 'gemini',\n 'ollama',\n 'azure',\n 'custom',\n 'custom-openai',\n])\n\n// Compression Mode\nexport const CompressionModeSchema = z.enum(['business', 'code'])\n\n// UI Language\nexport const UILanguageSchema = z.enum(['en', 'zh-CN'])\n\n// Safety Mode\nexport const SafetyModeSchema = z.enum(['yolo', 'smart', 'strict', 'free'])\n\n// Reasoning Effort\nexport const ReasoningEffortSchema = z.enum([\n 'minimal',\n 'low',\n 'medium',\n 'high',\n])\n\n// Model Profile\nexport const ModelProfileSchema = z.object({\n name: z.string(),\n provider: ProviderTypeSchema,\n modelName: z.string(),\n baseURL: z.string().optional(),\n apiKey: z.string(),\n maxTokens: z.number(),\n contextLength: z.number(),\n reasoningEffort: ReasoningEffortSchema.optional(),\n isActive: z.boolean(),\n createdAt: z.number(),\n lastUsed: z.number().optional(),\n isGPT5: z.boolean().optional(),\n validationStatus: z\n .enum(['valid', 'needs_repair', 'auto_repaired'])\n .optional(),\n lastValidation: z.number().optional(),\n})\n\n// Model Pointer Type\nexport const ModelPointerTypeSchema = z.enum([\n 'main',\n 'task',\n 'reasoning',\n 'quick',\n 'compact',\n])\n\n// Model Pointers\nexport const ModelPointersSchema = z.object({\n main: z.string(),\n task: z.string(),\n reasoning: z.string(),\n quick: z.string(),\n compact: z.string(),\n})\n\n// Account Info\nexport const AccountInfoSchema = z.object({\n accountUuid: z.string(),\n emailAddress: z.string(),\n organizationUuid: z.string().optional(),\n})\n\n// Project Configuration\nexport const ProjectConfigSchema = z.object({\n allowedTools: z.array(z.string()),\n context: z.record(z.string()),\n contextFiles: z.array(z.string()).optional(),\n history: z.array(z.string()),\n dontCrawlDirectory: z.boolean().optional(),\n enableArchitectTool: z.boolean().optional(),\n mcpContextUris: z.array(z.string()),\n mcpServers: z.record(McpServerConfigSchema).optional(),\n approvedMcprcServers: z.array(z.string()).optional(),\n rejectedMcprcServers: z.array(z.string()).optional(),\n lastAPIDuration: z.number().optional(),\n lastCost: z.number().optional(),\n lastDuration: z.number().optional(),\n lastSessionId: z.string().optional(),\n exampleFiles: z.array(z.string()).optional(),\n exampleFilesGeneratedAt: z.number().optional(),\n hasTrustDialogAccepted: z.boolean().optional(),\n hasCompletedProjectOnboarding: z.boolean().optional(),\n})\n\n// Global Configuration\nexport const GlobalConfigSchema = z.object({\n projects: z.record(ProjectConfigSchema).optional(),\n numStartups: z.number(),\n autoUpdaterStatus: AutoUpdaterStatusSchema.optional(),\n userID: z.string().optional(),\n theme: z.string(),\n hasCompletedOnboarding: z.boolean().optional(),\n lastOnboardingVersion: z.string().optional(),\n lastReleaseNotesSeen: z.string().optional(),\n mcpServers: z.record(McpServerConfigSchema).optional(),\n preferredNotifChannel: NotificationChannelSchema,\n verbose: z.boolean(),\n customApiKeyResponses: z\n .object({\n approved: z.array(z.string()).optional(),\n rejected: z.array(z.string()).optional(),\n })\n .optional(),\n primaryProvider: ProviderTypeSchema.optional(),\n maxTokens: z.number().optional(),\n hasAcknowledgedCostThreshold: z.boolean().optional(),\n oauthAccount: AccountInfoSchema.optional(),\n iterm2KeyBindingInstalled: z.boolean().optional(),\n shiftEnterKeyBindingInstalled: z.boolean().optional(),\n proxy: z.string().optional(),\n stream: z.boolean().optional(),\n modelProfiles: z.array(ModelProfileSchema).optional(),\n modelPointers: ModelPointersSchema.optional(),\n defaultModelName: z.string().optional(),\n lastDismissedUpdateVersion: z.string().optional(),\n compressionMode: CompressionModeSchema.optional(),\n thinking: z.boolean().optional(),\n language: UILanguageSchema.optional(),\n safetyMode: SafetyModeSchema.optional(),\n // Claude Code sync\n autoSyncClaudeCode: z.boolean().optional(),\n // File backup for /undo\n backupEnabled: z.boolean().optional(),\n // Config version for migrations\n configVersion: z.number().optional(),\n})\n\n// Type exports for TypeScript usage\nexport type McpStdioServerConfig = z.infer<typeof McpStdioServerConfigSchema>\nexport type McpSSEServerConfig = z.infer<typeof McpSSEServerConfigSchema>\nexport type McpServerConfig = z.infer<typeof McpServerConfigSchema>\nexport type AutoUpdaterStatus = z.infer<typeof AutoUpdaterStatusSchema>\nexport type NotificationChannel = z.infer<typeof NotificationChannelSchema>\nexport type ProviderType = z.infer<typeof ProviderTypeSchema>\nexport type CompressionMode = z.infer<typeof CompressionModeSchema>\nexport type UILanguage = z.infer<typeof UILanguageSchema>\nexport type SafetyMode = z.infer<typeof SafetyModeSchema>\nexport type ReasoningEffort = z.infer<typeof ReasoningEffortSchema>\nexport type ModelProfile = z.infer<typeof ModelProfileSchema>\nexport type ModelPointerType = z.infer<typeof ModelPointerTypeSchema>\nexport type ModelPointers = z.infer<typeof ModelPointersSchema>\nexport type AccountInfo = z.infer<typeof AccountInfoSchema>\nexport type ProjectConfig = z.infer<typeof ProjectConfigSchema>\nexport type GlobalConfig = z.infer<typeof GlobalConfigSchema>\n\n// Validation utilities\nexport function validateGlobalConfig(config: unknown): GlobalConfig {\n return GlobalConfigSchema.parse(config)\n}\n\nexport function validateProjectConfig(config: unknown): ProjectConfig {\n return ProjectConfigSchema.parse(config)\n}\n\nexport function safeValidateGlobalConfig(config: unknown): {\n success: boolean\n data?: GlobalConfig\n error?: z.ZodError\n} {\n const result = GlobalConfigSchema.safeParse(config)\n return result.success\n ? { success: true, data: result.data }\n : { success: false, error: result.error }\n}\n\nexport function safeValidateProjectConfig(config: unknown): {\n success: boolean\n data?: ProjectConfig\n error?: z.ZodError\n} {\n const result = ProjectConfigSchema.safeParse(config)\n return result.success\n ? { success: true, data: result.data }\n : { success: false, error: result.error }\n}\n"],
|
|
5
|
+
"mappings": "AAMA,SAAS,SAAS;AAGX,MAAM,6BAA6B,EAAE,OAAO;AAAA,EACjD,MAAM,EAAE,QAAQ,OAAO,EAAE,SAAS,EAAE,QAAQ,OAAO;AAAA,EACnD,SAAS,EAAE,OAAO;AAAA,EAClB,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EACxB,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnC,SAAS,EAAE,QAAQ,EAAE,SAAS;AAChC,CAAC;AAEM,MAAM,2BAA2B,EAAE,OAAO;AAAA,EAC/C,MAAM,EAAE,QAAQ,KAAK;AAAA,EACrB,KAAK,EAAE,OAAO;AAAA,EACd,SAAS,EAAE,QAAQ,EAAE,SAAS;AAChC,CAAC;AAEM,MAAM,wBAAwB,EAAE,MAAM;AAAA,EAC3C;AAAA,EACA;AACF,CAAC;AAGM,MAAM,0BAA0B,EAAE,KAAK;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,4BAA4B,EAAE,KAAK;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,qBAAqB,EAAE,KAAK;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,wBAAwB,EAAE,KAAK,CAAC,YAAY,MAAM,CAAC;AAGzD,MAAM,mBAAmB,EAAE,KAAK,CAAC,MAAM,OAAO,CAAC;AAG/C,MAAM,mBAAmB,EAAE,KAAK,CAAC,QAAQ,SAAS,UAAU,MAAM,CAAC;AAGnE,MAAM,wBAAwB,EAAE,KAAK;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,MAAM,EAAE,OAAO;AAAA,EACf,UAAU;AAAA,EACV,WAAW,EAAE,OAAO;AAAA,EACpB,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,QAAQ,EAAE,OAAO;AAAA,EACjB,WAAW,EAAE,OAAO;AAAA,EACpB,eAAe,EAAE,OAAO;AAAA,EACxB,iBAAiB,sBAAsB,SAAS;AAAA,EAChD,UAAU,EAAE,QAAQ;AAAA,EACpB,WAAW,EAAE,OAAO;AAAA,EACpB,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,QAAQ,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC7B,kBAAkB,EACf,KAAK,CAAC,SAAS,gBAAgB,eAAe,CAAC,EAC/C,SAAS;AAAA,EACZ,gBAAgB,EAAE,OAAO,EAAE,SAAS;AACtC,CAAC;AAGM,MAAM,yBAAyB,EAAE,KAAK;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,MAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,MAAM,EAAE,OAAO;AAAA,EACf,MAAM,EAAE,OAAO;AAAA,EACf,WAAW,EAAE,OAAO;AAAA,EACpB,OAAO,EAAE,OAAO;AAAA,EAChB,SAAS,EAAE,OAAO;AACpB,CAAC;AAGM,MAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,aAAa,EAAE,OAAO;AAAA,EACtB,cAAc,EAAE,OAAO;AAAA,EACvB,kBAAkB,EAAE,OAAO,EAAE,SAAS;AACxC,CAAC;AAGM,MAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAChC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;AAAA,EAC5B,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EAC3C,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAC3B,oBAAoB,EAAE,QAAQ,EAAE,SAAS;AAAA,EACzC,qBAAqB,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC1C,gBAAgB,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAClC,YAAY,EAAE,OAAO,qBAAqB,EAAE,SAAS;AAAA,EACrD,sBAAsB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnD,sBAAsB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnD,iBAAiB,EAAE,OAAO,EAAE,SAAS;AAAA,EACrC,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EAC3C,yBAAyB,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7C,wBAAwB,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC7C,+BAA+B,EAAE,QAAQ,EAAE,SAAS;AACtD,CAAC;AAGM,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,UAAU,EAAE,OAAO,mBAAmB,EAAE,SAAS;AAAA,EACjD,aAAa,EAAE,OAAO;AAAA,EACtB,mBAAmB,wBAAwB,SAAS;AAAA,EACpD,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,OAAO,EAAE,OAAO;AAAA,EAChB,wBAAwB,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC7C,uBAAuB,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3C,sBAAsB,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1C,YAAY,EAAE,OAAO,qBAAqB,EAAE,SAAS;AAAA,EACrD,uBAAuB;AAAA,EACvB,SAAS,EAAE,QAAQ;AAAA,EACnB,uBAAuB,EACpB,OAAO;AAAA,IACN,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,IACvC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACzC,CAAC,EACA,SAAS;AAAA,EACZ,iBAAiB,mBAAmB,SAAS;AAAA,EAC7C,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,8BAA8B,EAAE,QAAQ,EAAE,SAAS;AAAA,EACnD,cAAc,kBAAkB,SAAS;AAAA,EACzC,2BAA2B,EAAE,QAAQ,EAAE,SAAS;AAAA,EAChD,+BAA+B,EAAE,QAAQ,EAAE,SAAS;AAAA,EACpD,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,QAAQ,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC7B,eAAe,EAAE,MAAM,kBAAkB,EAAE,SAAS;AAAA,EACpD,eAAe,oBAAoB,SAAS;AAAA,EAC5C,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA,EACtC,4BAA4B,EAAE,OAAO,EAAE,SAAS;AAAA,EAChD,iBAAiB,sBAAsB,SAAS;AAAA,EAChD,UAAU,EAAE,QAAQ,EAAE,SAAS;AAAA,EAC/B,UAAU,iBAAiB,SAAS;AAAA,EACpC,YAAY,iBAAiB,SAAS;AAAA;AAAA,EAEtC,oBAAoB,EAAE,QAAQ,EAAE,SAAS;AAAA;AAAA,EAEzC,eAAe,EAAE,QAAQ,EAAE,SAAS;AAAA;AAAA,EAEpC,eAAe,EAAE,OAAO,EAAE,SAAS;AACrC,CAAC;AAqBM,SAAS,qBAAqB,QAA+B;AAClE,SAAO,mBAAmB,MAAM,MAAM;AACxC;AAEO,SAAS,sBAAsB,QAAgC;AACpE,SAAO,oBAAoB,MAAM,MAAM;AACzC;AAEO,SAAS,yBAAyB,QAIvC;AACA,QAAM,SAAS,mBAAmB,UAAU,MAAM;AAClD,SAAO,OAAO,UACV,EAAE,SAAS,MAAM,MAAM,OAAO,KAAK,IACnC,EAAE,SAAS,OAAO,OAAO,OAAO,MAAM;AAC5C;AAEO,SAAS,0BAA0B,QAIxC;AACA,QAAM,SAAS,oBAAoB,UAAU,MAAM;AACnD,SAAO,OAAO,UACV,EAAE,SAAS,MAAM,MAAM,OAAO,KAAK,IACnC,EAAE,SAAS,OAAO,OAAO,OAAO,MAAM;AAC5C;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/core/costTracker.js
CHANGED
|
@@ -2,11 +2,6 @@ import chalk from "chalk";
|
|
|
2
2
|
import { useEffect } from "react";
|
|
3
3
|
import signalExit from "signal-exit";
|
|
4
4
|
import { formatDuration } from "../utils/format.js";
|
|
5
|
-
import {
|
|
6
|
-
getCurrentProjectConfig,
|
|
7
|
-
saveCurrentProjectConfig
|
|
8
|
-
} from "../utils/config.js";
|
|
9
|
-
import { SESSION_ID } from "../utils/log.js";
|
|
10
5
|
import { tokenStatsManager } from "./tokenStatsManager.js";
|
|
11
6
|
const STATE = {
|
|
12
7
|
totalCost: 0,
|
|
@@ -86,17 +81,6 @@ function useCostSummary() {
|
|
|
86
81
|
signalExit(
|
|
87
82
|
() => {
|
|
88
83
|
process.stdout.write("\n" + formatTotalCost() + "\n");
|
|
89
|
-
try {
|
|
90
|
-
const projectConfig = getCurrentProjectConfig();
|
|
91
|
-
saveCurrentProjectConfig({
|
|
92
|
-
...projectConfig,
|
|
93
|
-
lastCost: STATE.totalCost,
|
|
94
|
-
lastAPIDuration: STATE.totalAPIDuration,
|
|
95
|
-
lastDuration: getTotalDuration(),
|
|
96
|
-
lastSessionId: SESSION_ID
|
|
97
|
-
});
|
|
98
|
-
} catch {
|
|
99
|
-
}
|
|
100
84
|
},
|
|
101
85
|
{ alwaysLast: true }
|
|
102
86
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/core/costTracker.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Cost Tracker\n *\n * Tracks API costs and usage metrics for the session.\n * This is the core implementation; the original cost-tracker.ts\n * re-exports from this module for backwards compatibility.\n *\n * NOTE: Token statistics are now managed by TokenStatsManager.\n * This module delegates getTokenCounts() to the unified manager.\n */\n\nimport chalk from 'chalk'\nimport { useEffect } from 'react'\nimport signalExit from 'signal-exit'\nimport { formatDuration } from '../utils/format'\nimport {
|
|
5
|
-
"mappings": "AAWA,OAAO,WAAW;AAClB,SAAS,iBAAiB;AAC1B,OAAO,gBAAgB;AACvB,SAAS,sBAAsB;AAC/B
|
|
4
|
+
"sourcesContent": ["/**\n * Cost Tracker\n *\n * Tracks API costs and usage metrics for the session.\n * This is the core implementation; the original cost-tracker.ts\n * re-exports from this module for backwards compatibility.\n *\n * NOTE: Token statistics are now managed by TokenStatsManager.\n * This module delegates getTokenCounts() to the unified manager.\n */\n\nimport chalk from 'chalk'\nimport { useEffect } from 'react'\nimport signalExit from 'signal-exit'\nimport { formatDuration } from '../utils/format'\nimport { tokenStatsManager } from './tokenStatsManager'\n\n/**\n * Cost tracking state\n *\n * NOTE: Token statistics are now managed by TokenStatsManager.\n * This state only tracks cost, duration, and request count.\n */\ninterface CostState {\n /** Total cost in dollars */\n totalCost: number\n\n /** Total API call duration in milliseconds */\n totalAPIDuration: number\n\n /** Session start timestamp */\n startTime: number\n\n /** Request count */\n requestCount: number\n}\n\n// Global state (token tracking delegated to TokenStatsManager)\nconst STATE: CostState = {\n totalCost: 0,\n totalAPIDuration: 0,\n startTime: Date.now(),\n requestCount: 0,\n}\n\n/**\n * Add to total cost\n */\nexport function addToTotalCost(cost: number, duration: number): void {\n STATE.totalCost += cost\n STATE.totalAPIDuration += duration\n STATE.requestCount += 1\n}\n\n/**\n * Add token usage\n *\n * @deprecated Use tokenStatsManager.recordUsage() instead.\n * This function is kept for backward compatibility but no longer updates\n * the global token statistics. Token tracking is now handled by TokenStatsManager.\n */\nexport function addTokenUsage(\n _inputTokens: number,\n _outputTokens: number,\n _cacheCreationTokens?: number,\n _cacheReadTokens?: number,\n): void {\n // DEPRECATED: This function is a no-op.\n // Token tracking is now handled by TokenStatsManager in claude.ts via recordTokenUsage().\n}\n\n/**\n * Get total cost\n */\nexport function getTotalCost(): number {\n return STATE.totalCost\n}\n\n/**\n * Get total duration (wall clock time)\n */\nexport function getTotalDuration(): number {\n return Date.now() - STATE.startTime\n}\n\n/**\n * Get total API duration\n */\nexport function getTotalAPIDuration(): number {\n return STATE.totalAPIDuration\n}\n\n/**\n * Get token counts\n *\n * Delegates to TokenStatsManager for unified token statistics.\n */\nexport function getTokenCounts(): {\n input: number\n output: number\n cacheCreation: number\n cacheRead: number\n total: number\n} {\n // Delegate to TokenStatsManager (single source of truth)\n const globalStats = tokenStatsManager.getGlobalStats()\n return {\n input: globalStats.totalInputTokens,\n output: globalStats.totalOutputTokens,\n cacheCreation: globalStats.totalCacheCreationTokens,\n cacheRead: globalStats.totalCacheReadTokens,\n total: globalStats.grandTotalTokens,\n }\n}\n\n/**\n * Get request count\n */\nexport function getRequestCount(): number {\n return STATE.requestCount\n}\n\n/**\n * Get full cost summary\n */\nexport function getCostSummary(): {\n cost: number\n apiDuration: number\n wallDuration: number\n tokens: ReturnType<typeof getTokenCounts>\n requests: number\n} {\n return {\n cost: STATE.totalCost,\n apiDuration: STATE.totalAPIDuration,\n wallDuration: getTotalDuration(),\n tokens: getTokenCounts(),\n requests: STATE.requestCount,\n }\n}\n\n/**\n * Format cost for display\n */\nfunction formatCost(cost: number): string {\n return `$${cost > 0.5 ? round(cost, 100).toFixed(2) : cost.toFixed(4)}`\n}\n\n/**\n * Round to precision\n */\nfunction round(number: number, precision: number): number {\n return Math.round(number * precision) / precision\n}\n\n/**\n * Format total cost for display\n */\nexport function formatTotalCost(): string {\n return chalk.grey(\n `Total cost: ${formatCost(STATE.totalCost)}\nTotal duration (API): ${formatDuration(STATE.totalAPIDuration)}\nTotal duration (wall): ${formatDuration(getTotalDuration())}`,\n )\n}\n\n/**\n * Format detailed cost summary\n */\nexport function formatDetailedCost(): string {\n const tokens = getTokenCounts()\n return chalk.grey(\n `Total cost: ${formatCost(STATE.totalCost)}\nTotal duration (API): ${formatDuration(STATE.totalAPIDuration)}\nTotal duration (wall): ${formatDuration(getTotalDuration())}\nTokens: ${tokens.input.toLocaleString()} in / ${tokens.output.toLocaleString()} out\nCache: ${tokens.cacheCreation.toLocaleString()} created / ${tokens.cacheRead.toLocaleString()} read\nRequests: ${STATE.requestCount}`,\n )\n}\n\n// Flag to ensure we only register once and never unregister\nlet exitHandlerRegistered = false\n\n/**\n * React hook for cost summary on exit\n *\n * CRITICAL FIX: Use signal-exit with alwaysLast: true\n *\n * The problem was that Ink also uses signal-exit (with alwaysLast: false)\n * to clean up on exit. During Ink's cleanup, it may:\n * 1. Call onRender() one last time\n * 2. Use ansiEscapes.eraseLines() to clear previous output\n * 3. Or even call clearTerminal if output height >= terminal rows\n *\n * By using signal-exit with alwaysLast: true, we ensure our handler\n * runs AFTER Ink's cleanup, so our statistics are the last thing printed.\n */\nexport function useCostSummary(): void {\n useEffect(() => {\n // Only register ONCE, and NEVER unregister\n if (exitHandlerRegistered) {\n return\n }\n exitHandlerRegistered = true\n\n // Use signal-exit with alwaysLast: true to run AFTER Ink's cleanup\n signalExit(\n () => {\n // Write statistics to stdout\n process.stdout.write('\\n' + formatTotalCost() + '\\n')\n },\n { alwaysLast: true },\n )\n\n // NO cleanup - never unregister the exit handler\n }, [])\n}\n\n/**\n * Reset state (for testing only)\n */\nexport function resetStateForTests(): void {\n if (process.env.NODE_ENV !== 'test') {\n throw new Error('resetStateForTests can only be called in tests')\n }\n STATE.startTime = Date.now()\n STATE.totalCost = 0\n STATE.totalAPIDuration = 0\n STATE.requestCount = 0\n // Token stats are managed by TokenStatsManager - reset there if needed\n tokenStatsManager.reset()\n}\n"],
|
|
5
|
+
"mappings": "AAWA,OAAO,WAAW;AAClB,SAAS,iBAAiB;AAC1B,OAAO,gBAAgB;AACvB,SAAS,sBAAsB;AAC/B,SAAS,yBAAyB;AAuBlC,MAAM,QAAmB;AAAA,EACvB,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,WAAW,KAAK,IAAI;AAAA,EACpB,cAAc;AAChB;AAKO,SAAS,eAAe,MAAc,UAAwB;AACnE,QAAM,aAAa;AACnB,QAAM,oBAAoB;AAC1B,QAAM,gBAAgB;AACxB;AASO,SAAS,cACd,cACA,eACA,sBACA,kBACM;AAGR;AAKO,SAAS,eAAuB;AACrC,SAAO,MAAM;AACf;AAKO,SAAS,mBAA2B;AACzC,SAAO,KAAK,IAAI,IAAI,MAAM;AAC5B;AAKO,SAAS,sBAA8B;AAC5C,SAAO,MAAM;AACf;AAOO,SAAS,iBAMd;AAEA,QAAM,cAAc,kBAAkB,eAAe;AACrD,SAAO;AAAA,IACL,OAAO,YAAY;AAAA,IACnB,QAAQ,YAAY;AAAA,IACpB,eAAe,YAAY;AAAA,IAC3B,WAAW,YAAY;AAAA,IACvB,OAAO,YAAY;AAAA,EACrB;AACF;AAKO,SAAS,kBAA0B;AACxC,SAAO,MAAM;AACf;AAKO,SAAS,iBAMd;AACA,SAAO;AAAA,IACL,MAAM,MAAM;AAAA,IACZ,aAAa,MAAM;AAAA,IACnB,cAAc,iBAAiB;AAAA,IAC/B,QAAQ,eAAe;AAAA,IACvB,UAAU,MAAM;AAAA,EAClB;AACF;AAKA,SAAS,WAAW,MAAsB;AACxC,SAAO,IAAI,OAAO,MAAM,MAAM,MAAM,GAAG,EAAE,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;AACvE;AAKA,SAAS,MAAM,QAAgB,WAA2B;AACxD,SAAO,KAAK,MAAM,SAAS,SAAS,IAAI;AAC1C;AAKO,SAAS,kBAA0B;AACxC,SAAO,MAAM;AAAA,IACX,eAAe,WAAW,MAAM,SAAS,CAAC;AAAA,wBACtB,eAAe,MAAM,gBAAgB,CAAC;AAAA,yBACrC,eAAe,iBAAiB,CAAC,CAAC;AAAA,EACzD;AACF;AAKO,SAAS,qBAA6B;AAC3C,QAAM,SAAS,eAAe;AAC9B,SAAO,MAAM;AAAA,IACX,eAAe,WAAW,MAAM,SAAS,CAAC;AAAA,wBACtB,eAAe,MAAM,gBAAgB,CAAC;AAAA,yBACrC,eAAe,iBAAiB,CAAC,CAAC;AAAA,UACjD,OAAO,MAAM,eAAe,CAAC,SAAS,OAAO,OAAO,eAAe,CAAC;AAAA,SACrE,OAAO,cAAc,eAAe,CAAC,cAAc,OAAO,UAAU,eAAe,CAAC;AAAA,YACjF,MAAM,YAAY;AAAA,EAC5B;AACF;AAGA,IAAI,wBAAwB;AAgBrB,SAAS,iBAAuB;AACrC,YAAU,MAAM;AAEd,QAAI,uBAAuB;AACzB;AAAA,IACF;AACA,4BAAwB;AAGxB;AAAA,MACE,MAAM;AAEJ,gBAAQ,OAAO,MAAM,OAAO,gBAAgB,IAAI,IAAI;AAAA,MACtD;AAAA,MACA,EAAE,YAAY,KAAK;AAAA,IACrB;AAAA,EAGF,GAAG,CAAC,CAAC;AACP;AAKO,SAAS,qBAA2B;AACzC,MAAI,QAAQ,IAAI,aAAa,QAAQ;AACnC,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AACA,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,YAAY;AAClB,QAAM,mBAAmB;AACzB,QAAM,eAAe;AAErB,oBAAkB,MAAM;AAC1B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cost-tracker.js
CHANGED
|
@@ -2,11 +2,6 @@ import chalk from "chalk";
|
|
|
2
2
|
import { useEffect } from "react";
|
|
3
3
|
import signalExit from "signal-exit";
|
|
4
4
|
import { formatDuration } from "./utils/format.js";
|
|
5
|
-
import {
|
|
6
|
-
getCurrentProjectConfig,
|
|
7
|
-
saveCurrentProjectConfig
|
|
8
|
-
} from "./utils/config.js";
|
|
9
|
-
import { SESSION_ID } from "./utils/log.js";
|
|
10
5
|
const STATE = {
|
|
11
6
|
totalCost: 0,
|
|
12
7
|
totalAPIDuration: 0,
|
|
@@ -45,17 +40,6 @@ function useCostSummary() {
|
|
|
45
40
|
signalExit(
|
|
46
41
|
() => {
|
|
47
42
|
process.stdout.write("\n" + formatTotalCost() + "\n");
|
|
48
|
-
try {
|
|
49
|
-
const projectConfig = getCurrentProjectConfig();
|
|
50
|
-
saveCurrentProjectConfig({
|
|
51
|
-
...projectConfig,
|
|
52
|
-
lastCost: STATE.totalCost,
|
|
53
|
-
lastAPIDuration: STATE.totalAPIDuration,
|
|
54
|
-
lastDuration: getTotalDuration(),
|
|
55
|
-
lastSessionId: SESSION_ID
|
|
56
|
-
});
|
|
57
|
-
} catch {
|
|
58
|
-
}
|
|
59
43
|
},
|
|
60
44
|
{ alwaysLast: true }
|
|
61
45
|
);
|
package/dist/cost-tracker.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/cost-tracker.ts"],
|
|
4
|
-
"sourcesContent": ["import chalk from 'chalk'\nimport { useEffect } from 'react'\nimport signalExit from 'signal-exit'\nimport { formatDuration } from './utils/format'\
|
|
5
|
-
"mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,iBAAiB;AAC1B,OAAO,gBAAgB;AACvB,SAAS,sBAAsB;
|
|
4
|
+
"sourcesContent": ["import chalk from 'chalk'\nimport { useEffect } from 'react'\nimport signalExit from 'signal-exit'\nimport { formatDuration } from './utils/format'\n\n// DO NOT ADD MORE STATE HERE OR BORIS WILL CURSE YOU\nconst STATE: {\n totalCost: number\n totalAPIDuration: number\n startTime: number\n} = {\n totalCost: 0,\n totalAPIDuration: 0,\n startTime: Date.now(),\n}\n\nexport function addToTotalCost(cost: number, duration: number): void {\n STATE.totalCost += cost\n STATE.totalAPIDuration += duration\n}\n\nexport function getTotalCost(): number {\n return STATE.totalCost\n}\n\nexport function getTotalDuration(): number {\n return Date.now() - STATE.startTime\n}\n\nexport function getTotalAPIDuration(): number {\n return STATE.totalAPIDuration\n}\n\nfunction formatCost(cost: number): string {\n return `$${cost > 0.5 ? round(cost, 100).toFixed(2) : cost.toFixed(4)}`\n}\n\nexport function formatTotalCost(): string {\n return chalk.grey(\n `Total cost: ${formatCost(STATE.totalCost)}\nTotal duration (API): ${formatDuration(STATE.totalAPIDuration)}\nTotal duration (wall): ${formatDuration(getTotalDuration())}`,\n )\n}\n\n/**\n * useCostSummary - Register exit handler to display cost summary\n *\n * CRITICAL FIX: Use signal-exit with alwaysLast: true\n *\n * The problem was that Ink also uses signal-exit (with alwaysLast: false)\n * to clean up on exit. During Ink's cleanup, it may:\n * 1. Call onRender() one last time\n * 2. Use ansiEscapes.eraseLines() to clear previous output\n * 3. Or even call clearTerminal if output height >= terminal rows\n *\n * By using signal-exit with alwaysLast: true, we ensure our handler\n * runs AFTER Ink's cleanup, so our statistics are the last thing printed.\n */\n// Flag to ensure we only register once and never unregister\nlet exitHandlerRegistered = false\n\nexport function useCostSummary(): void {\n useEffect(() => {\n // Only register ONCE, and NEVER unregister\n if (exitHandlerRegistered) {\n return\n }\n exitHandlerRegistered = true\n\n // Use signal-exit with alwaysLast: true to run AFTER Ink's cleanup\n // This ensures our statistics are printed after Ink finishes its final render\n signalExit(\n () => {\n // Write statistics to stdout\n // Using stdout.write directly to avoid any Ink interception\n process.stdout.write('\\n' + formatTotalCost() + '\\n')\n },\n { alwaysLast: true },\n )\n\n // NO cleanup function - we NEVER want to unregister this handler\n }, [])\n}\n\nfunction round(number: number, precision: number): number {\n return Math.round(number * precision) / precision\n}\n\n// Only used in tests\nexport function resetStateForTests(): void {\n if (process.env.NODE_ENV !== 'test') {\n throw new Error('resetStateForTests can only be called in tests')\n }\n STATE.startTime = Date.now()\n STATE.totalCost = 0\n STATE.totalAPIDuration = 0\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,iBAAiB;AAC1B,OAAO,gBAAgB;AACvB,SAAS,sBAAsB;AAG/B,MAAM,QAIF;AAAA,EACF,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,WAAW,KAAK,IAAI;AACtB;AAEO,SAAS,eAAe,MAAc,UAAwB;AACnE,QAAM,aAAa;AACnB,QAAM,oBAAoB;AAC5B;AAEO,SAAS,eAAuB;AACrC,SAAO,MAAM;AACf;AAEO,SAAS,mBAA2B;AACzC,SAAO,KAAK,IAAI,IAAI,MAAM;AAC5B;AAEO,SAAS,sBAA8B;AAC5C,SAAO,MAAM;AACf;AAEA,SAAS,WAAW,MAAsB;AACxC,SAAO,IAAI,OAAO,MAAM,MAAM,MAAM,GAAG,EAAE,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;AACvE;AAEO,SAAS,kBAA0B;AACxC,SAAO,MAAM;AAAA,IACX,eAAe,WAAW,MAAM,SAAS,CAAC;AAAA,wBACtB,eAAe,MAAM,gBAAgB,CAAC;AAAA,yBACrC,eAAe,iBAAiB,CAAC,CAAC;AAAA,EACzD;AACF;AAiBA,IAAI,wBAAwB;AAErB,SAAS,iBAAuB;AACrC,YAAU,MAAM;AAEd,QAAI,uBAAuB;AACzB;AAAA,IACF;AACA,4BAAwB;AAIxB;AAAA,MACE,MAAM;AAGJ,gBAAQ,OAAO,MAAM,OAAO,gBAAgB,IAAI,IAAI;AAAA,MACtD;AAAA,MACA,EAAE,YAAY,KAAK;AAAA,IACrB;AAAA,EAGF,GAAG,CAAC,CAAC;AACP;AAEA,SAAS,MAAM,QAAgB,WAA2B;AACxD,SAAO,KAAK,MAAM,SAAS,SAAS,IAAI;AAC1C;AAGO,SAAS,qBAA2B;AACzC,MAAI,QAAQ,IAAI,aAAa,QAAQ;AACnC,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AACA,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,YAAY;AAClB,QAAM,mBAAmB;AAC3B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -35,7 +35,9 @@ async function main() {
|
|
|
35
35
|
const message = err.message || "";
|
|
36
36
|
if (message.includes("react-devtools-core")) {
|
|
37
37
|
console.error("\n\u274C Failed to start: react-devtools-core not found");
|
|
38
|
-
console.error(
|
|
38
|
+
console.error(
|
|
39
|
+
" This may be caused by DEV environment variable being set."
|
|
40
|
+
);
|
|
39
41
|
console.error(" Try running: unset DEV && minto\n");
|
|
40
42
|
} else if (message.includes("yoga") || message.includes("YOGA")) {
|
|
41
43
|
console.error("\n\u274C Failed to start: yoga-layout initialization failed");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/entrypoints/bootstrap.ts"],
|
|
4
|
-
"sourcesContent": ["#!/usr/bin/env -S node --no-warnings=ExperimentalWarning --enable-source-maps\n\n/**\n * Bootstrap entry point for Minto CLI\n *\n * CRITICAL: This file MUST remain minimal with NO static imports of application code.\n *\n * This bootstrap exists because ESM static imports execute BEFORE the importing\n * module's code. We need certain initializations to happen BEFORE any application\n * modules are loaded:\n *\n * 1. DEV='false' - Ink checks process.env['DEV'] at module load time and tries to\n * import 'react-devtools-core' if DEV='true'. This package isn't bundled.\n *\n * 2. EventEmitter.defaultMaxListeners - Must be set before any modules create\n * EventEmitters to prevent \"MaxListenersExceededWarning\" during startup.\n *\n * 3. YOGA_WASM_PATH - Must be set before Ink initializes yoga-layout for proper\n * terminal layout rendering.\n *\n * Solution: Use dynamic import() to load cli.tsx AFTER all initializations.\n * Dynamic import() is evaluated at runtime, not at module load time.\n *\n * DO NOT add any static imports here except for Node.js built-ins that don't\n * chain to application code (directly or transitively).\n */\n\nimport { EventEmitter } from 'events'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\nimport { existsSync } from 'node:fs'\n\n// ============================================================================\n// 1. Disable Ink's development mode\n// ============================================================================\n// Prevents Ink from trying to load react-devtools-core which isn't bundled.\n// Must use both syntaxes because Bun's --define only replaces dot syntax.\nprocess.env.DEV = 'false'\nprocess.env['DEV'] = 'false'\n\n// ============================================================================\n// 2. Increase EventEmitter max listeners\n// ============================================================================\n// Components like ModelConfig, ModelSelector use multiple useInput hooks which\n// add listeners. Default is 10, we increase to 20 to accommodate deep hierarchies.\n// Must be set before any modules create EventEmitters.\nEventEmitter.defaultMaxListeners = 20\n\n// ============================================================================\n// 3. Set YOGA_WASM_PATH for Ink layout engine\n// ============================================================================\n// Ink uses yoga-layout for terminal UI layout. The WASM file path must be set\n// before Ink initializes. Resolve relative to this file for both dev and dist.\ntry {\n if (!process.env.YOGA_WASM_PATH) {\n const __filename = fileURLToPath(import.meta.url)\n const __dirname = dirname(__filename)\n // Check multiple possible locations\n const candidates = [\n join(__dirname, './yoga.wasm'),
|
|
5
|
-
"mappings": ";AA2BA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,SAAS,YAAY;AAC9B,SAAS,kBAAkB;AAO3B,QAAQ,IAAI,MAAM;AAClB,QAAQ,IAAI,KAAK,IAAI;AAQrB,aAAa,sBAAsB;AAOnC,IAAI;AACF,MAAI,CAAC,QAAQ,IAAI,gBAAgB;AAC/B,UAAM,aAAa,cAAc,YAAY,GAAG;AAChD,UAAM,YAAY,QAAQ,UAAU;AAEpC,UAAM,aAAa;AAAA,MACjB,KAAK,WAAW,aAAa;AAAA;AAAA,MAC7B,KAAK,WAAW,cAAc;AAAA;AAAA,MAC9B,KAAK,WAAW,iBAAiB;AAAA;AAAA,IACnC;AACA,eAAW,aAAa,YAAY;AAClC,UAAI,WAAW,SAAS,GAAG;AACzB,gBAAQ,IAAI,iBAAiB;AAC7B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,QAAQ;AAER;AAMA,eAAe,OAAsB;AACnC,MAAI;AACF,UAAM,OAAO,UAAU;AAAA,EACzB,SAAS,OAAO;AAEd,UAAM,MAAM;AACZ,UAAM,UAAU,IAAI,WAAW;AAE/B,QAAI,QAAQ,SAAS,qBAAqB,GAAG;AAC3C,cAAQ,MAAM,yDAAoD;AAClE,cAAQ,
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env -S node --no-warnings=ExperimentalWarning --enable-source-maps\n\n/**\n * Bootstrap entry point for Minto CLI\n *\n * CRITICAL: This file MUST remain minimal with NO static imports of application code.\n *\n * This bootstrap exists because ESM static imports execute BEFORE the importing\n * module's code. We need certain initializations to happen BEFORE any application\n * modules are loaded:\n *\n * 1. DEV='false' - Ink checks process.env['DEV'] at module load time and tries to\n * import 'react-devtools-core' if DEV='true'. This package isn't bundled.\n *\n * 2. EventEmitter.defaultMaxListeners - Must be set before any modules create\n * EventEmitters to prevent \"MaxListenersExceededWarning\" during startup.\n *\n * 3. YOGA_WASM_PATH - Must be set before Ink initializes yoga-layout for proper\n * terminal layout rendering.\n *\n * Solution: Use dynamic import() to load cli.tsx AFTER all initializations.\n * Dynamic import() is evaluated at runtime, not at module load time.\n *\n * DO NOT add any static imports here except for Node.js built-ins that don't\n * chain to application code (directly or transitively).\n */\n\nimport { EventEmitter } from 'events'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\nimport { existsSync } from 'node:fs'\n\n// ============================================================================\n// 1. Disable Ink's development mode\n// ============================================================================\n// Prevents Ink from trying to load react-devtools-core which isn't bundled.\n// Must use both syntaxes because Bun's --define only replaces dot syntax.\nprocess.env.DEV = 'false'\nprocess.env['DEV'] = 'false'\n\n// ============================================================================\n// 2. Increase EventEmitter max listeners\n// ============================================================================\n// Components like ModelConfig, ModelSelector use multiple useInput hooks which\n// add listeners. Default is 10, we increase to 20 to accommodate deep hierarchies.\n// Must be set before any modules create EventEmitters.\nEventEmitter.defaultMaxListeners = 20\n\n// ============================================================================\n// 3. Set YOGA_WASM_PATH for Ink layout engine\n// ============================================================================\n// Ink uses yoga-layout for terminal UI layout. The WASM file path must be set\n// before Ink initializes. Resolve relative to this file for both dev and dist.\ntry {\n if (!process.env.YOGA_WASM_PATH) {\n const __filename = fileURLToPath(import.meta.url)\n const __dirname = dirname(__filename)\n // Check multiple possible locations\n const candidates = [\n join(__dirname, './yoga.wasm'), // dist/entrypoints/yoga.wasm\n join(__dirname, '../yoga.wasm'), // dist/yoga.wasm\n join(__dirname, '../../yoga.wasm'), // project root (dev mode)\n ]\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n process.env.YOGA_WASM_PATH = candidate\n break\n }\n }\n }\n} catch {\n // Ignore errors - yoga will use fallback or error at runtime\n}\n\n// ============================================================================\n// Main: Dynamically import the CLI\n// ============================================================================\n// This ensures all static imports in cli.tsx see the initialized environment.\nasync function main(): Promise<void> {\n try {\n await import('./cli.js')\n } catch (error) {\n // Provide helpful error messages for common failures\n const err = error as Error\n const message = err.message || ''\n\n if (message.includes('react-devtools-core')) {\n console.error('\\n\u274C Failed to start: react-devtools-core not found')\n console.error(\n ' This may be caused by DEV environment variable being set.',\n )\n console.error(' Try running: unset DEV && minto\\n')\n } else if (message.includes('yoga') || message.includes('YOGA')) {\n console.error('\\n\u274C Failed to start: yoga-layout initialization failed')\n console.error(' The yoga.wasm file may be missing or corrupted.')\n console.error(' Try reinstalling: npm install -g @within-7/minto\\n')\n } else {\n console.error('\u274C Failed to start:', message || err)\n if (err.stack) {\n console.error(err.stack)\n }\n }\n process.exit(1)\n }\n}\n\nmain()\n"],
|
|
5
|
+
"mappings": ";AA2BA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,SAAS,YAAY;AAC9B,SAAS,kBAAkB;AAO3B,QAAQ,IAAI,MAAM;AAClB,QAAQ,IAAI,KAAK,IAAI;AAQrB,aAAa,sBAAsB;AAOnC,IAAI;AACF,MAAI,CAAC,QAAQ,IAAI,gBAAgB;AAC/B,UAAM,aAAa,cAAc,YAAY,GAAG;AAChD,UAAM,YAAY,QAAQ,UAAU;AAEpC,UAAM,aAAa;AAAA,MACjB,KAAK,WAAW,aAAa;AAAA;AAAA,MAC7B,KAAK,WAAW,cAAc;AAAA;AAAA,MAC9B,KAAK,WAAW,iBAAiB;AAAA;AAAA,IACnC;AACA,eAAW,aAAa,YAAY;AAClC,UAAI,WAAW,SAAS,GAAG;AACzB,gBAAQ,IAAI,iBAAiB;AAC7B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,QAAQ;AAER;AAMA,eAAe,OAAsB;AACnC,MAAI;AACF,UAAM,OAAO,UAAU;AAAA,EACzB,SAAS,OAAO;AAEd,UAAM,MAAM;AACZ,UAAM,UAAU,IAAI,WAAW;AAE/B,QAAI,QAAQ,SAAS,qBAAqB,GAAG;AAC3C,cAAQ,MAAM,yDAAoD;AAClE,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,MAAM,sCAAsC;AAAA,IACtD,WAAW,QAAQ,SAAS,MAAM,KAAK,QAAQ,SAAS,MAAM,GAAG;AAC/D,cAAQ,MAAM,6DAAwD;AACtE,cAAQ,MAAM,oDAAoD;AAClE,cAAQ,MAAM,uDAAuD;AAAA,IACvE,OAAO;AACL,cAAQ,MAAM,2BAAsB,WAAW,GAAG;AAClD,UAAI,IAAI,OAAO;AACb,gBAAQ,MAAM,IAAI,KAAK;AAAA,MACzB;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,KAAK;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/entrypoints/cli.js
CHANGED
|
@@ -94,6 +94,11 @@ import { ConfigParseError } from "../utils/errors.js";
|
|
|
94
94
|
import { grantReadPermissionForOriginalDir } from "../utils/permissions/filesystem.js";
|
|
95
95
|
import { MACRO } from "../constants/macros.js";
|
|
96
96
|
import { SEMANTIC_COLORS } from "../constants/colors.js";
|
|
97
|
+
import {
|
|
98
|
+
needsSync as needsCCSync,
|
|
99
|
+
syncFromClaudeCode,
|
|
100
|
+
hasClaudeCodeInstallation
|
|
101
|
+
} from "../utils/claudeCodeSync.js";
|
|
97
102
|
function completeOnboarding() {
|
|
98
103
|
const config = getGlobalConfig();
|
|
99
104
|
saveGlobalConfig({
|
|
@@ -226,6 +231,33 @@ async function setup(cwd2, safeMode) {
|
|
|
226
231
|
const projectConfig = getCurrentProjectConfig();
|
|
227
232
|
if (projectConfig.lastCost !== void 0 && projectConfig.lastDuration !== void 0) {
|
|
228
233
|
}
|
|
234
|
+
try {
|
|
235
|
+
const cfg = getGlobalConfig();
|
|
236
|
+
if (hasClaudeCodeInstallation() && needsCCSync()) {
|
|
237
|
+
const shouldSync = cfg.autoSyncClaudeCode === true || cfg.autoSyncClaudeCode === void 0;
|
|
238
|
+
if (shouldSync) {
|
|
239
|
+
const result = await syncFromClaudeCode();
|
|
240
|
+
const changed = result.installed.length + result.updated.length;
|
|
241
|
+
const mpRegistered = result.marketplaces.registered.length;
|
|
242
|
+
if (changed > 0 || mpRegistered > 0) {
|
|
243
|
+
const parts = [];
|
|
244
|
+
if (changed > 0) {
|
|
245
|
+
parts.push(
|
|
246
|
+
`${changed} plugin(s) (${result.installed.length} new, ${result.updated.length} updated)`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
if (mpRegistered > 0) {
|
|
250
|
+
parts.push(`${mpRegistered} marketplace(s)`);
|
|
251
|
+
}
|
|
252
|
+
console.log(`Synced from Claude Code: ${parts.join(", ")}`);
|
|
253
|
+
}
|
|
254
|
+
if (cfg.autoSyncClaudeCode === void 0) {
|
|
255
|
+
saveGlobalConfig({ ...getGlobalConfig(), autoSyncClaudeCode: true });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
229
261
|
}
|
|
230
262
|
if (process.env.NODE_ENV !== "test") {
|
|
231
263
|
process.on("uncaughtException", (error) => {
|