@within-7/minto 0.1.5 → 0.1.6

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 (264) 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.map +1 -1
  164. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +14 -8
  165. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  166. package/dist/tools/BashOutputTool/BashOutputTool.js.map +2 -2
  167. package/dist/tools/BashTool/BashTool.js.map +2 -2
  168. package/dist/tools/FileReadTool/FileReadTool.js.map +1 -1
  169. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  170. package/dist/tools/GrepTool/GrepTool.js +1 -4
  171. package/dist/tools/GrepTool/GrepTool.js.map +2 -2
  172. package/dist/tools/MultiEditTool/MultiEditTool.js +4 -1
  173. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  174. package/dist/tools/NotebookReadTool/NotebookReadTool.js +3 -1
  175. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +2 -2
  176. package/dist/tools/SkillTool/SkillTool.js +12 -6
  177. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  178. package/dist/tools/TaskTool/TaskTool.js +14 -5
  179. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  180. package/dist/tools/TaskTool/prompt.js.map +2 -2
  181. package/dist/tools/ThinkTool/ThinkTool.js +6 -1
  182. package/dist/tools/ThinkTool/ThinkTool.js.map +2 -2
  183. package/dist/tools/TodoWriteTool/TodoWriteTool.js +23 -3
  184. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +2 -2
  185. package/dist/tools/URLFetcherTool/URLFetcherTool.js +2 -2
  186. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
  187. package/dist/tools/URLFetcherTool/cache.js +6 -3
  188. package/dist/tools/URLFetcherTool/cache.js.map +2 -2
  189. package/dist/tools/URLFetcherTool/htmlToMarkdown.js +3 -1
  190. package/dist/tools/URLFetcherTool/htmlToMarkdown.js.map +2 -2
  191. package/dist/tools/WebSearchTool/WebSearchTool.js.map +2 -2
  192. package/dist/tools/WebSearchTool/prompt.js.map +2 -2
  193. package/dist/tools/WebSearchTool/searchProviders.js +15 -6
  194. package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
  195. package/dist/tools.js +4 -1
  196. package/dist/tools.js.map +2 -2
  197. package/dist/types/core.js +1 -0
  198. package/dist/types/core.js.map +7 -0
  199. package/dist/types/hooks.js +1 -4
  200. package/dist/types/hooks.js.map +2 -2
  201. package/dist/types/marketplace.js +8 -2
  202. package/dist/types/marketplace.js.map +2 -2
  203. package/dist/types/plugin.js +9 -6
  204. package/dist/types/plugin.js.map +2 -2
  205. package/dist/utils/BackgroundShellManager.js +76 -10
  206. package/dist/utils/BackgroundShellManager.js.map +2 -2
  207. package/dist/utils/PersistentShell.js +7 -2
  208. package/dist/utils/PersistentShell.js.map +2 -2
  209. package/dist/utils/advancedFuzzyMatcher.js +4 -1
  210. package/dist/utils/advancedFuzzyMatcher.js.map +2 -2
  211. package/dist/utils/agentLoader.js +69 -35
  212. package/dist/utils/agentLoader.js.map +2 -2
  213. package/dist/utils/agentStorage.js.map +2 -2
  214. package/dist/utils/async.js +163 -0
  215. package/dist/utils/async.js.map +7 -0
  216. package/dist/utils/autoUpdater.js +8 -2
  217. package/dist/utils/autoUpdater.js.map +2 -2
  218. package/dist/utils/commands.js +23 -11
  219. package/dist/utils/commands.js.map +2 -2
  220. package/dist/utils/commonUnixCommands.js +3 -1
  221. package/dist/utils/commonUnixCommands.js.map +2 -2
  222. package/dist/utils/compressionMode.js.map +2 -2
  223. package/dist/utils/config.js +30 -14
  224. package/dist/utils/config.js.map +2 -2
  225. package/dist/utils/debugLogger.js.map +2 -2
  226. package/dist/utils/env.js.map +2 -2
  227. package/dist/utils/envConfig.js +82 -0
  228. package/dist/utils/envConfig.js.map +7 -0
  229. package/dist/utils/errorHandling.js +89 -0
  230. package/dist/utils/errorHandling.js.map +7 -0
  231. package/dist/utils/expertChatStorage.js.map +2 -2
  232. package/dist/utils/fuzzyMatcher.js +13 -7
  233. package/dist/utils/fuzzyMatcher.js.map +2 -2
  234. package/dist/utils/hookManager.js +14 -4
  235. package/dist/utils/hookManager.js.map +2 -2
  236. package/dist/utils/log.js.map +2 -2
  237. package/dist/utils/marketplaceManager.js +44 -9
  238. package/dist/utils/marketplaceManager.js.map +2 -2
  239. package/dist/utils/messageContextManager.js.map +1 -1
  240. package/dist/utils/messages.js +6 -3
  241. package/dist/utils/messages.js.map +2 -2
  242. package/dist/utils/model.js +3 -1
  243. package/dist/utils/model.js.map +2 -2
  244. package/dist/utils/pluginInstaller.js +3 -15
  245. package/dist/utils/pluginInstaller.js.map +2 -2
  246. package/dist/utils/pluginLoader.js +41 -13
  247. package/dist/utils/pluginLoader.js.map +2 -2
  248. package/dist/utils/pluginRegistry.js.map +2 -2
  249. package/dist/utils/pluginValidator.js +71 -49
  250. package/dist/utils/pluginValidator.js.map +2 -2
  251. package/dist/utils/ptyCompat.js.map +2 -2
  252. package/dist/utils/roundConverter.js.map +2 -2
  253. package/dist/utils/secureFile.js +43 -14
  254. package/dist/utils/secureFile.js.map +2 -2
  255. package/dist/utils/sessionState.js.map +2 -2
  256. package/dist/utils/skillLoader.js.map +2 -2
  257. package/dist/utils/teamConfig.js +7 -4
  258. package/dist/utils/teamConfig.js.map +2 -2
  259. package/dist/utils/theme.js.map +2 -2
  260. package/dist/utils/thinking.js.map +2 -2
  261. package/dist/utils/unaryLogging.js.map +2 -2
  262. package/dist/version.js +2 -2
  263. package/dist/version.js.map +1 -1
  264. package/package.json +5 -5
