@within-7/minto 0.1.5 → 0.1.7
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 +2342 -0
- package/dist/commands/agents/AgentsCommand.js.map +7 -0
- package/dist/commands/agents/constants.js +58 -0
- package/dist/commands/agents/constants.js.map +7 -0
- package/dist/commands/agents/index.js +37 -0
- package/dist/commands/agents/index.js.map +7 -0
- package/dist/commands/agents/types.js +10 -0
- package/dist/commands/agents/types.js.map +7 -0
- package/dist/commands/agents/utils/fileOperations.js +185 -0
- package/dist/commands/agents/utils/fileOperations.js.map +7 -0
- package/dist/commands/agents/utils/index.js +21 -0
- package/dist/commands/agents/utils/index.js.map +7 -0
- package/dist/commands/bug.js +2 -2
- package/dist/commands/bug.js.map +2 -2
- package/dist/commands/compact.js +5 -5
- package/dist/commands/compact.js.map +2 -2
- package/dist/commands/ctx_viz.js +55 -22
- package/dist/commands/ctx_viz.js.map +2 -2
- package/dist/commands/mcp-interactive.js +11 -11
- package/dist/commands/mcp-interactive.js.map +2 -2
- package/dist/commands/model.js +94 -32
- package/dist/commands/model.js.map +3 -3
- package/dist/commands/plugin/AddMarketplaceForm.js +49 -21
- package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
- package/dist/commands/plugin/ConfirmDialog.js +38 -26
- package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
- package/dist/commands/plugin/InstalledPluginsByMarketplace.js +24 -8
- package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
- package/dist/commands/plugin/InstalledPluginsManager.js +3 -1
- package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
- package/dist/commands/plugin/MainMenu.js +16 -7
- package/dist/commands/plugin/MainMenu.js.map +2 -2
- package/dist/commands/plugin/MarketplaceManager.js +84 -39
- package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
- package/dist/commands/plugin/MarketplaceSelector.js +7 -3
- package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
- package/dist/commands/plugin/PlaceholderScreen.js +16 -2
- package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
- package/dist/commands/plugin/PluginBrowser.js +4 -2
- package/dist/commands/plugin/PluginBrowser.js.map +2 -2
- package/dist/commands/plugin/PluginDetailsInstall.js +12 -6
- package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
- package/dist/commands/plugin/PluginDetailsManage.js +14 -5
- package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
- package/dist/commands/plugin/example-usage.js.map +2 -2
- package/dist/commands/plugin/utils.js.map +2 -2
- package/dist/commands/plugin.js +226 -46
- package/dist/commands/plugin.js.map +2 -2
- package/dist/commands/refreshCommands.js +6 -3
- package/dist/commands/refreshCommands.js.map +2 -2
- package/dist/commands/resume.js +2 -1
- package/dist/commands/resume.js.map +2 -2
- package/dist/commands/setup.js +19 -5
- package/dist/commands/setup.js.map +2 -2
- package/dist/commands/terminalSetup.js +2 -2
- package/dist/commands/terminalSetup.js.map +1 -1
- package/dist/commands.js +14 -30
- package/dist/commands.js.map +2 -2
- package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
- package/dist/components/AskUserQuestionDialog/QuestionView.js +10 -1
- package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
- package/dist/components/BackgroundTasksPanel.js +5 -1
- package/dist/components/BackgroundTasksPanel.js.map +2 -2
- package/dist/components/Config.js +17 -4
- package/dist/components/Config.js.map +2 -2
- package/dist/components/ConsoleOAuthFlow.js.map +2 -2
- package/dist/components/CustomSelect/select-option.js +4 -1
- package/dist/components/CustomSelect/select-option.js.map +2 -2
- package/dist/components/Help.js +6 -8
- package/dist/components/Help.js.map +2 -2
- package/dist/components/Logo.js +1 -1
- package/dist/components/Logo.js.map +2 -2
- package/dist/components/ModelListManager.js.map +2 -2
- package/dist/components/ModelSelector/ModelSelector.js +2030 -0
- package/dist/components/ModelSelector/ModelSelector.js.map +7 -0
- package/dist/components/ModelSelector/ScreenContainer.js +27 -0
- package/dist/components/ModelSelector/ScreenContainer.js.map +7 -0
- package/dist/components/ModelSelector/constants.js +37 -0
- package/dist/components/ModelSelector/constants.js.map +7 -0
- package/dist/components/ModelSelector/hooks/index.js +5 -0
- package/dist/components/ModelSelector/hooks/index.js.map +7 -0
- package/dist/components/ModelSelector/hooks/useEscapeNavigation.js +21 -0
- package/dist/components/ModelSelector/hooks/useEscapeNavigation.js.map +7 -0
- package/dist/components/ModelSelector/index.js +17 -0
- package/dist/components/ModelSelector/index.js.map +7 -0
- package/dist/components/ModelSelector/types.js +1 -0
- package/dist/components/ModelSelector/types.js.map +7 -0
- package/dist/components/PressEnterToContinue.js +1 -1
- package/dist/components/PressEnterToContinue.js.map +2 -2
- package/dist/components/ProjectOnboarding.js +1 -1
- package/dist/components/ProjectOnboarding.js.map +2 -2
- package/dist/components/PromptInput.js +88 -37
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/QuitSummary.js +17 -10
- package/dist/components/QuitSummary.js.map +2 -2
- package/dist/components/SentryErrorBoundary.js.map +2 -2
- package/dist/components/StreamingBashOutput.js.map +2 -2
- package/dist/components/StructuredDiff.js.map +2 -2
- package/dist/components/SubagentProgress.js.map +2 -2
- package/dist/components/TaskCard.js.map +2 -2
- package/dist/components/TextInput.js.map +1 -1
- package/dist/components/TodoItem.js.map +1 -1
- package/dist/components/binary-feedback/BinaryFeedbackOption.js +1 -3
- package/dist/components/binary-feedback/BinaryFeedbackOption.js.map +2 -2
- package/dist/components/messages/AssistantLocalCommandOutputMessage.js.map +1 -1
- package/dist/components/messages/AssistantToolUseMessage.js +3 -1
- package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
- package/dist/components/messages/TaskProgressMessage.js.map +2 -2
- package/dist/components/messages/TaskToolMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/utils.js.map +2 -2
- package/dist/components/permissions/FileEditPermissionRequest/FileEditToolDiff.js.map +2 -2
- package/dist/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.js.map +2 -2
- package/dist/components/permissions/hooks.js.map +2 -2
- package/dist/constants/modelCapabilities.js +1 -1
- package/dist/constants/modelCapabilities.js.map +2 -2
- package/dist/constants/prompts.js.map +1 -1
- package/dist/constants/timing.js +34 -0
- package/dist/constants/timing.js.map +7 -0
- package/dist/entrypoints/cli.js +128 -33
- package/dist/entrypoints/cli.js.map +3 -3
- package/dist/entrypoints/mcp.js +13 -18
- package/dist/entrypoints/mcp.js.map +2 -2
- package/dist/hooks/useCanUseTool.js.map +2 -2
- package/dist/hooks/useCancelRequest.js.map +1 -1
- package/dist/hooks/useHistorySearch.js.map +2 -2
- package/dist/hooks/useLogStartupTime.js.map +2 -2
- package/dist/hooks/usePermissionRequestLogging.js.map +2 -2
- package/dist/hooks/useTextInput.js.map +1 -1
- package/dist/hooks/useUnifiedCompletion.js +493 -394
- package/dist/hooks/useUnifiedCompletion.js.map +2 -2
- package/dist/index.js.map +2 -2
- package/dist/permissions.js +4 -7
- package/dist/permissions.js.map +2 -2
- package/dist/query.js +6 -1
- package/dist/query.js.map +2 -2
- package/dist/screens/REPL.js +72 -36
- package/dist/screens/REPL.js.map +2 -2
- package/dist/screens/ResumeConversation.js +2 -1
- package/dist/screens/ResumeConversation.js.map +2 -2
- package/dist/services/adapters/base.js.map +2 -2
- package/dist/services/adapters/chatCompletions.js.map +2 -2
- package/dist/services/adapters/responsesAPI.js +3 -1
- package/dist/services/adapters/responsesAPI.js.map +2 -2
- package/dist/services/claude.js +327 -328
- package/dist/services/claude.js.map +2 -2
- package/dist/services/customCommands.js +6 -1
- package/dist/services/customCommands.js.map +2 -2
- package/dist/services/fileFreshness.js.map +2 -2
- package/dist/services/gpt5ConnectionTest.js +20 -7
- package/dist/services/gpt5ConnectionTest.js.map +2 -2
- package/dist/services/hookExecutor.js +6 -12
- package/dist/services/hookExecutor.js.map +2 -2
- package/dist/services/mcpClient.js +29 -2
- package/dist/services/mcpClient.js.map +2 -2
- package/dist/services/mentionProcessor.js +23 -10
- package/dist/services/mentionProcessor.js.map +2 -2
- package/dist/services/modelAdapterFactory.js.map +2 -2
- package/dist/services/oauth.js.map +2 -2
- package/dist/services/openai.js +109 -72
- package/dist/services/openai.js.map +3 -3
- package/dist/services/responseStateManager.js.map +2 -2
- package/dist/services/systemReminder.js.map +2 -2
- package/dist/tools/ArchitectTool/ArchitectTool.js +10 -9
- package/dist/tools/ArchitectTool/ArchitectTool.js.map +2 -2
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +14 -8
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
- package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +8 -1
- package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
- package/dist/tools/BashOutputTool/BashOutputTool.js.map +2 -2
- package/dist/tools/BashTool/BashTool.js.map +2 -2
- package/dist/tools/FileReadTool/FileReadTool.js +23 -4
- package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
- package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
- package/dist/tools/GlobTool/GlobTool.js +11 -2
- package/dist/tools/GlobTool/GlobTool.js.map +2 -2
- package/dist/tools/GrepTool/GrepTool.js +7 -5
- package/dist/tools/GrepTool/GrepTool.js.map +2 -2
- package/dist/tools/MCPTool/MCPTool.js +11 -12
- package/dist/tools/MCPTool/MCPTool.js.map +2 -2
- package/dist/tools/MultiEditTool/MultiEditTool.js +4 -1
- package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
- package/dist/tools/NotebookReadTool/NotebookReadTool.js +11 -5
- package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +2 -2
- package/dist/tools/SkillTool/SkillTool.js +18 -6
- package/dist/tools/SkillTool/SkillTool.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +37 -51
- package/dist/tools/TaskTool/TaskTool.js.map +2 -2
- package/dist/tools/TaskTool/prompt.js.map +2 -2
- package/dist/tools/ThinkTool/ThinkTool.js +6 -1
- package/dist/tools/ThinkTool/ThinkTool.js.map +2 -2
- package/dist/tools/TodoWriteTool/TodoWriteTool.js +29 -5
- package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +2 -2
- package/dist/tools/URLFetcherTool/URLFetcherTool.js +5 -2
- package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
- package/dist/tools/URLFetcherTool/cache.js +6 -3
- package/dist/tools/URLFetcherTool/cache.js.map +2 -2
- package/dist/tools/URLFetcherTool/htmlToMarkdown.js +3 -1
- package/dist/tools/URLFetcherTool/htmlToMarkdown.js.map +2 -2
- package/dist/tools/WebSearchTool/WebSearchTool.js +6 -1
- package/dist/tools/WebSearchTool/WebSearchTool.js.map +2 -2
- package/dist/tools/WebSearchTool/prompt.js.map +2 -2
- package/dist/tools/WebSearchTool/searchProviders.js +15 -6
- package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
- package/dist/tools.js +4 -1
- package/dist/tools.js.map +2 -2
- package/dist/types/core.js +1 -0
- package/dist/types/core.js.map +7 -0
- package/dist/types/hooks.js +1 -4
- package/dist/types/hooks.js.map +2 -2
- package/dist/types/marketplace.js +8 -2
- package/dist/types/marketplace.js.map +2 -2
- package/dist/types/plugin.js +9 -6
- package/dist/types/plugin.js.map +2 -2
- package/dist/utils/BackgroundShellManager.js +76 -10
- package/dist/utils/BackgroundShellManager.js.map +2 -2
- package/dist/utils/PersistentShell.js +7 -2
- package/dist/utils/PersistentShell.js.map +2 -2
- package/dist/utils/advancedFuzzyMatcher.js +4 -1
- package/dist/utils/advancedFuzzyMatcher.js.map +2 -2
- package/dist/utils/agentLoader.js +69 -35
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/agentStorage.js.map +2 -2
- package/dist/utils/async.js +163 -0
- package/dist/utils/async.js.map +7 -0
- package/dist/utils/autoUpdater.js +8 -2
- package/dist/utils/autoUpdater.js.map +2 -2
- package/dist/utils/commands.js +23 -11
- package/dist/utils/commands.js.map +2 -2
- package/dist/utils/commonUnixCommands.js +3 -1
- package/dist/utils/commonUnixCommands.js.map +2 -2
- package/dist/utils/compressionMode.js.map +2 -2
- package/dist/utils/config.js +30 -14
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/debugLogger.js.map +2 -2
- package/dist/utils/env.js.map +2 -2
- package/dist/utils/envConfig.js +82 -0
- package/dist/utils/envConfig.js.map +7 -0
- package/dist/utils/errorHandling.js +89 -0
- package/dist/utils/errorHandling.js.map +7 -0
- package/dist/utils/expertChatStorage.js.map +2 -2
- package/dist/utils/fuzzyMatcher.js +13 -7
- package/dist/utils/fuzzyMatcher.js.map +2 -2
- package/dist/utils/hookManager.js +14 -4
- package/dist/utils/hookManager.js.map +2 -2
- package/dist/utils/log.js.map +2 -2
- package/dist/utils/marketplaceManager.js +44 -9
- package/dist/utils/marketplaceManager.js.map +2 -2
- package/dist/utils/messageContextManager.js.map +1 -1
- package/dist/utils/messages.js +6 -3
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/model.js +3 -1
- package/dist/utils/model.js.map +2 -2
- package/dist/utils/pluginInstaller.js +3 -15
- package/dist/utils/pluginInstaller.js.map +2 -2
- package/dist/utils/pluginLoader.js +41 -13
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/pluginRegistry.js.map +2 -2
- package/dist/utils/pluginValidator.js +71 -49
- package/dist/utils/pluginValidator.js.map +2 -2
- package/dist/utils/ptyCompat.js.map +2 -2
- package/dist/utils/roundConverter.js.map +2 -2
- package/dist/utils/secureFile.js +43 -14
- package/dist/utils/secureFile.js.map +2 -2
- package/dist/utils/sessionState.js.map +2 -2
- package/dist/utils/skillLoader.js.map +2 -2
- package/dist/utils/teamConfig.js +7 -4
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/theme.js.map +2 -2
- package/dist/utils/thinking.js.map +2 -2
- package/dist/utils/unaryLogging.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +5 -5
|
@@ -3,7 +3,7 @@ const DEFAULT_CONFIG = {
|
|
|
3
3
|
prefix: 0.35,
|
|
4
4
|
// Strong weight for prefix matching
|
|
5
5
|
substring: 0.2,
|
|
6
|
-
// Good for partial matches
|
|
6
|
+
// Good for partial matches
|
|
7
7
|
abbreviation: 0.3,
|
|
8
8
|
// Key for "nde"→"node" cases
|
|
9
9
|
editDistance: 0.1,
|
|
@@ -38,7 +38,10 @@ class FuzzyMatcher {
|
|
|
38
38
|
config;
|
|
39
39
|
constructor(config = {}) {
|
|
40
40
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
41
|
-
const weightSum = Object.values(this.config.weights).reduce(
|
|
41
|
+
const weightSum = Object.values(this.config.weights).reduce(
|
|
42
|
+
(a, b) => a + b,
|
|
43
|
+
0
|
|
44
|
+
);
|
|
42
45
|
if (Math.abs(weightSum - 1) > 0.01) {
|
|
43
46
|
Object.keys(this.config.weights).forEach((key) => {
|
|
44
47
|
this.config.weights[key] /= weightSum;
|
|
@@ -68,10 +71,13 @@ class FuzzyMatcher {
|
|
|
68
71
|
editDistance: this.editDistanceScore(text, pattern),
|
|
69
72
|
popularity: this.popularityScore(text)
|
|
70
73
|
};
|
|
71
|
-
const rawScore = Object.entries(scores).reduce(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
const rawScore = Object.entries(scores).reduce(
|
|
75
|
+
(total, [algorithm, score]) => {
|
|
76
|
+
const weight = this.config.weights[algorithm];
|
|
77
|
+
return total + score * weight;
|
|
78
|
+
},
|
|
79
|
+
0
|
|
80
|
+
);
|
|
75
81
|
const lengthPenalty = Math.max(0, text.length - 6) * 1.5;
|
|
76
82
|
const finalScore = Math.max(0, rawScore - lengthPenalty);
|
|
77
83
|
const maxAlgorithm = Object.entries(scores).reduce(
|
|
@@ -95,7 +101,7 @@ class FuzzyMatcher {
|
|
|
95
101
|
return 100 * coverage;
|
|
96
102
|
}
|
|
97
103
|
/**
|
|
98
|
-
* Algorithm 2: Substring Matching (like pinyin contains)
|
|
104
|
+
* Algorithm 2: Substring Matching (like pinyin contains)
|
|
99
105
|
* Handles cases like "ode" → "node", "py3" → "python3"
|
|
100
106
|
*/
|
|
101
107
|
substringScore(text, pattern) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/fuzzyMatcher.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Input Method Inspired Fuzzy Matching Algorithm\n * \n * Multi-algorithm weighted scoring system inspired by:\n * - Sogou/Baidu Pinyin input method algorithms\n * - Double-pinyin abbreviation matching\n * - Terminal completion best practices (fzf, zsh, fish)\n * \n * Designed specifically for command/terminal completion scenarios\n * where users type abbreviations like \"nde\" expecting \"node\"\n */\n\nexport interface MatchResult {\n score: number\n algorithm: string // Which algorithm contributed most to the score\n confidence: number // 0-1 confidence level\n}\n\nexport interface FuzzyMatcherConfig {\n // Algorithm weights (must sum to 1.0)\n weights: {\n prefix: number // Direct prefix matching (\"nod\" \u2192 \"node\")\n substring: number // Substring matching (\"ode\" \u2192 \"node\") \n abbreviation: number // Key chars matching (\"nde\" \u2192 \"node\")\n editDistance: number // Typo tolerance (\"noda\" \u2192 \"node\")\n popularity: number // Common command boost\n }\n \n // Scoring parameters\n minScore: number // Minimum score threshold\n maxEditDistance: number // Maximum edits allowed\n popularCommands: string[] // Commands to boost\n}\n\nconst DEFAULT_CONFIG: FuzzyMatcherConfig = {\n weights: {\n prefix: 0.35, // Strong weight for prefix matching\n substring: 0.20, // Good for partial matches \n abbreviation: 0.30, // Key for \"nde\"\u2192\"node\" cases\n editDistance: 0.10, // Typo tolerance\n popularity: 0.05 // Slight bias for common commands\n },\n minScore: 10, // Lower threshold for better matching\n maxEditDistance: 2,\n popularCommands: [\n 'node', 'npm', 'git', 'ls', 'cd', 'cat', 'grep', 'find', 'cp', 'mv',\n 'python', 'java', 'docker', 'curl', 'wget', 'vim', 'nano'\n ]\n}\n\nexport class FuzzyMatcher {\n private config: FuzzyMatcherConfig\n\n constructor(config: Partial<FuzzyMatcherConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config }\n \n // Normalize weights to sum to 1.0\n const weightSum = Object.values(this.config.weights).reduce((a, b) => a + b, 0)\n if (Math.abs(weightSum - 1.0) > 0.01) {\n Object.keys(this.config.weights).forEach(key => {\n this.config.weights[key as keyof typeof this.config.weights] /= weightSum\n })\n }\n }\n\n /**\n * Calculate fuzzy match score for a candidate against a query\n */\n match(candidate: string, query: string): MatchResult {\n const text = candidate.toLowerCase()\n const pattern = query.toLowerCase()\n\n // Quick perfect match exits\n if (text === pattern) {\n return { score: 1000, algorithm: 'exact', confidence: 1.0 }\n }\n if (text.startsWith(pattern)) {\n return { \n score: 900 + (10 - pattern.length), \n algorithm: 'prefix-exact', \n confidence: 0.95 \n }\n }\n\n // Run all algorithms\n const scores = {\n prefix: this.prefixScore(text, pattern),\n substring: this.substringScore(text, pattern), \n abbreviation: this.abbreviationScore(text, pattern),\n editDistance: this.editDistanceScore(text, pattern),\n popularity: this.popularityScore(text)\n }\n\n // Weighted combination\n const rawScore = Object.entries(scores).reduce((total, [algorithm, score]) => {\n const weight = this.config.weights[algorithm as keyof typeof this.config.weights]\n return total + (score * weight)\n }, 0)\n\n // Length penalty (prefer shorter commands)\n const lengthPenalty = Math.max(0, text.length - 6) * 1.5\n const finalScore = Math.max(0, rawScore - lengthPenalty)\n\n // Determine primary algorithm and confidence\n const maxAlgorithm = Object.entries(scores).reduce((max, [alg, score]) => \n score > max.score ? { algorithm: alg, score } : max, \n { algorithm: 'none', score: 0 }\n )\n\n const confidence = Math.min(1.0, finalScore / 100)\n\n return {\n score: finalScore,\n algorithm: maxAlgorithm.algorithm,\n confidence\n }\n }\n\n /**\n * Algorithm 1: Prefix Matching (like pinyin prefix)\n * Handles cases like \"nod\" \u2192 \"node\"\n */\n private prefixScore(text: string, pattern: string): number {\n if (!text.startsWith(pattern)) return 0\n \n // Score based on prefix length vs total length\n const coverage = pattern.length / text.length\n return 100 * coverage\n }\n\n /**\n * Algorithm 2: Substring Matching (like pinyin contains) \n * Handles cases like \"ode\" \u2192 \"node\", \"py3\" \u2192 \"python3\"\n */\n private substringScore(text: string, pattern: string): number {\n // Direct substring match\n const index = text.indexOf(pattern)\n if (index !== -1) {\n // Earlier position and better coverage = higher score\n const positionFactor = Math.max(0, 10 - index) / 10\n const coverageFactor = pattern.length / text.length\n return 80 * positionFactor * coverageFactor\n }\n \n // Special handling for numeric suffixes (py3 \u2192 python3)\n // Check if pattern ends with a number and try prefix match + number\n const numMatch = pattern.match(/^(.+?)(\\d+)$/)\n if (numMatch) {\n const [, prefix, num] = numMatch\n // Check if text starts with prefix and ends with the same number\n if (text.startsWith(prefix) && text.endsWith(num)) {\n // Good match for patterns like \"py3\" \u2192 \"python3\"\n const coverageFactor = pattern.length / text.length\n return 70 * coverageFactor + 20 // Bonus for numeric suffix match\n }\n }\n \n return 0\n }\n\n /**\n * Algorithm 3: Abbreviation Matching (key innovation)\n * Handles cases like \"nde\" \u2192 \"node\", \"pyt3\" \u2192 \"python3\", \"gp5\" \u2192 \"gpt-5\"\n */\n private abbreviationScore(text: string, pattern: string): number {\n let score = 0\n let textPos = 0\n let perfectStart = false\n let consecutiveMatches = 0\n let wordBoundaryMatches = 0\n \n // Split text by hyphens to handle word boundaries better\n const textWords = text.split('-')\n const textClean = text.replace(/-/g, '').toLowerCase()\n \n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i]\n let charFound = false\n \n // Try to find in clean text (no hyphens)\n for (let j = textPos; j < textClean.length; j++) {\n if (textClean[j] === char) {\n charFound = true\n \n // Check if this character is at a word boundary in original text\n let originalPos = 0\n let cleanPos = 0\n for (let k = 0; k < text.length; k++) {\n if (text[k] === '-') continue\n if (cleanPos === j) {\n originalPos = k\n break\n }\n cleanPos++\n }\n \n // Consecutive character bonus\n if (j === textPos) {\n consecutiveMatches++\n } else {\n consecutiveMatches = 1\n }\n \n // Position-sensitive scoring\n if (i === 0 && j === 0) {\n score += 50 // Perfect first character\n perfectStart = true\n } else if (originalPos === 0 || text[originalPos - 1] === '-') {\n score += 35 // Word boundary match\n wordBoundaryMatches++\n } else if (j <= 2) {\n score += 20 // Early position\n } else if (j <= 6) {\n score += 10 // Mid position \n } else {\n score += 5 // Late position\n }\n \n // Consecutive character bonus\n if (consecutiveMatches > 1) {\n score += consecutiveMatches * 5\n }\n \n textPos = j + 1\n break\n }\n }\n \n if (!charFound) return 0 // Invalid abbreviation\n }\n \n // Critical bonuses\n if (perfectStart) score += 30\n if (wordBoundaryMatches >= 2) score += 25 // Multiple word boundaries\n if (textPos <= textClean.length * 0.8) score += 15 // Compact abbreviation\n \n // Special bonus for number matching at end\n const lastPatternChar = pattern[pattern.length - 1]\n const lastTextChar = text[text.length - 1]\n if (/\\d/.test(lastPatternChar) && lastPatternChar === lastTextChar) {\n score += 25\n }\n \n return score\n }\n\n /**\n * Algorithm 4: Edit Distance (typo tolerance)\n * Handles cases like \"noda\" \u2192 \"node\"\n */\n private editDistanceScore(text: string, pattern: string): number {\n if (pattern.length > text.length + this.config.maxEditDistance) return 0\n \n // Simplified Levenshtein distance \n const dp: number[][] = []\n const m = pattern.length\n const n = text.length\n \n // Initialize DP table\n for (let i = 0; i <= m; i++) {\n dp[i] = []\n for (let j = 0; j <= n; j++) {\n if (i === 0) dp[i][j] = j\n else if (j === 0) dp[i][j] = i\n else {\n const cost = pattern[i-1] === text[j-1] ? 0 : 1\n dp[i][j] = Math.min(\n dp[i-1][j] + 1, // deletion\n dp[i][j-1] + 1, // insertion\n dp[i-1][j-1] + cost // substitution\n )\n }\n }\n }\n \n const distance = dp[m][n]\n if (distance > this.config.maxEditDistance) return 0\n \n return Math.max(0, 30 - distance * 10)\n }\n\n /**\n * Algorithm 5: Command Popularity (like frequency in input method)\n * Boost common commands that users frequently type\n */\n private popularityScore(text: string): number {\n if (this.config.popularCommands.includes(text)) {\n return 40\n }\n \n // Short commands are often more commonly used\n if (text.length <= 5) return 10\n \n return 0\n }\n\n /**\n * Batch match multiple candidates and return sorted results\n */\n matchMany(candidates: string[], query: string): Array<{candidate: string, result: MatchResult}> {\n return candidates\n .map(candidate => ({ \n candidate, \n result: this.match(candidate, query) \n }))\n .filter(item => item.result.score >= this.config.minScore)\n .sort((a, b) => b.result.score - a.result.score)\n }\n}\n\n// Export convenience functions\nexport const defaultMatcher = new FuzzyMatcher()\n\nexport function matchCommand(command: string, query: string): MatchResult {\n return defaultMatcher.match(command, query)\n}\n\n// Import the advanced matcher\nimport { matchManyAdvanced } from './advancedFuzzyMatcher'\n\nexport function matchCommands(commands: string[], query: string): Array<{command: string, score: number}> {\n // Use the advanced matcher for better results\n return matchManyAdvanced(commands, query, 5) // Lower threshold for better matching\n .map(item => ({ \n command: item.candidate, \n score: item.score \n }))\n}"],
|
|
5
|
-
"mappings": "AAkCA,MAAM,iBAAqC;AAAA,EACzC,SAAS;AAAA,IACP,QAAQ;AAAA;AAAA,IACR,WAAW;AAAA;AAAA,IACX,cAAc;AAAA;AAAA,IACd,cAAc;AAAA;AAAA,IACd,YAAY;AAAA;AAAA,EACd;AAAA,EACA,UAAU;AAAA;AAAA,EACV,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,IACf;AAAA,
|
|
4
|
+
"sourcesContent": ["/**\n * Input Method Inspired Fuzzy Matching Algorithm\n *\n * Multi-algorithm weighted scoring system inspired by:\n * - Sogou/Baidu Pinyin input method algorithms\n * - Double-pinyin abbreviation matching\n * - Terminal completion best practices (fzf, zsh, fish)\n *\n * Designed specifically for command/terminal completion scenarios\n * where users type abbreviations like \"nde\" expecting \"node\"\n */\n\nexport interface MatchResult {\n score: number\n algorithm: string // Which algorithm contributed most to the score\n confidence: number // 0-1 confidence level\n}\n\nexport interface FuzzyMatcherConfig {\n // Algorithm weights (must sum to 1.0)\n weights: {\n prefix: number // Direct prefix matching (\"nod\" \u2192 \"node\")\n substring: number // Substring matching (\"ode\" \u2192 \"node\")\n abbreviation: number // Key chars matching (\"nde\" \u2192 \"node\")\n editDistance: number // Typo tolerance (\"noda\" \u2192 \"node\")\n popularity: number // Common command boost\n }\n\n // Scoring parameters\n minScore: number // Minimum score threshold\n maxEditDistance: number // Maximum edits allowed\n popularCommands: string[] // Commands to boost\n}\n\nconst DEFAULT_CONFIG: FuzzyMatcherConfig = {\n weights: {\n prefix: 0.35, // Strong weight for prefix matching\n substring: 0.2, // Good for partial matches\n abbreviation: 0.3, // Key for \"nde\"\u2192\"node\" cases\n editDistance: 0.1, // Typo tolerance\n popularity: 0.05, // Slight bias for common commands\n },\n minScore: 10, // Lower threshold for better matching\n maxEditDistance: 2,\n popularCommands: [\n 'node',\n 'npm',\n 'git',\n 'ls',\n 'cd',\n 'cat',\n 'grep',\n 'find',\n 'cp',\n 'mv',\n 'python',\n 'java',\n 'docker',\n 'curl',\n 'wget',\n 'vim',\n 'nano',\n ],\n}\n\nexport class FuzzyMatcher {\n private config: FuzzyMatcherConfig\n\n constructor(config: Partial<FuzzyMatcherConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config }\n\n // Normalize weights to sum to 1.0\n const weightSum = Object.values(this.config.weights).reduce(\n (a, b) => a + b,\n 0,\n )\n if (Math.abs(weightSum - 1.0) > 0.01) {\n Object.keys(this.config.weights).forEach(key => {\n this.config.weights[key as keyof typeof this.config.weights] /=\n weightSum\n })\n }\n }\n\n /**\n * Calculate fuzzy match score for a candidate against a query\n */\n match(candidate: string, query: string): MatchResult {\n const text = candidate.toLowerCase()\n const pattern = query.toLowerCase()\n\n // Quick perfect match exits\n if (text === pattern) {\n return { score: 1000, algorithm: 'exact', confidence: 1.0 }\n }\n if (text.startsWith(pattern)) {\n return {\n score: 900 + (10 - pattern.length),\n algorithm: 'prefix-exact',\n confidence: 0.95,\n }\n }\n\n // Run all algorithms\n const scores = {\n prefix: this.prefixScore(text, pattern),\n substring: this.substringScore(text, pattern),\n abbreviation: this.abbreviationScore(text, pattern),\n editDistance: this.editDistanceScore(text, pattern),\n popularity: this.popularityScore(text),\n }\n\n // Weighted combination\n const rawScore = Object.entries(scores).reduce(\n (total, [algorithm, score]) => {\n const weight =\n this.config.weights[algorithm as keyof typeof this.config.weights]\n return total + score * weight\n },\n 0,\n )\n\n // Length penalty (prefer shorter commands)\n const lengthPenalty = Math.max(0, text.length - 6) * 1.5\n const finalScore = Math.max(0, rawScore - lengthPenalty)\n\n // Determine primary algorithm and confidence\n const maxAlgorithm = Object.entries(scores).reduce(\n (max, [alg, score]) =>\n score > max.score ? { algorithm: alg, score } : max,\n { algorithm: 'none', score: 0 },\n )\n\n const confidence = Math.min(1.0, finalScore / 100)\n\n return {\n score: finalScore,\n algorithm: maxAlgorithm.algorithm,\n confidence,\n }\n }\n\n /**\n * Algorithm 1: Prefix Matching (like pinyin prefix)\n * Handles cases like \"nod\" \u2192 \"node\"\n */\n private prefixScore(text: string, pattern: string): number {\n if (!text.startsWith(pattern)) return 0\n\n // Score based on prefix length vs total length\n const coverage = pattern.length / text.length\n return 100 * coverage\n }\n\n /**\n * Algorithm 2: Substring Matching (like pinyin contains)\n * Handles cases like \"ode\" \u2192 \"node\", \"py3\" \u2192 \"python3\"\n */\n private substringScore(text: string, pattern: string): number {\n // Direct substring match\n const index = text.indexOf(pattern)\n if (index !== -1) {\n // Earlier position and better coverage = higher score\n const positionFactor = Math.max(0, 10 - index) / 10\n const coverageFactor = pattern.length / text.length\n return 80 * positionFactor * coverageFactor\n }\n\n // Special handling for numeric suffixes (py3 \u2192 python3)\n // Check if pattern ends with a number and try prefix match + number\n const numMatch = pattern.match(/^(.+?)(\\d+)$/)\n if (numMatch) {\n const [, prefix, num] = numMatch\n // Check if text starts with prefix and ends with the same number\n if (text.startsWith(prefix) && text.endsWith(num)) {\n // Good match for patterns like \"py3\" \u2192 \"python3\"\n const coverageFactor = pattern.length / text.length\n return 70 * coverageFactor + 20 // Bonus for numeric suffix match\n }\n }\n\n return 0\n }\n\n /**\n * Algorithm 3: Abbreviation Matching (key innovation)\n * Handles cases like \"nde\" \u2192 \"node\", \"pyt3\" \u2192 \"python3\", \"gp5\" \u2192 \"gpt-5\"\n */\n private abbreviationScore(text: string, pattern: string): number {\n let score = 0\n let textPos = 0\n let perfectStart = false\n let consecutiveMatches = 0\n let wordBoundaryMatches = 0\n\n // Split text by hyphens to handle word boundaries better\n const textWords = text.split('-')\n const textClean = text.replace(/-/g, '').toLowerCase()\n\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i]\n let charFound = false\n\n // Try to find in clean text (no hyphens)\n for (let j = textPos; j < textClean.length; j++) {\n if (textClean[j] === char) {\n charFound = true\n\n // Check if this character is at a word boundary in original text\n let originalPos = 0\n let cleanPos = 0\n for (let k = 0; k < text.length; k++) {\n if (text[k] === '-') continue\n if (cleanPos === j) {\n originalPos = k\n break\n }\n cleanPos++\n }\n\n // Consecutive character bonus\n if (j === textPos) {\n consecutiveMatches++\n } else {\n consecutiveMatches = 1\n }\n\n // Position-sensitive scoring\n if (i === 0 && j === 0) {\n score += 50 // Perfect first character\n perfectStart = true\n } else if (originalPos === 0 || text[originalPos - 1] === '-') {\n score += 35 // Word boundary match\n wordBoundaryMatches++\n } else if (j <= 2) {\n score += 20 // Early position\n } else if (j <= 6) {\n score += 10 // Mid position\n } else {\n score += 5 // Late position\n }\n\n // Consecutive character bonus\n if (consecutiveMatches > 1) {\n score += consecutiveMatches * 5\n }\n\n textPos = j + 1\n break\n }\n }\n\n if (!charFound) return 0 // Invalid abbreviation\n }\n\n // Critical bonuses\n if (perfectStart) score += 30\n if (wordBoundaryMatches >= 2) score += 25 // Multiple word boundaries\n if (textPos <= textClean.length * 0.8) score += 15 // Compact abbreviation\n\n // Special bonus for number matching at end\n const lastPatternChar = pattern[pattern.length - 1]\n const lastTextChar = text[text.length - 1]\n if (/\\d/.test(lastPatternChar) && lastPatternChar === lastTextChar) {\n score += 25\n }\n\n return score\n }\n\n /**\n * Algorithm 4: Edit Distance (typo tolerance)\n * Handles cases like \"noda\" \u2192 \"node\"\n */\n private editDistanceScore(text: string, pattern: string): number {\n if (pattern.length > text.length + this.config.maxEditDistance) return 0\n\n // Simplified Levenshtein distance\n const dp: number[][] = []\n const m = pattern.length\n const n = text.length\n\n // Initialize DP table\n for (let i = 0; i <= m; i++) {\n dp[i] = []\n for (let j = 0; j <= n; j++) {\n if (i === 0) dp[i][j] = j\n else if (j === 0) dp[i][j] = i\n else {\n const cost = pattern[i - 1] === text[j - 1] ? 0 : 1\n dp[i][j] = Math.min(\n dp[i - 1][j] + 1, // deletion\n dp[i][j - 1] + 1, // insertion\n dp[i - 1][j - 1] + cost, // substitution\n )\n }\n }\n }\n\n const distance = dp[m][n]\n if (distance > this.config.maxEditDistance) return 0\n\n return Math.max(0, 30 - distance * 10)\n }\n\n /**\n * Algorithm 5: Command Popularity (like frequency in input method)\n * Boost common commands that users frequently type\n */\n private popularityScore(text: string): number {\n if (this.config.popularCommands.includes(text)) {\n return 40\n }\n\n // Short commands are often more commonly used\n if (text.length <= 5) return 10\n\n return 0\n }\n\n /**\n * Batch match multiple candidates and return sorted results\n */\n matchMany(\n candidates: string[],\n query: string,\n ): Array<{ candidate: string; result: MatchResult }> {\n return candidates\n .map(candidate => ({\n candidate,\n result: this.match(candidate, query),\n }))\n .filter(item => item.result.score >= this.config.minScore)\n .sort((a, b) => b.result.score - a.result.score)\n }\n}\n\n// Export convenience functions\nexport const defaultMatcher = new FuzzyMatcher()\n\nexport function matchCommand(command: string, query: string): MatchResult {\n return defaultMatcher.match(command, query)\n}\n\n// Import the advanced matcher\nimport { matchManyAdvanced } from './advancedFuzzyMatcher'\n\nexport function matchCommands(\n commands: string[],\n query: string,\n): Array<{ command: string; score: number }> {\n // Use the advanced matcher for better results\n return matchManyAdvanced(commands, query, 5) // Lower threshold for better matching\n .map(item => ({\n command: item.candidate,\n score: item.score,\n }))\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -29,7 +29,9 @@ class HookManager {
|
|
|
29
29
|
registerPlugins(plugins) {
|
|
30
30
|
this.plugins = plugins;
|
|
31
31
|
const totalHooks = plugins.reduce((sum, p) => sum + p.hooks.length, 0);
|
|
32
|
-
logInfo(
|
|
32
|
+
logInfo(
|
|
33
|
+
`HookManager: Registered ${plugins.length} plugin(s) with ${totalHooks} hook(s)`
|
|
34
|
+
);
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
37
|
* Enable or disable hook execution
|
|
@@ -60,7 +62,9 @@ class HookManager {
|
|
|
60
62
|
if (matchingHooks.length === 0) {
|
|
61
63
|
return { shouldContinue: true, shouldAskUser: false };
|
|
62
64
|
}
|
|
63
|
-
logDebug(
|
|
65
|
+
logDebug(
|
|
66
|
+
`PreToolUse: ${matchingHooks.length} hook(s) matched for tool: ${toolName}`
|
|
67
|
+
);
|
|
64
68
|
const input = createPreToolUseInput(
|
|
65
69
|
this.sessionId,
|
|
66
70
|
this.transcriptPath,
|
|
@@ -108,7 +112,9 @@ class HookManager {
|
|
|
108
112
|
if (matchingHooks.length === 0) {
|
|
109
113
|
return;
|
|
110
114
|
}
|
|
111
|
-
logDebug(
|
|
115
|
+
logDebug(
|
|
116
|
+
`PostToolUse: ${matchingHooks.length} hook(s) matched for tool: ${toolName}`
|
|
117
|
+
);
|
|
112
118
|
const input = createPostToolUseInput(
|
|
113
119
|
this.sessionId,
|
|
114
120
|
this.transcriptPath,
|
|
@@ -208,7 +214,11 @@ class HookManager {
|
|
|
208
214
|
return;
|
|
209
215
|
}
|
|
210
216
|
logInfo(`SessionEnd: Executing ${matchingHooks.length} hook(s)`);
|
|
211
|
-
const input = createSessionEndInput(
|
|
217
|
+
const input = createSessionEndInput(
|
|
218
|
+
this.sessionId,
|
|
219
|
+
this.transcriptPath,
|
|
220
|
+
reason
|
|
221
|
+
);
|
|
212
222
|
for (const hook of matchingHooks) {
|
|
213
223
|
const plugin = this.plugins.find((p) => p.manifest.name === hook.pluginName);
|
|
214
224
|
if (!plugin) continue;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/hookManager.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Hook Manager\n *\n * Central manager for plugin hooks lifecycle.\n * Coordinates hook execution across the application.\n */\n\nimport { HookEvent } from '../types/hooks'\nimport { LoadedPlugin } from '../types/plugin'\nimport {\n executeHooksForEvent,\n processHookDecisions,\n createPreToolUseInput,\n createPostToolUseInput,\n createUserPromptSubmitInput,\n createSessionStartInput,\n createSessionEndInput,\n type HookExecutionOptions,\n type HookExecutionResult,\n} from '../services/hookExecutor'\nimport { logError } from './log'\n\n// Simple logging helpers\nconst logInfo = (msg: string) => {\n // Only log in debug mode to reduce noise\n if (process.env.DEBUG) console.log(`[INFO] ${msg}`)\n}\nconst logDebug = (msg: string, ...args: any[]) => {\n if (process.env.DEBUG) console.log(`[DEBUG] ${msg}`, ...args)\n}\n\n/**\n * Hook manager instance\n */\nexport class HookManager {\n private plugins: LoadedPlugin[] = []\n private sessionId: string\n private transcriptPath: string\n private enabled: boolean = true\n\n constructor(sessionId: string, transcriptPath: string) {\n this.sessionId = sessionId\n this.transcriptPath = transcriptPath\n }\n\n /**\n * Register plugins with hooks\n */\n registerPlugins(plugins: LoadedPlugin[]) {\n this.plugins = plugins\n const totalHooks = plugins.reduce((sum, p) => sum + p.hooks.length, 0)\n logInfo(`HookManager: Registered ${plugins.length} plugin(s) with ${totalHooks} hook(s)
|
|
5
|
-
"mappings": "AAOA,SAAS,iBAAiB;AAE1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAIP,MAAM,UAAU,CAAC,QAAgB;AAE/B,MAAI,QAAQ,IAAI,MAAO,SAAQ,IAAI,UAAU,GAAG,EAAE;AACpD;AACA,MAAM,WAAW,CAAC,QAAgB,SAAgB;AAChD,MAAI,QAAQ,IAAI,MAAO,SAAQ,IAAI,WAAW,GAAG,IAAI,GAAG,IAAI;AAC9D;AAKO,MAAM,YAAY;AAAA,EACf,UAA0B,CAAC;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,UAAmB;AAAA,EAE3B,YAAY,WAAmB,gBAAwB;AACrD,SAAK,YAAY;AACjB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,SAAyB;AACvC,SAAK,UAAU;AACf,UAAM,aAAa,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrE,
|
|
4
|
+
"sourcesContent": ["/**\n * Hook Manager\n *\n * Central manager for plugin hooks lifecycle.\n * Coordinates hook execution across the application.\n */\n\nimport { HookEvent } from '../types/hooks'\nimport { LoadedPlugin } from '../types/plugin'\nimport {\n executeHooksForEvent,\n processHookDecisions,\n createPreToolUseInput,\n createPostToolUseInput,\n createUserPromptSubmitInput,\n createSessionStartInput,\n createSessionEndInput,\n type HookExecutionOptions,\n type HookExecutionResult,\n} from '../services/hookExecutor'\nimport { logError } from './log'\n\n// Simple logging helpers\nconst logInfo = (msg: string) => {\n // Only log in debug mode to reduce noise\n if (process.env.DEBUG) console.log(`[INFO] ${msg}`)\n}\nconst logDebug = (msg: string, ...args: any[]) => {\n if (process.env.DEBUG) console.log(`[DEBUG] ${msg}`, ...args)\n}\n\n/**\n * Hook manager instance\n */\nexport class HookManager {\n private plugins: LoadedPlugin[] = []\n private sessionId: string\n private transcriptPath: string\n private enabled: boolean = true\n\n constructor(sessionId: string, transcriptPath: string) {\n this.sessionId = sessionId\n this.transcriptPath = transcriptPath\n }\n\n /**\n * Register plugins with hooks\n */\n registerPlugins(plugins: LoadedPlugin[]) {\n this.plugins = plugins\n const totalHooks = plugins.reduce((sum, p) => sum + p.hooks.length, 0)\n logInfo(\n `HookManager: Registered ${plugins.length} plugin(s) with ${totalHooks} hook(s)`,\n )\n }\n\n /**\n * Enable or disable hook execution\n */\n setEnabled(enabled: boolean) {\n this.enabled = enabled\n logDebug(`HookManager: ${enabled ? 'Enabled' : 'Disabled'}`)\n }\n\n /**\n * Execute PreToolUse hooks\n */\n async executePreToolUse(\n toolName: string,\n toolInput: Record<string, unknown>,\n ): Promise<{\n shouldContinue: boolean\n shouldAskUser: boolean\n reason?: string\n }> {\n if (!this.enabled) {\n return { shouldContinue: true, shouldAskUser: false }\n }\n\n // Collect all hooks across all plugins\n const allHooks = this.plugins.flatMap(p => p.hooks)\n\n // Filter hooks that match this tool\n const matchingHooks = allHooks.filter(hook => {\n if (hook.event !== HookEvent.PreToolUse) return false\n if (!hook.matcher) return true // No matcher means match all\n if (hook.matcher === '*' || hook.matcher === '') return true\n\n // Check if tool name matches the pattern\n try {\n const regex = new RegExp(hook.matcher)\n return regex.test(toolName)\n } catch {\n // If regex is invalid, try exact match\n return hook.matcher === toolName\n }\n })\n\n if (matchingHooks.length === 0) {\n return { shouldContinue: true, shouldAskUser: false }\n }\n\n logDebug(\n `PreToolUse: ${matchingHooks.length} hook(s) matched for tool: ${toolName}`,\n )\n\n // Execute hooks\n const input = createPreToolUseInput(\n this.sessionId,\n this.transcriptPath,\n toolName,\n toolInput,\n )\n\n const results: HookExecutionResult[] = []\n\n for (const hook of matchingHooks) {\n const plugin = this.plugins.find(p => p.manifest.name === hook.pluginName)\n if (!plugin) continue\n\n const options: HookExecutionOptions = {\n sessionId: this.sessionId,\n transcriptPath: this.transcriptPath,\n pluginRoot: plugin.location,\n }\n\n const result = await executeHooksForEvent(\n HookEvent.PreToolUse,\n [hook],\n input,\n options,\n )\n\n results.push(...result)\n }\n\n return processHookDecisions(results)\n }\n\n /**\n * Execute PostToolUse hooks\n */\n async executePostToolUse(\n toolName: string,\n toolInput: Record<string, unknown>,\n toolOutput: Record<string, unknown>,\n ): Promise<void> {\n if (!this.enabled) {\n return\n }\n\n const allHooks = this.plugins.flatMap(p => p.hooks)\n\n const matchingHooks = allHooks.filter(hook => {\n if (hook.event !== HookEvent.PostToolUse) return false\n if (!hook.matcher) return true\n if (hook.matcher === '*' || hook.matcher === '') return true\n\n try {\n const regex = new RegExp(hook.matcher)\n return regex.test(toolName)\n } catch {\n return hook.matcher === toolName\n }\n })\n\n if (matchingHooks.length === 0) {\n return\n }\n\n logDebug(\n `PostToolUse: ${matchingHooks.length} hook(s) matched for tool: ${toolName}`,\n )\n\n const input = createPostToolUseInput(\n this.sessionId,\n this.transcriptPath,\n toolName,\n toolInput,\n toolOutput,\n )\n\n for (const hook of matchingHooks) {\n const plugin = this.plugins.find(p => p.manifest.name === hook.pluginName)\n if (!plugin) continue\n\n const options: HookExecutionOptions = {\n sessionId: this.sessionId,\n transcriptPath: this.transcriptPath,\n pluginRoot: plugin.location,\n }\n\n // PostToolUse hooks don't affect workflow, just execute them\n await executeHooksForEvent(HookEvent.PostToolUse, [hook], input, options)\n }\n }\n\n /**\n * Execute UserPromptSubmit hooks\n */\n async executeUserPromptSubmit(userPrompt: string): Promise<{\n shouldContinue: boolean\n shouldAskUser: boolean\n reason?: string\n }> {\n if (!this.enabled) {\n return { shouldContinue: true, shouldAskUser: false }\n }\n\n const allHooks = this.plugins.flatMap(p => p.hooks)\n const matchingHooks = allHooks.filter(\n hook => hook.event === HookEvent.UserPromptSubmit,\n )\n\n if (matchingHooks.length === 0) {\n return { shouldContinue: true, shouldAskUser: false }\n }\n\n logDebug(`UserPromptSubmit: ${matchingHooks.length} hook(s)`)\n\n const input = createUserPromptSubmitInput(\n this.sessionId,\n this.transcriptPath,\n userPrompt,\n )\n\n const results: HookExecutionResult[] = []\n\n for (const hook of matchingHooks) {\n const plugin = this.plugins.find(p => p.manifest.name === hook.pluginName)\n if (!plugin) continue\n\n const options: HookExecutionOptions = {\n sessionId: this.sessionId,\n transcriptPath: this.transcriptPath,\n pluginRoot: plugin.location,\n }\n\n const result = await executeHooksForEvent(\n HookEvent.UserPromptSubmit,\n [hook],\n input,\n options,\n )\n\n results.push(...result)\n }\n\n return processHookDecisions(results)\n }\n\n /**\n * Execute SessionStart hooks\n */\n async executeSessionStart(): Promise<void> {\n if (!this.enabled) {\n return\n }\n\n const allHooks = this.plugins.flatMap(p => p.hooks)\n const matchingHooks = allHooks.filter(\n hook => hook.event === HookEvent.SessionStart,\n )\n\n if (matchingHooks.length === 0) {\n return\n }\n\n logInfo(`SessionStart: Executing ${matchingHooks.length} hook(s)`)\n\n const input = createSessionStartInput(this.sessionId, this.transcriptPath)\n\n for (const hook of matchingHooks) {\n const plugin = this.plugins.find(p => p.manifest.name === hook.pluginName)\n if (!plugin) continue\n\n const options: HookExecutionOptions = {\n sessionId: this.sessionId,\n transcriptPath: this.transcriptPath,\n pluginRoot: plugin.location,\n }\n\n await executeHooksForEvent(HookEvent.SessionStart, [hook], input, options)\n }\n }\n\n /**\n * Execute SessionEnd hooks\n */\n async executeSessionEnd(\n reason: 'clear' | 'logout' | 'prompt_input_exit' | 'other' = 'other',\n ): Promise<void> {\n if (!this.enabled) {\n return\n }\n\n const allHooks = this.plugins.flatMap(p => p.hooks)\n const matchingHooks = allHooks.filter(\n hook => hook.event === HookEvent.SessionEnd,\n )\n\n if (matchingHooks.length === 0) {\n return\n }\n\n logInfo(`SessionEnd: Executing ${matchingHooks.length} hook(s)`)\n\n const input = createSessionEndInput(\n this.sessionId,\n this.transcriptPath,\n reason,\n )\n\n for (const hook of matchingHooks) {\n const plugin = this.plugins.find(p => p.manifest.name === hook.pluginName)\n if (!plugin) continue\n\n const options: HookExecutionOptions = {\n sessionId: this.sessionId,\n transcriptPath: this.transcriptPath,\n pluginRoot: plugin.location,\n }\n\n await executeHooksForEvent(HookEvent.SessionEnd, [hook], input, options)\n }\n }\n}\n\n/**\n * Global hook manager instance\n */\nlet globalHookManager: HookManager | null = null\n\n/**\n * Initialize global hook manager\n */\nexport function initializeHookManager(\n sessionId: string,\n transcriptPath: string,\n plugins: LoadedPlugin[],\n): HookManager {\n globalHookManager = new HookManager(sessionId, transcriptPath)\n globalHookManager.registerPlugins(plugins)\n return globalHookManager\n}\n\n/**\n * Get global hook manager\n */\nexport function getHookManager(): HookManager | null {\n return globalHookManager\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/utils/log.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/log.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n existsSync,\n mkdirSync,\n writeFileSync,\n readFileSync,\n promises as fsPromises,\n} from 'fs'\nimport { dirname, join } from 'path'\nimport { captureException } from '@services/sentry'\nimport { randomUUID } from 'crypto'\nimport envPaths from 'env-paths'\nimport type { LogOption, SerializedMessage } from '@minto-types/logs'\nimport { MACRO } from '@constants/macros'\nimport { PRODUCT_COMMAND } from '@constants/product'\n\nconst IN_MEMORY_ERROR_LOG: Array<{ error: string; timestamp: string }> = []\nconst MAX_IN_MEMORY_ERRORS = 100 // Limit to prevent memory issues\n\nconst PERMISSION_ERROR_CODES = new Set(['EACCES', 'EPERM', 'EROFS'])\n\nfunction isPermissionError(error: unknown): error is NodeJS.ErrnoException {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n PERMISSION_ERROR_CODES.has((error as NodeJS.ErrnoException).code ?? '')\n )\n}\n\nfunction safeMkdir(dir: string): boolean {\n if (existsSync(dir)) return true\n try {\n mkdirSync(dir, { recursive: true })\n return true\n } catch (error) {\n if (isPermissionError(error)) {\n return false\n }\n throw error\n }\n}\n\nfunction safeWriteFile(path: string, data: string, encoding: BufferEncoding = 'utf8'): boolean {\n try {\n writeFileSync(path, data, encoding)\n return true\n } catch (error) {\n if (isPermissionError(error)) {\n return false\n }\n throw error\n }\n}\n\nexport const SESSION_ID = randomUUID()\n\nconst paths = envPaths(PRODUCT_COMMAND)\n\nfunction getProjectDir(cwd: string): string {\n return cwd.replace(/[^a-zA-Z0-9]/g, '-')\n}\n\nexport const CACHE_PATHS = {\n errors: () => join(paths.cache, getProjectDir(process.cwd()), 'errors'),\n messages: () => join(paths.cache, getProjectDir(process.cwd()), 'messages'),\n mcpLogs: (serverName: string) =>\n join(paths.cache, getProjectDir(process.cwd()), `mcp-logs-${serverName}`),\n}\n\nexport function dateToFilename(date: Date): string {\n return date.toISOString().replace(/[:.]/g, '-')\n}\n\nconst DATE = dateToFilename(new Date())\n\nfunction getErrorsPath(): string {\n return join(CACHE_PATHS.errors(), DATE + '.txt')\n}\n\nexport function getMessagesPath(\n messageLogName: string,\n forkNumber: number,\n sidechainNumber: number,\n): string {\n return join(\n CACHE_PATHS.messages(),\n `${messageLogName}${forkNumber > 0 ? `-${forkNumber}` : ''}${\n sidechainNumber > 0 ? `-sidechain-${sidechainNumber}` : ''\n }.json`,\n )\n}\n\nexport function logError(error: unknown): void {\n try {\n if (process.env.NODE_ENV === 'test') {\n console.error(error)\n }\n\n const errorStr =\n error instanceof Error ? error.stack || error.message : String(error)\n\n const errorInfo = {\n error: errorStr,\n timestamp: new Date().toISOString(),\n }\n\n if (IN_MEMORY_ERROR_LOG.length >= MAX_IN_MEMORY_ERRORS) {\n IN_MEMORY_ERROR_LOG.shift() // Remove oldest error\n }\n IN_MEMORY_ERROR_LOG.push(errorInfo)\n\n appendToLog(getErrorsPath(), {\n error: errorStr,\n })\n } catch {\n // pass\n }\n // Also send to Sentry with session ID, but don't await\n captureException(error)\n}\n\nexport function getErrorsLog(): object[] {\n return readLog(getErrorsPath())\n}\n\nexport function getInMemoryErrors(): object[] {\n return [...IN_MEMORY_ERROR_LOG]\n}\n\nfunction readLog(path: string): object[] {\n if (!existsSync(path)) {\n return []\n }\n try {\n return JSON.parse(readFileSync(path, 'utf8'))\n } catch {\n return []\n }\n}\n\nfunction appendToLog(path: string, message: object): void {\n if (process.env.USER_TYPE === 'external') {\n return\n }\n\n const dir = dirname(path)\n if (!safeMkdir(dir)) {\n return\n }\n\n // Create messages file with empty array if it doesn't exist\n if (!existsSync(path) && !safeWriteFile(path, '[]')) {\n return\n }\n\n const messages = readLog(path)\n const messageWithTimestamp = {\n ...message,\n cwd: process.cwd(),\n userType: process.env.USER_TYPE,\n sessionId: SESSION_ID,\n timestamp: new Date().toISOString(),\n version: MACRO.VERSION,\n }\n messages.push(messageWithTimestamp)\n\n safeWriteFile(path, JSON.stringify(messages, null, 2))\n}\n\nexport function overwriteLog(path: string, messages: object[]): void {\n if (process.env.USER_TYPE === 'external') {\n return\n }\n\n if (!messages.length) {\n return\n }\n\n const dir = dirname(path)\n if (!safeMkdir(dir)) {\n return\n }\n\n const messagesWithMetadata = messages.map(message => ({\n ...message,\n cwd: process.cwd(),\n userType: process.env.USER_TYPE,\n sessionId: SESSION_ID,\n timestamp: new Date().toISOString(),\n version: MACRO.VERSION,\n }))\n\n safeWriteFile(path, JSON.stringify(messagesWithMetadata, null, 2))\n}\n\nexport async function loadLogList(\n path = CACHE_PATHS.messages(),\n): Promise<LogOption[]> {\n if (!existsSync(path)) {\n logError(`No logs found at ${path}`)\n return []\n }\n\n const files = await fsPromises.readdir(path)\n const logData = await Promise.all(\n files.map(async (file, i) => {\n const fullPath = join(path, file)\n const content = await fsPromises.readFile(fullPath, 'utf8')\n const messages = JSON.parse(content) as SerializedMessage[]\n const firstMessage = messages[0]\n const lastMessage = messages[messages.length - 1]\n const firstPrompt =\n firstMessage?.type === 'user' &&\n typeof firstMessage?.message?.content === 'string'\n ? firstMessage?.message?.content\n : 'No prompt'\n\n const { date, forkNumber, sidechainNumber } = parseLogFilename(file)\n return {\n date,\n forkNumber,\n fullPath,\n messages,\n value: i, // hack: overwritten after sorting, right below this\n created: parseISOString(firstMessage?.timestamp || date),\n modified: lastMessage?.timestamp\n ? parseISOString(lastMessage.timestamp)\n : parseISOString(date),\n firstPrompt:\n firstPrompt.split('\\n')[0]?.slice(0, 50) +\n (firstPrompt.length > 50 ? '\u2026' : '') || 'No prompt',\n messageCount: messages.length,\n sidechainNumber,\n }\n }),\n )\n\n return sortLogs(logData.filter(_ => _.messages.length)).map((_, i) => ({\n ..._,\n value: i,\n }))\n}\n\nexport function parseLogFilename(filename: string): {\n date: string\n forkNumber: number | undefined\n sidechainNumber: number | undefined\n} {\n const base = filename.split('.')[0]!\n // Default timestamp format has 6 segments: 2025-01-27T01-31-35-104Z\n const segments = base.split('-')\n const hasSidechain = base.includes('-sidechain-')\n\n let date = base\n let forkNumber: number | undefined = undefined\n let sidechainNumber: number | undefined = undefined\n\n if (hasSidechain) {\n const sidechainIndex = segments.indexOf('sidechain')\n sidechainNumber = Number(segments[sidechainIndex + 1])\n // Fork number is before sidechain if exists\n if (sidechainIndex > 6) {\n forkNumber = Number(segments[sidechainIndex - 1])\n date = segments.slice(0, 6).join('-')\n } else {\n date = segments.slice(0, 6).join('-')\n }\n } else if (segments.length > 6) {\n // Has fork number\n const lastSegment = Number(segments[segments.length - 1])\n forkNumber = lastSegment >= 0 ? lastSegment : undefined\n date = segments.slice(0, 6).join('-')\n } else {\n // Basic timestamp only\n date = base\n }\n\n return { date, forkNumber, sidechainNumber }\n}\n\nexport function getNextAvailableLogForkNumber(\n date: string,\n forkNumber: number,\n // Main chain has sidechainNumber 0\n sidechainNumber: number,\n): number {\n while (existsSync(getMessagesPath(date, forkNumber, sidechainNumber))) {\n forkNumber++\n }\n return forkNumber\n}\n\nexport function getNextAvailableLogSidechainNumber(\n date: string,\n forkNumber: number,\n): number {\n let sidechainNumber = 1\n while (existsSync(getMessagesPath(date, forkNumber, sidechainNumber))) {\n sidechainNumber++\n }\n return sidechainNumber\n}\n\nexport function getForkNumberFromFilename(\n filename: string,\n): number | undefined {\n const base = filename.split('.')[0]!\n const segments = base.split('-')\n const hasSidechain = base.includes('-sidechain-')\n\n if (hasSidechain) {\n const sidechainIndex = segments.indexOf('sidechain')\n if (sidechainIndex > 6) {\n return Number(segments[sidechainIndex - 1])\n }\n return undefined\n }\n\n if (segments.length > 6) {\n const lastNumber = Number(segments[segments.length - 1])\n return lastNumber >= 0 ? lastNumber : undefined\n }\n return undefined\n}\n\nexport function sortLogs(logs: LogOption[]): LogOption[] {\n return logs.sort((a, b) => {\n // Sort by modified date (newest first)\n const modifiedDiff = b.modified.getTime() - a.modified.getTime()\n if (modifiedDiff !== 0) {\n return modifiedDiff\n }\n\n // If modified dates are equal, sort by created date\n const createdDiff = b.created.getTime() - a.created.getTime()\n if (createdDiff !== 0) {\n return createdDiff\n }\n\n // If both dates are equal, sort by fork number\n return (b.forkNumber ?? 0) - (a.forkNumber ?? 0)\n })\n}\n\nexport function formatDate(date: Date): string {\n const now = new Date()\n const yesterday = new Date(now)\n yesterday.setDate(yesterday.getDate() - 1)\n\n const isToday = date.toDateString() === now.toDateString()\n const isYesterday = date.toDateString() === yesterday.toDateString()\n\n const timeStr = date\n .toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n })\n .toLowerCase()\n\n if (isToday) {\n return `Today at ${timeStr}`\n } else if (isYesterday) {\n return `Yesterday at ${timeStr}`\n } else {\n return (\n date.toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n }) + ` at ${timeStr}`\n )\n }\n}\n\nexport function parseISOString(s: string): Date {\n const b = s.split(/\\D+/)\n return new Date(\n Date.UTC(\n parseInt(b[0]!, 10),\n parseInt(b[1]!, 10) - 1,\n parseInt(b[2]!, 10),\n parseInt(b[3]!, 10),\n parseInt(b[4]!, 10),\n parseInt(b[5]!, 10),\n parseInt(b[6]!, 10),\n ),\n )\n}\n\nexport function logMCPError(serverName: string, error: unknown): void {\n try {\n const logDir = CACHE_PATHS.mcpLogs(serverName)\n const errorStr =\n error instanceof Error ? error.stack || error.message : String(error)\n const timestamp = new Date().toISOString()\n\n const logFile = join(logDir, DATE + '.txt')\n\n if (!existsSync(logDir)) {\n mkdirSync(logDir, { recursive: true })\n }\n\n if (!existsSync(logFile)) {\n writeFileSync(logFile, '[]', 'utf8')\n }\n\n const errorInfo = {\n error: errorStr,\n timestamp,\n sessionId: SESSION_ID,\n cwd: process.cwd(),\n }\n\n const messages = readLog(logFile)\n messages.push(errorInfo)\n writeFileSync(logFile, JSON.stringify(messages, null, 2), 'utf8')\n } catch {\n // Silently fail\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,OACP;AACP,SAAS,SAAS,YAAY;AAC9B,SAAS,wBAAwB;AACjC,SAAS,kBAAkB;AAC3B,OAAO,cAAc;AAErB,SAAS,aAAa;AACtB,SAAS,uBAAuB;AAEhC,MAAM,sBAAmE,CAAC;AAC1E,MAAM,uBAAuB;AAE7B,MAAM,yBAAyB,oBAAI,IAAI,CAAC,UAAU,SAAS,OAAO,CAAC;AAEnE,SAAS,kBAAkB,OAAgD;AACzE,SACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,uBAAuB,IAAK,MAAgC,QAAQ,EAAE;AAE1E;AAEA,SAAS,UAAU,KAAsB;AACvC,MAAI,WAAW,GAAG,EAAG,QAAO;AAC5B,MAAI;AACF,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAClC,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,kBAAkB,KAAK,GAAG;AAC5B,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,
|
|
4
|
+
"sourcesContent": ["import {\n existsSync,\n mkdirSync,\n writeFileSync,\n readFileSync,\n promises as fsPromises,\n} from 'fs'\nimport { dirname, join } from 'path'\nimport { captureException } from '@services/sentry'\nimport { randomUUID } from 'crypto'\nimport envPaths from 'env-paths'\nimport type { LogOption, SerializedMessage } from '@minto-types/logs'\nimport { MACRO } from '@constants/macros'\nimport { PRODUCT_COMMAND } from '@constants/product'\n\nconst IN_MEMORY_ERROR_LOG: Array<{ error: string; timestamp: string }> = []\nconst MAX_IN_MEMORY_ERRORS = 100 // Limit to prevent memory issues\n\nconst PERMISSION_ERROR_CODES = new Set(['EACCES', 'EPERM', 'EROFS'])\n\nfunction isPermissionError(error: unknown): error is NodeJS.ErrnoException {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n PERMISSION_ERROR_CODES.has((error as NodeJS.ErrnoException).code ?? '')\n )\n}\n\nfunction safeMkdir(dir: string): boolean {\n if (existsSync(dir)) return true\n try {\n mkdirSync(dir, { recursive: true })\n return true\n } catch (error) {\n if (isPermissionError(error)) {\n return false\n }\n throw error\n }\n}\n\nfunction safeWriteFile(\n path: string,\n data: string,\n encoding: BufferEncoding = 'utf8',\n): boolean {\n try {\n writeFileSync(path, data, encoding)\n return true\n } catch (error) {\n if (isPermissionError(error)) {\n return false\n }\n throw error\n }\n}\n\nexport const SESSION_ID = randomUUID()\n\nconst paths = envPaths(PRODUCT_COMMAND)\n\nfunction getProjectDir(cwd: string): string {\n return cwd.replace(/[^a-zA-Z0-9]/g, '-')\n}\n\nexport const CACHE_PATHS = {\n errors: () => join(paths.cache, getProjectDir(process.cwd()), 'errors'),\n messages: () => join(paths.cache, getProjectDir(process.cwd()), 'messages'),\n mcpLogs: (serverName: string) =>\n join(paths.cache, getProjectDir(process.cwd()), `mcp-logs-${serverName}`),\n}\n\nexport function dateToFilename(date: Date): string {\n return date.toISOString().replace(/[:.]/g, '-')\n}\n\nconst DATE = dateToFilename(new Date())\n\nfunction getErrorsPath(): string {\n return join(CACHE_PATHS.errors(), DATE + '.txt')\n}\n\nexport function getMessagesPath(\n messageLogName: string,\n forkNumber: number,\n sidechainNumber: number,\n): string {\n return join(\n CACHE_PATHS.messages(),\n `${messageLogName}${forkNumber > 0 ? `-${forkNumber}` : ''}${\n sidechainNumber > 0 ? `-sidechain-${sidechainNumber}` : ''\n }.json`,\n )\n}\n\nexport function logError(error: unknown): void {\n try {\n if (process.env.NODE_ENV === 'test') {\n console.error(error)\n }\n\n const errorStr =\n error instanceof Error ? error.stack || error.message : String(error)\n\n const errorInfo = {\n error: errorStr,\n timestamp: new Date().toISOString(),\n }\n\n if (IN_MEMORY_ERROR_LOG.length >= MAX_IN_MEMORY_ERRORS) {\n IN_MEMORY_ERROR_LOG.shift() // Remove oldest error\n }\n IN_MEMORY_ERROR_LOG.push(errorInfo)\n\n appendToLog(getErrorsPath(), {\n error: errorStr,\n })\n } catch {\n // pass\n }\n // Also send to Sentry with session ID, but don't await\n captureException(error)\n}\n\nexport function getErrorsLog(): object[] {\n return readLog(getErrorsPath())\n}\n\nexport function getInMemoryErrors(): object[] {\n return [...IN_MEMORY_ERROR_LOG]\n}\n\nfunction readLog(path: string): object[] {\n if (!existsSync(path)) {\n return []\n }\n try {\n return JSON.parse(readFileSync(path, 'utf8'))\n } catch {\n return []\n }\n}\n\nfunction appendToLog(path: string, message: object): void {\n if (process.env.USER_TYPE === 'external') {\n return\n }\n\n const dir = dirname(path)\n if (!safeMkdir(dir)) {\n return\n }\n\n // Create messages file with empty array if it doesn't exist\n if (!existsSync(path) && !safeWriteFile(path, '[]')) {\n return\n }\n\n const messages = readLog(path)\n const messageWithTimestamp = {\n ...message,\n cwd: process.cwd(),\n userType: process.env.USER_TYPE,\n sessionId: SESSION_ID,\n timestamp: new Date().toISOString(),\n version: MACRO.VERSION,\n }\n messages.push(messageWithTimestamp)\n\n safeWriteFile(path, JSON.stringify(messages, null, 2))\n}\n\nexport function overwriteLog(path: string, messages: object[]): void {\n if (process.env.USER_TYPE === 'external') {\n return\n }\n\n if (!messages.length) {\n return\n }\n\n const dir = dirname(path)\n if (!safeMkdir(dir)) {\n return\n }\n\n const messagesWithMetadata = messages.map(message => ({\n ...message,\n cwd: process.cwd(),\n userType: process.env.USER_TYPE,\n sessionId: SESSION_ID,\n timestamp: new Date().toISOString(),\n version: MACRO.VERSION,\n }))\n\n safeWriteFile(path, JSON.stringify(messagesWithMetadata, null, 2))\n}\n\nexport async function loadLogList(\n path = CACHE_PATHS.messages(),\n): Promise<LogOption[]> {\n if (!existsSync(path)) {\n logError(`No logs found at ${path}`)\n return []\n }\n\n const files = await fsPromises.readdir(path)\n const logData = await Promise.all(\n files.map(async (file, i) => {\n const fullPath = join(path, file)\n const content = await fsPromises.readFile(fullPath, 'utf8')\n const messages = JSON.parse(content) as SerializedMessage[]\n const firstMessage = messages[0]\n const lastMessage = messages[messages.length - 1]\n const firstPrompt =\n firstMessage?.type === 'user' &&\n typeof firstMessage?.message?.content === 'string'\n ? firstMessage?.message?.content\n : 'No prompt'\n\n const { date, forkNumber, sidechainNumber } = parseLogFilename(file)\n return {\n date,\n forkNumber,\n fullPath,\n messages,\n value: i, // hack: overwritten after sorting, right below this\n created: parseISOString(firstMessage?.timestamp || date),\n modified: lastMessage?.timestamp\n ? parseISOString(lastMessage.timestamp)\n : parseISOString(date),\n firstPrompt:\n firstPrompt.split('\\n')[0]?.slice(0, 50) +\n (firstPrompt.length > 50 ? '\u2026' : '') || 'No prompt',\n messageCount: messages.length,\n sidechainNumber,\n }\n }),\n )\n\n return sortLogs(logData.filter(_ => _.messages.length)).map((_, i) => ({\n ..._,\n value: i,\n }))\n}\n\nexport function parseLogFilename(filename: string): {\n date: string\n forkNumber: number | undefined\n sidechainNumber: number | undefined\n} {\n const base = filename.split('.')[0]!\n // Default timestamp format has 6 segments: 2025-01-27T01-31-35-104Z\n const segments = base.split('-')\n const hasSidechain = base.includes('-sidechain-')\n\n let date = base\n let forkNumber: number | undefined = undefined\n let sidechainNumber: number | undefined = undefined\n\n if (hasSidechain) {\n const sidechainIndex = segments.indexOf('sidechain')\n sidechainNumber = Number(segments[sidechainIndex + 1])\n // Fork number is before sidechain if exists\n if (sidechainIndex > 6) {\n forkNumber = Number(segments[sidechainIndex - 1])\n date = segments.slice(0, 6).join('-')\n } else {\n date = segments.slice(0, 6).join('-')\n }\n } else if (segments.length > 6) {\n // Has fork number\n const lastSegment = Number(segments[segments.length - 1])\n forkNumber = lastSegment >= 0 ? lastSegment : undefined\n date = segments.slice(0, 6).join('-')\n } else {\n // Basic timestamp only\n date = base\n }\n\n return { date, forkNumber, sidechainNumber }\n}\n\nexport function getNextAvailableLogForkNumber(\n date: string,\n forkNumber: number,\n // Main chain has sidechainNumber 0\n sidechainNumber: number,\n): number {\n while (existsSync(getMessagesPath(date, forkNumber, sidechainNumber))) {\n forkNumber++\n }\n return forkNumber\n}\n\nexport function getNextAvailableLogSidechainNumber(\n date: string,\n forkNumber: number,\n): number {\n let sidechainNumber = 1\n while (existsSync(getMessagesPath(date, forkNumber, sidechainNumber))) {\n sidechainNumber++\n }\n return sidechainNumber\n}\n\nexport function getForkNumberFromFilename(\n filename: string,\n): number | undefined {\n const base = filename.split('.')[0]!\n const segments = base.split('-')\n const hasSidechain = base.includes('-sidechain-')\n\n if (hasSidechain) {\n const sidechainIndex = segments.indexOf('sidechain')\n if (sidechainIndex > 6) {\n return Number(segments[sidechainIndex - 1])\n }\n return undefined\n }\n\n if (segments.length > 6) {\n const lastNumber = Number(segments[segments.length - 1])\n return lastNumber >= 0 ? lastNumber : undefined\n }\n return undefined\n}\n\nexport function sortLogs(logs: LogOption[]): LogOption[] {\n return logs.sort((a, b) => {\n // Sort by modified date (newest first)\n const modifiedDiff = b.modified.getTime() - a.modified.getTime()\n if (modifiedDiff !== 0) {\n return modifiedDiff\n }\n\n // If modified dates are equal, sort by created date\n const createdDiff = b.created.getTime() - a.created.getTime()\n if (createdDiff !== 0) {\n return createdDiff\n }\n\n // If both dates are equal, sort by fork number\n return (b.forkNumber ?? 0) - (a.forkNumber ?? 0)\n })\n}\n\nexport function formatDate(date: Date): string {\n const now = new Date()\n const yesterday = new Date(now)\n yesterday.setDate(yesterday.getDate() - 1)\n\n const isToday = date.toDateString() === now.toDateString()\n const isYesterday = date.toDateString() === yesterday.toDateString()\n\n const timeStr = date\n .toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n })\n .toLowerCase()\n\n if (isToday) {\n return `Today at ${timeStr}`\n } else if (isYesterday) {\n return `Yesterday at ${timeStr}`\n } else {\n return (\n date.toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n }) + ` at ${timeStr}`\n )\n }\n}\n\nexport function parseISOString(s: string): Date {\n const b = s.split(/\\D+/)\n return new Date(\n Date.UTC(\n parseInt(b[0]!, 10),\n parseInt(b[1]!, 10) - 1,\n parseInt(b[2]!, 10),\n parseInt(b[3]!, 10),\n parseInt(b[4]!, 10),\n parseInt(b[5]!, 10),\n parseInt(b[6]!, 10),\n ),\n )\n}\n\nexport function logMCPError(serverName: string, error: unknown): void {\n try {\n const logDir = CACHE_PATHS.mcpLogs(serverName)\n const errorStr =\n error instanceof Error ? error.stack || error.message : String(error)\n const timestamp = new Date().toISOString()\n\n const logFile = join(logDir, DATE + '.txt')\n\n if (!existsSync(logDir)) {\n mkdirSync(logDir, { recursive: true })\n }\n\n if (!existsSync(logFile)) {\n writeFileSync(logFile, '[]', 'utf8')\n }\n\n const errorInfo = {\n error: errorStr,\n timestamp,\n sessionId: SESSION_ID,\n cwd: process.cwd(),\n }\n\n const messages = readLog(logFile)\n messages.push(errorInfo)\n writeFileSync(logFile, JSON.stringify(messages, null, 2), 'utf8')\n } catch {\n // Silently fail\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
readFileSync,
|
|
4
|
+
writeFileSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
cpSync,
|
|
8
|
+
symlinkSync,
|
|
9
|
+
lstatSync
|
|
10
|
+
} from "fs";
|
|
2
11
|
import { join, resolve, isAbsolute } from "path";
|
|
3
12
|
import { homedir } from "os";
|
|
4
13
|
import { execFileNoThrow } from "./execFileNoThrow.js";
|
|
@@ -184,10 +193,16 @@ async function updateMarketplace(name) {
|
|
|
184
193
|
let manifest;
|
|
185
194
|
switch (marketplace.source.type) {
|
|
186
195
|
case "github":
|
|
187
|
-
manifest = await fetchGitHubMarketplace(
|
|
196
|
+
manifest = await fetchGitHubMarketplace(
|
|
197
|
+
marketplace.source.repo,
|
|
198
|
+
marketplace.source.ref
|
|
199
|
+
);
|
|
188
200
|
break;
|
|
189
201
|
case "url":
|
|
190
|
-
manifest = await fetchUrlMarketplace(
|
|
202
|
+
manifest = await fetchUrlMarketplace(
|
|
203
|
+
marketplace.source.url,
|
|
204
|
+
marketplace.source.ref
|
|
205
|
+
);
|
|
191
206
|
break;
|
|
192
207
|
case "local":
|
|
193
208
|
manifest = loadLocalMarketplace(marketplace.source.path);
|
|
@@ -239,7 +254,10 @@ function loadMarketplaceSettings() {
|
|
|
239
254
|
const settings = JSON.parse(content);
|
|
240
255
|
return settings;
|
|
241
256
|
} catch (error) {
|
|
242
|
-
console.error(
|
|
257
|
+
console.error(
|
|
258
|
+
`Error loading marketplace settings from ${label} (${path}):`,
|
|
259
|
+
error
|
|
260
|
+
);
|
|
243
261
|
}
|
|
244
262
|
}
|
|
245
263
|
}
|
|
@@ -249,12 +267,18 @@ async function autoRegisterMarketplaces() {
|
|
|
249
267
|
const settings = loadMarketplaceSettings();
|
|
250
268
|
if (!settings.extraKnownMarketplaces) return;
|
|
251
269
|
const registry = loadRegistry();
|
|
252
|
-
for (const [name, config] of Object.entries(
|
|
270
|
+
for (const [name, config] of Object.entries(
|
|
271
|
+
settings.extraKnownMarketplaces
|
|
272
|
+
)) {
|
|
253
273
|
if (registry.some((m) => m.name === name)) continue;
|
|
254
274
|
try {
|
|
255
275
|
let source;
|
|
256
276
|
if (config.source.source === "github" && config.source.repo) {
|
|
257
|
-
source = {
|
|
277
|
+
source = {
|
|
278
|
+
type: "github",
|
|
279
|
+
repo: config.source.repo,
|
|
280
|
+
ref: config.source.ref
|
|
281
|
+
};
|
|
258
282
|
} else if (config.source.source === "url" && config.source.url) {
|
|
259
283
|
source = { type: "url", url: config.source.url, ref: config.source.ref };
|
|
260
284
|
} else if (config.source.source === "local" && config.source.path) {
|
|
@@ -295,7 +319,10 @@ async function getMarketplaceRepoPath(marketplace) {
|
|
|
295
319
|
return { path: tempDir, cleanup: true };
|
|
296
320
|
}
|
|
297
321
|
case "url": {
|
|
298
|
-
const tempDir = await cloneGitRepo(
|
|
322
|
+
const tempDir = await cloneGitRepo(
|
|
323
|
+
marketplace.source.url,
|
|
324
|
+
marketplace.source.ref
|
|
325
|
+
);
|
|
299
326
|
return { path: tempDir, cleanup: true };
|
|
300
327
|
}
|
|
301
328
|
case "local": {
|
|
@@ -388,7 +415,11 @@ async function installPluginFromMarketplace(pluginName, marketplaceName, targetD
|
|
|
388
415
|
marketplaceRepoPath = repoInfo.path;
|
|
389
416
|
cleanupMarketplaceRepo = repoInfo.cleanup;
|
|
390
417
|
const pluginRoot = marketplace.manifest.metadata?.pluginRoot || "";
|
|
391
|
-
const pluginSourcePath = join(
|
|
418
|
+
const pluginSourcePath = join(
|
|
419
|
+
marketplaceRepoPath,
|
|
420
|
+
pluginRoot,
|
|
421
|
+
plugin.source
|
|
422
|
+
);
|
|
392
423
|
if (!existsSync(pluginSourcePath)) {
|
|
393
424
|
throw new MarketplaceError(
|
|
394
425
|
`Plugin source path does not exist: ${pluginSourcePath} (relative path: ${plugin.source})`,
|
|
@@ -448,7 +479,11 @@ async function installPluginFromMarketplace(pluginName, marketplaceName, targetD
|
|
|
448
479
|
keywords: plugin.keywords || [],
|
|
449
480
|
category: plugin.category
|
|
450
481
|
};
|
|
451
|
-
writeFileSync(
|
|
482
|
+
writeFileSync(
|
|
483
|
+
pluginManifestPath,
|
|
484
|
+
JSON.stringify(pluginManifest, null, 2),
|
|
485
|
+
"utf-8"
|
|
486
|
+
);
|
|
452
487
|
}
|
|
453
488
|
return installDir;
|
|
454
489
|
} finally {
|