@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.
Files changed (273) hide show
  1. package/dist/commands/agents/AgentsCommand.js +2342 -0
  2. package/dist/commands/agents/AgentsCommand.js.map +7 -0
  3. package/dist/commands/agents/constants.js +58 -0
  4. package/dist/commands/agents/constants.js.map +7 -0
  5. package/dist/commands/agents/index.js +37 -0
  6. package/dist/commands/agents/index.js.map +7 -0
  7. package/dist/commands/agents/types.js +10 -0
  8. package/dist/commands/agents/types.js.map +7 -0
  9. package/dist/commands/agents/utils/fileOperations.js +185 -0
  10. package/dist/commands/agents/utils/fileOperations.js.map +7 -0
  11. package/dist/commands/agents/utils/index.js +21 -0
  12. package/dist/commands/agents/utils/index.js.map +7 -0
  13. package/dist/commands/bug.js +2 -2
  14. package/dist/commands/bug.js.map +2 -2
  15. package/dist/commands/compact.js +5 -5
  16. package/dist/commands/compact.js.map +2 -2
  17. package/dist/commands/ctx_viz.js +55 -22
  18. package/dist/commands/ctx_viz.js.map +2 -2
  19. package/dist/commands/mcp-interactive.js +11 -11
  20. package/dist/commands/mcp-interactive.js.map +2 -2
  21. package/dist/commands/model.js +94 -32
  22. package/dist/commands/model.js.map +3 -3
  23. package/dist/commands/plugin/AddMarketplaceForm.js +49 -21
  24. package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
  25. package/dist/commands/plugin/ConfirmDialog.js +38 -26
  26. package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
  27. package/dist/commands/plugin/InstalledPluginsByMarketplace.js +24 -8
  28. package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
  29. package/dist/commands/plugin/InstalledPluginsManager.js +3 -1
  30. package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
  31. package/dist/commands/plugin/MainMenu.js +16 -7
  32. package/dist/commands/plugin/MainMenu.js.map +2 -2
  33. package/dist/commands/plugin/MarketplaceManager.js +84 -39
  34. package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
  35. package/dist/commands/plugin/MarketplaceSelector.js +7 -3
  36. package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
  37. package/dist/commands/plugin/PlaceholderScreen.js +16 -2
  38. package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
  39. package/dist/commands/plugin/PluginBrowser.js +4 -2
  40. package/dist/commands/plugin/PluginBrowser.js.map +2 -2
  41. package/dist/commands/plugin/PluginDetailsInstall.js +12 -6
  42. package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
  43. package/dist/commands/plugin/PluginDetailsManage.js +14 -5
  44. package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
  45. package/dist/commands/plugin/example-usage.js.map +2 -2
  46. package/dist/commands/plugin/utils.js.map +2 -2
  47. package/dist/commands/plugin.js +226 -46
  48. package/dist/commands/plugin.js.map +2 -2
  49. package/dist/commands/refreshCommands.js +6 -3
  50. package/dist/commands/refreshCommands.js.map +2 -2
  51. package/dist/commands/resume.js +2 -1
  52. package/dist/commands/resume.js.map +2 -2
  53. package/dist/commands/setup.js +19 -5
  54. package/dist/commands/setup.js.map +2 -2
  55. package/dist/commands/terminalSetup.js +2 -2
  56. package/dist/commands/terminalSetup.js.map +1 -1
  57. package/dist/commands.js +14 -30
  58. package/dist/commands.js.map +2 -2
  59. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
  60. package/dist/components/AskUserQuestionDialog/QuestionView.js +10 -1
  61. package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
  62. package/dist/components/BackgroundTasksPanel.js +5 -1
  63. package/dist/components/BackgroundTasksPanel.js.map +2 -2
  64. package/dist/components/Config.js +17 -4
  65. package/dist/components/Config.js.map +2 -2
  66. package/dist/components/ConsoleOAuthFlow.js.map +2 -2
  67. package/dist/components/CustomSelect/select-option.js +4 -1
  68. package/dist/components/CustomSelect/select-option.js.map +2 -2
  69. package/dist/components/Help.js +6 -8
  70. package/dist/components/Help.js.map +2 -2
  71. package/dist/components/Logo.js +1 -1
  72. package/dist/components/Logo.js.map +2 -2
  73. package/dist/components/ModelListManager.js.map +2 -2
  74. package/dist/components/ModelSelector/ModelSelector.js +2030 -0
  75. package/dist/components/ModelSelector/ModelSelector.js.map +7 -0
  76. package/dist/components/ModelSelector/ScreenContainer.js +27 -0
  77. package/dist/components/ModelSelector/ScreenContainer.js.map +7 -0
  78. package/dist/components/ModelSelector/constants.js +37 -0
  79. package/dist/components/ModelSelector/constants.js.map +7 -0
  80. package/dist/components/ModelSelector/hooks/index.js +5 -0
  81. package/dist/components/ModelSelector/hooks/index.js.map +7 -0
  82. package/dist/components/ModelSelector/hooks/useEscapeNavigation.js +21 -0
  83. package/dist/components/ModelSelector/hooks/useEscapeNavigation.js.map +7 -0
  84. package/dist/components/ModelSelector/index.js +17 -0
  85. package/dist/components/ModelSelector/index.js.map +7 -0
  86. package/dist/components/ModelSelector/types.js +1 -0
  87. package/dist/components/ModelSelector/types.js.map +7 -0
  88. package/dist/components/PressEnterToContinue.js +1 -1
  89. package/dist/components/PressEnterToContinue.js.map +2 -2
  90. package/dist/components/ProjectOnboarding.js +1 -1
  91. package/dist/components/ProjectOnboarding.js.map +2 -2
  92. package/dist/components/PromptInput.js +88 -37
  93. package/dist/components/PromptInput.js.map +2 -2
  94. package/dist/components/QuitSummary.js +17 -10
  95. package/dist/components/QuitSummary.js.map +2 -2
  96. package/dist/components/SentryErrorBoundary.js.map +2 -2
  97. package/dist/components/StreamingBashOutput.js.map +2 -2
  98. package/dist/components/StructuredDiff.js.map +2 -2
  99. package/dist/components/SubagentProgress.js.map +2 -2
  100. package/dist/components/TaskCard.js.map +2 -2
  101. package/dist/components/TextInput.js.map +1 -1
  102. package/dist/components/TodoItem.js.map +1 -1
  103. package/dist/components/binary-feedback/BinaryFeedbackOption.js +1 -3
  104. package/dist/components/binary-feedback/BinaryFeedbackOption.js.map +2 -2
  105. package/dist/components/messages/AssistantLocalCommandOutputMessage.js.map +1 -1
  106. package/dist/components/messages/AssistantToolUseMessage.js +3 -1
  107. package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
  108. package/dist/components/messages/TaskProgressMessage.js.map +2 -2
  109. package/dist/components/messages/TaskToolMessage.js.map +2 -2
  110. package/dist/components/messages/UserToolResultMessage/utils.js.map +2 -2
  111. package/dist/components/permissions/FileEditPermissionRequest/FileEditToolDiff.js.map +2 -2
  112. package/dist/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.js.map +2 -2
  113. package/dist/components/permissions/hooks.js.map +2 -2
  114. package/dist/constants/modelCapabilities.js +1 -1
  115. package/dist/constants/modelCapabilities.js.map +2 -2
  116. package/dist/constants/prompts.js.map +1 -1
  117. package/dist/constants/timing.js +34 -0
  118. package/dist/constants/timing.js.map +7 -0
  119. package/dist/entrypoints/cli.js +128 -33
  120. package/dist/entrypoints/cli.js.map +3 -3
  121. package/dist/entrypoints/mcp.js +13 -18
  122. package/dist/entrypoints/mcp.js.map +2 -2
  123. package/dist/hooks/useCanUseTool.js.map +2 -2
  124. package/dist/hooks/useCancelRequest.js.map +1 -1
  125. package/dist/hooks/useHistorySearch.js.map +2 -2
  126. package/dist/hooks/useLogStartupTime.js.map +2 -2
  127. package/dist/hooks/usePermissionRequestLogging.js.map +2 -2
  128. package/dist/hooks/useTextInput.js.map +1 -1
  129. package/dist/hooks/useUnifiedCompletion.js +493 -394
  130. package/dist/hooks/useUnifiedCompletion.js.map +2 -2
  131. package/dist/index.js.map +2 -2
  132. package/dist/permissions.js +4 -7
  133. package/dist/permissions.js.map +2 -2
  134. package/dist/query.js +6 -1
  135. package/dist/query.js.map +2 -2
  136. package/dist/screens/REPL.js +72 -36
  137. package/dist/screens/REPL.js.map +2 -2
  138. package/dist/screens/ResumeConversation.js +2 -1
  139. package/dist/screens/ResumeConversation.js.map +2 -2
  140. package/dist/services/adapters/base.js.map +2 -2
  141. package/dist/services/adapters/chatCompletions.js.map +2 -2
  142. package/dist/services/adapters/responsesAPI.js +3 -1
  143. package/dist/services/adapters/responsesAPI.js.map +2 -2
  144. package/dist/services/claude.js +327 -328
  145. package/dist/services/claude.js.map +2 -2
  146. package/dist/services/customCommands.js +6 -1
  147. package/dist/services/customCommands.js.map +2 -2
  148. package/dist/services/fileFreshness.js.map +2 -2
  149. package/dist/services/gpt5ConnectionTest.js +20 -7
  150. package/dist/services/gpt5ConnectionTest.js.map +2 -2
  151. package/dist/services/hookExecutor.js +6 -12
  152. package/dist/services/hookExecutor.js.map +2 -2
  153. package/dist/services/mcpClient.js +29 -2
  154. package/dist/services/mcpClient.js.map +2 -2
  155. package/dist/services/mentionProcessor.js +23 -10
  156. package/dist/services/mentionProcessor.js.map +2 -2
  157. package/dist/services/modelAdapterFactory.js.map +2 -2
  158. package/dist/services/oauth.js.map +2 -2
  159. package/dist/services/openai.js +109 -72
  160. package/dist/services/openai.js.map +3 -3
  161. package/dist/services/responseStateManager.js.map +2 -2
  162. package/dist/services/systemReminder.js.map +2 -2
  163. package/dist/tools/ArchitectTool/ArchitectTool.js +10 -9
  164. package/dist/tools/ArchitectTool/ArchitectTool.js.map +2 -2
  165. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +14 -8
  166. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  167. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +8 -1
  168. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
  169. package/dist/tools/BashOutputTool/BashOutputTool.js.map +2 -2
  170. package/dist/tools/BashTool/BashTool.js.map +2 -2
  171. package/dist/tools/FileReadTool/FileReadTool.js +23 -4
  172. package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
  173. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  174. package/dist/tools/GlobTool/GlobTool.js +11 -2
  175. package/dist/tools/GlobTool/GlobTool.js.map +2 -2
  176. package/dist/tools/GrepTool/GrepTool.js +7 -5
  177. package/dist/tools/GrepTool/GrepTool.js.map +2 -2
  178. package/dist/tools/MCPTool/MCPTool.js +11 -12
  179. package/dist/tools/MCPTool/MCPTool.js.map +2 -2
  180. package/dist/tools/MultiEditTool/MultiEditTool.js +4 -1
  181. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  182. package/dist/tools/NotebookReadTool/NotebookReadTool.js +11 -5
  183. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +2 -2
  184. package/dist/tools/SkillTool/SkillTool.js +18 -6
  185. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  186. package/dist/tools/TaskTool/TaskTool.js +37 -51
  187. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  188. package/dist/tools/TaskTool/prompt.js.map +2 -2
  189. package/dist/tools/ThinkTool/ThinkTool.js +6 -1
  190. package/dist/tools/ThinkTool/ThinkTool.js.map +2 -2
  191. package/dist/tools/TodoWriteTool/TodoWriteTool.js +29 -5
  192. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +2 -2
  193. package/dist/tools/URLFetcherTool/URLFetcherTool.js +5 -2
  194. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
  195. package/dist/tools/URLFetcherTool/cache.js +6 -3
  196. package/dist/tools/URLFetcherTool/cache.js.map +2 -2
  197. package/dist/tools/URLFetcherTool/htmlToMarkdown.js +3 -1
  198. package/dist/tools/URLFetcherTool/htmlToMarkdown.js.map +2 -2
  199. package/dist/tools/WebSearchTool/WebSearchTool.js +6 -1
  200. package/dist/tools/WebSearchTool/WebSearchTool.js.map +2 -2
  201. package/dist/tools/WebSearchTool/prompt.js.map +2 -2
  202. package/dist/tools/WebSearchTool/searchProviders.js +15 -6
  203. package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
  204. package/dist/tools.js +4 -1
  205. package/dist/tools.js.map +2 -2
  206. package/dist/types/core.js +1 -0
  207. package/dist/types/core.js.map +7 -0
  208. package/dist/types/hooks.js +1 -4
  209. package/dist/types/hooks.js.map +2 -2
  210. package/dist/types/marketplace.js +8 -2
  211. package/dist/types/marketplace.js.map +2 -2
  212. package/dist/types/plugin.js +9 -6
  213. package/dist/types/plugin.js.map +2 -2
  214. package/dist/utils/BackgroundShellManager.js +76 -10
  215. package/dist/utils/BackgroundShellManager.js.map +2 -2
  216. package/dist/utils/PersistentShell.js +7 -2
  217. package/dist/utils/PersistentShell.js.map +2 -2
  218. package/dist/utils/advancedFuzzyMatcher.js +4 -1
  219. package/dist/utils/advancedFuzzyMatcher.js.map +2 -2
  220. package/dist/utils/agentLoader.js +69 -35
  221. package/dist/utils/agentLoader.js.map +2 -2
  222. package/dist/utils/agentStorage.js.map +2 -2
  223. package/dist/utils/async.js +163 -0
  224. package/dist/utils/async.js.map +7 -0
  225. package/dist/utils/autoUpdater.js +8 -2
  226. package/dist/utils/autoUpdater.js.map +2 -2
  227. package/dist/utils/commands.js +23 -11
  228. package/dist/utils/commands.js.map +2 -2
  229. package/dist/utils/commonUnixCommands.js +3 -1
  230. package/dist/utils/commonUnixCommands.js.map +2 -2
  231. package/dist/utils/compressionMode.js.map +2 -2
  232. package/dist/utils/config.js +30 -14
  233. package/dist/utils/config.js.map +2 -2
  234. package/dist/utils/debugLogger.js.map +2 -2
  235. package/dist/utils/env.js.map +2 -2
  236. package/dist/utils/envConfig.js +82 -0
  237. package/dist/utils/envConfig.js.map +7 -0
  238. package/dist/utils/errorHandling.js +89 -0
  239. package/dist/utils/errorHandling.js.map +7 -0
  240. package/dist/utils/expertChatStorage.js.map +2 -2
  241. package/dist/utils/fuzzyMatcher.js +13 -7
  242. package/dist/utils/fuzzyMatcher.js.map +2 -2
  243. package/dist/utils/hookManager.js +14 -4
  244. package/dist/utils/hookManager.js.map +2 -2
  245. package/dist/utils/log.js.map +2 -2
  246. package/dist/utils/marketplaceManager.js +44 -9
  247. package/dist/utils/marketplaceManager.js.map +2 -2
  248. package/dist/utils/messageContextManager.js.map +1 -1
  249. package/dist/utils/messages.js +6 -3
  250. package/dist/utils/messages.js.map +2 -2
  251. package/dist/utils/model.js +3 -1
  252. package/dist/utils/model.js.map +2 -2
  253. package/dist/utils/pluginInstaller.js +3 -15
  254. package/dist/utils/pluginInstaller.js.map +2 -2
  255. package/dist/utils/pluginLoader.js +41 -13
  256. package/dist/utils/pluginLoader.js.map +2 -2
  257. package/dist/utils/pluginRegistry.js.map +2 -2
  258. package/dist/utils/pluginValidator.js +71 -49
  259. package/dist/utils/pluginValidator.js.map +2 -2
  260. package/dist/utils/ptyCompat.js.map +2 -2
  261. package/dist/utils/roundConverter.js.map +2 -2
  262. package/dist/utils/secureFile.js +43 -14
  263. package/dist/utils/secureFile.js.map +2 -2
  264. package/dist/utils/sessionState.js.map +2 -2
  265. package/dist/utils/skillLoader.js.map +2 -2
  266. package/dist/utils/teamConfig.js +7 -4
  267. package/dist/utils/teamConfig.js.map +2 -2
  268. package/dist/utils/theme.js.map +2 -2
  269. package/dist/utils/thinking.js.map +2 -2
  270. package/dist/utils/unaryLogging.js.map +2 -2
  271. package/dist/version.js +2 -2
  272. package/dist/version.js.map +1 -1
  273. package/package.json +5 -5