@@ -0,0 +1,185 @@
1
+ import {
2
+ writeFileSync,
3
+ unlinkSync,
4
+ mkdirSync,
5
+ existsSync,
6
+ renameSync
7
+ } from "fs";
8
+ import { join, resolve, relative, isAbsolute } from "path";
9
+ import { homedir } from "os";
10
+ import { spawn } from "child_process";
11
+ import { getCwd } from "../../../utils/state.js";
12
+ import { AGENT_LOCATIONS, FOLDER_CONFIG } from "../constants.js";
13
+ function getAgentDirectory(location) {
14
+ if (location === AGENT_LOCATIONS.BUILT_IN || location === AGENT_LOCATIONS.ALL) {
15
+ throw new Error(`Cannot get directory path for ${location} agents`);
16
+ }
17
+ if (location === AGENT_LOCATIONS.USER) {
18
+ return join(homedir(), FOLDER_CONFIG.FOLDER_NAME, FOLDER_CONFIG.AGENTS_DIR);
19
+ } else {
20
+ return join(getCwd(), FOLDER_CONFIG.FOLDER_NAME, FOLDER_CONFIG.AGENTS_DIR);
21
+ }
22
+ }
23
+ function getAgentFilePath(agent) {
24
+ if (agent.location === "built-in") {
25
+ throw new Error("Cannot get file path for built-in agents");
26
+ }
27
+ const dir = getAgentDirectory(agent.location);
28
+ return join(dir, `${agent.agentType}.md`);
29
+ }
30
+ function ensureDirectoryExists(location) {
31
+ const dir = getAgentDirectory(location);
32
+ if (!existsSync(dir)) {
33
+ mkdirSync(dir, { recursive: true });
34
+ }
35
+ return dir;
36
+ }
37
+ function generateAgentFileContent(agentType, description, tools, systemPrompt, model, color) {
38
+ const descriptionLines = description.split("\n");
39
+ const formattedDescription = descriptionLines.length > 1 ? `|
40
+ ${descriptionLines.join("\n ")}` : JSON.stringify(description);
41
+ const lines = [
42
+ "---",
43
+ `name: ${agentType}`,
44
+ `description: ${formattedDescription}`
45
+ ];
46
+ if (tools) {
47
+ if (tools === "*") {
48
+ lines.push(`tools: "*"`);
49
+ } else if (Array.isArray(tools) && tools.length > 0) {
50
+ lines.push(`tools: [${tools.map((t) => `"${t}"`).join(", ")}]`);
51
+ }
52
+ }
53
+ if (model) {
54
+ lines.push(`model: ${model}`);
55
+ }
56
+ if (color) {
57
+ lines.push(`color: ${color}`);
58
+ }
59
+ lines.push("---", "", systemPrompt);
60
+ return lines.join("\n");
61
+ }
62
+ async function saveAgent(location, agentType, description, tools, systemPrompt, model, color, throwIfExists = true) {
63
+ if (location === AGENT_LOCATIONS.BUILT_IN) {
64
+ throw new Error("Cannot save built-in agents");
65
+ }
66
+ ensureDirectoryExists(location);
67
+ const filePath = join(getAgentDirectory(location), `${agentType}.md`);
68
+ const tempFile = `${filePath}.tmp.${Date.now()}.${Math.random().toString(36).substr(2, 9)}`;
69
+ const toolsForFile = Array.isArray(tools) && tools.length === 1 && tools[0] === "*" ? "*" : tools;
70
+ const content = generateAgentFileContent(
71
+ agentType,
72
+ description,
73
+ toolsForFile,
74
+ systemPrompt,
75
+ model,
76
+ color
77
+ );
78
+ try {
79
+ writeFileSync(tempFile, content, { encoding: "utf-8", flag: "wx" });
80
+ if (throwIfExists && existsSync(filePath)) {
81
+ try {
82
+ unlinkSync(tempFile);
83
+ } catch {
84
+ }
85
+ throw new Error(`Agent file already exists: ${filePath}`);
86
+ }
87
+ renameSync(tempFile, filePath);
88
+ } catch (error) {
89
+ try {
90
+ if (existsSync(tempFile)) {
91
+ unlinkSync(tempFile);
92
+ }
93
+ } catch (cleanupError) {
94
+ console.warn("Failed to cleanup temp file:", cleanupError);
95
+ }
96
+ throw error;
97
+ }
98
+ }
99
+ async function deleteAgent(agent) {
100
+ if (agent.location === "built-in") {
101
+ throw new Error("Cannot delete built-in agents");
102
+ }
103
+ const filePath = getAgentFilePath(agent);
104
+ unlinkSync(filePath);
105
+ }
106
+ async function openInEditor(filePath) {
107
+ const resolvedPath = resolve(filePath);
108
+ const projectDir = process.cwd();
109
+ const homeDir = homedir();
110
+ const isSub = (base, target) => {
111
+ const rel = relative(resolve(base), resolve(target));
112
+ if (!rel || rel === "") return true;
113
+ if (rel.startsWith("..")) return false;
114
+ if (isAbsolute(rel)) return false;
115
+ return true;
116
+ };
117
+ if (!isSub(projectDir, resolvedPath) && !isSub(homeDir, resolvedPath)) {
118
+ throw new Error("Access denied: File path outside allowed directories");
119
+ }
120
+ if (!resolvedPath.endsWith(".md")) {
121
+ throw new Error("Invalid file type: Only .md files are allowed");
122
+ }
123
+ return new Promise((resolve2, reject) => {
124
+ const platform = process.platform;
125
+ let command;
126
+ let args;
127
+ switch (platform) {
128
+ case "darwin":
129
+ command = "open";
130
+ args = [resolvedPath];
131
+ break;
132
+ case "win32":
133
+ command = "cmd";
134
+ args = ["/c", "start", "", resolvedPath];
135
+ break;
136
+ default:
137
+ command = "xdg-open";
138
+ args = [resolvedPath];
139
+ break;
140
+ }
141
+ const child = spawn(command, args, {
142
+ detached: true,
143
+ stdio: "ignore",
144
+ shell: false
145
+ });
146
+ child.unref();
147
+ child.on("error", (error) => {
148
+ reject(new Error(`Failed to open editor: ${error.message}`));
149
+ });
150
+ child.on("exit", (code) => {
151
+ if (code === 0) {
152
+ resolve2();
153
+ } else {
154
+ reject(new Error(`Editor exited with code ${code}`));
155
+ }
156
+ });
157
+ });
158
+ }
159
+ async function updateAgent(agent, description, tools, systemPrompt, color, model) {
160
+ if (agent.location === "built-in") {
161
+ throw new Error("Cannot update built-in agents");
162
+ }
163
+ const toolsForFile = tools.length === 1 && tools[0] === "*" ? "*" : tools;
164
+ const content = generateAgentFileContent(
165
+ agent.agentType,
166
+ description,
167
+ toolsForFile,
168
+ systemPrompt,
169
+ model,
170
+ color
171
+ );
172
+ const filePath = getAgentFilePath(agent);
173
+ writeFileSync(filePath, content, { encoding: "utf-8", flag: "w" });
174
+ }
175
+ export {
176
+ deleteAgent,
177
+ ensureDirectoryExists,
178
+ generateAgentFileContent,
179
+ getAgentDirectory,
180
+ getAgentFilePath,
181
+ openInEditor,
182
+ saveAgent,
183
+ updateAgent
184
+ };
185
+ //# sourceMappingURL=fileOperations.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/commands/agents/utils/fileOperations.ts"],
4
+ "sourcesContent": ["/**\n * File system operations for agents\n */\nimport {\n writeFileSync,\n unlinkSync,\n mkdirSync,\n existsSync,\n renameSync,\n} from 'fs'\nimport { join, resolve, relative, isAbsolute } from 'path'\nimport { homedir } from 'os'\nimport { spawn } from 'child_process'\nimport { getCwd } from '@utils/state'\nimport type { AgentConfig } from '@utils/agentLoader'\nimport { AGENT_LOCATIONS, FOLDER_CONFIG } from '../constants'\nimport type { AgentLocation } from '../types'\n\n/**\n * Get the directory path for agents of a specific location\n */\nexport function getAgentDirectory(location: AgentLocation): string {\n if (\n location === AGENT_LOCATIONS.BUILT_IN ||\n location === AGENT_LOCATIONS.ALL\n ) {\n throw new Error(`Cannot get directory path for ${location} agents`)\n }\n\n if (location === AGENT_LOCATIONS.USER) {\n return join(homedir(), FOLDER_CONFIG.FOLDER_NAME, FOLDER_CONFIG.AGENTS_DIR)\n } else {\n return join(getCwd(), FOLDER_CONFIG.FOLDER_NAME, FOLDER_CONFIG.AGENTS_DIR)\n }\n}\n\n/**\n * Get the file path for an agent\n */\nexport function getAgentFilePath(agent: AgentConfig): string {\n if (agent.location === 'built-in') {\n throw new Error('Cannot get file path for built-in agents')\n }\n const dir = getAgentDirectory(agent.location as AgentLocation)\n return join(dir, `${agent.agentType}.md`)\n}\n\n/**\n * Ensure the agent directory exists\n */\nexport function ensureDirectoryExists(location: AgentLocation): string {\n const dir = getAgentDirectory(location)\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n return dir\n}\n\n/**\n * Generate agent file content in markdown format with YAML frontmatter\n */\nexport function generateAgentFileContent(\n agentType: string,\n description: string,\n tools: string[] | '*',\n systemPrompt: string,\n model?: string,\n color?: string,\n): string {\n // Use YAML multi-line string for description to avoid escaping issues\n const descriptionLines = description.split('\\n')\n const formattedDescription =\n descriptionLines.length > 1\n ? `|\\n ${descriptionLines.join('\\n ')}`\n : JSON.stringify(description)\n\n const lines = [\n '---',\n `name: ${agentType}`,\n `description: ${formattedDescription}`,\n ]\n\n if (tools) {\n if (tools === '*') {\n lines.push(`tools: \"*\"`)\n } else if (Array.isArray(tools) && tools.length > 0) {\n lines.push(`tools: [${tools.map(t => `\"${t}\"`).join(', ')}]`)\n }\n }\n\n if (model) {\n lines.push(`model: ${model}`)\n }\n\n if (color) {\n lines.push(`color: ${color}`)\n }\n\n lines.push('---', '', systemPrompt)\n return lines.join('\\n')\n}\n\n/**\n * Save an agent to file\n */\nexport async function saveAgent(\n location: AgentLocation,\n agentType: string,\n description: string,\n tools: string[],\n systemPrompt: string,\n model?: string,\n color?: string,\n throwIfExists: boolean = true,\n): Promise<void> {\n if (location === AGENT_LOCATIONS.BUILT_IN) {\n throw new Error('Cannot save built-in agents')\n }\n\n ensureDirectoryExists(location)\n\n const filePath = join(getAgentDirectory(location), `${agentType}.md`)\n const tempFile = `${filePath}.tmp.${Date.now()}.${Math.random().toString(36).substr(2, 9)}`\n\n // Ensure tools is properly typed for file saving\n const toolsForFile: string[] | '*' =\n Array.isArray(tools) && tools.length === 1 && tools[0] === '*' ? '*' : tools\n const content = generateAgentFileContent(\n agentType,\n description,\n toolsForFile,\n systemPrompt,\n model,\n color,\n )\n\n try {\n // Write to temp file first using 'wx' to avoid overwriting\n writeFileSync(tempFile, content, { encoding: 'utf-8', flag: 'wx' })\n\n // Atomic check if target file exists\n if (throwIfExists && existsSync(filePath)) {\n // Clean up temp file\n try {\n unlinkSync(tempFile)\n } catch {\n // Ignore cleanup errors\n }\n throw new Error(`Agent file already exists: ${filePath}`)\n }\n\n // Atomic rename (on most filesystems, rename is atomic)\n renameSync(tempFile, filePath)\n } catch (error) {\n // Ensure temp file is cleaned up\n try {\n if (existsSync(tempFile)) {\n unlinkSync(tempFile)\n }\n } catch (cleanupError) {\n console.warn('Failed to cleanup temp file:', cleanupError)\n }\n throw error\n }\n}\n\n/**\n * Delete an agent file\n */\nexport async function deleteAgent(agent: AgentConfig): Promise<void> {\n if (agent.location === 'built-in') {\n throw new Error('Cannot delete built-in agents')\n }\n\n const filePath = getAgentFilePath(agent)\n unlinkSync(filePath)\n}\n\n/**\n * Open file in system editor - secure version to prevent command injection\n */\nexport async function openInEditor(filePath: string): Promise<void> {\n // Security validation: ensure path is in allowed directories\n const resolvedPath = resolve(filePath)\n const projectDir = process.cwd()\n const homeDir = homedir()\n\n const isSub = (base: string, target: string) => {\n const rel = relative(resolve(base), resolve(target))\n if (!rel || rel === '') return true\n if (rel.startsWith('..')) return false\n if (isAbsolute(rel)) return false\n return true\n }\n\n if (!isSub(projectDir, resolvedPath) && !isSub(homeDir, resolvedPath)) {\n throw new Error('Access denied: File path outside allowed directories')\n }\n\n // Validate file extension\n if (!resolvedPath.endsWith('.md')) {\n throw new Error('Invalid file type: Only .md files are allowed')\n }\n\n return new Promise((resolve, reject) => {\n const platform = process.platform\n let command: string\n let args: string[]\n\n // Use spawn instead of exec to avoid shell injection\n switch (platform) {\n case 'darwin':\n command = 'open'\n args = [resolvedPath]\n break\n case 'win32':\n command = 'cmd'\n args = ['/c', 'start', '', resolvedPath]\n break\n default:\n command = 'xdg-open'\n args = [resolvedPath]\n break\n }\n\n // Use spawn instead of exec to avoid shell interpretation\n const child = spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n shell: false,\n })\n\n child.unref()\n\n child.on('error', error => {\n reject(new Error(`Failed to open editor: ${error.message}`))\n })\n\n child.on('exit', code => {\n if (code === 0) {\n resolve()\n } else {\n reject(new Error(`Editor exited with code ${code}`))\n }\n })\n })\n}\n\n/**\n * Update an existing agent\n */\nexport async function updateAgent(\n agent: AgentConfig,\n description: string,\n tools: string[] | '*',\n systemPrompt: string,\n color?: string,\n model?: string,\n): Promise<void> {\n if (agent.location === 'built-in') {\n throw new Error('Cannot update built-in agents')\n }\n\n const toolsForFile = tools.length === 1 && tools[0] === '*' ? '*' : tools\n const content = generateAgentFileContent(\n agent.agentType,\n description,\n toolsForFile,\n systemPrompt,\n model,\n color,\n )\n const filePath = getAgentFilePath(agent)\n\n writeFileSync(filePath, content, { encoding: 'utf-8', flag: 'w' })\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["resolve"]
7
+ }
@@ -0,0 +1,21 @@
1
+ import {
2
+ getAgentDirectory,
3
+ getAgentFilePath,
4
+ ensureDirectoryExists,
5
+ generateAgentFileContent,
6
+ saveAgent,
7
+ deleteAgent,
8
+ openInEditor,
9
+ updateAgent
10
+ } from "./fileOperations.js";
11
+ export {
12
+ deleteAgent,
13
+ ensureDirectoryExists,
14
+ generateAgentFileContent,
15
+ getAgentDirectory,
16
+ getAgentFilePath,
17
+ openInEditor,
18
+ saveAgent,
19
+ updateAgent
20
+ };
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/commands/agents/utils/index.ts"],
4
+ "sourcesContent": ["/**\n * Utility exports for the agents command\n */\nexport {\n getAgentDirectory,\n getAgentFilePath,\n ensureDirectoryExists,\n generateAgentFileContent,\n saveAgent,\n deleteAgent,\n openInEditor,\n updateAgent,\n} from './fileOperations'\n"],
5
+ "mappings": "AAGA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;",
6
+ "names": []
7
+ }
@@ -1,12 +1,12 @@
1
1
  import { Bug } from "../components/Bug.js";