@@ -1,4 +1,10 @@
1
- import { existsSync, readFileSync, readdirSync, statSync, watch } from "fs";
1
+ import {
2
+ existsSync,
3
+ readFileSync,
4
+ readdirSync,
5
+ statSync,
6
+ watch
7
+ } from "fs";
2
8
  import { join } from "path";
3
9
  import { homedir } from "os";
4
10
  import matter from "gray-matter";
@@ -29,7 +35,9 @@ function parseTools(tools) {
29
35
  if (!tools) return "*";
30
36
  if (tools === "*") return "*";
31
37
  if (Array.isArray(tools)) {
32
- const filteredTools = tools.filter((t) => typeof t === "string");
38
+ const filteredTools = tools.filter(
39
+ (t) => typeof t === "string"
40
+ );
33
41
  return filteredTools.length > 0 ? filteredTools : "*";
34
42
  }
35
43
  if (typeof tools === "string") {
@@ -53,11 +61,15 @@ async function scanAgentDirectory(dirPath, location) {
53
61
  const content = readFileSync(filePath, "utf-8");
54
62
  const { data: frontmatter, content: body } = matter(content);
55
63
  if (!frontmatter.name || !frontmatter.description) {
56
- console.warn(`Skipping ${filePath}: missing required fields (name, description)`);
64
+ console.warn(
65
+ `Skipping ${filePath}: missing required fields (name, description)`
66
+ );
57
67
  continue;
58
68
  }
59
69
  if (frontmatter.model && !frontmatter.model_name && !warnedAgents.has(frontmatter.name) && process.env.MINTO_DEBUG_AGENTS) {
60
- console.warn(`\u26A0\uFE0F Agent ${frontmatter.name}: 'model' field is deprecated and ignored. Use 'model_name' instead, or omit to use default 'task' model.`);
70
+ console.warn(
71
+ `\u26A0\uFE0F Agent ${frontmatter.name}: 'model' field is deprecated and ignored. Use 'model_name' instead, or omit to use default 'task' model.`
72
+ );
61
73
  warnedAgents.add(frontmatter.name);
62
74
  }
63
75
  const agent = {
@@ -91,11 +103,15 @@ async function loadPluginAgents() {
91
103
  const content = readFileSync(pluginAgent.filePath, "utf-8");
92
104
  const { data: frontmatter, content: body } = matter(content);
93
105
  if (!frontmatter.name || !frontmatter.description) {
94
- console.warn(`Skipping plugin agent ${pluginAgent.filePath}: missing required fields (name, description)`);
106
+ console.warn(
107
+ `Skipping plugin agent ${pluginAgent.filePath}: missing required fields (name, description)`
108
+ );
95
109
  continue;
96
110
  }
97
111
  if (frontmatter.model && !frontmatter.model_name && !warnedAgents.has(frontmatter.name) && process.env.MINTO_DEBUG_AGENTS) {
98
- console.warn(`\u26A0\uFE0F Plugin agent ${frontmatter.name} (from ${plugin.manifest.name}): 'model' field is deprecated and ignored. Use 'model_name' instead, or omit to use default 'task' model.`);
112
+ console.warn(
113
+ `\u26A0\uFE0F Plugin agent ${frontmatter.name} (from ${plugin.manifest.name}): 'model' field is deprecated and ignored. Use 'model_name' instead, or omit to use default 'task' model.`
114
+ );
99
115
  warnedAgents.add(frontmatter.name);
100
116
  }
101
117
  const agent = {
@@ -107,11 +123,16 @@ async function loadPluginAgents() {
107
123
  pluginName: plugin.manifest.name,
108
124
  ...frontmatter.color && { color: frontmatter.color },
109
125
  // Only use model_name field, ignore deprecated 'model' field
110
- ...frontmatter.model_name && { model_name: frontmatter.model_name }
126
+ ...frontmatter.model_name && {
127
+ model_name: frontmatter.model_name
128
+ }
111
129
  };
112
130
  agents.push(agent);
113
131
  } catch (error) {
114
- console.warn(`Failed to load plugin agent ${pluginAgent.name} from ${plugin.manifest.name}:`, error);
132
+ console.warn(
133
+ `Failed to load plugin agent ${pluginAgent.name} from ${plugin.manifest.name}:`,
134
+ error
135
+ );
115
136
  }
116
137
  }
117
138
  }
@@ -126,7 +147,13 @@ async function loadAllAgents() {
126
147
  const userMintoDir = join(homedir(), ".minto", "agents");
127
148
  const projectClaudeDir = join(getCwd(), ".claude", "agents");
128
149
  const projectMintoDir = join(getCwd(), ".minto", "agents");
129
- const [pluginAgents, userClaudeAgents, userMintoAgents, projectClaudeAgents, projectMintoAgents] = await Promise.all([
150
+ const [
151
+ pluginAgents,
152
+ userClaudeAgents,
153
+ userMintoAgents,
154
+ projectClaudeAgents,
155
+ projectMintoAgents
156
+ ] = await Promise.all([
130
157
  loadPluginAgents(),
131
158
  scanAgentDirectory(userClaudeDir, "user"),
132
159
  scanAgentDirectory(userMintoDir, "user"),
@@ -154,7 +181,14 @@ async function loadAllAgents() {
154
181
  agentMap.set(agent.agentType, agent);
155
182
  }
156
183
  const activeAgents = Array.from(agentMap.values());
157
- const allAgents = [...builtinAgents, ...pluginAgents, ...userClaudeAgents, ...userMintoAgents, ...projectClaudeAgents, ...projectMintoAgents];
184
+ const allAgents = [
185
+ ...builtinAgents,
186
+ ...pluginAgents,
187
+ ...userClaudeAgents,
188
+ ...userMintoAgents,
189
+ ...projectClaudeAgents,
190
+ ...projectMintoAgents
191
+ ];
158
192
  return { activeAgents, allAgents };
159
193
  } catch (error) {
160
194
  console.error("Failed to load agents, falling back to built-in:", error);
@@ -164,18 +198,14 @@ async function loadAllAgents() {
164
198
  };
165
199
  }
166
200
  }
167
- const getActiveAgents = memoize(
168
- async () => {
169
- const { activeAgents } = await loadAllAgents();
170
- return activeAgents;
171
- }
172
- );
173
- const getAllAgents = memoize(
174
- async () => {
175
- const { allAgents } = await loadAllAgents();
176
- return allAgents;
177
- }
178
- );
201
+ const getActiveAgents = memoize(async () => {
202
+ const { activeAgents } = await loadAllAgents();
203
+ return activeAgents;
204
+ });
205
+ const getAllAgents = memoize(async () => {
206
+ const { allAgents } = await loadAllAgents();
207
+ return allAgents;
208
+ });
179
209
  function clearAgentCache() {
180
210
  getActiveAgents.cache?.clear?.();
181
211
  getAllAgents.cache?.clear?.();
@@ -188,12 +218,10 @@ const getAgentByType = memoize(
188
218
  return agents.find((agent) => agent.agentType === agentType);
189
219
  }
190
220
  );
191
- const getAvailableAgentTypes = memoize(
192
- async () => {
193
- const agents = await getActiveAgents();
194
- return agents.map((agent) => agent.agentType);
195
- }
196
- );
221
+ const getAvailableAgentTypes = memoize(async () => {
222
+ const agents = await getActiveAgents();
223
+ return agents.map((agent) => agent.agentType);
224
+ });
197
225
  let watchers = [];
198
226
  async function startAgentWatcher(onChange) {
199
227
  await stopAgentWatcher();
@@ -203,14 +231,20 @@ async function startAgentWatcher(onChange) {
203
231
  const projectMintoDir = join(getCwd(), ".minto", "agents");
204
232
  const watchDirectory = (dirPath, label) => {
205
233
  if (existsSync(dirPath)) {
206
- const watcher = watch(dirPath, { recursive: false }, async (eventType, filename) => {
207
- if (filename && filename.endsWith(".md")) {
208
- console.log(`\u{1F504} Agent configuration changed in ${label}: ${filename}`);
209
- clearAgentCache();
210
- getAllAgents.cache?.clear?.();
211
- onChange?.();
234
+ const watcher = watch(
235
+ dirPath,
236
+ { recursive: false },
237
+ async (eventType, filename) => {
238
+ if (filename && filename.endsWith(".md")) {
239
+ console.log(
240
+ `\u{1F504} Agent configuration changed in ${label}: ${filename}`
241
+ );
242
+ clearAgentCache();
243
+ getAllAgents.cache?.clear?.();
244
+ onChange?.();
245
+ }
212
246
  }
213
- });
247
+ );
214
248
  watchers.push(watcher);
215
249
  }
216
250
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/agentLoader.ts"],
4
- "sourcesContent": ["/**\n * Agent configuration loader\n * Loads agent configurations from markdown files with YAML frontmatter.\n * Maintains compatibility with Claude Code `.claude` agent directories while\n * prioritizing Minto-specific overrides.\n */\n\nimport { existsSync, readFileSync, readdirSync, statSync, watch, FSWatcher } from 'fs'\nimport { join, resolve } from 'path'\nimport { homedir } from 'os'\nimport matter from 'gray-matter'\nimport { getCwd } from './state'\nimport { memoize } from 'lodash-es'\nimport { loadAllPlugins } from './pluginLoader'\n\n// Track warned agents to avoid spam\nconst warnedAgents = new Set<string>()\n\nexport interface AgentConfig {\n agentType: string // Agent identifier (matches subagent_type)\n whenToUse: string // Description of when to use this agent\n tools: string[] | '*' // Tool permissions\n systemPrompt: string // System prompt content\n location: 'built-in' | 'user' | 'project' | 'plugin'\n color?: string // Optional UI color\n model_name?: string // Optional model override\n pluginName?: string // Source plugin name (if loaded from plugin)\n}\n\n// Built-in general-purpose agent as fallback\nconst BUILTIN_GENERAL_PURPOSE: AgentConfig = {\n agentType: 'general-purpose',\n whenToUse: 'General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks',\n tools: '*',\n systemPrompt: `You are a general-purpose agent. Given the user's task, use the tools available to complete it efficiently and thoroughly.\n\nWhen to use your capabilities:\n- Searching for code, configurations, and patterns across large codebases\n- Analyzing multiple files to understand system architecture \n- Investigating complex questions that require exploring many files\n- Performing multi-step research tasks\n\nGuidelines:\n- For file searches: Use Grep or Glob when you need to search broadly. Use FileRead when you know the specific file path.\n- For analysis: Start broad and narrow down. Use multiple search strategies if the first doesn't yield results.\n- Be thorough: Check multiple locations, consider different naming conventions, look for related files.\n- Complete tasks directly using your capabilities.`,\n location: 'built-in'\n}\n\n/**\n * Parse tools field from frontmatter\n */\nfunction parseTools(tools: any): string[] | '*' {\n if (!tools) return '*'\n if (tools === '*') return '*'\n if (Array.isArray(tools)) {\n // Ensure all items are strings and filter out non-strings\n const filteredTools = tools.filter((t): t is string => typeof t === 'string')\n return filteredTools.length > 0 ? filteredTools : '*'\n }\n if (typeof tools === 'string') {\n return [tools]\n }\n return '*'\n}\n\n/**\n * Scan a directory for agent configuration files\n */\nasync function scanAgentDirectory(dirPath: string, location: 'user' | 'project'): Promise<AgentConfig[]> {\n if (!existsSync(dirPath)) {\n return []\n }\n\n const agents: AgentConfig[] = []\n\n try {\n const files = readdirSync(dirPath)\n\n for (const file of files) {\n if (!file.endsWith('.md')) continue\n\n const filePath = join(dirPath, file)\n const stat = statSync(filePath)\n\n if (!stat.isFile()) continue\n\n try {\n const content = readFileSync(filePath, 'utf-8')\n const { data: frontmatter, content: body } = matter(content)\n\n // Validate required fields\n if (!frontmatter.name || !frontmatter.description) {\n console.warn(`Skipping ${filePath}: missing required fields (name, description)`)\n continue\n }\n\n // Silently ignore deprecated 'model' field - no warnings by default\n // Only warn if MINTO_DEBUG_AGENTS environment variable is set\n if (frontmatter.model && !frontmatter.model_name && !warnedAgents.has(frontmatter.name) && process.env.MINTO_DEBUG_AGENTS) {\n console.warn(`\u26A0\uFE0F Agent ${frontmatter.name}: 'model' field is deprecated and ignored. Use 'model_name' instead, or omit to use default 'task' model.`)\n warnedAgents.add(frontmatter.name)\n }\n\n // Build agent config\n const agent: AgentConfig = {\n agentType: frontmatter.name,\n whenToUse: frontmatter.description.replace(/\\\\n/g, '\\n'),\n tools: parseTools(frontmatter.tools),\n systemPrompt: body.trim(),\n location,\n ...(frontmatter.color && { color: frontmatter.color }),\n // Only use model_name field, ignore deprecated 'model' field\n ...(frontmatter.model_name && { model_name: frontmatter.model_name })\n }\n\n agents.push(agent)\n } catch (error) {\n console.warn(`Failed to parse agent file ${filePath}:`, error)\n }\n }\n } catch (error) {\n console.warn(`Failed to scan directory ${dirPath}:`, error)\n }\n\n return agents\n}\n\n/**\n * Load agents from installed plugins\n */\nasync function loadPluginAgents(): Promise<AgentConfig[]> {\n const agents: AgentConfig[] = []\n\n try {\n const plugins = loadAllPlugins()\n\n for (const plugin of plugins) {\n // Skip disabled plugins\n if (!plugin.enabled) continue\n\n for (const pluginAgent of plugin.agents) {\n try {\n // Read the agent file to parse frontmatter and content\n const content = readFileSync(pluginAgent.filePath, 'utf-8')\n const { data: frontmatter, content: body } = matter(content)\n\n // Validate required fields\n if (!frontmatter.name || !frontmatter.description) {\n console.warn(`Skipping plugin agent ${pluginAgent.filePath}: missing required fields (name, description)`)\n continue\n }\n\n // Warn about deprecated 'model' field if debug mode is enabled\n if (frontmatter.model && !frontmatter.model_name && !warnedAgents.has(frontmatter.name) && process.env.MINTO_DEBUG_AGENTS) {\n console.warn(`\u26A0\uFE0F Plugin agent ${frontmatter.name} (from ${plugin.manifest.name}): 'model' field is deprecated and ignored. Use 'model_name' instead, or omit to use default 'task' model.`)\n warnedAgents.add(frontmatter.name)\n }\n\n // Build agent config\n const agent: AgentConfig = {\n agentType: frontmatter.name,\n whenToUse: frontmatter.description.replace(/\\\\n/g, '\\n'),\n tools: parseTools(frontmatter.tools),\n systemPrompt: body.trim(),\n location: 'plugin',\n pluginName: plugin.manifest.name,\n ...(frontmatter.color && { color: frontmatter.color }),\n // Only use model_name field, ignore deprecated 'model' field\n ...(frontmatter.model_name && { model_name: frontmatter.model_name })\n }\n\n agents.push(agent)\n } catch (error) {\n console.warn(`Failed to load plugin agent ${pluginAgent.name} from ${plugin.manifest.name}:`, error)\n }\n }\n }\n } catch (error) {\n console.warn('Failed to load plugin agents:', error)\n }\n\n return agents\n}\n\n/**\n * Load all agent configurations\n *\n * Directory Priority (later overrides earlier):\n * 1. Built-in agents\n * 2. Plugin agents\n * 3. ~/.claude/agents (Claude Code user directory - compatible)\n * 4. ~/.minto/agents (Minto user directory - primary)\n * 5. ./.claude/agents (Claude Code project directory - compatible)\n * 6. ./.minto/agents (Minto project directory - highest priority)\n */\nasync function loadAllAgents(): Promise<{\n activeAgents: AgentConfig[]\n allAgents: AgentConfig[]\n}> {\n try {\n // Define directories in priority order\n const userClaudeDir = join(homedir(), '.claude', 'agents')\n const userMintoDir = join(homedir(), '.minto', 'agents')\n const projectClaudeDir = join(getCwd(), '.claude', 'agents')\n const projectMintoDir = join(getCwd(), '.minto', 'agents')\n\n // Load from all sources in parallel\n const [pluginAgents, userClaudeAgents, userMintoAgents, projectClaudeAgents, projectMintoAgents] = await Promise.all([\n loadPluginAgents(),\n scanAgentDirectory(userClaudeDir, 'user'),\n scanAgentDirectory(userMintoDir, 'user'),\n scanAgentDirectory(projectClaudeDir, 'project'),\n scanAgentDirectory(projectMintoDir, 'project')\n ])\n\n // Built-in agents (currently just general-purpose)\n const builtinAgents = [BUILTIN_GENERAL_PURPOSE]\n\n // Apply priority override: built-in < plugin < .claude (user) < .minto (user) < .claude (project) < .minto (project)\n // Later entries override earlier ones with the same agentType\n const agentMap = new Map<string, AgentConfig>()\n\n // Add in priority order (later entries override earlier ones)\n for (const agent of builtinAgents) {\n agentMap.set(agent.agentType, agent)\n }\n for (const agent of pluginAgents) {\n agentMap.set(agent.agentType, agent)\n }\n for (const agent of userClaudeAgents) {\n agentMap.set(agent.agentType, agent)\n }\n for (const agent of userMintoAgents) {\n agentMap.set(agent.agentType, agent)\n }\n for (const agent of projectClaudeAgents) {\n agentMap.set(agent.agentType, agent)\n }\n for (const agent of projectMintoAgents) {\n agentMap.set(agent.agentType, agent)\n }\n\n const activeAgents = Array.from(agentMap.values())\n const allAgents = [...builtinAgents, ...pluginAgents, ...userClaudeAgents, ...userMintoAgents, ...projectClaudeAgents, ...projectMintoAgents]\n\n return { activeAgents, allAgents }\n } catch (error) {\n console.error('Failed to load agents, falling back to built-in:', error)\n return {\n activeAgents: [BUILTIN_GENERAL_PURPOSE],\n allAgents: [BUILTIN_GENERAL_PURPOSE]\n }\n }\n}\n\n// Memoized version for performance\nexport const getActiveAgents = memoize(\n async (): Promise<AgentConfig[]> => {\n const { activeAgents } = await loadAllAgents()\n return activeAgents\n }\n)\n\n// Get all agents (both active and overridden)\nexport const getAllAgents = memoize(\n async (): Promise<AgentConfig[]> => {\n const { allAgents } = await loadAllAgents()\n return allAgents\n }\n)\n\n// Clear cache when needed\nexport function clearAgentCache() {\n getActiveAgents.cache?.clear?.()\n getAllAgents.cache?.clear?.()\n getAgentByType.cache?.clear?.()\n getAvailableAgentTypes.cache?.clear?.()\n}\n\n// Get a specific agent by type\nexport const getAgentByType = memoize(\n async (agentType: string): Promise<AgentConfig | undefined> => {\n const agents = await getActiveAgents()\n return agents.find(agent => agent.agentType === agentType)\n }\n)\n\n// Get all available agent types for validation\nexport const getAvailableAgentTypes = memoize(\n async (): Promise<string[]> => {\n const agents = await getActiveAgents()\n return agents.map(agent => agent.agentType)\n }\n)\n\n// File watcher for hot reload\nlet watchers: FSWatcher[] = []\n\n/**\n * Start watching agent configuration directories for changes\n */\nexport async function startAgentWatcher(onChange?: () => void): Promise<void> {\n await stopAgentWatcher() // Clean up any existing watchers\n \n // Watch both Claude (.claude) and Minto (.minto) directories\n const userClaudeDir = join(homedir(), '.claude', 'agents')\n const userMintoDir = join(homedir(), '.minto', 'agents')\n const projectClaudeDir = join(getCwd(), '.claude', 'agents')\n const projectMintoDir = join(getCwd(), '.minto', 'agents')\n\n const watchDirectory = (dirPath: string, label: string) => {\n if (existsSync(dirPath)) {\n const watcher = watch(dirPath, { recursive: false }, async (eventType, filename) => {\n if (filename && filename.endsWith('.md')) {\n console.log(`\uD83D\uDD04 Agent configuration changed in ${label}: ${filename}`)\n clearAgentCache()\n // Also clear any other related caches\n getAllAgents.cache?.clear?.()\n onChange?.()\n }\n })\n watchers.push(watcher)\n }\n }\n\n // Watch all directories\n watchDirectory(userClaudeDir, 'user/.claude')\n watchDirectory(userMintoDir, 'user/.minto')\n watchDirectory(projectClaudeDir, 'project/.claude')\n watchDirectory(projectMintoDir, 'project/.minto')\n}\n\n/**\n * Stop watching agent configuration directories\n */\nexport async function stopAgentWatcher(): Promise<void> {\n // FSWatcher.close() is synchronous and does not accept a callback on Node 18/20\n try {\n for (const watcher of watchers) {\n try {\n watcher.close()\n } catch (err) {\n console.error('Failed to close file watcher:', err)\n }\n }\n } finally {\n watchers = []\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Agent configuration loader\n * Loads agent configurations from markdown files with YAML frontmatter.\n * Maintains compatibility with Claude Code `.claude` agent directories while\n * prioritizing Minto-specific overrides.\n */\n\nimport {\n existsSync,\n readFileSync,\n readdirSync,\n statSync,\n watch,\n FSWatcher,\n} from 'fs'\nimport { join, resolve } from 'path'\nimport { homedir } from 'os'\nimport matter from 'gray-matter'\nimport { getCwd } from './state'\nimport { memoize } from 'lodash-es'\nimport { loadAllPlugins } from './pluginLoader'\n\n// Track warned agents to avoid spam\nconst warnedAgents = new Set<string>()\n\nexport interface AgentConfig {\n agentType: string // Agent identifier (matches subagent_type)\n whenToUse: string // Description of when to use this agent\n tools: string[] | '*' // Tool permissions\n systemPrompt: string // System prompt content\n location: 'built-in' | 'user' | 'project' | 'plugin'\n color?: string // Optional UI color\n model_name?: string // Optional model override\n pluginName?: string // Source plugin name (if loaded from plugin)\n}\n\n// Built-in general-purpose agent as fallback\nconst BUILTIN_GENERAL_PURPOSE: AgentConfig = {\n agentType: 'general-purpose',\n whenToUse:\n 'General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks',\n tools: '*',\n systemPrompt: `You are a general-purpose agent. Given the user's task, use the tools available to complete it efficiently and thoroughly.\n\nWhen to use your capabilities:\n- Searching for code, configurations, and patterns across large codebases\n- Analyzing multiple files to understand system architecture \n- Investigating complex questions that require exploring many files\n- Performing multi-step research tasks\n\nGuidelines:\n- For file searches: Use Grep or Glob when you need to search broadly. Use FileRead when you know the specific file path.\n- For analysis: Start broad and narrow down. Use multiple search strategies if the first doesn't yield results.\n- Be thorough: Check multiple locations, consider different naming conventions, look for related files.\n- Complete tasks directly using your capabilities.`,\n location: 'built-in',\n}\n\n/**\n * Parse tools field from frontmatter\n */\nfunction parseTools(tools: any): string[] | '*' {\n if (!tools) return '*'\n if (tools === '*') return '*'\n if (Array.isArray(tools)) {\n // Ensure all items are strings and filter out non-strings\n const filteredTools = tools.filter(\n (t): t is string => typeof t === 'string',\n )\n return filteredTools.length > 0 ? filteredTools : '*'\n }\n if (typeof tools === 'string') {\n return [tools]\n }\n return '*'\n}\n\n/**\n * Scan a directory for agent configuration files\n */\nasync function scanAgentDirectory(\n dirPath: string,\n location: 'user' | 'project',\n): Promise<AgentConfig[]> {\n if (!existsSync(dirPath)) {\n return []\n }\n\n const agents: AgentConfig[] = []\n\n try {\n const files = readdirSync(dirPath)\n\n for (const file of files) {\n if (!file.endsWith('.md')) continue\n\n const filePath = join(dirPath, file)\n const stat = statSync(filePath)\n\n if (!stat.isFile()) continue\n\n try {\n const content = readFileSync(filePath, 'utf-8')\n const { data: frontmatter, content: body } = matter(content)\n\n // Validate required fields\n if (!frontmatter.name || !frontmatter.description) {\n console.warn(\n `Skipping ${filePath}: missing required fields (name, description)`,\n )\n continue\n }\n\n // Silently ignore deprecated 'model' field - no warnings by default\n // Only warn if MINTO_DEBUG_AGENTS environment variable is set\n if (\n frontmatter.model &&\n !frontmatter.model_name &&\n !warnedAgents.has(frontmatter.name) &&\n process.env.MINTO_DEBUG_AGENTS\n ) {\n console.warn(\n `\u26A0\uFE0F Agent ${frontmatter.name}: 'model' field is deprecated and ignored. Use 'model_name' instead, or omit to use default 'task' model.`,\n )\n warnedAgents.add(frontmatter.name)\n }\n\n // Build agent config\n const agent: AgentConfig = {\n agentType: frontmatter.name,\n whenToUse: frontmatter.description.replace(/\\\\n/g, '\\n'),\n tools: parseTools(frontmatter.tools),\n systemPrompt: body.trim(),\n location,\n ...(frontmatter.color && { color: frontmatter.color }),\n // Only use model_name field, ignore deprecated 'model' field\n ...(frontmatter.model_name && { model_name: frontmatter.model_name }),\n }\n\n agents.push(agent)\n } catch (error) {\n console.warn(`Failed to parse agent file ${filePath}:`, error)\n }\n }\n } catch (error) {\n console.warn(`Failed to scan directory ${dirPath}:`, error)\n }\n\n return agents\n}\n\n/**\n * Load agents from installed plugins\n */\nasync function loadPluginAgents(): Promise<AgentConfig[]> {\n const agents: AgentConfig[] = []\n\n try {\n const plugins = loadAllPlugins()\n\n for (const plugin of plugins) {\n // Skip disabled plugins\n if (!plugin.enabled) continue\n\n for (const pluginAgent of plugin.agents) {\n try {\n // Read the agent file to parse frontmatter and content\n const content = readFileSync(pluginAgent.filePath, 'utf-8')\n const { data: frontmatter, content: body } = matter(content)\n\n // Validate required fields\n if (!frontmatter.name || !frontmatter.description) {\n console.warn(\n `Skipping plugin agent ${pluginAgent.filePath}: missing required fields (name, description)`,\n )\n continue\n }\n\n // Warn about deprecated 'model' field if debug mode is enabled\n if (\n frontmatter.model &&\n !frontmatter.model_name &&\n !warnedAgents.has(frontmatter.name) &&\n process.env.MINTO_DEBUG_AGENTS\n ) {\n console.warn(\n `\u26A0\uFE0F Plugin agent ${frontmatter.name} (from ${plugin.manifest.name}): 'model' field is deprecated and ignored. Use 'model_name' instead, or omit to use default 'task' model.`,\n )\n warnedAgents.add(frontmatter.name)\n }\n\n // Build agent config\n const agent: AgentConfig = {\n agentType: frontmatter.name,\n whenToUse: frontmatter.description.replace(/\\\\n/g, '\\n'),\n tools: parseTools(frontmatter.tools),\n systemPrompt: body.trim(),\n location: 'plugin',\n pluginName: plugin.manifest.name,\n ...(frontmatter.color && { color: frontmatter.color }),\n // Only use model_name field, ignore deprecated 'model' field\n ...(frontmatter.model_name && {\n model_name: frontmatter.model_name,\n }),\n }\n\n agents.push(agent)\n } catch (error) {\n console.warn(\n `Failed to load plugin agent ${pluginAgent.name} from ${plugin.manifest.name}:`,\n error,\n )\n }\n }\n }\n } catch (error) {\n console.warn('Failed to load plugin agents:', error)\n }\n\n return agents\n}\n\n/**\n * Load all agent configurations\n *\n * Directory Priority (later overrides earlier):\n * 1. Built-in agents\n * 2. Plugin agents\n * 3. ~/.claude/agents (Claude Code user directory - compatible)\n * 4. ~/.minto/agents (Minto user directory - primary)\n * 5. ./.claude/agents (Claude Code project directory - compatible)\n * 6. ./.minto/agents (Minto project directory - highest priority)\n */\nasync function loadAllAgents(): Promise<{\n activeAgents: AgentConfig[]\n allAgents: AgentConfig[]\n}> {\n try {\n // Define directories in priority order\n const userClaudeDir = join(homedir(), '.claude', 'agents')\n const userMintoDir = join(homedir(), '.minto', 'agents')\n const projectClaudeDir = join(getCwd(), '.claude', 'agents')\n const projectMintoDir = join(getCwd(), '.minto', 'agents')\n\n // Load from all sources in parallel\n const [\n pluginAgents,\n userClaudeAgents,\n userMintoAgents,\n projectClaudeAgents,\n projectMintoAgents,\n ] = await Promise.all([\n loadPluginAgents(),\n scanAgentDirectory(userClaudeDir, 'user'),\n scanAgentDirectory(userMintoDir, 'user'),\n scanAgentDirectory(projectClaudeDir, 'project'),\n scanAgentDirectory(projectMintoDir, 'project'),\n ])\n\n // Built-in agents (currently just general-purpose)\n const builtinAgents = [BUILTIN_GENERAL_PURPOSE]\n\n // Apply priority override: built-in < plugin < .claude (user) < .minto (user) < .claude (project) < .minto (project)\n // Later entries override earlier ones with the same agentType\n const agentMap = new Map<string, AgentConfig>()\n\n // Add in priority order (later entries override earlier ones)\n for (const agent of builtinAgents) {\n agentMap.set(agent.agentType, agent)\n }\n for (const agent of pluginAgents) {\n agentMap.set(agent.agentType, agent)\n }\n for (const agent of userClaudeAgents) {\n agentMap.set(agent.agentType, agent)\n }\n for (const agent of userMintoAgents) {\n agentMap.set(agent.agentType, agent)\n }\n for (const agent of projectClaudeAgents) {\n agentMap.set(agent.agentType, agent)\n }\n for (const agent of projectMintoAgents) {\n agentMap.set(agent.agentType, agent)\n }\n\n const activeAgents = Array.from(agentMap.values())\n const allAgents = [\n ...builtinAgents,\n ...pluginAgents,\n ...userClaudeAgents,\n ...userMintoAgents,\n ...projectClaudeAgents,\n ...projectMintoAgents,\n ]\n\n return { activeAgents, allAgents }\n } catch (error) {\n console.error('Failed to load agents, falling back to built-in:', error)\n return {\n activeAgents: [BUILTIN_GENERAL_PURPOSE],\n allAgents: [BUILTIN_GENERAL_PURPOSE],\n }\n }\n}\n\n// Memoized version for performance\nexport const getActiveAgents = memoize(async (): Promise<AgentConfig[]> => {\n const { activeAgents } = await loadAllAgents()\n return activeAgents\n})\n\n// Get all agents (both active and overridden)\nexport const getAllAgents = memoize(async (): Promise<AgentConfig[]> => {\n const { allAgents } = await loadAllAgents()\n return allAgents\n})\n\n// Clear cache when needed\nexport function clearAgentCache() {\n getActiveAgents.cache?.clear?.()\n getAllAgents.cache?.clear?.()\n getAgentByType.cache?.clear?.()\n getAvailableAgentTypes.cache?.clear?.()\n}\n\n// Get a specific agent by type\nexport const getAgentByType = memoize(\n async (agentType: string): Promise<AgentConfig | undefined> => {\n const agents = await getActiveAgents()\n return agents.find(agent => agent.agentType === agentType)\n },\n)\n\n// Get all available agent types for validation\nexport const getAvailableAgentTypes = memoize(async (): Promise<string[]> => {\n const agents = await getActiveAgents()\n return agents.map(agent => agent.agentType)\n})\n\n// File watcher for hot reload\nlet watchers: FSWatcher[] = []\n\n/**\n * Start watching agent configuration directories for changes\n */\nexport async function startAgentWatcher(onChange?: () => void): Promise<void> {\n await stopAgentWatcher() // Clean up any existing watchers\n\n // Watch both Claude (.claude) and Minto (.minto) directories\n const userClaudeDir = join(homedir(), '.claude', 'agents')\n const userMintoDir = join(homedir(), '.minto', 'agents')\n const projectClaudeDir = join(getCwd(), '.claude', 'agents')\n const projectMintoDir = join(getCwd(), '.minto', 'agents')\n\n const watchDirectory = (dirPath: string, label: string) => {\n if (existsSync(dirPath)) {\n const watcher = watch(\n dirPath,\n { recursive: false },\n async (eventType, filename) => {\n if (filename && filename.endsWith('.md')) {\n console.log(\n `\uD83D\uDD04 Agent configuration changed in ${label}: ${filename}`,\n )\n clearAgentCache()\n // Also clear any other related caches\n getAllAgents.cache?.clear?.()\n onChange?.()\n }\n },\n )\n watchers.push(watcher)\n }\n }\n\n // Watch all directories\n watchDirectory(userClaudeDir, 'user/.claude')\n watchDirectory(userMintoDir, 'user/.minto')\n watchDirectory(projectClaudeDir, 'project/.claude')\n watchDirectory(projectMintoDir, 'project/.minto')\n}\n\n/**\n * Stop watching agent configuration directories\n */\nexport async function stopAgentWatcher(): Promise<void> {\n // FSWatcher.close() is synchronous and does not accept a callback on Node 18/20\n try {\n for (const watcher of watchers) {\n try {\n watcher.close()\n } catch (err) {\n console.error('Failed to close file watcher:', err)\n }\n }\n } finally {\n watchers = []\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/agentStorage.ts"],
4
- "sourcesContent": ["import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'\nimport { join } from 'path'\nimport { homedir } from 'os'\nimport { randomUUID } from 'crypto'\n\n/**\n * Agent Storage Utilities\n * Provides file-based state isolation for different agents\n * Based on Minto's Agent ID architecture\n */\n\n/**\n * Get the minto config directory\n * Priority: MINTO_CONFIG_DIR > CLAUDE_CONFIG_DIR > ~/.minto\n */\nfunction getConfigDirectory(): string {\n return process.env.MINTO_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), '.minto')\n}\n\n/**\n * Get the current session ID\n */\nfunction getSessionId(): string {\n // This should be set when the session starts\n return process.env.MINTO_SESSION_ID ?? 'default-session'\n}\n\n/**\n * Generate agent-specific file path\n * Pattern: ${sessionId}-agent-${agentId}.json\n * Stored in ~/.minto/ directory\n */\nexport function getAgentFilePath(agentId: string): string {\n const sessionId = getSessionId()\n const filename = `${sessionId}-agent-${agentId}.json`\n const configDir = getConfigDirectory()\n\n // Ensure minto config directory exists\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true })\n }\n\n return join(configDir, filename)\n}\n\n/**\n * Read agent-specific data from storage\n */\nexport function readAgentData<T = any>(agentId: string): T | null {\n const filePath = getAgentFilePath(agentId)\n\n if (!existsSync(filePath)) {\n return null\n }\n\n try {\n const content = readFileSync(filePath, 'utf-8')\n return JSON.parse(content) as T\n } catch (error) {\n console.error(`Failed to read agent data for ${agentId}:`, error)\n return null\n }\n}\n\n/**\n * Write agent-specific data to storage\n */\nexport function writeAgentData<T = any>(agentId: string, data: T): void {\n const filePath = getAgentFilePath(agentId)\n\n try {\n writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8')\n } catch (error) {\n console.error(`Failed to write agent data for ${agentId}:`, error)\n throw error\n }\n}\n\n/**\n * Get default agent ID if none is provided\n */\nexport function getDefaultAgentId(): string {\n return 'default'\n}\n\n/**\n * Resolve agent ID from context\n */\nexport function resolveAgentId(agentId?: string): string {\n return agentId || getDefaultAgentId()\n}\n\n/**\n * Generate a new unique Agent ID\n */\nexport function generateAgentId(): string {\n return randomUUID()\n}\n"],
5
- "mappings": "AAAA,SAAS,YAAY,cAAc,eAAe,iBAAiB;AACnE,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAY3B,SAAS,qBAA6B;AACpC,SAAO,QAAQ,IAAI,oBAAoB,QAAQ,IAAI,qBAAqB,KAAK,QAAQ,GAAG,QAAQ;AAClG;AAKA,SAAS,eAAuB;AAE9B,SAAO,QAAQ,IAAI,oBAAoB;AACzC;AAOO,SAAS,iBAAiB,SAAyB;AACxD,QAAM,YAAY,aAAa;AAC/B,QAAM,WAAW,GAAG,SAAS,UAAU,OAAO;AAC9C,QAAM,YAAY,mBAAmB;AAGrC,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,cAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,EAC1C;AAEA,SAAO,KAAK,WAAW,QAAQ;AACjC;AAKO,SAAS,cAAuB,SAA2B;AAChE,QAAM,WAAW,iBAAiB,OAAO;AAEzC,MAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,SAAS,OAAO;AACd,YAAQ,MAAM,iCAAiC,OAAO,KAAK,KAAK;AAChE,WAAO;AAAA,EACT;AACF;AAKO,SAAS,eAAwB,SAAiB,MAAe;AACtE,QAAM,WAAW,iBAAiB,OAAO;AAEzC,MAAI;AACF,kBAAc,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,OAAO;AAAA,EAChE,SAAS,OAAO;AACd,YAAQ,MAAM,kCAAkC,OAAO,KAAK,KAAK;AACjE,UAAM;AAAA,EACR;AACF;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AAKO,SAAS,eAAe,SAA0B;AACvD,SAAO,WAAW,kBAAkB;AACtC;AAKO,SAAS,kBAA0B;AACxC,SAAO,WAAW;AACpB;",
4
+ "sourcesContent": ["import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'\nimport { join } from 'path'\nimport { homedir } from 'os'\nimport { randomUUID } from 'crypto'\n\n/**\n * Agent Storage Utilities\n * Provides file-based state isolation for different agents\n * Based on Minto's Agent ID architecture\n */\n\n/**\n * Get the minto config directory\n * Priority: MINTO_CONFIG_DIR > CLAUDE_CONFIG_DIR > ~/.minto\n */\nfunction getConfigDirectory(): string {\n return (\n process.env.MINTO_CONFIG_DIR ??\n process.env.CLAUDE_CONFIG_DIR ??\n join(homedir(), '.minto')\n )\n}\n\n/**\n * Get the current session ID\n */\nfunction getSessionId(): string {\n // This should be set when the session starts\n return process.env.MINTO_SESSION_ID ?? 'default-session'\n}\n\n/**\n * Generate agent-specific file path\n * Pattern: ${sessionId}-agent-${agentId}.json\n * Stored in ~/.minto/ directory\n */\nexport function getAgentFilePath(agentId: string): string {\n const sessionId = getSessionId()\n const filename = `${sessionId}-agent-${agentId}.json`\n const configDir = getConfigDirectory()\n\n // Ensure minto config directory exists\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true })\n }\n\n return join(configDir, filename)\n}\n\n/**\n * Read agent-specific data from storage\n */\nexport function readAgentData<T = any>(agentId: string): T | null {\n const filePath = getAgentFilePath(agentId)\n\n if (!existsSync(filePath)) {\n return null\n }\n\n try {\n const content = readFileSync(filePath, 'utf-8')\n return JSON.parse(content) as T\n } catch (error) {\n console.error(`Failed to read agent data for ${agentId}:`, error)\n return null\n }\n}\n\n/**\n * Write agent-specific data to storage\n */\nexport function writeAgentData<T = any>(agentId: string, data: T): void {\n const filePath = getAgentFilePath(agentId)\n\n try {\n writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8')\n } catch (error) {\n console.error(`Failed to write agent data for ${agentId}:`, error)\n throw error\n }\n}\n\n/**\n * Get default agent ID if none is provided\n */\nexport function getDefaultAgentId(): string {\n return 'default'\n}\n\n/**\n * Resolve agent ID from context\n */\nexport function resolveAgentId(agentId?: string): string {\n return agentId || getDefaultAgentId()\n}\n\n/**\n * Generate a new unique Agent ID\n */\nexport function generateAgentId(): string {\n return randomUUID()\n}\n"],
5
+ "mappings": "AAAA,SAAS,YAAY,cAAc,eAAe,iBAAiB;AACnE,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAY3B,SAAS,qBAA6B;AACpC,SACE,QAAQ,IAAI,oBACZ,QAAQ,IAAI,qBACZ,KAAK,QAAQ,GAAG,QAAQ;AAE5B;AAKA,SAAS,eAAuB;AAE9B,SAAO,QAAQ,IAAI,oBAAoB;AACzC;AAOO,SAAS,iBAAiB,SAAyB;AACxD,QAAM,YAAY,aAAa;AAC/B,QAAM,WAAW,GAAG,SAAS,UAAU,OAAO;AAC9C,QAAM,YAAY,mBAAmB;AAGrC,MAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,cAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,EAC1C;AAEA,SAAO,KAAK,WAAW,QAAQ;AACjC;AAKO,SAAS,cAAuB,SAA2B;AAChE,QAAM,WAAW,iBAAiB,OAAO;AAEzC,MAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,SAAS,OAAO;AACd,YAAQ,MAAM,iCAAiC,OAAO,KAAK,KAAK;AAChE,WAAO;AAAA,EACT;AACF;AAKO,SAAS,eAAwB,SAAiB,MAAe;AACtE,QAAM,WAAW,iBAAiB,OAAO;AAEzC,MAAI;AACF,kBAAc,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,OAAO;AAAA,EAChE,SAAS,OAAO;AACd,YAAQ,MAAM,kCAAkC,OAAO,KAAK,KAAK;AACjE,UAAM;AAAA,EACR;AACF;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AAKO,SAAS,eAAe,SAA0B;AACvD,SAAO,WAAW,kBAAkB;AACtC;AAKO,SAAS,kBAA0B;AACxC,SAAO,WAAW;AACpB;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,163 @@
1
+ const DEFAULT_RETRY_CONFIG = {
2
+ baseDelayMs: 1e3,
3
+ maxDelayMs: 32e3,
4
+ maxServerDelayMs: 6e4,
5
+ jitterFactor: 0.1
6
+ };
7
+ function abortableDelay(delayMs, signal) {
8
+ return new Promise((resolve, reject) => {
9
+ if (signal?.aborted) {
10
+ reject(new Error("Request was aborted"));
11
+ return;
12
+ }
13
+ const timeoutId = setTimeout(() => {
14
+ resolve();
15
+ }, delayMs);
16
+ if (signal) {
17
+ const abortHandler = () => {
18
+ clearTimeout(timeoutId);
19
+ reject(new Error("Request was aborted"));
20
+ };
21
+ signal.addEventListener("abort", abortHandler, { once: true });
22
+ }
23
+ });
24
+ }
25
+ function getRetryDelay(attempt, retryAfterHeader, config = DEFAULT_RETRY_CONFIG) {
26
+ if (retryAfterHeader) {
27
+ const retryAfterMs = parseInt(retryAfterHeader, 10) * 1e3;
28
+ if (!isNaN(retryAfterMs) && retryAfterMs > 0) {
29
+ return Math.min(retryAfterMs, config.maxServerDelayMs);
30
+ }
31
+ }
32
+ const delay = config.baseDelayMs * Math.pow(2, attempt - 1);
33
+ const jitter = Math.random() * config.jitterFactor * delay;
34
+ return Math.min(delay + jitter, config.maxDelayMs);
35
+ }
36
+ function withTimeout(promise, timeoutMs, message = "Operation timed out") {
37
+ return new Promise((resolve, reject) => {
38
+ const timeoutId = setTimeout(() => {
39
+ reject(new Error(message));
40
+ }, timeoutMs);
41
+ promise.then((result) => {
42
+ clearTimeout(timeoutId);
43
+ resolve(result);
44
+ }).catch((error) => {
45
+ clearTimeout(timeoutId);
46
+ reject(error);
47
+ });
48
+ });
49
+ }
50
+ function createTimeoutController(timeoutMs) {
51
+ const controller = new AbortController();
52
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
53
+ return {
54
+ controller,
55
+ cleanup: () => clearTimeout(timeoutId)
56
+ };
57
+ }
58
+ async function retryWithBackoff(fn, options = {}) {
59
+ const {
60
+ maxRetries = 3,
61
+ signal,
62
+ config = DEFAULT_RETRY_CONFIG,
63
+ onRetry
64
+ } = options;
65
+ const fullConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
66
+ let lastError;
67
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
68
+ if (signal?.aborted) {
69
+ throw new Error("Request was aborted");
70
+ }
71
+ try {
72
+ return await fn();
73
+ } catch (error) {
74
+ lastError = error instanceof Error ? error : new Error(String(error));
75
+ if (attempt < maxRetries) {
76
+ onRetry?.(attempt, lastError);
77
+ const delay = getRetryDelay(attempt, void 0, fullConfig);
78
+ await abortableDelay(delay, signal);
79
+ }
80
+ }
81
+ }
82
+ throw lastError ?? new Error("Retry failed with unknown error");
83
+ }
84
+ function memoizeWithLimit(fn, options = {}) {
85
+ const { max = 100, ttl = 6e4, resolver } = options;
86
+ const cache = /* @__PURE__ */ new Map();
87
+ const evictOldest = () => {
88
+ if (cache.size >= max) {
89
+ const oldest = cache.keys().next().value;
90
+ if (oldest) cache.delete(oldest);
91
+ }
92
+ };
93
+ const isExpired = (entry) => {
94
+ return Date.now() - entry.timestamp > ttl;
95
+ };
96
+ return ((...args) => {
97
+ const key = resolver ? resolver(...args) : JSON.stringify(args);
98
+ const existing = cache.get(key);
99
+ if (existing && !isExpired(existing)) {
100
+ cache.delete(key);
101
+ cache.set(key, existing);
102
+ return existing.value;
103
+ }
104
+ if (existing) {
105
+ cache.delete(key);
106
+ }
107
+ const value = fn(...args);
108
+ evictOldest();
109
+ cache.set(key, { value, timestamp: Date.now() });
110
+ return value;
111
+ });
112
+ }
113
+ function memoizeAsyncWithLimit(fn, options = {}) {
114
+ const { max = 100, ttl = 6e4, resolver } = options;
115
+ const cache = /* @__PURE__ */ new Map();
116
+ const pending = /* @__PURE__ */ new Map();
117
+ const evictOldest = () => {
118
+ if (cache.size >= max) {
119
+ const oldest = cache.keys().next().value;
120
+ if (oldest) cache.delete(oldest);
121
+ }
122
+ };
123
+ const isExpired = (entry) => {
124
+ return Date.now() - entry.timestamp > ttl;
125
+ };
126
+ return (async (...args) => {
127
+ const key = resolver ? resolver(...args) : JSON.stringify(args);
128
+ const existing = cache.get(key);
129
+ if (existing && !isExpired(existing)) {
130
+ cache.delete(key);
131
+ cache.set(key, existing);
132
+ return existing.value;
133
+ }
134
+ const pendingPromise = pending.get(key);
135
+ if (pendingPromise) {
136
+ return pendingPromise;
137
+ }
138
+ if (existing) {
139
+ cache.delete(key);
140
+ }
141
+ const promise = fn(...args);
142
+ pending.set(key, promise);
143
+ try {
144
+ const value = await promise;
145
+ evictOldest();
146
+ cache.set(key, { value, timestamp: Date.now() });
147
+ return value;
148
+ } finally {
149
+ pending.delete(key);
150
+ }
151
+ });
152
+ }
153
+ export {
154
+ DEFAULT_RETRY_CONFIG,
155
+ abortableDelay,
156
+ createTimeoutController,
157
+ getRetryDelay,
158
+ memoizeAsyncWithLimit,
159
+ memoizeWithLimit,
160
+ retryWithBackoff,
161
+ withTimeout
162
+ };
163
+ //# sourceMappingURL=async.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/async.ts"],
4
+ "sourcesContent": ["/**\n * Shared async utilities for consistent async patterns across the codebase.\n * Extracted from claude.ts and openai.ts to eliminate code duplication.\n */\n\nexport interface RetryConfig {\n baseDelayMs: number\n maxDelayMs: number\n maxServerDelayMs: number\n jitterFactor: number\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n baseDelayMs: 1000,\n maxDelayMs: 32000,\n maxServerDelayMs: 60000,\n jitterFactor: 0.1,\n}\n\nexport interface RetryOptions {\n maxRetries?: number\n signal?: AbortSignal\n config?: Partial<RetryConfig>\n onRetry?: (attempt: number, error: Error) => void\n}\n\n/**\n * Creates a promise that resolves after the specified delay.\n * Can be aborted via an AbortSignal.\n */\nexport function abortableDelay(\n delayMs: number,\n signal?: AbortSignal,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error('Request was aborted'))\n return\n }\n\n const timeoutId = setTimeout(() => {\n resolve()\n }, delayMs)\n\n if (signal) {\n const abortHandler = () => {\n clearTimeout(timeoutId)\n reject(new Error('Request was aborted'))\n }\n signal.addEventListener('abort', abortHandler, { once: true })\n }\n })\n}\n\n/**\n * Calculate retry delay with exponential backoff and optional jitter.\n * Respects server-provided Retry-After headers when available.\n */\nexport function getRetryDelay(\n attempt: number,\n retryAfterHeader?: string | null,\n config: RetryConfig = DEFAULT_RETRY_CONFIG,\n): number {\n if (retryAfterHeader) {\n const retryAfterMs = parseInt(retryAfterHeader, 10) * 1000\n if (!isNaN(retryAfterMs) && retryAfterMs > 0) {\n return Math.min(retryAfterMs, config.maxServerDelayMs)\n }\n }\n\n const delay = config.baseDelayMs * Math.pow(2, attempt - 1)\n const jitter = Math.random() * config.jitterFactor * delay\n\n return Math.min(delay + jitter, config.maxDelayMs)\n}\n\n/**\n * Wraps a promise with a timeout.\n * Rejects with a timeout error if the promise doesn't resolve in time.\n */\nexport function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n message = 'Operation timed out',\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(new Error(message))\n }, timeoutMs)\n\n promise\n .then(result => {\n clearTimeout(timeoutId)\n resolve(result)\n })\n .catch(error => {\n clearTimeout(timeoutId)\n reject(error)\n })\n })\n}\n\n/**\n * Creates an AbortController that automatically aborts after the specified timeout.\n * Returns both the controller and a cleanup function.\n */\nexport function createTimeoutController(timeoutMs: number): {\n controller: AbortController\n cleanup: () => void\n} {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs)\n\n return {\n controller,\n cleanup: () => clearTimeout(timeoutId),\n }\n}\n\n/**\n * Retries an async function with exponential backoff.\n * Supports abort signals and custom retry conditions.\n */\nexport async function retryWithBackoff<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const {\n maxRetries = 3,\n signal,\n config = DEFAULT_RETRY_CONFIG,\n onRetry,\n } = options\n\n const fullConfig = { ...DEFAULT_RETRY_CONFIG, ...config }\n let lastError: Error | undefined\n\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n if (signal?.aborted) {\n throw new Error('Request was aborted')\n }\n\n try {\n return await fn()\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error))\n\n if (attempt < maxRetries) {\n onRetry?.(attempt, lastError)\n const delay = getRetryDelay(attempt, undefined, fullConfig)\n await abortableDelay(delay, signal)\n }\n }\n }\n\n throw lastError ?? new Error('Retry failed with unknown error')\n}\n\n/**\n * Options for memoizeWithLimit\n */\nexport interface MemoizeOptions<T extends (...args: unknown[]) => unknown> {\n /** Maximum number of cached entries (default: 100) */\n max?: number\n /** Time-to-live in milliseconds (default: 60000) */\n ttl?: number\n /** Custom resolver function to generate cache key from arguments */\n resolver?: (...args: Parameters<T>) => string\n}\n\n/**\n * Creates a memoized version of a function with LRU cache and TTL.\n * Unlike lodash memoize which has unlimited cache, this has bounded memory usage.\n *\n * @param fn - The function to memoize\n * @param options - Cache configuration options\n * @returns Memoized function\n */\nexport function memoizeWithLimit<T extends (...args: unknown[]) => unknown>(\n fn: T,\n options: MemoizeOptions<T> = {},\n): T {\n const { max = 100, ttl = 60000, resolver } = options\n\n interface CacheEntry {\n value: ReturnType<T>\n timestamp: number\n }\n\n const cache = new Map<string, CacheEntry>()\n\n // LRU eviction helper\n const evictOldest = () => {\n if (cache.size >= max) {\n const oldest = cache.keys().next().value\n if (oldest) cache.delete(oldest)\n }\n }\n\n // Check if entry is expired\n const isExpired = (entry: CacheEntry): boolean => {\n return Date.now() - entry.timestamp > ttl\n }\n\n return ((...args: Parameters<T>): ReturnType<T> => {\n const key = resolver ? resolver(...args) : JSON.stringify(args)\n\n // Check cache\n const existing = cache.get(key)\n if (existing && !isExpired(existing)) {\n // Move to end for LRU\n cache.delete(key)\n cache.set(key, existing)\n return existing.value\n }\n\n // Remove expired entry if exists\n if (existing) {\n cache.delete(key)\n }\n\n // Compute new value\n const value = fn(...args) as ReturnType<T>\n\n // Evict oldest if at capacity\n evictOldest()\n\n // Store in cache\n cache.set(key, { value, timestamp: Date.now() })\n\n return value\n }) as T\n}\n\n/**\n * Async version of memoizeWithLimit for async functions\n */\nexport function memoizeAsyncWithLimit<\n T extends (...args: unknown[]) => Promise<unknown>,\n>(fn: T, options: MemoizeOptions<T> = {}): T {\n const { max = 100, ttl = 60000, resolver } = options\n\n interface CacheEntry {\n value: Awaited<ReturnType<T>>\n timestamp: number\n }\n\n const cache = new Map<string, CacheEntry>()\n const pending = new Map<string, Promise<Awaited<ReturnType<T>>>>()\n\n const evictOldest = () => {\n if (cache.size >= max) {\n const oldest = cache.keys().next().value\n if (oldest) cache.delete(oldest)\n }\n }\n\n const isExpired = (entry: CacheEntry): boolean => {\n return Date.now() - entry.timestamp > ttl\n }\n\n return (async (...args: Parameters<T>): Promise<Awaited<ReturnType<T>>> => {\n const key = resolver ? resolver(...args) : JSON.stringify(args)\n\n // Check cache\n const existing = cache.get(key)\n if (existing && !isExpired(existing)) {\n cache.delete(key)\n cache.set(key, existing)\n return existing.value\n }\n\n // Check pending\n const pendingPromise = pending.get(key)\n if (pendingPromise) {\n return pendingPromise\n }\n\n // Remove expired\n if (existing) {\n cache.delete(key)\n }\n\n // Compute new value\n const promise = fn(...args) as Promise<Awaited<ReturnType<T>>>\n pending.set(key, promise)\n\n try {\n const value = await promise\n evictOldest()\n cache.set(key, { value, timestamp: Date.now() })\n return value\n } finally {\n pending.delete(key)\n }\n }) as T\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -3,7 +3,11 @@ import { logError } from "./log.js";
3
3
  import { lt, gt } from "semver";
4
4
  import { MACRO } from "../constants/macros.js";
5
5
  import { PRODUCT_NAME } from "../constants/product.js";
6
- import { getGlobalConfig, saveGlobalConfig, isAutoUpdaterDisabled } from "./config.js";
6
+ import {
7
+ getGlobalConfig,
8
+ saveGlobalConfig,
9
+ isAutoUpdaterDisabled
10
+ } from "./config.js";
7
11
  import { env } from "./env.js";
8
12
  async function assertMinVersion() {
9
13
  try {
@@ -88,7 +92,9 @@ async function checkAndNotifyUpdate() {
88
92
  lastSuggestedVersion: latest
89
93
  });
90
94
  const suggestions = await getUpdateCommandSuggestions();
91
- console.log(`New version available: ${latest} (current: ${MACRO.VERSION})`);
95
+ console.log(
96
+ `New version available: ${latest} (current: ${MACRO.VERSION})`
97
+ );
92
98
  console.log("Run the following command to update:");
93
99
  for (const command of suggestions) console.log(` ${command}`);
94
100
  } else {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/autoUpdater.ts"],
4
- "sourcesContent": ["import { execFileNoThrow } from './execFileNoThrow'\nimport { logError } from './log'\n \nimport { lt, gt } from 'semver'\nimport { MACRO } from '@constants/macros'\nimport { PRODUCT_NAME } from '@constants/product'\nimport { getGlobalConfig, saveGlobalConfig, isAutoUpdaterDisabled } from './config'\nimport { env } from './env'\n\nexport type VersionConfig = {\n minVersion: string\n}\n\n// Ensure current version meets minimum supported version; exit if too old\nexport async function assertMinVersion(): Promise<void> {\n try {\n const versionConfig: VersionConfig = { minVersion: '0.0.0' }\n if (versionConfig.minVersion && lt(MACRO.VERSION, versionConfig.minVersion)) {\n const suggestions = await getUpdateCommandSuggestions()\n // Intentionally minimal: caller may print its own message; we just exit\n // eslint-disable-next-line no-console\n console.error(\n `Your ${PRODUCT_NAME} version ${MACRO.VERSION} is below the minimum supported ${versionConfig.minVersion}.\\n` +\n 'Update using one of:\\n' +\n suggestions.map(c => ` ${c}`).join('\\n'),\n )\n process.exit(1)\n }\n } catch (error) {\n logError(`Error checking minimum version: ${error}`)\n }\n}\n\n// Get latest version from npm (via npm CLI or HTTP fallback)\nexport async function getLatestVersion(): Promise<string | null> {\n // Prefer npm CLI (fast when available)\n try {\n const abortController = new AbortController()\n setTimeout(() => abortController.abort(), 5000)\n const result = await execFileNoThrow(\n 'npm',\n ['view', MACRO.PACKAGE_URL, 'version'],\n abortController.signal,\n )\n if (result.code === 0) {\n const v = result.stdout.trim()\n if (v) return v\n }\n } catch {}\n\n // Fallback: query npm registry directly\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), 5000)\n const res = await fetch(\n `https://registry.npmjs.org/${encodeURIComponent(MACRO.PACKAGE_URL)}`,\n {\n method: 'GET',\n headers: {\n Accept: 'application/vnd.npm.install-v1+json',\n 'User-Agent': `${PRODUCT_NAME}/${MACRO.VERSION}`,\n },\n signal: controller.signal,\n },\n )\n clearTimeout(timer)\n if (!res.ok) return null\n const json: any = await res.json().catch(() => null)\n const latest = json && json['dist-tags'] && json['dist-tags'].latest\n return typeof latest === 'string' ? latest : null\n } catch {\n return null\n }\n}\n\n// Suggest manual update commands; prefer Bun first, then npm\nexport async function getUpdateCommandSuggestions(): Promise<string[]> {\n return [\n `bun add -g ${MACRO.PACKAGE_URL}@latest`,\n `npm install -g ${MACRO.PACKAGE_URL}@latest`,\n ]\n}\n\n// Optional: background notifier that prints a simple banner\nexport async function checkAndNotifyUpdate(): Promise<void> {\n try {\n if (process.env.NODE_ENV === 'test') return\n if (await isAutoUpdaterDisabled()) return\n if (await env.getIsDocker()) return\n if (!(await env.hasInternetAccess())) return\n\n const config: any = getGlobalConfig()\n const now = Date.now()\n const DAY_MS = 24 * 60 * 60 * 1000\n const lastCheck = Number(config.lastUpdateCheckAt || 0)\n if (lastCheck && now - lastCheck < DAY_MS) return\n\n const latest = await getLatestVersion()\n if (!latest) {\n saveGlobalConfig({ ...config, lastUpdateCheckAt: now })\n return\n }\n\n if (gt(latest, MACRO.VERSION)) {\n saveGlobalConfig({\n ...config,\n lastUpdateCheckAt: now,\n lastSuggestedVersion: latest,\n })\n const suggestions = await getUpdateCommandSuggestions()\n // eslint-disable-next-line no-console\n console.log(`New version available: ${latest} (current: ${MACRO.VERSION})`)\n console.log('Run the following command to update:')\n for (const command of suggestions) console.log(` ${command}`)\n } else {\n saveGlobalConfig({ ...config, lastUpdateCheckAt: now })\n }\n } catch (error) {\n logError(`update-notify: ${error}`)\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AAEzB,SAAS,IAAI,UAAU;AACvB,SAAS,aAAa;AACtB,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB,kBAAkB,6BAA6B;AACzE,SAAS,WAAW;AAOpB,eAAsB,mBAAkC;AACtD,MAAI;AACF,UAAM,gBAA+B,EAAE,YAAY,QAAQ;AAC3D,QAAI,cAAc,cAAc,GAAG,MAAM,SAAS,cAAc,UAAU,GAAG;AAC3E,YAAM,cAAc,MAAM,4BAA4B;AAGtD,cAAQ;AAAA,QACN,QAAQ,YAAY,YAAY,MAAM,OAAO,mCAAmC,cAAc,UAAU;AAAA;AAAA,IAEtG,YAAY,IAAI,OAAK,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI;AAAA,MAC5C;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,SAAS,OAAO;AACd,aAAS,mCAAmC,KAAK,EAAE;AAAA,EACrD;AACF;AAGA,eAAsB,mBAA2C;AAE/D,MAAI;AACF,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,eAAW,MAAM,gBAAgB,MAAM,GAAG,GAAI;AAC9C,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA,CAAC,QAAQ,MAAM,aAAa,SAAS;AAAA,MACrC,gBAAgB;AAAA,IAClB;AACA,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,IAAI,OAAO,OAAO,KAAK;AAC7B,UAAI,EAAG,QAAO;AAAA,IAChB;AAAA,EACF,QAAQ;AAAA,EAAC;AAGT,MAAI;AACF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,GAAI;AACvD,UAAM,MAAM,MAAM;AAAA,MAChB,8BAA8B,mBAAmB,MAAM,WAAW,CAAC;AAAA,MACnE;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,QAAQ;AAAA,UACR,cAAc,GAAG,YAAY,IAAI,MAAM,OAAO;AAAA,QAChD;AAAA,QACA,QAAQ,WAAW;AAAA,MACrB;AAAA,IACF;AACA,iBAAa,KAAK;AAClB,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAY,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AACnD,UAAM,SAAS,QAAQ,KAAK,WAAW,KAAK,KAAK,WAAW,EAAE;AAC9D,WAAO,OAAO,WAAW,WAAW,SAAS;AAAA,EAC/C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,8BAAiD;AACrE,SAAO;AAAA,IACL,cAAc,MAAM,WAAW;AAAA,IAC/B,kBAAkB,MAAM,WAAW;AAAA,EACrC;AACF;AAGA,eAAsB,uBAAsC;AAC1D,MAAI;AACF,QAAI,QAAQ,IAAI,aAAa,OAAQ;AACrC,QAAI,MAAM,sBAAsB,EAAG;AACnC,QAAI,MAAM,IAAI,YAAY,EAAG;AAC7B,QAAI,CAAE,MAAM,IAAI,kBAAkB,EAAI;AAEtC,UAAM,SAAc,gBAAgB;AACpC,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,KAAK,KAAK,KAAK;AAC9B,UAAM,YAAY,OAAO,OAAO,qBAAqB,CAAC;AACtD,QAAI,aAAa,MAAM,YAAY,OAAQ;AAE3C,UAAM,SAAS,MAAM,iBAAiB;AACtC,QAAI,CAAC,QAAQ;AACX,uBAAiB,EAAE,GAAG,QAAQ,mBAAmB,IAAI,CAAC;AACtD;AAAA,IACF;AAEA,QAAI,GAAG,QAAQ,MAAM,OAAO,GAAG;AAC7B,uBAAiB;AAAA,QACf,GAAG;AAAA,QACH,mBAAmB;AAAA,QACnB,sBAAsB;AAAA,MACxB,CAAC;AACD,YAAM,cAAc,MAAM,4BAA4B;AAEtD,cAAQ,IAAI,0BAA0B,MAAM,cAAc,MAAM,OAAO,GAAG;AAC1E,cAAQ,IAAI,sCAAsC;AAClD,iBAAW,WAAW,YAAa,SAAQ,IAAI,KAAK,OAAO,EAAE;AAAA,IAC/D,OAAO;AACL,uBAAiB,EAAE,GAAG,QAAQ,mBAAmB,IAAI,CAAC;AAAA,IACxD;AAAA,EACF,SAAS,OAAO;AACd,aAAS,kBAAkB,KAAK,EAAE;AAAA,EACpC;AACF;",
4
+ "sourcesContent": ["import { execFileNoThrow } from './execFileNoThrow'\nimport { logError } from './log'\n\nimport { lt, gt } from 'semver'\nimport { MACRO } from '@constants/macros'\nimport { PRODUCT_NAME } from '@constants/product'\nimport {\n getGlobalConfig,\n saveGlobalConfig,\n isAutoUpdaterDisabled,\n} from './config'\nimport { env } from './env'\n\nexport type VersionConfig = {\n minVersion: string\n}\n\n// Ensure current version meets minimum supported version; exit if too old\nexport async function assertMinVersion(): Promise<void> {\n try {\n const versionConfig: VersionConfig = { minVersion: '0.0.0' }\n if (\n versionConfig.minVersion &&\n lt(MACRO.VERSION, versionConfig.minVersion)\n ) {\n const suggestions = await getUpdateCommandSuggestions()\n // Intentionally minimal: caller may print its own message; we just exit\n // eslint-disable-next-line no-console\n console.error(\n `Your ${PRODUCT_NAME} version ${MACRO.VERSION} is below the minimum supported ${versionConfig.minVersion}.\\n` +\n 'Update using one of:\\n' +\n suggestions.map(c => ` ${c}`).join('\\n'),\n )\n process.exit(1)\n }\n } catch (error) {\n logError(`Error checking minimum version: ${error}`)\n }\n}\n\n// Get latest version from npm (via npm CLI or HTTP fallback)\nexport async function getLatestVersion(): Promise<string | null> {\n // Prefer npm CLI (fast when available)\n try {\n const abortController = new AbortController()\n setTimeout(() => abortController.abort(), 5000)\n const result = await execFileNoThrow(\n 'npm',\n ['view', MACRO.PACKAGE_URL, 'version'],\n abortController.signal,\n )\n if (result.code === 0) {\n const v = result.stdout.trim()\n if (v) return v\n }\n } catch {}\n\n // Fallback: query npm registry directly\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), 5000)\n const res = await fetch(\n `https://registry.npmjs.org/${encodeURIComponent(MACRO.PACKAGE_URL)}`,\n {\n method: 'GET',\n headers: {\n Accept: 'application/vnd.npm.install-v1+json',\n 'User-Agent': `${PRODUCT_NAME}/${MACRO.VERSION}`,\n },\n signal: controller.signal,\n },\n )\n clearTimeout(timer)\n if (!res.ok) return null\n const json: any = await res.json().catch(() => null)\n const latest = json && json['dist-tags'] && json['dist-tags'].latest\n return typeof latest === 'string' ? latest : null\n } catch {\n return null\n }\n}\n\n// Suggest manual update commands; prefer Bun first, then npm\nexport async function getUpdateCommandSuggestions(): Promise<string[]> {\n return [\n `bun add -g ${MACRO.PACKAGE_URL}@latest`,\n `npm install -g ${MACRO.PACKAGE_URL}@latest`,\n ]\n}\n\n// Optional: background notifier that prints a simple banner\nexport async function checkAndNotifyUpdate(): Promise<void> {\n try {\n if (process.env.NODE_ENV === 'test') return\n if (await isAutoUpdaterDisabled()) return\n if (await env.getIsDocker()) return\n if (!(await env.hasInternetAccess())) return\n\n const config: any = getGlobalConfig()\n const now = Date.now()\n const DAY_MS = 24 * 60 * 60 * 1000\n const lastCheck = Number(config.lastUpdateCheckAt || 0)\n if (lastCheck && now - lastCheck < DAY_MS) return\n\n const latest = await getLatestVersion()\n if (!latest) {\n saveGlobalConfig({ ...config, lastUpdateCheckAt: now })\n return\n }\n\n if (gt(latest, MACRO.VERSION)) {\n saveGlobalConfig({\n ...config,\n lastUpdateCheckAt: now,\n lastSuggestedVersion: latest,\n })\n const suggestions = await getUpdateCommandSuggestions()\n // eslint-disable-next-line no-console\n console.log(\n `New version available: ${latest} (current: ${MACRO.VERSION})`,\n )\n console.log('Run the following command to update:')\n for (const command of suggestions) console.log(` ${command}`)\n } else {\n saveGlobalConfig({ ...config, lastUpdateCheckAt: now })\n }\n } catch (error) {\n logError(`update-notify: ${error}`)\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
- import { memoize } from "lodash-es";
2
1
  import { API_ERROR_MESSAGE_PREFIX, queryQuick } from "../services/claude.js";
2
+ import { memoizeAsyncWithLimit } from "./async.js";
3
3
  import { parse } from "shell-quote";
4
4
  import { PRODUCT_NAME } from "../constants/product.js";
5
5
  const SINGLE_QUOTE = "__SINGLE_QUOTE__";
@@ -42,7 +42,7 @@ function splitCommand(command) {
42
42
  (part) => !COMMAND_LIST_SEPARATORS.has(part)
43
43
  );
44
44
  }
45
- const getCommandSubcommandPrefix = memoize(
45
+ const getCommandSubcommandPrefix = memoizeAsyncWithLimit(
46
46
  async (command, abortSignal) => {
47
47
  const subcommands = splitCommand(command);
48
48
  const [fullCommandPrefix, ...subcommandPrefixesResults] = await Promise.all(
@@ -71,10 +71,16 @@ const getCommandSubcommandPrefix = memoize(
71
71
  subcommandPrefixes
72
72
  };
73
73
  },
74
- (command) => command
75
- // memoize by command only
74
+ {
75
+ max: 200,
76
+ // Cache up to 200 unique commands
77
+ ttl: 3e5,
78
+ // 5 minutes TTL
79
+ resolver: (command) => command
80
+ // memoize by command only, ignore abortSignal
81
+ }
76
82
  );
77
- const getCommandPrefix = memoize(
83
+ const getCommandPrefix = memoizeAsyncWithLimit(
78
84
  async (command, abortSignal) => {
79
85
  const response = await queryQuick({
80
86
  systemPrompt: [
@@ -125,10 +131,10 @@ The user has allowed certain command prefixes to be run, and will otherwise be a
125
131
  Your task is to determine the command prefix for the following command.
126
132
 
127
133
  IMPORTANT: Bash commands may run multiple commands that are chained together.
128
- For safety, if the command seems to contain command injection, you must return "command_injection_detected".
129
- (This will help protect the user: if they think that they're allowlisting command A,
130
- but the AI coding agent sends a malicious command that technically has the same prefix as command A,
131
- then the safety system will see that you said \u201Ccommand_injection_detected\u201D and ask the user for manual confirmation.)
134
+ For safety, if the command seems to contain command injection, you must return "command_injection_detected".
135
+ (This will help protect the user: if they think that they're allowlisting command A,
136
+ but the AI coding agent sends a malicious command that technically has the same prefix as command A,
137
+ then the safety system will see that you said "command_injection_detected" and ask the user for manual confirmation.)
132
138
 
133
139
  Note that not every command has a prefix. If a command has no prefix, return "none".
134
140
 
@@ -163,8 +169,14 @@ Command: ${command}
163
169
  commandInjectionDetected: false
164
170
  };
165
171
  },
166
- (command) => command
167
- // memoize by command only
172
+ {
173
+ max: 200,
174
+ // Cache up to 200 unique commands
175
+ ttl: 3e5,
176
+ // 5 minutes TTL
177
+ resolver: (command) => command
178
+ // memoize by command only, ignore abortSignal
179
+ }
168
180
  );
169
181
  const COMMAND_LIST_SEPARATORS = /* @__PURE__ */ new Set([
170
182
  "&&",