2
2
  import * as React from "react";
3
- import { PRODUCT_NAME } from "../constants/product.js";
4
3
  const bug = {
5
4
  type: "local-jsx",
6
5
  name: "bug",
7
- description: `Submit feedback about ${PRODUCT_NAME}`,
6
+ description: `Report bugs or submit feedback via GitHub Issues`,
8
7
  isEnabled: true,
9
8
  isHidden: false,
9
+ aliases: ["feedback", "report"],
10
10
  async call(onDone) {
11
11
  return /* @__PURE__ */ React.createElement(Bug, { onDone });
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/commands/bug.tsx"],
4
- "sourcesContent": ["import { Command } from '@commands'\nimport { Bug } from '@components/Bug'\nimport * as React from 'react'\nimport { PRODUCT_NAME } from '@constants/product'\n\nconst bug = {\n type: 'local-jsx',\n name: 'bug',\n description: `Submit feedback about ${PRODUCT_NAME}`,\n isEnabled: true,\n isHidden: false,\n async call(onDone) {\n return <Bug onDone={onDone} />\n },\n userFacingName() {\n return 'bug'\n },\n} satisfies Command\n\nexport default bug\n"],
5
- "mappings": "AACA,SAAS,WAAW;AACpB,YAAY,WAAW;AACvB,SAAS,oBAAoB;AAE7B,MAAM,MAAM;AAAA,EACV,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa,yBAAyB,YAAY;AAAA,EAClD,WAAW;AAAA,EACX,UAAU;AAAA,EACV,MAAM,KAAK,QAAQ;AACjB,WAAO,oCAAC,OAAI,QAAgB;AAAA,EAC9B;AAAA,EACA,iBAAiB;AACf,WAAO;AAAA,EACT;AACF;AAEA,IAAO,cAAQ;",
4
+ "sourcesContent": ["import { Command } from '@commands'\nimport { Bug } from '@components/Bug'\nimport * as React from 'react'\nimport { PRODUCT_NAME } from '@constants/product'\n\nconst bug = {\n type: 'local-jsx',\n name: 'bug',\n description: `Report bugs or submit feedback via GitHub Issues`,\n isEnabled: true,\n isHidden: false,\n aliases: ['feedback', 'report'],\n async call(onDone) {\n return <Bug onDone={onDone} />\n },\n userFacingName() {\n return 'bug'\n },\n} satisfies Command\n\nexport default bug\n"],
5
+ "mappings": "AACA,SAAS,WAAW;AACpB,YAAY,WAAW;AAGvB,MAAM,MAAM;AAAA,EACV,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AAAA,EACb,WAAW;AAAA,EACX,UAAU;AAAA,EACV,SAAS,CAAC,YAAY,QAAQ;AAAA,EAC9B,MAAM,KAAK,QAAQ;AACjB,WAAO,oCAAC,OAAI,QAAgB;AAAA,EAC9B;AAAA,EACA,iBAAiB;AACf,WAAO;AAAA,EACT;AACF;AAEA,IAAO,cAAQ;",
6
6
  "names": []
7
7
  }
@@ -1,16 +1,16 @@
1
1
  import { getContext } from "../context.js";
2
2
  import { getMessagesGetter, getMessagesSetter } from "../messages.js";
3
3
  import { API_ERROR_MESSAGE_PREFIX, queryLLM } from "../services/claude.js";
4
- import {
5
- createUserMessage,
6
- normalizeMessagesForAPI
7
- } from "../utils/messages.js";
4
+ import { createUserMessage, normalizeMessagesForAPI } from "../utils/messages.js";
8
5
  import { getCodeStyle } from "../utils/style.js";
9
6
  import { clearTerminal } from "../utils/terminal.js";
10
7
  import { resetReminderSession } from "../services/systemReminder.js";
11
8
  import { resetFileFreshnessSession } from "../services/fileFreshness.js";
12
9
  import { getCompressionPrompt } from "../constants/compressionPrompts.js";
13
- import { getCompressionMode, getCompressionModeDescription } from "../utils/compressionMode.js";
10
+ import {
11
+ getCompressionMode,
12
+ getCompressionModeDescription
13
+ } from "../utils/compressionMode.js";
14
14
  const compact = {
15
15
  type: "local",
16
16
  name: "compact",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/commands/compact.ts"],
4
- "sourcesContent": ["import { Command } from '@commands'\nimport { getContext } from '@context'\nimport { getMessagesGetter, getMessagesSetter } from '@messages'\nimport { API_ERROR_MESSAGE_PREFIX, queryLLM } from '@services/claude'\nimport {\n createUserMessage,\n normalizeMessagesForAPI,\n} from '@utils/messages'\nimport { getCodeStyle } from '@utils/style'\nimport { clearTerminal } from '@utils/terminal'\nimport { resetReminderSession } from '@services/systemReminder'\nimport { resetFileFreshnessSession } from '@services/fileFreshness'\nimport { getCompressionPrompt } from '@constants/compressionPrompts'\nimport { getCompressionMode, getCompressionModeDescription } from '@utils/compressionMode'\n\nconst compact = {\n type: 'local',\n name: 'compact',\n description: 'Clear conversation history but keep a summary in context',\n isEnabled: true,\n isHidden: false,\n async call(\n _,\n {\n options: { tools },\n abortController,\n setForkConvoWithMessagesOnTheNextRender,\n },\n ) {\n const messages = getMessagesGetter()()\n\n // Get the compression prompt based on current mode (business or code)\n const compressionMode = getCompressionMode()\n const compressionPrompt = getCompressionPrompt(compressionMode)\n const modeDescription = getCompressionModeDescription(compressionMode)\n\n const summaryRequest = createUserMessage(compressionPrompt)\n\n const summaryResponse = await queryLLM(\n normalizeMessagesForAPI([...messages, summaryRequest]),\n [\n 'You are a helpful AI assistant tasked with creating comprehensive conversation summaries that preserve all essential context for continuing development work.',\n ],\n 0,\n tools,\n abortController.signal,\n {\n safeMode: false,\n model: 'main', // \u4F7F\u7528\u6A21\u578B\u6307\u9488\uFF0C\u8BA9queryLLM\u7EDF\u4E00\u89E3\u6790\n prependCLISysprompt: true,\n },\n )\n\n const content = summaryResponse.message.content\n const summary =\n typeof content === 'string'\n ? content\n : content.length > 0 && content[0]?.type === 'text'\n ? content[0].text\n : null\n\n if (!summary) {\n throw new Error(\n `Failed to generate conversation summary - response did not contain valid text content - ${summaryResponse}`,\n )\n } else if (summary.startsWith(API_ERROR_MESSAGE_PREFIX)) {\n throw new Error(summary)\n }\n\n summaryResponse.message.usage = {\n input_tokens: 0,\n output_tokens: summaryResponse.message.usage.output_tokens,\n cache_creation_input_tokens: 0,\n cache_read_input_tokens: 0,\n }\n\n await clearTerminal()\n getMessagesSetter()([])\n setForkConvoWithMessagesOnTheNextRender([\n createUserMessage(\n `Context has been compressed using ${compressionMode === 'business' ? '9-section Business Consulting' : '8-section Code Development'} algorithm. Mode: ${modeDescription}. All essential information has been preserved for seamless continuation.`,\n ),\n summaryResponse,\n ])\n getContext.cache.clear?.()\n getCodeStyle.cache.clear?.()\n resetFileFreshnessSession()\n\n // Reset reminder and file freshness sessions to clean up state\n resetReminderSession()\n\n return '' // not used, just for typesafety. TODO: avoid this hack\n },\n userFacingName() {\n return 'compact'\n },\n} satisfies Command\n\nexport default compact\n"],
5
- "mappings": "AACA,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB,yBAAyB;AACrD,SAAS,0BAA0B,gBAAgB;AACnD;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,4BAA4B;AACrC,SAAS,iCAAiC;AAC1C,SAAS,4BAA4B;AACrC,SAAS,oBAAoB,qCAAqC;AAElE,MAAM,UAAU;AAAA,EACd,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AAAA,EACb,WAAW;AAAA,EACX,UAAU;AAAA,EACV,MAAM,KACJ,GACA;AAAA,IACE,SAAS,EAAE,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,EACF,GACA;AACA,UAAM,WAAW,kBAAkB,EAAE;AAGrC,UAAM,kBAAkB,mBAAmB;AAC3C,UAAM,oBAAoB,qBAAqB,eAAe;AAC9D,UAAM,kBAAkB,8BAA8B,eAAe;AAErE,UAAM,iBAAiB,kBAAkB,iBAAiB;AAE1D,UAAM,kBAAkB,MAAM;AAAA,MAC5B,wBAAwB,CAAC,GAAG,UAAU,cAAc,CAAC;AAAA,MACrD;AAAA,QACE;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB;AAAA,MAChB;AAAA,QACE,UAAU;AAAA,QACV,OAAO;AAAA;AAAA,QACP,qBAAqB;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,UAAU,gBAAgB,QAAQ;AACxC,UAAM,UACJ,OAAO,YAAY,WACf,UACA,QAAQ,SAAS,KAAK,QAAQ,CAAC,GAAG,SAAS,SACzC,QAAQ,CAAC,EAAE,OACX;AAER,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR,2FAA2F,eAAe;AAAA,MAC5G;AAAA,IACF,WAAW,QAAQ,WAAW,wBAAwB,GAAG;AACvD,YAAM,IAAI,MAAM,OAAO;AAAA,IACzB;AAEA,oBAAgB,QAAQ,QAAQ;AAAA,MAC9B,cAAc;AAAA,MACd,eAAe,gBAAgB,QAAQ,MAAM;AAAA,MAC7C,6BAA6B;AAAA,MAC7B,yBAAyB;AAAA,IAC3B;AAEA,UAAM,cAAc;AACpB,sBAAkB,EAAE,CAAC,CAAC;AACtB,4CAAwC;AAAA,MACtC;AAAA,QACE,qCAAqC,oBAAoB,aAAa,kCAAkC,4BAA4B,qBAAqB,eAAe;AAAA,MAC1K;AAAA,MACA;AAAA,IACF,CAAC;AACD,eAAW,MAAM,QAAQ;AACzB,iBAAa,MAAM,QAAQ;AAC3B,8BAA0B;AAG1B,yBAAqB;AAErB,WAAO;AAAA,EACT;AAAA,EACA,iBAAiB;AACf,WAAO;AAAA,EACT;AACF;AAEA,IAAO,kBAAQ;",
4
+ "sourcesContent": ["import { Command } from '@commands'\nimport { getContext } from '@context'\nimport { getMessagesGetter, getMessagesSetter } from '@messages'\nimport { API_ERROR_MESSAGE_PREFIX, queryLLM } from '@services/claude'\nimport { createUserMessage, normalizeMessagesForAPI } from '@utils/messages'\nimport { getCodeStyle } from '@utils/style'\nimport { clearTerminal } from '@utils/terminal'\nimport { resetReminderSession } from '@services/systemReminder'\nimport { resetFileFreshnessSession } from '@services/fileFreshness'\nimport { getCompressionPrompt } from '@constants/compressionPrompts'\nimport {\n getCompressionMode,\n getCompressionModeDescription,\n} from '@utils/compressionMode'\n\nconst compact = {\n type: 'local',\n name: 'compact',\n description: 'Clear conversation history but keep a summary in context',\n isEnabled: true,\n isHidden: false,\n async call(\n _,\n {\n options: { tools },\n abortController,\n setForkConvoWithMessagesOnTheNextRender,\n },\n ) {\n const messages = getMessagesGetter()()\n\n // Get the compression prompt based on current mode (business or code)\n const compressionMode = getCompressionMode()\n const compressionPrompt = getCompressionPrompt(compressionMode)\n const modeDescription = getCompressionModeDescription(compressionMode)\n\n const summaryRequest = createUserMessage(compressionPrompt)\n\n const summaryResponse = await queryLLM(\n normalizeMessagesForAPI([...messages, summaryRequest]),\n [\n 'You are a helpful AI assistant tasked with creating comprehensive conversation summaries that preserve all essential context for continuing development work.',\n ],\n 0,\n tools,\n abortController.signal,\n {\n safeMode: false,\n model: 'main', // \u4F7F\u7528\u6A21\u578B\u6307\u9488\uFF0C\u8BA9queryLLM\u7EDF\u4E00\u89E3\u6790\n prependCLISysprompt: true,\n },\n )\n\n const content = summaryResponse.message.content\n const summary =\n typeof content === 'string'\n ? content\n : content.length > 0 && content[0]?.type === 'text'\n ? content[0].text\n : null\n\n if (!summary) {\n throw new Error(\n `Failed to generate conversation summary - response did not contain valid text content - ${summaryResponse}`,\n )\n } else if (summary.startsWith(API_ERROR_MESSAGE_PREFIX)) {\n throw new Error(summary)\n }\n\n summaryResponse.message.usage = {\n input_tokens: 0,\n output_tokens: summaryResponse.message.usage.output_tokens,\n cache_creation_input_tokens: 0,\n cache_read_input_tokens: 0,\n }\n\n await clearTerminal()\n getMessagesSetter()([])\n setForkConvoWithMessagesOnTheNextRender([\n createUserMessage(\n `Context has been compressed using ${compressionMode === 'business' ? '9-section Business Consulting' : '8-section Code Development'} algorithm. Mode: ${modeDescription}. All essential information has been preserved for seamless continuation.`,\n ),\n summaryResponse,\n ])\n getContext.cache.clear?.()\n getCodeStyle.cache.clear?.()\n resetFileFreshnessSession()\n\n // Reset reminder and file freshness sessions to clean up state\n resetReminderSession()\n\n return '' // not used, just for typesafety. TODO: avoid this hack\n },\n userFacingName() {\n return 'compact'\n },\n} satisfies Command\n\nexport default compact\n"],
5
+ "mappings": "AACA,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB,yBAAyB;AACrD,SAAS,0BAA0B,gBAAgB;AACnD,SAAS,mBAAmB,+BAA+B;AAC3D,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,4BAA4B;AACrC,SAAS,iCAAiC;AAC1C,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,MAAM,UAAU;AAAA,EACd,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AAAA,EACb,WAAW;AAAA,EACX,UAAU;AAAA,EACV,MAAM,KACJ,GACA;AAAA,IACE,SAAS,EAAE,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,EACF,GACA;AACA,UAAM,WAAW,kBAAkB,EAAE;AAGrC,UAAM,kBAAkB,mBAAmB;AAC3C,UAAM,oBAAoB,qBAAqB,eAAe;AAC9D,UAAM,kBAAkB,8BAA8B,eAAe;AAErE,UAAM,iBAAiB,kBAAkB,iBAAiB;AAE1D,UAAM,kBAAkB,MAAM;AAAA,MAC5B,wBAAwB,CAAC,GAAG,UAAU,cAAc,CAAC;AAAA,MACrD;AAAA,QACE;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB;AAAA,MAChB;AAAA,QACE,UAAU;AAAA,QACV,OAAO;AAAA;AAAA,QACP,qBAAqB;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,UAAU,gBAAgB,QAAQ;AACxC,UAAM,UACJ,OAAO,YAAY,WACf,UACA,QAAQ,SAAS,KAAK,QAAQ,CAAC,GAAG,SAAS,SACzC,QAAQ,CAAC,EAAE,OACX;AAER,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR,2FAA2F,eAAe;AAAA,MAC5G;AAAA,IACF,WAAW,QAAQ,WAAW,wBAAwB,GAAG;AACvD,YAAM,IAAI,MAAM,OAAO;AAAA,IACzB;AAEA,oBAAgB,QAAQ,QAAQ;AAAA,MAC9B,cAAc;AAAA,MACd,eAAe,gBAAgB,QAAQ,MAAM;AAAA,MAC7C,6BAA6B;AAAA,MAC7B,yBAAyB;AAAA,IAC3B;AAEA,UAAM,cAAc;AACpB,sBAAkB,EAAE,CAAC,CAAC;AACtB,4CAAwC;AAAA,MACtC;AAAA,QACE,qCAAqC,oBAAoB,aAAa,kCAAkC,4BAA4B,qBAAqB,eAAe;AAAA,MAC1K;AAAA,MACA;AAAA,IACF,CAAC;AACD,eAAW,MAAM,QAAQ;AACzB,iBAAa,MAAM,QAAQ;AAC3B,8BAA0B;AAG1B,yBAAqB;AAErB,WAAO;AAAA,EACT;AAAA,EACA,iBAAiB;AACf,WAAO;AAAA,EACT;AACF;AAEA,IAAO,kBAAQ;",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,17 @@ import { getContext } from "../context.js";
4
4
  import { zodToJsonSchema } from "zod-to-json-schema";
5
5
  import { getMessagesGetter } from "../messages.js";
6
6
  import { PROJECT_FILE } from "../constants/product.js";
7
- const BYTES_PER_TOKEN = 4;
7
+ import { countTokens } from "../utils/tokens.js";
8
+ function estimateStringTokens(text) {
9
+ if (!text) return 0;
10
+ const cjkPattern = /[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g;
11
+ const cjkMatches = text.match(cjkPattern);
12
+ const cjkCount = cjkMatches ? cjkMatches.length : 0;
13
+ const nonCjkCount = text.length - cjkCount;
14
+ const cjkTokens = cjkCount / 1.5;
15
+ const nonCjkTokens = nonCjkCount / 4;
16
+ return Math.ceil(cjkTokens + nonCjkTokens);
17
+ }
8
18
  function getContextSections(text) {
9
19
  const sections = [];
10
20
  const firstContextIndex = text.indexOf("<context");
@@ -44,18 +54,23 @@ function getContextSections(text) {
44
54
  }
45
55
  return sections;
46
56
  }
47
- function formatTokenCount(bytes) {
48
- const tokens = bytes / BYTES_PER_TOKEN;
57
+ function formatTokenCount(tokens) {
58
+ if (tokens < 1e3) {
59
+ return `${tokens}`;
60
+ }
49
61
  const k = tokens / 1e3;
50
62
  return `${Math.round(k * 10) / 10}k`;
51
63
  }
52
64
  function formatByteCount(bytes) {
65
+ if (bytes < 1024) {
66
+ return `${bytes}b`;
67
+ }
53
68
  const kb = bytes / 1024;
54
69
  return `${Math.round(kb * 10) / 10}kb`;
55
70
  }
56
- function createSummaryTable(systemText, systemSections, tools, messages) {
71
+ function createSummaryTable(systemText, systemSections, tools, messages, actualMessageTokens) {
57
72
  const table = new Table({
58
- head: ["Component", "Tokens", "Size", "% Used"],
73
+ head: ["Component", "Tokens (est.)", "Size", "% Tokens"],
59
74
  style: { head: ["bold"] },
60
75
  chars: {
61
76
  mid: "\u2500",
@@ -66,52 +81,63 @@ function createSummaryTable(systemText, systemSections, tools, messages) {
66
81
  });
67
82
  const messagesStr = JSON.stringify(messages);
68
83
  const toolsStr = JSON.stringify(tools);
69
- const total = systemText.length + toolsStr.length + messagesStr.length;
70
- const getPercentage = (n) => `${Math.round(n / total * 100)}%`;
84
+ const systemTokens = estimateStringTokens(systemText);
85
+ const toolsTokens = estimateStringTokens(toolsStr);
86
+ const messageTokens = actualMessageTokens > 0 ? actualMessageTokens : estimateStringTokens(messagesStr);
87
+ const totalTokens = systemTokens + toolsTokens + messageTokens;
88
+ const getPercentage = (tokens) => totalTokens > 0 ? `${Math.round(tokens / totalTokens * 100)}%` : "0%";
71
89
  table.push([
72
90
  "System prompt",
73
- formatTokenCount(systemText.length),
91
+ formatTokenCount(systemTokens),
74
92
  formatByteCount(systemText.length),
75
- getPercentage(systemText.length)
93
+ getPercentage(systemTokens)
76
94
  ]);
77
95
  for (const section of systemSections) {
96
+ const sectionTokens = estimateStringTokens(section.content);
78
97
  table.push([
79
98
  ` ${section.title}`,
80
- formatTokenCount(section.content.length),
99
+ formatTokenCount(sectionTokens),
81
100
  formatByteCount(section.content.length),
82
- getPercentage(section.content.length)
101
+ getPercentage(sectionTokens)
83
102
  ]);
84
103
  }
85
104
  table.push([
86
105
  "Tool definitions",
87
- formatTokenCount(toolsStr.length),
106
+ formatTokenCount(toolsTokens),
88
107
  formatByteCount(toolsStr.length),
89
- getPercentage(toolsStr.length)
108
+ getPercentage(toolsTokens)
90
109
  ]);
91
110
  for (const tool of tools) {
111
+ const toolTokens = estimateStringTokens(tool.description);
92
112
  table.push([
93
113
  ` ${tool.name}`,
94
- formatTokenCount(tool.description.length),
114
+ formatTokenCount(toolTokens),
95
115
  formatByteCount(tool.description.length),
96
- getPercentage(tool.description.length)
116
+ getPercentage(toolTokens)
97
117
  ]);
98
118
  }
119
+ const messageLabel = actualMessageTokens > 0 ? "Messages (actual)" : "Messages (est.)";
99
120
  table.push(
100
121
  [
101
- "Messages",
102
- formatTokenCount(messagesStr.length),
122
+ messageLabel,
123
+ formatTokenCount(messageTokens),
103
124
  formatByteCount(messagesStr.length),
104
- getPercentage(messagesStr.length)
125
+ getPercentage(messageTokens)
105
126
  ],
106
- ["Total", formatTokenCount(total), formatByteCount(total), "100%"]
127
+ [
128
+ "Total",
129
+ formatTokenCount(totalTokens),
130
+ formatByteCount(systemText.length + toolsStr.length + messagesStr.length),
131
+ "100%"
132
+ ]
107
133
  );
108
134
  return table.toString();
109
135
  }
110
136
  const command = {
111
137
  name: "ctx-viz",
112
- description: "Show token usage breakdown for the current conversation context",
138
+ description: "[Developer] Show token usage breakdown for the current conversation context",
113
139
  isEnabled: true,
114
- isHidden: false,
140
+ isHidden: true,
115
141
  type: "local",
116
142
  userFacingName() {
117
143
  return this.name;
@@ -141,8 +167,15 @@ ${schema}`
141
167
  };
142
168
  });
143
169
  const messages = getMessagesGetter()();
170
+ const actualMessageTokens = countTokens(messages);
144
171
  const sections = getContextSections(systemPrompt);
145
- return createSummaryTable(systemPrompt, sections, tools, messages);
172
+ return createSummaryTable(
173
+ systemPrompt,
174
+ sections,
175
+ tools,
176
+ messages,
177
+ actualMessageTokens
178
+ );
146
179
  }
147
180
  };
148
181
  var ctx_viz_default = command;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/commands/ctx_viz.ts"],
4
- "sourcesContent": ["import type { Command } from '@commands'\nimport type { Tool } from '@tool'\nimport Table from 'cli-table3'\nimport { getSystemPrompt } from '@constants/prompts'\nimport { getContext } from '@context'\nimport { zodToJsonSchema } from 'zod-to-json-schema'\nimport { getMessagesGetter } from '@messages'\nimport { PROJECT_FILE } from '@constants/product'\n// Quick and dirty estimate of bytes per token for rough token counts\nconst BYTES_PER_TOKEN = 4\n\ninterface Section {\n title: string\n content: string\n}\n\ninterface ToolSummary {\n name: string\n description: string\n}\n\nfunction getContextSections(text: string): Section[] {\n const sections: Section[] = []\n\n // Find first <context> tag\n const firstContextIndex = text.indexOf('<context')\n\n // Everything before first tag is Core Sysprompt\n if (firstContextIndex > 0) {\n const coreSysprompt = text.slice(0, firstContextIndex).trim()\n if (coreSysprompt) {\n sections.push({\n title: 'Core Sysprompt',\n content: coreSysprompt,\n })\n }\n }\n\n let currentPos = firstContextIndex\n let nonContextContent = ''\n\n const regex = /<context\\s+name=\"([^\"]*)\">([\\s\\S]*?)<\\/context>/g\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(text)) !== null) {\n // Collect text between context tags\n if (match.index > currentPos) {\n nonContextContent += text.slice(currentPos, match.index)\n }\n\n const [, name = 'Unnamed Section', content = ''] = match\n sections.push({\n title: name === 'codeStyle' ? `CodeStyle + ${PROJECT_FILE}'s` : name,\n content: content.trim(),\n })\n\n currentPos = match.index + match[0].length\n }\n\n // Collect remaining text after last tag\n if (currentPos < text.length) {\n nonContextContent += text.slice(currentPos)\n }\n\n // Add non-contextualized content if present\n const trimmedNonContext = nonContextContent.trim()\n if (trimmedNonContext) {\n sections.push({\n title: 'Non-contextualized Content',\n content: trimmedNonContext,\n })\n }\n\n return sections\n}\n\nfunction formatTokenCount(bytes: number): string {\n const tokens = bytes / BYTES_PER_TOKEN\n const k = tokens / 1000\n return `${Math.round(k * 10) / 10}k`\n}\n\nfunction formatByteCount(bytes: number): string {\n const kb = bytes / 1024\n return `${Math.round(kb * 10) / 10}kb`\n}\n\nfunction createSummaryTable(\n systemText: string,\n systemSections: Section[],\n tools: ToolSummary[],\n messages: unknown,\n): string {\n const table = new Table({\n head: ['Component', 'Tokens', 'Size', '% Used'],\n style: { head: ['bold'] },\n chars: {\n mid: '\u2500',\n 'left-mid': '\u251C',\n 'mid-mid': '\u253C',\n 'right-mid': '\u2524',\n },\n })\n\n const messagesStr = JSON.stringify(messages)\n const toolsStr = JSON.stringify(tools)\n\n // Calculate total for percentages\n const total = systemText.length + toolsStr.length + messagesStr.length\n const getPercentage = (n: number) => `${Math.round((n / total) * 100)}%`\n\n // System prompt and its sections\n table.push([\n 'System prompt',\n formatTokenCount(systemText.length),\n formatByteCount(systemText.length),\n getPercentage(systemText.length),\n ])\n for (const section of systemSections) {\n table.push([\n ` ${section.title}`,\n formatTokenCount(section.content.length),\n formatByteCount(section.content.length),\n getPercentage(section.content.length),\n ])\n }\n\n // Tools\n table.push([\n 'Tool definitions',\n formatTokenCount(toolsStr.length),\n formatByteCount(toolsStr.length),\n getPercentage(toolsStr.length),\n ])\n for (const tool of tools) {\n table.push([\n ` ${tool.name}`,\n formatTokenCount(tool.description.length),\n formatByteCount(tool.description.length),\n getPercentage(tool.description.length),\n ])\n }\n\n // Messages and total\n table.push(\n [\n 'Messages',\n formatTokenCount(messagesStr.length),\n formatByteCount(messagesStr.length),\n getPercentage(messagesStr.length),\n ],\n ['Total', formatTokenCount(total), formatByteCount(total), '100%'],\n )\n\n return table.toString()\n}\n\nconst command: Command = {\n name: 'ctx-viz',\n description:\n 'Show token usage breakdown for the current conversation context',\n isEnabled: true,\n isHidden: false,\n type: 'local',\n\n userFacingName() {\n return this.name\n },\n\n async call(_args: string, cmdContext: { options: { tools: Tool[] } }) {\n // Get tools and system prompt with injected context\n const [systemPromptRaw, sysContext] = await Promise.all([\n getSystemPrompt(),\n getContext(),\n ])\n\n const rawTools = cmdContext.options.tools\n\n // Full system prompt with context sections injected\n let systemPrompt = systemPromptRaw.join('\\n')\n for (const [name, content] of Object.entries(sysContext)) {\n systemPrompt += `\\n<context name=\"${name}\">${content}</context>`\n }\n\n // Get full tool definitions including prompts and schemas\n const tools = rawTools.map(t => {\n // Get full prompt and schema\n const fullPrompt = t.prompt({ safeMode: false })\n const schema = JSON.stringify(\n 'inputJSONSchema' in t && t.inputJSONSchema\n ? t.inputJSONSchema\n : zodToJsonSchema(t.inputSchema),\n )\n\n return {\n name: t.name,\n description: `${fullPrompt}\\n\\nSchema:\\n${schema}`,\n }\n })\n\n // Get current messages from REPL\n const messages = getMessagesGetter()()\n\n const sections = getContextSections(systemPrompt)\n return createSummaryTable(systemPrompt, sections, tools, messages)\n },\n}\n\nexport default command\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Command } from '@commands'\nimport type { Tool } from '@tool'\nimport Table from 'cli-table3'\nimport { getSystemPrompt } from '@constants/prompts'\nimport { getContext } from '@context'\nimport { zodToJsonSchema } from 'zod-to-json-schema'\nimport { getMessagesGetter } from '@messages'\nimport { PROJECT_FILE } from '@constants/product'\nimport { countTokens } from '@utils/tokens'\n\n/**\n * Estimate token count for a string.\n * Uses a heuristic that accounts for different character types:\n * - ASCII/Latin: ~4 characters per token\n * - CJK (Chinese/Japanese/Korean): ~1.5 characters per token\n * - Mixed content: weighted average\n */\nfunction estimateStringTokens(text: string): number {\n if (!text) return 0\n\n // Count CJK characters (Chinese, Japanese, Korean)\n const cjkPattern = /[\\u4e00-\\u9fff\\u3040-\\u309f\\u30a0-\\u30ff\\uac00-\\ud7af]/g\n const cjkMatches = text.match(cjkPattern)\n const cjkCount = cjkMatches ? cjkMatches.length : 0\n const nonCjkCount = text.length - cjkCount\n\n // Estimate tokens: CJK ~1.5 chars/token, ASCII ~4 chars/token\n const cjkTokens = cjkCount / 1.5\n const nonCjkTokens = nonCjkCount / 4\n\n return Math.ceil(cjkTokens + nonCjkTokens)\n}\n\ninterface Section {\n title: string\n content: string\n}\n\ninterface ToolSummary {\n name: string\n description: string\n}\n\nfunction getContextSections(text: string): Section[] {\n const sections: Section[] = []\n\n // Find first <context> tag\n const firstContextIndex = text.indexOf('<context')\n\n // Everything before first tag is Core Sysprompt\n if (firstContextIndex > 0) {\n const coreSysprompt = text.slice(0, firstContextIndex).trim()\n if (coreSysprompt) {\n sections.push({\n title: 'Core Sysprompt',\n content: coreSysprompt,\n })\n }\n }\n\n let currentPos = firstContextIndex\n let nonContextContent = ''\n\n const regex = /<context\\s+name=\"([^\"]*)\">([\\s\\S]*?)<\\/context>/g\n let match: RegExpExecArray | null\n\n while ((match = regex.exec(text)) !== null) {\n // Collect text between context tags\n if (match.index > currentPos) {\n nonContextContent += text.slice(currentPos, match.index)\n }\n\n const [, name = 'Unnamed Section', content = ''] = match\n sections.push({\n title: name === 'codeStyle' ? `CodeStyle + ${PROJECT_FILE}'s` : name,\n content: content.trim(),\n })\n\n currentPos = match.index + match[0].length\n }\n\n // Collect remaining text after last tag\n if (currentPos < text.length) {\n nonContextContent += text.slice(currentPos)\n }\n\n // Add non-contextualized content if present\n const trimmedNonContext = nonContextContent.trim()\n if (trimmedNonContext) {\n sections.push({\n title: 'Non-contextualized Content',\n content: trimmedNonContext,\n })\n }\n\n return sections\n}\n\nfunction formatTokenCount(tokens: number): string {\n if (tokens < 1000) {\n return `${tokens}`\n }\n const k = tokens / 1000\n return `${Math.round(k * 10) / 10}k`\n}\n\nfunction formatByteCount(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes}b`\n }\n const kb = bytes / 1024\n return `${Math.round(kb * 10) / 10}kb`\n}\n\nfunction createSummaryTable(\n systemText: string,\n systemSections: Section[],\n tools: ToolSummary[],\n messages: unknown,\n actualMessageTokens: number,\n): string {\n const table = new Table({\n head: ['Component', 'Tokens (est.)', 'Size', '% Tokens'],\n style: { head: ['bold'] },\n chars: {\n mid: '\u2500',\n 'left-mid': '\u251C',\n 'mid-mid': '\u253C',\n 'right-mid': '\u2524',\n },\n })\n\n const messagesStr = JSON.stringify(messages)\n const toolsStr = JSON.stringify(tools)\n\n // Estimate tokens for each component\n const systemTokens = estimateStringTokens(systemText)\n const toolsTokens = estimateStringTokens(toolsStr)\n // Use actual token count for messages if available, otherwise estimate\n const messageTokens =\n actualMessageTokens > 0\n ? actualMessageTokens\n : estimateStringTokens(messagesStr)\n\n // Calculate total tokens for percentages\n const totalTokens = systemTokens + toolsTokens + messageTokens\n const getPercentage = (tokens: number) =>\n totalTokens > 0 ? `${Math.round((tokens / totalTokens) * 100)}%` : '0%'\n\n // System prompt and its sections\n table.push([\n 'System prompt',\n formatTokenCount(systemTokens),\n formatByteCount(systemText.length),\n getPercentage(systemTokens),\n ])\n for (const section of systemSections) {\n const sectionTokens = estimateStringTokens(section.content)\n table.push([\n ` ${section.title}`,\n formatTokenCount(sectionTokens),\n formatByteCount(section.content.length),\n getPercentage(sectionTokens),\n ])\n }\n\n // Tools\n table.push([\n 'Tool definitions',\n formatTokenCount(toolsTokens),\n formatByteCount(toolsStr.length),\n getPercentage(toolsTokens),\n ])\n for (const tool of tools) {\n const toolTokens = estimateStringTokens(tool.description)\n table.push([\n ` ${tool.name}`,\n formatTokenCount(toolTokens),\n formatByteCount(tool.description.length),\n getPercentage(toolTokens),\n ])\n }\n\n // Messages and total\n const messageLabel =\n actualMessageTokens > 0 ? 'Messages (actual)' : 'Messages (est.)'\n table.push(\n [\n messageLabel,\n formatTokenCount(messageTokens),\n formatByteCount(messagesStr.length),\n getPercentage(messageTokens),\n ],\n [\n 'Total',\n formatTokenCount(totalTokens),\n formatByteCount(systemText.length + toolsStr.length + messagesStr.length),\n '100%',\n ],\n )\n\n return table.toString()\n}\n\nconst command: Command = {\n name: 'ctx-viz',\n description:\n '[Developer] Show token usage breakdown for the current conversation context',\n isEnabled: true,\n isHidden: true,\n type: 'local',\n\n userFacingName() {\n return this.name\n },\n\n async call(_args: string, cmdContext: { options: { tools: Tool[] } }) {\n // Get tools and system prompt with injected context\n const [systemPromptRaw, sysContext] = await Promise.all([\n getSystemPrompt(),\n getContext(),\n ])\n\n const rawTools = cmdContext.options.tools\n\n // Full system prompt with context sections injected\n let systemPrompt = systemPromptRaw.join('\\n')\n for (const [name, content] of Object.entries(sysContext)) {\n systemPrompt += `\\n<context name=\"${name}\">${content}</context>`\n }\n\n // Get full tool definitions including prompts and schemas\n const tools = rawTools.map(t => {\n // Get full prompt and schema\n const fullPrompt = t.prompt({ safeMode: false })\n const schema = JSON.stringify(\n 'inputJSONSchema' in t && t.inputJSONSchema\n ? t.inputJSONSchema\n : zodToJsonSchema(t.inputSchema),\n )\n\n return {\n name: t.name,\n description: `${fullPrompt}\\n\\nSchema:\\n${schema}`,\n }\n })\n\n // Get current messages from REPL\n const messages = getMessagesGetter()()\n\n // Get actual token count from API usage if available\n const actualMessageTokens = countTokens(messages)\n\n const sections = getContextSections(systemPrompt)\n return createSummaryTable(\n systemPrompt,\n sections,\n tools,\n messages,\n actualMessageTokens,\n )\n },\n}\n\nexport default command\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -9,7 +9,10 @@ import {
9
9
  getMcpServer
10
10
  } from "../services/mcpClient.js";
11
11
  import { SimpleSpinner } from "../components/Spinner.js";
12
- import { getCurrentProjectConfig, saveCurrentProjectConfig } from "../utils/config.js";
12
+ import {
13
+ getCurrentProjectConfig,
14
+ saveCurrentProjectConfig
15
+ } from "../utils/config.js";
13
16
  const MainMenu = ({ onNavigate, onExit }) => {
14
17
  const theme = getTheme();
15
18
  const options = [
@@ -68,9 +71,12 @@ const ServerList = ({ onNavigate, onBack }) => {
68
71
  };
69
72
  loadServers();
70
73
  }, []);
71
- const handleServerSelect = useCallback((serverName) => {
72
- onNavigate({ screen: "server-details", serverName });
73
- }, [onNavigate]);
74
+ const handleServerSelect = useCallback(
75
+ (serverName) => {
76
+ onNavigate({ screen: "server-details", serverName });
77
+ },
78
+ [onNavigate]
79
+ );
74
80
  useInput((input, key) => {
75
81
  if (key.escape) onBack();
76
82
  });
@@ -97,13 +103,7 @@ const ServerList = ({ onNavigate, onBack }) => {
97
103
  padding: 1
98
104
  },
99
105
  /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: theme.primary }, "MCP Servers")),
100
- /* @__PURE__ */ React.createElement(
101
- Select,
102
- {
103
- options,
104
- onChange: handleServerSelect
105
- }
106
- ),
106
+ /* @__PURE__ */ React.createElement(Select, { options, onChange: handleServerSelect }),
107
107
  /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Press ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "ESC"), " to go back"))
108
108
  );
109
109
  };