@within-7/minto 0.3.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/dist/Tool.js.map +2 -2
  2. package/dist/commands/agents/AgentsCommand.js +461 -657
  3. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  4. package/dist/commands/agents/types.js +1 -0
  5. package/dist/commands/agents/types.js.map +2 -2
  6. package/dist/commands/agents/utils/fileOperations.js +96 -36
  7. package/dist/commands/agents/utils/fileOperations.js.map +3 -3
  8. package/dist/commands/agents/utils/index.js +3 -1
  9. package/dist/commands/agents/utils/index.js.map +2 -2
  10. package/dist/commands/context.js +54 -23
  11. package/dist/commands/context.js.map +2 -2
  12. package/dist/commands/ctx_viz.js +1 -1
  13. package/dist/commands/effort.js +87 -0
  14. package/dist/commands/effort.js.map +7 -0
  15. package/dist/commands/export.js +684 -94
  16. package/dist/commands/export.js.map +2 -2
  17. package/dist/commands/ide.js +18 -0
  18. package/dist/commands/ide.js.map +7 -0
  19. package/dist/commands/language.js +19 -46
  20. package/dist/commands/language.js.map +2 -2
  21. package/dist/commands/mcp-interactive.js +425 -217
  22. package/dist/commands/mcp-interactive.js.map +2 -2
  23. package/dist/commands/memory.js +168 -0
  24. package/dist/commands/memory.js.map +7 -0
  25. package/dist/commands/model.js +457 -65
  26. package/dist/commands/model.js.map +2 -2
  27. package/dist/commands/outputStyle.js +64 -0
  28. package/dist/commands/outputStyle.js.map +7 -0
  29. package/dist/commands/permissions.js +75 -49
  30. package/dist/commands/permissions.js.map +2 -2
  31. package/dist/commands/plugin/utils.js +33 -1
  32. package/dist/commands/plugin/utils.js.map +2 -2
  33. package/dist/commands/plugin.js +891 -185
  34. package/dist/commands/plugin.js.map +3 -3
  35. package/dist/commands/refreshCommands.js +2 -0
  36. package/dist/commands/refreshCommands.js.map +2 -2
  37. package/dist/commands/resume.js +1 -1
  38. package/dist/commands/resume.js.map +1 -1
  39. package/dist/commands/review.js +51 -0
  40. package/dist/commands/review.js.map +7 -0
  41. package/dist/commands/sandbox.js +168 -70
  42. package/dist/commands/sandbox.js.map +2 -2
  43. package/dist/commands/setup.js +593 -107
  44. package/dist/commands/setup.js.map +2 -2
  45. package/dist/commands/stats.js +188 -131
  46. package/dist/commands/stats.js.map +2 -2
  47. package/dist/commands/status.js +75 -13
  48. package/dist/commands/status.js.map +2 -2
  49. package/dist/commands/terminalSetup.js +6 -0
  50. package/dist/commands/terminalSetup.js.map +2 -2
  51. package/dist/commands/undo.js +146 -174
  52. package/dist/commands/undo.js.map +2 -2
  53. package/dist/commands/vim.js +22 -0
  54. package/dist/commands/vim.js.map +7 -0
  55. package/dist/commands.js +12 -0
  56. package/dist/commands.js.map +2 -2
  57. package/dist/components/Help.js +165 -32
  58. package/dist/components/Help.js.map +2 -2
  59. package/dist/components/HighlightedCode.js +1 -0
  60. package/dist/components/HighlightedCode.js.map +2 -2
  61. package/dist/components/InfoPanel/InfoPanel.js +123 -0
  62. package/dist/components/InfoPanel/InfoPanel.js.map +7 -0
  63. package/dist/components/InfoPanel/index.js +5 -0
  64. package/dist/components/InfoPanel/index.js.map +7 -0
  65. package/dist/components/InfoPanel/types.js +1 -0
  66. package/dist/components/InfoPanel/types.js.map +7 -0
  67. package/dist/components/ModelSelector/BrandTextInput.js +43 -0
  68. package/dist/components/ModelSelector/BrandTextInput.js.map +7 -0
  69. package/dist/components/ModelSelector/ModelSelector.js +590 -565
  70. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  71. package/dist/components/ModelSelector/WizardContainer.js +45 -0
  72. package/dist/components/ModelSelector/WizardContainer.js.map +7 -0
  73. package/dist/components/ModelSelector/index.js +1 -3
  74. package/dist/components/ModelSelector/index.js.map +2 -2
  75. package/dist/components/PromptInput.js +26 -11
  76. package/dist/components/PromptInput.js.map +2 -2
  77. package/dist/components/PulseLabel.js +44 -0
  78. package/dist/components/PulseLabel.js.map +7 -0
  79. package/dist/components/RequestStatusIndicator.js +1 -1
  80. package/dist/components/RequestStatusIndicator.js.map +1 -1
  81. package/dist/components/SimpleSelector/SimpleSelector.js +154 -0
  82. package/dist/components/SimpleSelector/SimpleSelector.js.map +7 -0
  83. package/dist/components/SimpleSelector/index.js +5 -0
  84. package/dist/components/SimpleSelector/index.js.map +7 -0
  85. package/dist/components/SimpleSelector/types.js +1 -0
  86. package/dist/components/SimpleSelector/types.js.map +7 -0
  87. package/dist/components/Spinner.js +12 -42
  88. package/dist/components/Spinner.js.map +3 -3
  89. package/dist/components/StartupStatus.js +57 -0
  90. package/dist/components/StartupStatus.js.map +7 -0
  91. package/dist/components/StatusOverlayContent.js +21 -0
  92. package/dist/components/StatusOverlayContent.js.map +7 -0
  93. package/dist/components/SubagentBlock.js +43 -6
  94. package/dist/components/SubagentBlock.js.map +2 -2
  95. package/dist/components/TabbedListView/ScrollableList.js +31 -5
  96. package/dist/components/TabbedListView/ScrollableList.js.map +2 -2
  97. package/dist/components/TabbedListView/TabBar.js +13 -8
  98. package/dist/components/TabbedListView/TabBar.js.map +2 -2
  99. package/dist/components/TabbedListView/TabbedListView.js +123 -48
  100. package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
  101. package/dist/components/TodoPanel.js +1 -1
  102. package/dist/components/TodoPanel.js.map +1 -1
  103. package/dist/components/ToolUseLoader.js +5 -0
  104. package/dist/components/ToolUseLoader.js.map +2 -2
  105. package/dist/components/TrustDialog.js +0 -2
  106. package/dist/components/TrustDialog.js.map +2 -2
  107. package/dist/components/messages/TaskInModuleView.js +1 -1
  108. package/dist/components/messages/TaskInModuleView.js.map +2 -2
  109. package/dist/components/messages/TaskToolMessage.js +1 -1
  110. package/dist/components/messages/TaskToolMessage.js.map +2 -2
  111. package/dist/components/messages/UserPromptMessage.js +6 -1
  112. package/dist/components/messages/UserPromptMessage.js.map +2 -2
  113. package/dist/constants/modelCapabilities.js +103 -18
  114. package/dist/constants/modelCapabilities.js.map +2 -2
  115. package/dist/constants/product.js +2 -0
  116. package/dist/constants/product.js.map +2 -2
  117. package/dist/constants/prompts/agentPrompt.js +30 -0
  118. package/dist/constants/prompts/agentPrompt.js.map +7 -0
  119. package/dist/constants/prompts/codeConventions.js +27 -0
  120. package/dist/constants/prompts/codeConventions.js.map +7 -0
  121. package/dist/constants/prompts/doingTasks.js +15 -0
  122. package/dist/constants/prompts/doingTasks.js.map +7 -0
  123. package/dist/constants/prompts/envInfo.js +17 -0
  124. package/dist/constants/prompts/envInfo.js.map +7 -0
  125. package/dist/constants/prompts/executingWithCare.js +17 -0
  126. package/dist/constants/prompts/executingWithCare.js.map +7 -0
  127. package/dist/constants/prompts/identity.js +10 -0
  128. package/dist/constants/prompts/identity.js.map +7 -0
  129. package/dist/constants/prompts/index.js +78 -0
  130. package/dist/constants/prompts/index.js.map +7 -0
  131. package/dist/constants/prompts/taskManagement.js +60 -0
  132. package/dist/constants/prompts/taskManagement.js.map +7 -0
  133. package/dist/constants/prompts/toneAndStyle.js +62 -0
  134. package/dist/constants/prompts/toneAndStyle.js.map +7 -0
  135. package/dist/constants/prompts/toolUsagePolicy.js +38 -0
  136. package/dist/constants/prompts/toolUsagePolicy.js.map +7 -0
  137. package/dist/constants/prompts.js +5 -176
  138. package/dist/constants/prompts.js.map +2 -2
  139. package/dist/constants/providerRegistry.js +235 -0
  140. package/dist/constants/providerRegistry.js.map +7 -0
  141. package/dist/constants/providers.js +35 -0
  142. package/dist/constants/providers.js.map +7 -0
  143. package/dist/context/PermissionContext.js +0 -1
  144. package/dist/context/PermissionContext.js.map +2 -2
  145. package/dist/context.js +87 -31
  146. package/dist/context.js.map +2 -2
  147. package/dist/core/backupHook.js +29 -0
  148. package/dist/core/backupHook.js.map +7 -0
  149. package/dist/core/config/defaults.js +11 -2
  150. package/dist/core/config/defaults.js.map +2 -2
  151. package/dist/core/config/schema.js +21 -3
  152. package/dist/core/config/schema.js.map +2 -2
  153. package/dist/core/costTracker.js +18 -16
  154. package/dist/core/costTracker.js.map +2 -2
  155. package/dist/core/index.js +0 -1
  156. package/dist/core/index.js.map +2 -2
  157. package/dist/core/tokenStatsManager.js +22 -4
  158. package/dist/core/tokenStatsManager.js.map +2 -2
  159. package/dist/cost-tracker.js +0 -16
  160. package/dist/cost-tracker.js.map +2 -2
  161. package/dist/entrypoints/bootstrap.js +3 -1
  162. package/dist/entrypoints/bootstrap.js.map +2 -2
  163. package/dist/entrypoints/cli.js +81 -68
  164. package/dist/entrypoints/cli.js.map +2 -2
  165. package/dist/hooks/useAgentTokenStats.js +1 -1
  166. package/dist/hooks/useAgentTokenStats.js.map +2 -2
  167. package/dist/hooks/useAgentTranscripts.js +2 -1
  168. package/dist/hooks/useAgentTranscripts.js.map +2 -2
  169. package/dist/hooks/useBackgroundShells.js +29 -0
  170. package/dist/hooks/useBackgroundShells.js.map +7 -0
  171. package/dist/hooks/useCanUseTool.js +1 -1
  172. package/dist/hooks/useCanUseTool.js.map +2 -2
  173. package/dist/hooks/useDeferredLoading.js +64 -0
  174. package/dist/hooks/useDeferredLoading.js.map +7 -0
  175. package/dist/hooks/useHookStatus.js +1 -1
  176. package/dist/hooks/useHookStatus.js.map +2 -2
  177. package/dist/hooks/useSessionTracking.js +55 -0
  178. package/dist/hooks/useSessionTracking.js.map +7 -0
  179. package/dist/hooks/useTerminalSize.js +21 -0
  180. package/dist/hooks/useTerminalSize.js.map +2 -2
  181. package/dist/hooks/useTextInput.js +1 -0
  182. package/dist/hooks/useTextInput.js.map +2 -2
  183. package/dist/hooks/useUnifiedCompletion.js +3 -2
  184. package/dist/hooks/useUnifiedCompletion.js.map +2 -2
  185. package/dist/i18n/locales/en.js +299 -1
  186. package/dist/i18n/locales/en.js.map +2 -2
  187. package/dist/i18n/locales/zh-CN.js +300 -2
  188. package/dist/i18n/locales/zh-CN.js.map +2 -2
  189. package/dist/i18n/types.js.map +1 -1
  190. package/dist/messages.js +41 -17
  191. package/dist/messages.js.map +2 -2
  192. package/dist/permissions.js +94 -1
  193. package/dist/permissions.js.map +2 -2
  194. package/dist/query.js +27 -19
  195. package/dist/query.js.map +2 -2
  196. package/dist/screens/REPL.js +83 -74
  197. package/dist/screens/REPL.js.map +2 -2
  198. package/dist/services/adapters/responsesAPI.js +6 -0
  199. package/dist/services/adapters/responsesAPI.js.map +2 -2
  200. package/dist/services/agentTeams/index.js +35 -0
  201. package/dist/services/agentTeams/index.js.map +7 -0
  202. package/dist/services/agentTeams/mailbox.js +114 -0
  203. package/dist/services/agentTeams/mailbox.js.map +7 -0
  204. package/dist/services/agentTeams/teamManager.js +149 -0
  205. package/dist/services/agentTeams/teamManager.js.map +7 -0
  206. package/dist/services/agentTeams/teamTaskStore.js +114 -0
  207. package/dist/services/agentTeams/teamTaskStore.js.map +7 -0
  208. package/dist/services/agentTeams/teammateSpawner.js +80 -0
  209. package/dist/services/agentTeams/teammateSpawner.js.map +7 -0
  210. package/dist/services/checkpointManager.js +16 -3
  211. package/dist/services/checkpointManager.js.map +2 -2
  212. package/dist/services/claude.js +19 -1728
  213. package/dist/services/claude.js.map +3 -3
  214. package/dist/services/customCommands.js +30 -8
  215. package/dist/services/customCommands.js.map +2 -2
  216. package/dist/services/gpt5ConnectionTest.js +4 -2
  217. package/dist/services/gpt5ConnectionTest.js.map +2 -2
  218. package/dist/services/hookExecutor.js +411 -127
  219. package/dist/services/hookExecutor.js.map +2 -2
  220. package/dist/services/llm/anthropicProvider.js +807 -0
  221. package/dist/services/llm/anthropicProvider.js.map +7 -0
  222. package/dist/services/llm/dispatch.js +218 -0
  223. package/dist/services/llm/dispatch.js.map +7 -0
  224. package/dist/services/llm/index.js +44 -0
  225. package/dist/services/llm/index.js.map +7 -0
  226. package/dist/services/llm/mintoContext.js +69 -0
  227. package/dist/services/llm/mintoContext.js.map +7 -0
  228. package/dist/services/llm/openaiProvider.js +622 -0
  229. package/dist/services/llm/openaiProvider.js.map +7 -0
  230. package/dist/services/llm/types.js +157 -0
  231. package/dist/services/llm/types.js.map +7 -0
  232. package/dist/services/mcpClient.js +183 -33
  233. package/dist/services/mcpClient.js.map +2 -2
  234. package/dist/services/notifier.js +14 -0
  235. package/dist/services/notifier.js.map +2 -2
  236. package/dist/services/oauth.js +4 -2
  237. package/dist/services/oauth.js.map +2 -2
  238. package/dist/services/openai.js +66 -56
  239. package/dist/services/openai.js.map +3 -3
  240. package/dist/services/outputStyles.js +102 -21
  241. package/dist/services/outputStyles.js.map +2 -2
  242. package/dist/services/plugins/lspServers.js +1 -1
  243. package/dist/services/plugins/lspServers.js.map +2 -2
  244. package/dist/services/plugins/pluginRuntime.js +2 -1
  245. package/dist/services/plugins/pluginRuntime.js.map +2 -2
  246. package/dist/services/plugins/pluginValidation.js +10 -3
  247. package/dist/services/plugins/pluginValidation.js.map +2 -2
  248. package/dist/services/plugins/skillMarketplace.js +20 -9
  249. package/dist/services/plugins/skillMarketplace.js.map +2 -2
  250. package/dist/services/sentry.js +1 -1
  251. package/dist/services/sentry.js.map +2 -2
  252. package/dist/services/sessionMemory.js +16 -3
  253. package/dist/services/sessionMemory.js.map +2 -2
  254. package/dist/services/systemReminder.js +367 -9
  255. package/dist/services/systemReminder.js.map +2 -2
  256. package/dist/services/taskStore.js +19 -0
  257. package/dist/services/taskStore.js.map +2 -2
  258. package/dist/tools/ArchitectTool/ArchitectTool.js.map +1 -1
  259. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +1 -1
  260. package/dist/tools/BashOutputTool/BashOutputTool.js.map +1 -1
  261. package/dist/tools/BashTool/BashTool.js +28 -0
  262. package/dist/tools/BashTool/BashTool.js.map +2 -2
  263. package/dist/tools/FileEditTool/FileEditTool.js +8 -1
  264. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  265. package/dist/tools/FileReadTool/FileReadTool.js +14 -0
  266. package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
  267. package/dist/tools/FileWriteTool/FileWriteTool.js +10 -1
  268. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  269. package/dist/tools/GlobTool/GlobTool.js.map +1 -1
  270. package/dist/tools/GrepTool/GrepTool.js.map +1 -1
  271. package/dist/tools/KillShellTool/KillShellTool.js.map +1 -1
  272. package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +2 -2
  273. package/dist/tools/LspTool/LspTool.js +11 -2
  274. package/dist/tools/LspTool/LspTool.js.map +2 -2
  275. package/dist/tools/MCPTool/MCPTool.js.map +1 -1
  276. package/dist/tools/MemoryReadTool/MemoryReadTool.js +2 -1
  277. package/dist/tools/MemoryReadTool/MemoryReadTool.js.map +2 -2
  278. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js +2 -1
  279. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js.map +2 -2
  280. package/dist/tools/MultiEditTool/MultiEditTool.js +7 -0
  281. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  282. package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -0
  283. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  284. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +1 -1
  285. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +8 -2
  286. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
  287. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +2 -0
  288. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
  289. package/dist/tools/ReadMcpResourceTool/ReadMcpResourceTool.js.map +1 -1
  290. package/dist/tools/SlashCommandTool/SlashCommandTool.js +174 -18
  291. package/dist/tools/SlashCommandTool/SlashCommandTool.js.map +3 -3
  292. package/dist/tools/TaskCreateTool/TaskCreateTool.js.map +1 -1
  293. package/dist/tools/TaskGetTool/TaskGetTool.js.map +1 -1
  294. package/dist/tools/TaskListTool/TaskListTool.js.map +1 -1
  295. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +1 -1
  296. package/dist/tools/TaskStopTool/TaskStopTool.js.map +1 -1
  297. package/dist/tools/TaskTool/TaskTool.js +84 -11
  298. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  299. package/dist/tools/TaskTool/prompt.js +12 -6
  300. package/dist/tools/TaskTool/prompt.js.map +2 -2
  301. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +1 -1
  302. package/dist/tools/ThinkTool/ThinkTool.js.map +1 -1
  303. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +1 -1
  304. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +1 -1
  305. package/dist/tools/WebSearchTool/WebSearchTool.js.map +1 -1
  306. package/dist/tools/WebSearchTool/searchProviders.js +2 -1
  307. package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
  308. package/dist/tools/lsTool/lsTool.js.map +2 -2
  309. package/dist/tools/lsTool/prompt.js.map +1 -1
  310. package/dist/tools.js +14 -3
  311. package/dist/tools.js.map +2 -2
  312. package/dist/types/PermissionMode.js +21 -1
  313. package/dist/types/PermissionMode.js.map +2 -2
  314. package/dist/types/agentTeams.js +1 -0
  315. package/dist/types/agentTeams.js.map +7 -0
  316. package/dist/types/hooks.js +8 -2
  317. package/dist/types/hooks.js.map +2 -2
  318. package/dist/types/plugin.js +3 -5
  319. package/dist/types/plugin.js.map +2 -2
  320. package/dist/utils/agentHookExecutor.js +1 -4
  321. package/dist/utils/agentHookExecutor.js.map +2 -2
  322. package/dist/utils/agentLoader.js +91 -15
  323. package/dist/utils/agentLoader.js.map +2 -2
  324. package/dist/utils/agentMemory.js.map +2 -2
  325. package/dist/utils/animationManager.js +1 -1
  326. package/dist/utils/animationManager.js.map +2 -2
  327. package/dist/utils/ask.js +1 -1
  328. package/dist/utils/async.js +5 -1
  329. package/dist/utils/async.js.map +2 -2
  330. package/dist/utils/autoCompactCore.js +60 -0
  331. package/dist/utils/autoCompactCore.js.map +2 -2
  332. package/dist/utils/claudeCodeSync.js +439 -0
  333. package/dist/utils/claudeCodeSync.js.map +7 -0
  334. package/dist/utils/config.js +27 -151
  335. package/dist/utils/config.js.map +2 -2
  336. package/dist/utils/configSchema.js +227 -0
  337. package/dist/utils/configSchema.js.map +7 -0
  338. package/dist/utils/debugLogger.js.map +2 -2
  339. package/dist/utils/env.js +4 -3
  340. package/dist/utils/env.js.map +2 -2
  341. package/dist/utils/envConfig.js +34 -0
  342. package/dist/utils/envConfig.js.map +3 -3
  343. package/dist/utils/execFileNoThrow.js +2 -1
  344. package/dist/utils/execFileNoThrow.js.map +2 -2
  345. package/dist/utils/gpt5.js +146 -0
  346. package/dist/utils/gpt5.js.map +7 -0
  347. package/dist/utils/hookManager.js +374 -140
  348. package/dist/utils/hookManager.js.map +2 -2
  349. package/dist/utils/markdown.js +47 -0
  350. package/dist/utils/markdown.js.map +2 -2
  351. package/dist/utils/marketplaceManager.js +80 -43
  352. package/dist/utils/marketplaceManager.js.map +2 -2
  353. package/dist/utils/memoizeWithTTL.js +25 -0
  354. package/dist/utils/memoizeWithTTL.js.map +7 -0
  355. package/dist/utils/messages.js +2 -2
  356. package/dist/utils/messages.js.map +2 -2
  357. package/dist/utils/model.js +34 -9
  358. package/dist/utils/model.js.map +2 -2
  359. package/dist/utils/pluginInstaller.js +68 -29
  360. package/dist/utils/pluginInstaller.js.map +2 -2
  361. package/dist/utils/pluginLoader.js +249 -57
  362. package/dist/utils/pluginLoader.js.map +2 -2
  363. package/dist/utils/repoFetcher.js +110 -0
  364. package/dist/utils/repoFetcher.js.map +7 -0
  365. package/dist/utils/safeFetch.js +45 -0
  366. package/dist/utils/safeFetch.js.map +7 -0
  367. package/dist/utils/skillLoader.js +77 -12
  368. package/dist/utils/skillLoader.js.map +2 -2
  369. package/dist/utils/streamingState.js +52 -0
  370. package/dist/utils/streamingState.js.map +7 -0
  371. package/dist/utils/stringSubstitution.js +4 -5
  372. package/dist/utils/stringSubstitution.js.map +2 -2
  373. package/dist/utils/style.js +6 -3
  374. package/dist/utils/style.js.map +2 -2
  375. package/dist/utils/teamConfig.js +162 -16
  376. package/dist/utils/teamConfig.js.map +2 -2
  377. package/dist/utils/terminal.js +1 -1
  378. package/dist/utils/terminal.js.map +2 -2
  379. package/dist/utils/toolRiskClassification.js +0 -6
  380. package/dist/utils/toolRiskClassification.js.map +2 -2
  381. package/dist/version.js +2 -2
  382. package/dist/version.js.map +1 -1
  383. package/package.json +7 -6
@@ -1,5 +1,6 @@
1
1
  import { EventEmitter } from "events";
2
2
  import { DEFAULT_ESTIMATION_CONFIG } from "./tokenStats.js";
3
+ import { emitReminderEvent } from "../services/systemReminder.js";
3
4
  class TokenStatsManagerImpl extends EventEmitter {
4
5
  static instance = null;
5
6
  // Global aggregated stats
@@ -306,16 +307,33 @@ class TokenStatsManagerImpl extends EventEmitter {
306
307
  this.globalStats = this.createEmptyStats();
307
308
  this.agentStats.clear();
308
309
  this.requestRecords.clear();
310
+ emittedTokenThresholds.clear();
309
311
  }
310
312
  }
311
313
  const tokenStatsManager = TokenStatsManagerImpl.getInstance();
314
+ const TOKEN_THRESHOLDS = [50, 75, 90];
315
+ const emittedTokenThresholds = /* @__PURE__ */ new Set();
316
+ const DEFAULT_CONTEXT_LIMIT = 2e5;
312
317
  function recordTokenUsage(usage, costUSD, model, context) {
313
318
  tokenStatsManager.recordUsage(usage, costUSD, model, context);
314
- try {
315
- const { recordMessage } = require("@utils/stats");
316
- recordMessage(model, usage.inputTokens, usage.outputTokens, costUSD);
317
- } catch {
319
+ const globalStats = tokenStatsManager.getGlobalStats();
320
+ const total = globalStats.grandTotalTokens;
321
+ const percentage = Math.round(total / DEFAULT_CONTEXT_LIMIT * 100);
322
+ for (const threshold of TOKEN_THRESHOLDS) {
323
+ if (percentage >= threshold && !emittedTokenThresholds.has(threshold)) {
324
+ emittedTokenThresholds.add(threshold);
325
+ emitReminderEvent("token:usage", {
326
+ used: total,
327
+ total: DEFAULT_CONTEXT_LIMIT,
328
+ percentage,
329
+ threshold
330
+ });
331
+ }
318
332
  }
333
+ import("../utils/stats.js").then(({ recordMessage }) => {
334
+ recordMessage(model, usage.inputTokens, usage.outputTokens, costUSD);
335
+ }).catch(() => {
336
+ });
319
337
  }
320
338
  function getGlobalTokenStats() {
321
339
  return tokenStatsManager.getGlobalStats();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/core/tokenStatsManager.ts"],
4
- "sourcesContent": ["/**\n * Token Statistics Manager\n *\n * Centralized token statistics tracking for the entire application.\n * This is the single source of truth for all token usage data.\n *\n * Architecture:\n * - Singleton pattern for global access\n * - Event-driven updates for real-time UI\n * - Hierarchical aggregation (global \u2192 agent \u2192 request)\n * - Backward compatible with existing costTracker\n *\n * Usage:\n * ```typescript\n * import { tokenStatsManager, recordTokenUsage } from '@core/tokenStatsManager'\n *\n * // Record usage from API response\n * recordTokenUsage(\n * { inputTokens: 100, outputTokens: 50 },\n * 0.0015,\n * 'gpt-4',\n * { agentId: 'agent-123' }\n * )\n *\n * // Get global stats\n * const stats = tokenStatsManager.getGlobalStats()\n *\n * // Subscribe to updates\n * const unsubscribe = tokenStatsManager.onStatsUpdate((event) => {\n * console.log('Stats updated:', event)\n * })\n * ```\n */\n\nimport { EventEmitter } from 'events'\nimport type {\n TokenUsageRecord,\n AggregatedTokenStats,\n TokenStatsScope,\n TokenStatsEvent,\n TokenTrackingContext,\n RawTokenUsage,\n ModelTokenStats,\n} from './tokenStats'\nimport { DEFAULT_ESTIMATION_CONFIG } from './tokenStats'\n\n/**\n * TokenStatsManager - Centralized token statistics tracking\n */\nclass TokenStatsManagerImpl extends EventEmitter {\n private static instance: TokenStatsManagerImpl | null = null\n\n // Global aggregated stats\n private globalStats: AggregatedTokenStats = this.createEmptyStats()\n\n // Per-agent stats (agentId \u2192 stats)\n private agentStats: Map<string, AggregatedTokenStats> = new Map()\n\n // Per-request records (requestId \u2192 record) - for debugging, with LRU eviction\n private requestRecords: Map<string, TokenUsageRecord> = new Map()\n\n // Configuration\n private static readonly MAX_REQUEST_RECORDS = 1000\n\n private constructor() {\n super()\n this.setMaxListeners(100)\n }\n\n /**\n * Get the singleton instance\n */\n static getInstance(): TokenStatsManagerImpl {\n if (!TokenStatsManagerImpl.instance) {\n TokenStatsManagerImpl.instance = new TokenStatsManagerImpl()\n }\n return TokenStatsManagerImpl.instance\n }\n\n /**\n * Record token usage from an API response\n *\n * This is the PRIMARY entry point - called from claude.ts\n * after every API response.\n *\n * @param usage - Raw token usage from API\n * @param costUSD - Calculated cost in USD\n * @param model - Model name used\n * @param context - Optional tracking context (agentId, toolUseId)\n */\n recordUsage(\n usage: RawTokenUsage,\n costUSD: number,\n model: string,\n context?: TokenTrackingContext,\n ): void {\n const record: TokenUsageRecord = {\n inputTokens: usage.inputTokens,\n outputTokens: usage.outputTokens,\n cacheCreationTokens: usage.cacheCreationTokens ?? 0,\n cacheReadTokens: usage.cacheReadTokens ?? 0,\n totalTokens: usage.inputTokens + usage.outputTokens,\n estimatedCostUSD: costUSD,\n source: 'api',\n timestamp: Date.now(),\n model,\n }\n\n // Update global stats\n this.aggregateInto(this.globalStats, record)\n\n // Update agent stats if in agent context\n if (context?.agentId) {\n let stats = this.agentStats.get(context.agentId)\n if (!stats) {\n stats = this.createEmptyStats()\n this.agentStats.set(context.agentId, stats)\n }\n this.aggregateInto(stats, record)\n }\n\n // Store request record (with LRU eviction)\n const requestId =\n context?.toolUseId ??\n `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n this.requestRecords.set(requestId, record)\n if (this.requestRecords.size > TokenStatsManagerImpl.MAX_REQUEST_RECORDS) {\n const oldestKey = this.requestRecords.keys().next().value\n if (oldestKey) this.requestRecords.delete(oldestKey)\n }\n\n // Emit event for UI updates\n const scope: TokenStatsScope = context?.agentId\n ? { type: 'agent', agentId: context.agentId }\n : { type: 'global' }\n\n this.emitEvent({\n eventType: 'usage_recorded',\n scope,\n usage: record,\n aggregated: context?.agentId\n ? this.agentStats.get(context.agentId)!\n : this.globalStats,\n globalStats: this.globalStats,\n })\n }\n\n /**\n * Estimate token count from text\n *\n * Uses a simple character-based estimation. This is a fallback\n * when the API doesn't return token counts.\n *\n * @param text - Text to estimate tokens for\n * @returns Estimated token count\n */\n estimateTokens(text: string): number {\n return Math.ceil(text.length / DEFAULT_ESTIMATION_CONFIG.charsPerToken)\n }\n\n /**\n * Record estimated usage when API doesn't return token counts\n *\n * This is used as a fallback for APIs that don't return usage data.\n *\n * @param inputText - Input text (prompt)\n * @param outputText - Output text (completion)\n * @param model - Model name\n * @param context - Optional tracking context\n */\n recordEstimatedUsage(\n inputText: string,\n outputText: string,\n model: string,\n context?: TokenTrackingContext,\n ): void {\n const inputTokens = this.estimateTokens(inputText)\n const outputTokens = this.estimateTokens(outputText)\n\n const costUSD =\n (inputTokens / 1_000_000) *\n DEFAULT_ESTIMATION_CONFIG.defaultInputCostPerMillion +\n (outputTokens / 1_000_000) *\n DEFAULT_ESTIMATION_CONFIG.defaultOutputCostPerMillion\n\n const record: TokenUsageRecord = {\n inputTokens,\n outputTokens,\n cacheCreationTokens: 0,\n cacheReadTokens: 0,\n totalTokens: inputTokens + outputTokens,\n estimatedCostUSD: costUSD,\n source: 'estimated',\n timestamp: Date.now(),\n model,\n }\n\n // Update global stats\n this.aggregateInto(this.globalStats, record)\n\n // Update agent stats if in context\n if (context?.agentId) {\n let stats = this.agentStats.get(context.agentId)\n if (!stats) {\n stats = this.createEmptyStats()\n this.agentStats.set(context.agentId, stats)\n }\n this.aggregateInto(stats, record)\n }\n\n // Emit event\n this.emitEvent({\n eventType: 'usage_recorded',\n scope: { type: 'global' },\n usage: record,\n aggregated: this.globalStats,\n globalStats: this.globalStats,\n })\n }\n\n // ===== Getters =====\n\n /**\n * Get global aggregated statistics\n */\n getGlobalStats(): AggregatedTokenStats {\n return this.cloneStats(this.globalStats)\n }\n\n /**\n * Get statistics for a specific agent\n *\n * @param agentId - Agent ID to get stats for\n * @returns Agent stats or null if not found\n */\n getAgentStats(agentId: string): AggregatedTokenStats | null {\n const stats = this.agentStats.get(agentId)\n return stats ? this.cloneStats(stats) : null\n }\n\n /**\n * Get all agent statistics\n */\n getAllAgentStats(): Map<string, AggregatedTokenStats> {\n const result = new Map<string, AggregatedTokenStats>()\n for (const [agentId, stats] of this.agentStats) {\n result.set(agentId, this.cloneStats(stats))\n }\n return result\n }\n\n /**\n * Get a specific request record\n */\n getRequestRecord(requestId: string): TokenUsageRecord | null {\n return this.requestRecords.get(requestId) ?? null\n }\n\n // ===== Backward Compatibility Layer =====\n\n /**\n * Get total tokens (for costTracker.getTokenCounts compatibility)\n *\n * This method provides backward compatibility with the existing\n * costTracker.getTokenCounts() interface.\n */\n getTokenCounts(): {\n input: number\n output: number\n cacheCreation: number\n cacheRead: number\n total: number\n } {\n return {\n input: this.globalStats.totalInputTokens,\n output: this.globalStats.totalOutputTokens,\n cacheCreation: this.globalStats.totalCacheCreationTokens,\n cacheRead: this.globalStats.totalCacheReadTokens,\n total: this.globalStats.grandTotalTokens,\n }\n }\n\n // ===== Event Subscription =====\n\n /**\n * Subscribe to all stats updates\n *\n * @param callback - Called when any stats change\n * @returns Unsubscribe function\n */\n onStatsUpdate(callback: (event: TokenStatsEvent) => void): () => void {\n this.on('stats_update', callback)\n return () => this.off('stats_update', callback)\n }\n\n /**\n * Subscribe to updates for a specific agent\n *\n * @param agentId - Agent ID to subscribe to\n * @param callback - Called when agent stats change\n * @returns Unsubscribe function\n */\n onAgentStatsUpdate(\n agentId: string,\n callback: (stats: AggregatedTokenStats) => void,\n ): () => void {\n const handler = (event: TokenStatsEvent) => {\n if (event.scope.type === 'agent' && event.scope.agentId === agentId) {\n callback(event.aggregated)\n }\n }\n this.on('stats_update', handler)\n return () => this.off('stats_update', handler)\n }\n\n /**\n * Subscribe to global stats updates only\n *\n * @param callback - Called when global stats change\n * @returns Unsubscribe function\n */\n onGlobalStatsUpdate(\n callback: (stats: AggregatedTokenStats) => void,\n ): () => void {\n const handler = (event: TokenStatsEvent) => {\n callback(event.globalStats)\n }\n this.on('stats_update', handler)\n return () => this.off('stats_update', handler)\n }\n\n // ===== Agent Lifecycle =====\n\n /**\n * Mark an agent's scope as completed\n *\n * This emits a scope_completed event and optionally\n * cleans up the agent's stats from memory.\n *\n * @param agentId - Agent ID to complete\n * @param cleanup - Whether to remove from memory (default: false)\n */\n completeAgentScope(agentId: string, cleanup: boolean = false): void {\n const stats = this.agentStats.get(agentId)\n if (!stats) return\n\n this.emitEvent({\n eventType: 'scope_completed',\n scope: { type: 'agent', agentId },\n aggregated: stats,\n globalStats: this.globalStats,\n })\n\n if (cleanup) {\n this.agentStats.delete(agentId)\n }\n }\n\n // ===== Internal Methods =====\n\n private createEmptyStats(): AggregatedTokenStats {\n return {\n totalInputTokens: 0,\n totalOutputTokens: 0,\n totalCacheCreationTokens: 0,\n totalCacheReadTokens: 0,\n grandTotalTokens: 0,\n totalCostUSD: 0,\n requestCount: 0,\n byModel: new Map(),\n }\n }\n\n private aggregateInto(\n stats: AggregatedTokenStats,\n record: TokenUsageRecord,\n ): void {\n stats.totalInputTokens += record.inputTokens\n stats.totalOutputTokens += record.outputTokens\n stats.totalCacheCreationTokens += record.cacheCreationTokens\n stats.totalCacheReadTokens += record.cacheReadTokens\n stats.grandTotalTokens += record.totalTokens\n stats.totalCostUSD += record.estimatedCostUSD\n stats.requestCount += 1\n\n if (!stats.firstRequestTime) {\n stats.firstRequestTime = record.timestamp\n }\n stats.lastRequestTime = record.timestamp\n\n // Update per-model breakdown\n const modelStats = stats.byModel.get(record.model)\n if (modelStats) {\n modelStats.inputTokens += record.inputTokens\n modelStats.outputTokens += record.outputTokens\n modelStats.totalTokens += record.totalTokens\n modelStats.estimatedCostUSD += record.estimatedCostUSD\n modelStats.requestCount += 1\n } else {\n stats.byModel.set(record.model, {\n model: record.model,\n inputTokens: record.inputTokens,\n outputTokens: record.outputTokens,\n totalTokens: record.totalTokens,\n estimatedCostUSD: record.estimatedCostUSD,\n requestCount: 1,\n })\n }\n }\n\n private cloneStats(stats: AggregatedTokenStats): AggregatedTokenStats {\n return {\n ...stats,\n byModel: new Map(stats.byModel),\n }\n }\n\n private emitEvent(event: TokenStatsEvent): void {\n this.emit('stats_update', event)\n }\n\n /**\n * Reset all stats (for testing only)\n */\n resetForTests(): void {\n if (process.env.NODE_ENV !== 'test') {\n throw new Error('resetForTests can only be called in tests')\n }\n this.reset()\n }\n\n /**\n * Reset all statistics\n *\n * Called by costTracker.resetStateForTests() for backward compatibility.\n */\n reset(): void {\n this.globalStats = this.createEmptyStats()\n this.agentStats.clear()\n this.requestRecords.clear()\n }\n}\n\n// Export singleton instance\nexport const tokenStatsManager = TokenStatsManagerImpl.getInstance()\n\n// ===== Convenience Functions =====\n\n/**\n * Record token usage from an API response\n *\n * This is the main function to call from claude.ts after each API response.\n * Also updates the persistent stats manager for cross-session tracking.\n */\nexport function recordTokenUsage(\n usage: RawTokenUsage,\n costUSD: number,\n model: string,\n context?: TokenTrackingContext,\n): void {\n tokenStatsManager.recordUsage(usage, costUSD, model, context)\n\n // Also record to persistent stats manager (fire-and-forget)\n try {\n const { recordMessage } = require('@utils/stats')\n recordMessage(model, usage.inputTokens, usage.outputTokens, costUSD)\n } catch {\n // Stats manager not available, ignore\n }\n}\n\n/**\n * Get global token statistics\n */\nexport function getGlobalTokenStats(): AggregatedTokenStats {\n return tokenStatsManager.getGlobalStats()\n}\n\n/**\n * Get token statistics for a specific agent\n */\nexport function getAgentTokenStats(\n agentId: string,\n): AggregatedTokenStats | null {\n return tokenStatsManager.getAgentStats(agentId)\n}\n\n/**\n * Estimate token count from text\n */\nexport function estimateTokenCount(text: string): number {\n return tokenStatsManager.estimateTokens(text)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Token Statistics Manager\n *\n * Centralized token statistics tracking for the entire application.\n * This is the single source of truth for all token usage data.\n *\n * Architecture:\n * - Singleton pattern for global access\n * - Event-driven updates for real-time UI\n * - Hierarchical aggregation (global \u2192 agent \u2192 request)\n * - Backward compatible with existing costTracker\n *\n * Usage:\n * ```typescript\n * import { tokenStatsManager, recordTokenUsage } from '@core/tokenStatsManager'\n *\n * // Record usage from API response\n * recordTokenUsage(\n * { inputTokens: 100, outputTokens: 50 },\n * 0.0015,\n * 'gpt-4',\n * { agentId: 'agent-123' }\n * )\n *\n * // Get global stats\n * const stats = tokenStatsManager.getGlobalStats()\n *\n * // Subscribe to updates\n * const unsubscribe = tokenStatsManager.onStatsUpdate((event) => {\n * console.log('Stats updated:', event)\n * })\n * ```\n */\n\nimport { EventEmitter } from 'events'\nimport type {\n TokenUsageRecord,\n AggregatedTokenStats,\n TokenStatsScope,\n TokenStatsEvent,\n TokenTrackingContext,\n RawTokenUsage,\n ModelTokenStats,\n} from './tokenStats'\nimport { DEFAULT_ESTIMATION_CONFIG } from './tokenStats'\nimport { emitReminderEvent } from '../services/systemReminder'\n\n/**\n * TokenStatsManager - Centralized token statistics tracking\n */\nclass TokenStatsManagerImpl extends EventEmitter {\n private static instance: TokenStatsManagerImpl | null = null\n\n // Global aggregated stats\n private globalStats: AggregatedTokenStats = this.createEmptyStats()\n\n // Per-agent stats (agentId \u2192 stats)\n private agentStats: Map<string, AggregatedTokenStats> = new Map()\n\n // Per-request records (requestId \u2192 record) - for debugging, with LRU eviction\n private requestRecords: Map<string, TokenUsageRecord> = new Map()\n\n // Configuration\n private static readonly MAX_REQUEST_RECORDS = 1000\n\n private constructor() {\n super()\n this.setMaxListeners(100)\n }\n\n /**\n * Get the singleton instance\n */\n static getInstance(): TokenStatsManagerImpl {\n if (!TokenStatsManagerImpl.instance) {\n TokenStatsManagerImpl.instance = new TokenStatsManagerImpl()\n }\n return TokenStatsManagerImpl.instance\n }\n\n /**\n * Record token usage from an API response\n *\n * This is the PRIMARY entry point - called from claude.ts\n * after every API response.\n *\n * @param usage - Raw token usage from API\n * @param costUSD - Calculated cost in USD\n * @param model - Model name used\n * @param context - Optional tracking context (agentId, toolUseId)\n */\n recordUsage(\n usage: RawTokenUsage,\n costUSD: number,\n model: string,\n context?: TokenTrackingContext,\n ): void {\n const record: TokenUsageRecord = {\n inputTokens: usage.inputTokens,\n outputTokens: usage.outputTokens,\n cacheCreationTokens: usage.cacheCreationTokens ?? 0,\n cacheReadTokens: usage.cacheReadTokens ?? 0,\n totalTokens: usage.inputTokens + usage.outputTokens,\n estimatedCostUSD: costUSD,\n source: 'api',\n timestamp: Date.now(),\n model,\n }\n\n // Update global stats\n this.aggregateInto(this.globalStats, record)\n\n // Update agent stats if in agent context\n if (context?.agentId) {\n let stats = this.agentStats.get(context.agentId)\n if (!stats) {\n stats = this.createEmptyStats()\n this.agentStats.set(context.agentId, stats)\n }\n this.aggregateInto(stats, record)\n }\n\n // Store request record (with LRU eviction)\n const requestId =\n context?.toolUseId ??\n `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n this.requestRecords.set(requestId, record)\n if (this.requestRecords.size > TokenStatsManagerImpl.MAX_REQUEST_RECORDS) {\n const oldestKey = this.requestRecords.keys().next().value\n if (oldestKey) this.requestRecords.delete(oldestKey)\n }\n\n // Emit event for UI updates\n const scope: TokenStatsScope = context?.agentId\n ? { type: 'agent', agentId: context.agentId }\n : { type: 'global' }\n\n this.emitEvent({\n eventType: 'usage_recorded',\n scope,\n usage: record,\n aggregated: context?.agentId\n ? this.agentStats.get(context.agentId)!\n : this.globalStats,\n globalStats: this.globalStats,\n })\n }\n\n /**\n * Estimate token count from text\n *\n * Uses a simple character-based estimation. This is a fallback\n * when the API doesn't return token counts.\n *\n * @param text - Text to estimate tokens for\n * @returns Estimated token count\n */\n estimateTokens(text: string): number {\n return Math.ceil(text.length / DEFAULT_ESTIMATION_CONFIG.charsPerToken)\n }\n\n /**\n * Record estimated usage when API doesn't return token counts\n *\n * This is used as a fallback for APIs that don't return usage data.\n *\n * @param inputText - Input text (prompt)\n * @param outputText - Output text (completion)\n * @param model - Model name\n * @param context - Optional tracking context\n */\n recordEstimatedUsage(\n inputText: string,\n outputText: string,\n model: string,\n context?: TokenTrackingContext,\n ): void {\n const inputTokens = this.estimateTokens(inputText)\n const outputTokens = this.estimateTokens(outputText)\n\n const costUSD =\n (inputTokens / 1_000_000) *\n DEFAULT_ESTIMATION_CONFIG.defaultInputCostPerMillion +\n (outputTokens / 1_000_000) *\n DEFAULT_ESTIMATION_CONFIG.defaultOutputCostPerMillion\n\n const record: TokenUsageRecord = {\n inputTokens,\n outputTokens,\n cacheCreationTokens: 0,\n cacheReadTokens: 0,\n totalTokens: inputTokens + outputTokens,\n estimatedCostUSD: costUSD,\n source: 'estimated',\n timestamp: Date.now(),\n model,\n }\n\n // Update global stats\n this.aggregateInto(this.globalStats, record)\n\n // Update agent stats if in context\n if (context?.agentId) {\n let stats = this.agentStats.get(context.agentId)\n if (!stats) {\n stats = this.createEmptyStats()\n this.agentStats.set(context.agentId, stats)\n }\n this.aggregateInto(stats, record)\n }\n\n // Emit event\n this.emitEvent({\n eventType: 'usage_recorded',\n scope: { type: 'global' },\n usage: record,\n aggregated: this.globalStats,\n globalStats: this.globalStats,\n })\n }\n\n // ===== Getters =====\n\n /**\n * Get global aggregated statistics\n */\n getGlobalStats(): AggregatedTokenStats {\n return this.cloneStats(this.globalStats)\n }\n\n /**\n * Get statistics for a specific agent\n *\n * @param agentId - Agent ID to get stats for\n * @returns Agent stats or null if not found\n */\n getAgentStats(agentId: string): AggregatedTokenStats | null {\n const stats = this.agentStats.get(agentId)\n return stats ? this.cloneStats(stats) : null\n }\n\n /**\n * Get all agent statistics\n */\n getAllAgentStats(): Map<string, AggregatedTokenStats> {\n const result = new Map<string, AggregatedTokenStats>()\n for (const [agentId, stats] of this.agentStats) {\n result.set(agentId, this.cloneStats(stats))\n }\n return result\n }\n\n /**\n * Get a specific request record\n */\n getRequestRecord(requestId: string): TokenUsageRecord | null {\n return this.requestRecords.get(requestId) ?? null\n }\n\n // ===== Backward Compatibility Layer =====\n\n /**\n * Get total tokens (for costTracker.getTokenCounts compatibility)\n *\n * This method provides backward compatibility with the existing\n * costTracker.getTokenCounts() interface.\n */\n getTokenCounts(): {\n input: number\n output: number\n cacheCreation: number\n cacheRead: number\n total: number\n } {\n return {\n input: this.globalStats.totalInputTokens,\n output: this.globalStats.totalOutputTokens,\n cacheCreation: this.globalStats.totalCacheCreationTokens,\n cacheRead: this.globalStats.totalCacheReadTokens,\n total: this.globalStats.grandTotalTokens,\n }\n }\n\n // ===== Event Subscription =====\n\n /**\n * Subscribe to all stats updates\n *\n * @param callback - Called when any stats change\n * @returns Unsubscribe function\n */\n onStatsUpdate(callback: (event: TokenStatsEvent) => void): () => void {\n this.on('stats_update', callback)\n return () => this.off('stats_update', callback)\n }\n\n /**\n * Subscribe to updates for a specific agent\n *\n * @param agentId - Agent ID to subscribe to\n * @param callback - Called when agent stats change\n * @returns Unsubscribe function\n */\n onAgentStatsUpdate(\n agentId: string,\n callback: (stats: AggregatedTokenStats) => void,\n ): () => void {\n const handler = (event: TokenStatsEvent) => {\n if (event.scope.type === 'agent' && event.scope.agentId === agentId) {\n callback(event.aggregated)\n }\n }\n this.on('stats_update', handler)\n return () => this.off('stats_update', handler)\n }\n\n /**\n * Subscribe to global stats updates only\n *\n * @param callback - Called when global stats change\n * @returns Unsubscribe function\n */\n onGlobalStatsUpdate(\n callback: (stats: AggregatedTokenStats) => void,\n ): () => void {\n const handler = (event: TokenStatsEvent) => {\n callback(event.globalStats)\n }\n this.on('stats_update', handler)\n return () => this.off('stats_update', handler)\n }\n\n // ===== Agent Lifecycle =====\n\n /**\n * Mark an agent's scope as completed\n *\n * This emits a scope_completed event and optionally\n * cleans up the agent's stats from memory.\n *\n * @param agentId - Agent ID to complete\n * @param cleanup - Whether to remove from memory (default: false)\n */\n completeAgentScope(agentId: string, cleanup: boolean = false): void {\n const stats = this.agentStats.get(agentId)\n if (!stats) return\n\n this.emitEvent({\n eventType: 'scope_completed',\n scope: { type: 'agent', agentId },\n aggregated: stats,\n globalStats: this.globalStats,\n })\n\n if (cleanup) {\n this.agentStats.delete(agentId)\n }\n }\n\n // ===== Internal Methods =====\n\n private createEmptyStats(): AggregatedTokenStats {\n return {\n totalInputTokens: 0,\n totalOutputTokens: 0,\n totalCacheCreationTokens: 0,\n totalCacheReadTokens: 0,\n grandTotalTokens: 0,\n totalCostUSD: 0,\n requestCount: 0,\n byModel: new Map(),\n }\n }\n\n private aggregateInto(\n stats: AggregatedTokenStats,\n record: TokenUsageRecord,\n ): void {\n stats.totalInputTokens += record.inputTokens\n stats.totalOutputTokens += record.outputTokens\n stats.totalCacheCreationTokens += record.cacheCreationTokens\n stats.totalCacheReadTokens += record.cacheReadTokens\n stats.grandTotalTokens += record.totalTokens\n stats.totalCostUSD += record.estimatedCostUSD\n stats.requestCount += 1\n\n if (!stats.firstRequestTime) {\n stats.firstRequestTime = record.timestamp\n }\n stats.lastRequestTime = record.timestamp\n\n // Update per-model breakdown\n const modelStats = stats.byModel.get(record.model)\n if (modelStats) {\n modelStats.inputTokens += record.inputTokens\n modelStats.outputTokens += record.outputTokens\n modelStats.totalTokens += record.totalTokens\n modelStats.estimatedCostUSD += record.estimatedCostUSD\n modelStats.requestCount += 1\n } else {\n stats.byModel.set(record.model, {\n model: record.model,\n inputTokens: record.inputTokens,\n outputTokens: record.outputTokens,\n totalTokens: record.totalTokens,\n estimatedCostUSD: record.estimatedCostUSD,\n requestCount: 1,\n })\n }\n }\n\n private cloneStats(stats: AggregatedTokenStats): AggregatedTokenStats {\n return {\n ...stats,\n byModel: new Map(stats.byModel),\n }\n }\n\n private emitEvent(event: TokenStatsEvent): void {\n this.emit('stats_update', event)\n }\n\n /**\n * Reset all stats (for testing only)\n */\n resetForTests(): void {\n if (process.env.NODE_ENV !== 'test') {\n throw new Error('resetForTests can only be called in tests')\n }\n this.reset()\n }\n\n /**\n * Reset all statistics\n *\n * Called by costTracker.resetStateForTests() for backward compatibility.\n */\n reset(): void {\n this.globalStats = this.createEmptyStats()\n this.agentStats.clear()\n this.requestRecords.clear()\n emittedTokenThresholds.clear()\n }\n}\n\n// Export singleton instance\nexport const tokenStatsManager = TokenStatsManagerImpl.getInstance()\n\n// ===== Convenience Functions =====\n\n/**\n * Record token usage from an API response\n *\n * This is the main function to call from claude.ts after each API response.\n * Also updates the persistent stats manager for cross-session tracking.\n */\n// Token usage thresholds (percentage of context limit)\nconst TOKEN_THRESHOLDS = [50, 75, 90] as const\nconst emittedTokenThresholds = new Set<number>()\nconst DEFAULT_CONTEXT_LIMIT = 200_000\n\nexport function recordTokenUsage(\n usage: RawTokenUsage,\n costUSD: number,\n model: string,\n context?: TokenTrackingContext,\n): void {\n tokenStatsManager.recordUsage(usage, costUSD, model, context)\n\n // Emit token:usage at threshold crossings\n const globalStats = tokenStatsManager.getGlobalStats()\n const total = globalStats.grandTotalTokens\n const percentage = Math.round((total / DEFAULT_CONTEXT_LIMIT) * 100)\n for (const threshold of TOKEN_THRESHOLDS) {\n if (percentage >= threshold && !emittedTokenThresholds.has(threshold)) {\n emittedTokenThresholds.add(threshold)\n emitReminderEvent('token:usage', {\n used: total,\n total: DEFAULT_CONTEXT_LIMIT,\n percentage,\n threshold,\n })\n }\n }\n\n // Also record to persistent stats manager (fire-and-forget)\n import('@utils/stats')\n .then(({ recordMessage }) => {\n recordMessage(model, usage.inputTokens, usage.outputTokens, costUSD)\n })\n .catch(() => {\n // Stats manager not available, ignore\n })\n}\n\n/**\n * Get global token statistics\n */\nexport function getGlobalTokenStats(): AggregatedTokenStats {\n return tokenStatsManager.getGlobalStats()\n}\n\n/**\n * Get token statistics for a specific agent\n */\nexport function getAgentTokenStats(\n agentId: string,\n): AggregatedTokenStats | null {\n return tokenStatsManager.getAgentStats(agentId)\n}\n\n/**\n * Estimate token count from text\n */\nexport function estimateTokenCount(text: string): number {\n return tokenStatsManager.estimateTokens(text)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,11 +2,6 @@ import chalk from "chalk";
2
2
  import { useEffect } from "react";
3
3
  import signalExit from "signal-exit";
4
4
  import { formatDuration } from "./utils/format.js";
5
- import {
6
- getCurrentProjectConfig,
7
- saveCurrentProjectConfig
8
- } from "./utils/config.js";
9
- import { SESSION_ID } from "./utils/log.js";
10
5
  const STATE = {
11
6
  totalCost: 0,
12
7
  totalAPIDuration: 0,
@@ -45,17 +40,6 @@ function useCostSummary() {
45
40
  signalExit(
46
41
  () => {
47
42
  process.stdout.write("\n" + formatTotalCost() + "\n");
48
- try {
49
- const projectConfig = getCurrentProjectConfig();
50
- saveCurrentProjectConfig({
51
- ...projectConfig,
52
- lastCost: STATE.totalCost,
53
- lastAPIDuration: STATE.totalAPIDuration,
54
- lastDuration: getTotalDuration(),
55
- lastSessionId: SESSION_ID
56
- });
57
- } catch {
58
- }
59
43
  },
60
44
  { alwaysLast: true }
61
45
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/cost-tracker.ts"],
4
- "sourcesContent": ["import chalk from 'chalk'\nimport { useEffect } from 'react'\nimport signalExit from 'signal-exit'\nimport { formatDuration } from './utils/format'\nimport {\n getCurrentProjectConfig,\n saveCurrentProjectConfig,\n} from '@utils/config'\nimport { SESSION_ID } from './utils/log'\n\n// DO NOT ADD MORE STATE HERE OR BORIS WILL CURSE YOU\nconst STATE: {\n totalCost: number\n totalAPIDuration: number\n startTime: number\n} = {\n totalCost: 0,\n totalAPIDuration: 0,\n startTime: Date.now(),\n}\n\nexport function addToTotalCost(cost: number, duration: number): void {\n STATE.totalCost += cost\n STATE.totalAPIDuration += duration\n}\n\nexport function getTotalCost(): number {\n return STATE.totalCost\n}\n\nexport function getTotalDuration(): number {\n return Date.now() - STATE.startTime\n}\n\nexport function getTotalAPIDuration(): number {\n return STATE.totalAPIDuration\n}\n\nfunction formatCost(cost: number): string {\n return `$${cost > 0.5 ? round(cost, 100).toFixed(2) : cost.toFixed(4)}`\n}\n\nexport function formatTotalCost(): string {\n return chalk.grey(\n `Total cost: ${formatCost(STATE.totalCost)}\nTotal duration (API): ${formatDuration(STATE.totalAPIDuration)}\nTotal duration (wall): ${formatDuration(getTotalDuration())}`,\n )\n}\n\n/**\n * useCostSummary - Register exit handler to display cost summary\n *\n * CRITICAL FIX: Use signal-exit with alwaysLast: true\n *\n * The problem was that Ink also uses signal-exit (with alwaysLast: false)\n * to clean up on exit. During Ink's cleanup, it may:\n * 1. Call onRender() one last time\n * 2. Use ansiEscapes.eraseLines() to clear previous output\n * 3. Or even call clearTerminal if output height >= terminal rows\n *\n * By using signal-exit with alwaysLast: true, we ensure our handler\n * runs AFTER Ink's cleanup, so our statistics are the last thing printed.\n */\n// Flag to ensure we only register once and never unregister\nlet exitHandlerRegistered = false\n\nexport function useCostSummary(): void {\n useEffect(() => {\n // Only register ONCE, and NEVER unregister\n if (exitHandlerRegistered) {\n return\n }\n exitHandlerRegistered = true\n\n // Use signal-exit with alwaysLast: true to run AFTER Ink's cleanup\n // This ensures our statistics are printed after Ink finishes its final render\n signalExit(\n () => {\n // Write statistics to stdout\n // Using stdout.write directly to avoid any Ink interception\n process.stdout.write('\\n' + formatTotalCost() + '\\n')\n\n // Save last cost and duration to project config\n try {\n const projectConfig = getCurrentProjectConfig()\n saveCurrentProjectConfig({\n ...projectConfig,\n lastCost: STATE.totalCost,\n lastAPIDuration: STATE.totalAPIDuration,\n lastDuration: getTotalDuration(),\n lastSessionId: SESSION_ID,\n })\n } catch {\n // Ignore errors during exit - config save is best-effort\n }\n },\n { alwaysLast: true },\n )\n\n // NO cleanup function - we NEVER want to unregister this handler\n }, [])\n}\n\nfunction round(number: number, precision: number): number {\n return Math.round(number * precision) / precision\n}\n\n// Only used in tests\nexport function resetStateForTests(): void {\n if (process.env.NODE_ENV !== 'test') {\n throw new Error('resetStateForTests can only be called in tests')\n }\n STATE.startTime = Date.now()\n STATE.totalCost = 0\n STATE.totalAPIDuration = 0\n}\n"],
5
- "mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,iBAAiB;AAC1B,OAAO,gBAAgB;AACvB,SAAS,sBAAsB;AAC/B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAG3B,MAAM,QAIF;AAAA,EACF,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,WAAW,KAAK,IAAI;AACtB;AAEO,SAAS,eAAe,MAAc,UAAwB;AACnE,QAAM,aAAa;AACnB,QAAM,oBAAoB;AAC5B;AAEO,SAAS,eAAuB;AACrC,SAAO,MAAM;AACf;AAEO,SAAS,mBAA2B;AACzC,SAAO,KAAK,IAAI,IAAI,MAAM;AAC5B;AAEO,SAAS,sBAA8B;AAC5C,SAAO,MAAM;AACf;AAEA,SAAS,WAAW,MAAsB;AACxC,SAAO,IAAI,OAAO,MAAM,MAAM,MAAM,GAAG,EAAE,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;AACvE;AAEO,SAAS,kBAA0B;AACxC,SAAO,MAAM;AAAA,IACX,eAAe,WAAW,MAAM,SAAS,CAAC;AAAA,wBACtB,eAAe,MAAM,gBAAgB,CAAC;AAAA,yBACrC,eAAe,iBAAiB,CAAC,CAAC;AAAA,EACzD;AACF;AAiBA,IAAI,wBAAwB;AAErB,SAAS,iBAAuB;AACrC,YAAU,MAAM;AAEd,QAAI,uBAAuB;AACzB;AAAA,IACF;AACA,4BAAwB;AAIxB;AAAA,MACE,MAAM;AAGJ,gBAAQ,OAAO,MAAM,OAAO,gBAAgB,IAAI,IAAI;AAGpD,YAAI;AACF,gBAAM,gBAAgB,wBAAwB;AAC9C,mCAAyB;AAAA,YACvB,GAAG;AAAA,YACH,UAAU,MAAM;AAAA,YAChB,iBAAiB,MAAM;AAAA,YACvB,cAAc,iBAAiB;AAAA,YAC/B,eAAe;AAAA,UACjB,CAAC;AAAA,QACH,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,MACA,EAAE,YAAY,KAAK;AAAA,IACrB;AAAA,EAGF,GAAG,CAAC,CAAC;AACP;AAEA,SAAS,MAAM,QAAgB,WAA2B;AACxD,SAAO,KAAK,MAAM,SAAS,SAAS,IAAI;AAC1C;AAGO,SAAS,qBAA2B;AACzC,MAAI,QAAQ,IAAI,aAAa,QAAQ;AACnC,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AACA,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,YAAY;AAClB,QAAM,mBAAmB;AAC3B;",
4
+ "sourcesContent": ["import chalk from 'chalk'\nimport { useEffect } from 'react'\nimport signalExit from 'signal-exit'\nimport { formatDuration } from './utils/format'\n\n// DO NOT ADD MORE STATE HERE OR BORIS WILL CURSE YOU\nconst STATE: {\n totalCost: number\n totalAPIDuration: number\n startTime: number\n} = {\n totalCost: 0,\n totalAPIDuration: 0,\n startTime: Date.now(),\n}\n\nexport function addToTotalCost(cost: number, duration: number): void {\n STATE.totalCost += cost\n STATE.totalAPIDuration += duration\n}\n\nexport function getTotalCost(): number {\n return STATE.totalCost\n}\n\nexport function getTotalDuration(): number {\n return Date.now() - STATE.startTime\n}\n\nexport function getTotalAPIDuration(): number {\n return STATE.totalAPIDuration\n}\n\nfunction formatCost(cost: number): string {\n return `$${cost > 0.5 ? round(cost, 100).toFixed(2) : cost.toFixed(4)}`\n}\n\nexport function formatTotalCost(): string {\n return chalk.grey(\n `Total cost: ${formatCost(STATE.totalCost)}\nTotal duration (API): ${formatDuration(STATE.totalAPIDuration)}\nTotal duration (wall): ${formatDuration(getTotalDuration())}`,\n )\n}\n\n/**\n * useCostSummary - Register exit handler to display cost summary\n *\n * CRITICAL FIX: Use signal-exit with alwaysLast: true\n *\n * The problem was that Ink also uses signal-exit (with alwaysLast: false)\n * to clean up on exit. During Ink's cleanup, it may:\n * 1. Call onRender() one last time\n * 2. Use ansiEscapes.eraseLines() to clear previous output\n * 3. Or even call clearTerminal if output height >= terminal rows\n *\n * By using signal-exit with alwaysLast: true, we ensure our handler\n * runs AFTER Ink's cleanup, so our statistics are the last thing printed.\n */\n// Flag to ensure we only register once and never unregister\nlet exitHandlerRegistered = false\n\nexport function useCostSummary(): void {\n useEffect(() => {\n // Only register ONCE, and NEVER unregister\n if (exitHandlerRegistered) {\n return\n }\n exitHandlerRegistered = true\n\n // Use signal-exit with alwaysLast: true to run AFTER Ink's cleanup\n // This ensures our statistics are printed after Ink finishes its final render\n signalExit(\n () => {\n // Write statistics to stdout\n // Using stdout.write directly to avoid any Ink interception\n process.stdout.write('\\n' + formatTotalCost() + '\\n')\n },\n { alwaysLast: true },\n )\n\n // NO cleanup function - we NEVER want to unregister this handler\n }, [])\n}\n\nfunction round(number: number, precision: number): number {\n return Math.round(number * precision) / precision\n}\n\n// Only used in tests\nexport function resetStateForTests(): void {\n if (process.env.NODE_ENV !== 'test') {\n throw new Error('resetStateForTests can only be called in tests')\n }\n STATE.startTime = Date.now()\n STATE.totalCost = 0\n STATE.totalAPIDuration = 0\n}\n"],
5
+ "mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,iBAAiB;AAC1B,OAAO,gBAAgB;AACvB,SAAS,sBAAsB;AAG/B,MAAM,QAIF;AAAA,EACF,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,WAAW,KAAK,IAAI;AACtB;AAEO,SAAS,eAAe,MAAc,UAAwB;AACnE,QAAM,aAAa;AACnB,QAAM,oBAAoB;AAC5B;AAEO,SAAS,eAAuB;AACrC,SAAO,MAAM;AACf;AAEO,SAAS,mBAA2B;AACzC,SAAO,KAAK,IAAI,IAAI,MAAM;AAC5B;AAEO,SAAS,sBAA8B;AAC5C,SAAO,MAAM;AACf;AAEA,SAAS,WAAW,MAAsB;AACxC,SAAO,IAAI,OAAO,MAAM,MAAM,MAAM,GAAG,EAAE,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;AACvE;AAEO,SAAS,kBAA0B;AACxC,SAAO,MAAM;AAAA,IACX,eAAe,WAAW,MAAM,SAAS,CAAC;AAAA,wBACtB,eAAe,MAAM,gBAAgB,CAAC;AAAA,yBACrC,eAAe,iBAAiB,CAAC,CAAC;AAAA,EACzD;AACF;AAiBA,IAAI,wBAAwB;AAErB,SAAS,iBAAuB;AACrC,YAAU,MAAM;AAEd,QAAI,uBAAuB;AACzB;AAAA,IACF;AACA,4BAAwB;AAIxB;AAAA,MACE,MAAM;AAGJ,gBAAQ,OAAO,MAAM,OAAO,gBAAgB,IAAI,IAAI;AAAA,MACtD;AAAA,MACA,EAAE,YAAY,KAAK;AAAA,IACrB;AAAA,EAGF,GAAG,CAAC,CAAC;AACP;AAEA,SAAS,MAAM,QAAgB,WAA2B;AACxD,SAAO,KAAK,MAAM,SAAS,SAAS,IAAI;AAC1C;AAGO,SAAS,qBAA2B;AACzC,MAAI,QAAQ,IAAI,aAAa,QAAQ;AACnC,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AACA,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,YAAY;AAClB,QAAM,mBAAmB;AAC3B;",
6
6
  "names": []
7
7
  }
@@ -35,7 +35,9 @@ async function main() {
35
35
  const message = err.message || "";
36
36
  if (message.includes("react-devtools-core")) {
37
37
  console.error("\n\u274C Failed to start: react-devtools-core not found");
38
- console.error(" This may be caused by DEV environment variable being set.");
38
+ console.error(
39
+ " This may be caused by DEV environment variable being set."
40
+ );
39
41
  console.error(" Try running: unset DEV && minto\n");
40
42
  } else if (message.includes("yoga") || message.includes("YOGA")) {
41
43
  console.error("\n\u274C Failed to start: yoga-layout initialization failed");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/entrypoints/bootstrap.ts"],
4
- "sourcesContent": ["#!/usr/bin/env -S node --no-warnings=ExperimentalWarning --enable-source-maps\n\n/**\n * Bootstrap entry point for Minto CLI\n *\n * CRITICAL: This file MUST remain minimal with NO static imports of application code.\n *\n * This bootstrap exists because ESM static imports execute BEFORE the importing\n * module's code. We need certain initializations to happen BEFORE any application\n * modules are loaded:\n *\n * 1. DEV='false' - Ink checks process.env['DEV'] at module load time and tries to\n * import 'react-devtools-core' if DEV='true'. This package isn't bundled.\n *\n * 2. EventEmitter.defaultMaxListeners - Must be set before any modules create\n * EventEmitters to prevent \"MaxListenersExceededWarning\" during startup.\n *\n * 3. YOGA_WASM_PATH - Must be set before Ink initializes yoga-layout for proper\n * terminal layout rendering.\n *\n * Solution: Use dynamic import() to load cli.tsx AFTER all initializations.\n * Dynamic import() is evaluated at runtime, not at module load time.\n *\n * DO NOT add any static imports here except for Node.js built-ins that don't\n * chain to application code (directly or transitively).\n */\n\nimport { EventEmitter } from 'events'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\nimport { existsSync } from 'node:fs'\n\n// ============================================================================\n// 1. Disable Ink's development mode\n// ============================================================================\n// Prevents Ink from trying to load react-devtools-core which isn't bundled.\n// Must use both syntaxes because Bun's --define only replaces dot syntax.\nprocess.env.DEV = 'false'\nprocess.env['DEV'] = 'false'\n\n// ============================================================================\n// 2. Increase EventEmitter max listeners\n// ============================================================================\n// Components like ModelConfig, ModelSelector use multiple useInput hooks which\n// add listeners. Default is 10, we increase to 20 to accommodate deep hierarchies.\n// Must be set before any modules create EventEmitters.\nEventEmitter.defaultMaxListeners = 20\n\n// ============================================================================\n// 3. Set YOGA_WASM_PATH for Ink layout engine\n// ============================================================================\n// Ink uses yoga-layout for terminal UI layout. The WASM file path must be set\n// before Ink initializes. Resolve relative to this file for both dev and dist.\ntry {\n if (!process.env.YOGA_WASM_PATH) {\n const __filename = fileURLToPath(import.meta.url)\n const __dirname = dirname(__filename)\n // Check multiple possible locations\n const candidates = [\n join(__dirname, './yoga.wasm'), // dist/entrypoints/yoga.wasm\n join(__dirname, '../yoga.wasm'), // dist/yoga.wasm\n join(__dirname, '../../yoga.wasm'), // project root (dev mode)\n ]\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n process.env.YOGA_WASM_PATH = candidate\n break\n }\n }\n }\n} catch {\n // Ignore errors - yoga will use fallback or error at runtime\n}\n\n// ============================================================================\n// Main: Dynamically import the CLI\n// ============================================================================\n// This ensures all static imports in cli.tsx see the initialized environment.\nasync function main(): Promise<void> {\n try {\n await import('./cli.js')\n } catch (error) {\n // Provide helpful error messages for common failures\n const err = error as Error\n const message = err.message || ''\n\n if (message.includes('react-devtools-core')) {\n console.error('\\n\u274C Failed to start: react-devtools-core not found')\n console.error(' This may be caused by DEV environment variable being set.')\n console.error(' Try running: unset DEV && minto\\n')\n } else if (message.includes('yoga') || message.includes('YOGA')) {\n console.error('\\n\u274C Failed to start: yoga-layout initialization failed')\n console.error(' The yoga.wasm file may be missing or corrupted.')\n console.error(' Try reinstalling: npm install -g @within-7/minto\\n')\n } else {\n console.error('\u274C Failed to start:', message || err)\n if (err.stack) {\n console.error(err.stack)\n }\n }\n process.exit(1)\n }\n}\n\nmain()\n"],
5
- "mappings": ";AA2BA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,SAAS,YAAY;AAC9B,SAAS,kBAAkB;AAO3B,QAAQ,IAAI,MAAM;AAClB,QAAQ,IAAI,KAAK,IAAI;AAQrB,aAAa,sBAAsB;AAOnC,IAAI;AACF,MAAI,CAAC,QAAQ,IAAI,gBAAgB;AAC/B,UAAM,aAAa,cAAc,YAAY,GAAG;AAChD,UAAM,YAAY,QAAQ,UAAU;AAEpC,UAAM,aAAa;AAAA,MACjB,KAAK,WAAW,aAAa;AAAA;AAAA,MAC7B,KAAK,WAAW,cAAc;AAAA;AAAA,MAC9B,KAAK,WAAW,iBAAiB;AAAA;AAAA,IACnC;AACA,eAAW,aAAa,YAAY;AAClC,UAAI,WAAW,SAAS,GAAG;AACzB,gBAAQ,IAAI,iBAAiB;AAC7B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,QAAQ;AAER;AAMA,eAAe,OAAsB;AACnC,MAAI;AACF,UAAM,OAAO,UAAU;AAAA,EACzB,SAAS,OAAO;AAEd,UAAM,MAAM;AACZ,UAAM,UAAU,IAAI,WAAW;AAE/B,QAAI,QAAQ,SAAS,qBAAqB,GAAG;AAC3C,cAAQ,MAAM,yDAAoD;AAClE,cAAQ,MAAM,8DAA8D;AAC5E,cAAQ,MAAM,sCAAsC;AAAA,IACtD,WAAW,QAAQ,SAAS,MAAM,KAAK,QAAQ,SAAS,MAAM,GAAG;AAC/D,cAAQ,MAAM,6DAAwD;AACtE,cAAQ,MAAM,oDAAoD;AAClE,cAAQ,MAAM,uDAAuD;AAAA,IACvE,OAAO;AACL,cAAQ,MAAM,2BAAsB,WAAW,GAAG;AAClD,UAAI,IAAI,OAAO;AACb,gBAAQ,MAAM,IAAI,KAAK;AAAA,MACzB;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,KAAK;",
4
+ "sourcesContent": ["#!/usr/bin/env -S node --no-warnings=ExperimentalWarning --enable-source-maps\n\n/**\n * Bootstrap entry point for Minto CLI\n *\n * CRITICAL: This file MUST remain minimal with NO static imports of application code.\n *\n * This bootstrap exists because ESM static imports execute BEFORE the importing\n * module's code. We need certain initializations to happen BEFORE any application\n * modules are loaded:\n *\n * 1. DEV='false' - Ink checks process.env['DEV'] at module load time and tries to\n * import 'react-devtools-core' if DEV='true'. This package isn't bundled.\n *\n * 2. EventEmitter.defaultMaxListeners - Must be set before any modules create\n * EventEmitters to prevent \"MaxListenersExceededWarning\" during startup.\n *\n * 3. YOGA_WASM_PATH - Must be set before Ink initializes yoga-layout for proper\n * terminal layout rendering.\n *\n * Solution: Use dynamic import() to load cli.tsx AFTER all initializations.\n * Dynamic import() is evaluated at runtime, not at module load time.\n *\n * DO NOT add any static imports here except for Node.js built-ins that don't\n * chain to application code (directly or transitively).\n */\n\nimport { EventEmitter } from 'events'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\nimport { existsSync } from 'node:fs'\n\n// ============================================================================\n// 1. Disable Ink's development mode\n// ============================================================================\n// Prevents Ink from trying to load react-devtools-core which isn't bundled.\n// Must use both syntaxes because Bun's --define only replaces dot syntax.\nprocess.env.DEV = 'false'\nprocess.env['DEV'] = 'false'\n\n// ============================================================================\n// 2. Increase EventEmitter max listeners\n// ============================================================================\n// Components like ModelConfig, ModelSelector use multiple useInput hooks which\n// add listeners. Default is 10, we increase to 20 to accommodate deep hierarchies.\n// Must be set before any modules create EventEmitters.\nEventEmitter.defaultMaxListeners = 20\n\n// ============================================================================\n// 3. Set YOGA_WASM_PATH for Ink layout engine\n// ============================================================================\n// Ink uses yoga-layout for terminal UI layout. The WASM file path must be set\n// before Ink initializes. Resolve relative to this file for both dev and dist.\ntry {\n if (!process.env.YOGA_WASM_PATH) {\n const __filename = fileURLToPath(import.meta.url)\n const __dirname = dirname(__filename)\n // Check multiple possible locations\n const candidates = [\n join(__dirname, './yoga.wasm'), // dist/entrypoints/yoga.wasm\n join(__dirname, '../yoga.wasm'), // dist/yoga.wasm\n join(__dirname, '../../yoga.wasm'), // project root (dev mode)\n ]\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n process.env.YOGA_WASM_PATH = candidate\n break\n }\n }\n }\n} catch {\n // Ignore errors - yoga will use fallback or error at runtime\n}\n\n// ============================================================================\n// Main: Dynamically import the CLI\n// ============================================================================\n// This ensures all static imports in cli.tsx see the initialized environment.\nasync function main(): Promise<void> {\n try {\n await import('./cli.js')\n } catch (error) {\n // Provide helpful error messages for common failures\n const err = error as Error\n const message = err.message || ''\n\n if (message.includes('react-devtools-core')) {\n console.error('\\n\u274C Failed to start: react-devtools-core not found')\n console.error(\n ' This may be caused by DEV environment variable being set.',\n )\n console.error(' Try running: unset DEV && minto\\n')\n } else if (message.includes('yoga') || message.includes('YOGA')) {\n console.error('\\n\u274C Failed to start: yoga-layout initialization failed')\n console.error(' The yoga.wasm file may be missing or corrupted.')\n console.error(' Try reinstalling: npm install -g @within-7/minto\\n')\n } else {\n console.error('\u274C Failed to start:', message || err)\n if (err.stack) {\n console.error(err.stack)\n }\n }\n process.exit(1)\n }\n}\n\nmain()\n"],
5
+ "mappings": ";AA2BA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,SAAS,YAAY;AAC9B,SAAS,kBAAkB;AAO3B,QAAQ,IAAI,MAAM;AAClB,QAAQ,IAAI,KAAK,IAAI;AAQrB,aAAa,sBAAsB;AAOnC,IAAI;AACF,MAAI,CAAC,QAAQ,IAAI,gBAAgB;AAC/B,UAAM,aAAa,cAAc,YAAY,GAAG;AAChD,UAAM,YAAY,QAAQ,UAAU;AAEpC,UAAM,aAAa;AAAA,MACjB,KAAK,WAAW,aAAa;AAAA;AAAA,MAC7B,KAAK,WAAW,cAAc;AAAA;AAAA,MAC9B,KAAK,WAAW,iBAAiB;AAAA;AAAA,IACnC;AACA,eAAW,aAAa,YAAY;AAClC,UAAI,WAAW,SAAS,GAAG;AACzB,gBAAQ,IAAI,iBAAiB;AAC7B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,QAAQ;AAER;AAMA,eAAe,OAAsB;AACnC,MAAI;AACF,UAAM,OAAO,UAAU;AAAA,EACzB,SAAS,OAAO;AAEd,UAAM,MAAM;AACZ,UAAM,UAAU,IAAI,WAAW;AAE/B,QAAI,QAAQ,SAAS,qBAAqB,GAAG;AAC3C,cAAQ,MAAM,yDAAoD;AAClE,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,MAAM,sCAAsC;AAAA,IACtD,WAAW,QAAQ,SAAS,MAAM,KAAK,QAAQ,SAAS,MAAM,GAAG;AAC/D,cAAQ,MAAM,6DAAwD;AACtE,cAAQ,MAAM,oDAAoD;AAClE,cAAQ,MAAM,uDAAuD;AAAA,IACvE,OAAO;AACL,cAAQ,MAAM,2BAAsB,WAAW,GAAG;AAClD,UAAI,IAAI,OAAO;AACb,gBAAQ,MAAM,IAAI,KAAK;AAAA,MACzB;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,KAAK;",
6
6
  "names": []
7
7
  }
@@ -15,7 +15,7 @@ import { getContext, setContext, removeContext } from "../context.js";
15
15
  import { Command } from "@commander-js/extra-typings";
16
16
  import { ask } from "../utils/ask.js";
17
17
  import { hasPermissionsToUseTool } from "../permissions.js";
18
- import { getTools } from "../tools.js";
18
+ import { getBuiltInTools, getTools } from "../tools.js";
19
19
  import {
20
20
  getGlobalConfig,
21
21
  getCurrentProjectConfig,
@@ -42,7 +42,7 @@ import { ResumeConversation } from "../screens/ResumeConversation.js";
42
42
  import { startMCPServer } from "./mcp.js";
43
43
  import { getCwd, setCwd, setOriginalCwd } from "../utils/state.js";
44
44
  import { omit } from "lodash-es";
45
- import { getCommands } from "../commands.js";
45
+ import { getBuiltInCommands, getCommands } from "../commands.js";
46
46
  import { getNextAvailableLogForkNumber, loadLogList } from "../utils/log.js";
47
47
  import { loadMessagesFromLog } from "../utils/conversationRecovery.js";
48
48
  import { cleanupOldMessageFilesInBackground } from "../utils/cleanup.js";
@@ -74,8 +74,7 @@ import {
74
74
  import { cursorShow } from "ansi-escapes";
75
75
  import {
76
76
  getLatestVersion,
77
- assertMinVersion,
78
- getUpdateCommandSuggestions
77
+ assertMinVersion
79
78
  } from "../utils/autoUpdater.js";
80
79
  import { ensureConfigDirs } from "../utils/configPaths.js";
81
80
  import { runStartupMigration } from "../utils/migration/index.js";
@@ -84,7 +83,6 @@ import {
84
83
  setLanguage,
85
84
  isLanguageSupported
86
85
  } from "../i18n/index.js";
87
- import { gt } from "semver";
88
86
  import { CACHE_PATHS } from "../utils/log.js";
89
87
  import { onShellCrash } from "../utils/PersistentShell.js";
90
88
  import { clearTerminal, prepareTerminalForREPL } from "../utils/terminal.js";
@@ -94,6 +92,11 @@ import { ConfigParseError } from "../utils/errors.js";
94
92
  import { grantReadPermissionForOriginalDir } from "../utils/permissions/filesystem.js";
95
93
  import { MACRO } from "../constants/macros.js";
96
94
  import { SEMANTIC_COLORS } from "../constants/colors.js";
95
+ import {
96
+ needsSync as needsCCSync,
97
+ syncFromClaudeCode,
98
+ hasClaudeCodeInstallation
99
+ } from "../utils/claudeCodeSync.js";
97
100
  function completeOnboarding() {
98
101
  const config = getGlobalConfig();
99
102
  saveGlobalConfig({
@@ -226,6 +229,38 @@ async function setup(cwd2, safeMode) {
226
229
  const projectConfig = getCurrentProjectConfig();
227
230
  if (projectConfig.lastCost !== void 0 && projectConfig.lastDuration !== void 0) {
228
231
  }
232
+ try {
233
+ const cfg = getGlobalConfig();
234
+ if (hasClaudeCodeInstallation() && needsCCSync()) {
235
+ const shouldSync = cfg.autoSyncClaudeCode === true || cfg.autoSyncClaudeCode === void 0;
236
+ if (shouldSync) {
237
+ syncFromClaudeCode().then((result) => {
238
+ const changed = result.installed.length + result.updated.length;
239
+ const mpRegistered = result.marketplaces.registered.length;
240
+ if (changed > 0 || mpRegistered > 0) {
241
+ const parts = [];
242
+ if (changed > 0) {
243
+ parts.push(
244
+ `${changed} plugin(s) (${result.installed.length} new, ${result.updated.length} updated)`
245
+ );
246
+ }
247
+ if (mpRegistered > 0) {
248
+ parts.push(`${mpRegistered} marketplace(s)`);
249
+ }
250
+ console.log(`Synced from Claude Code: ${parts.join(", ")}`);
251
+ }
252
+ if (cfg.autoSyncClaudeCode === void 0) {
253
+ saveGlobalConfig({
254
+ ...getGlobalConfig(),
255
+ autoSyncClaudeCode: true
256
+ });
257
+ }
258
+ }).catch(() => {
259
+ });
260
+ }
261
+ }
262
+ } catch {
263
+ }
229
264
  }
230
265
  if (process.env.NODE_ENV !== "test") {
231
266
  process.on("uncaughtException", (error) => {
@@ -350,7 +385,7 @@ async function parseArgs(stdinContent, renderContext, rawModeSupported) {
350
385
  ...renderContext,
351
386
  exitOnCtrlC: true
352
387
  };
353
- const commands = await getCommands();
388
+ const commands = getBuiltInCommands();
354
389
  const commandList = commands.filter((cmd) => !cmd.isHidden).map((cmd) => `/${cmd.name} - ${cmd.description}`).join("\n");
355
390
  program.name(PRODUCT_COMMAND).description(
356
391
  `${PRODUCT_NAME} - starts an interactive session by default, use -p/--print for non-interactive output
@@ -393,6 +428,10 @@ ${commandList}`
393
428
  "-l, --lang <language>",
394
429
  "Set interface language (en, zh-CN)",
395
430
  String
431
+ ).option(
432
+ "--plugin-dir <dir>",
433
+ "Additional plugin directory to scan (merged with defaults)",
434
+ String
396
435
  ).action(
397
436
  async (prompt, {
398
437
  cwd: cwd2,
@@ -405,23 +444,29 @@ ${commandList}`
405
444
  strict,
406
445
  new: startNew,
407
446
  resume: selectResume,
408
- lang
447
+ lang,
448
+ pluginDir
409
449
  }) => {
410
450
  if (lang && isLanguageSupported(lang)) {
411
451
  setLanguage(lang);
412
452
  }
453
+ if (pluginDir) {
454
+ const { addExtraPluginDir } = await import("../utils/pluginLoader.js");
455
+ addExtraPluginDir(pluginDir);
456
+ }
413
457
  const envSafetyMode = process.env.MINTO_SAFETY_MODE;
414
458
  const safetyMode = envSafetyMode ? envSafetyMode : strict ? "strict" : smart ? "smart" : free ? "free" : "yolo";
415
459
  const safeMode = safetyMode !== "yolo";
460
+ const inkPromise = import("ink");
461
+ const replPromise = import("../screens/REPL.js");
416
462
  await showSetupScreens(safeMode, print, rawModeSupported);
417
463
  await setup(cwd2, safeMode);
418
464
  assertMinVersion();
419
- const [tools, mcpClients] = await Promise.all([
420
- getTools(
421
- enableArchitect ?? getCurrentProjectConfig().enableArchitectTool
422
- ),
423
- getClients()
424
- ]);
465
+ const isDefaultModel = await isDefaultSlowAndCapableModel();
466
+ const tools = await getBuiltInTools(
467
+ enableArchitect ?? getCurrentProjectConfig().enableArchitectTool,
468
+ { skipDescriptionCache: true }
469
+ );
425
470
  const inputPrompt = [prompt, stdinContent].filter(Boolean).join("\n");
426
471
  if (print) {
427
472
  if (!inputPrompt) {
@@ -430,6 +475,9 @@ ${commandList}`
430
475
  );
431
476
  process.exit(1);
432
477
  }
478
+ const fullTools = await getTools(
479
+ enableArchitect ?? getCurrentProjectConfig().enableArchitectTool
480
+ );
433
481
  addToHistory(inputPrompt);
434
482
  const { resultText: response } = await ask({
435
483
  commands,
@@ -437,28 +485,13 @@ ${commandList}`
437
485
  messageLogName: dateToFilename(/* @__PURE__ */ new Date()),
438
486
  prompt: inputPrompt,
439
487
  cwd: cwd2,
440
- tools,
488
+ tools: fullTools,
441
489
  safeMode,
442
490
  safetyMode
443
491
  });
444
492
  console.log(response);
445
493
  process.exit(0);
446
494
  } else {
447
- const isDefaultModel = await isDefaultSlowAndCapableModel();
448
- const updateInfo = await (async () => {
449
- try {
450
- const latest = await getLatestVersion();
451
- if (latest && gt(latest, MACRO.VERSION)) {
452
- const cmds = await getUpdateCommandSuggestions();
453
- return { version: latest, commands: cmds };
454
- }
455
- } catch {
456
- }
457
- return {
458
- version: null,
459
- commands: null
460
- };
461
- })();
462
495
  if (renderContext?.fallbackMode) {
463
496
  requireRawModeOrExit(process.stdin, true);
464
497
  }
@@ -482,38 +515,11 @@ ${commandList}`
482
515
  context2.unmount = unmount;
483
516
  return;
484
517
  }
485
- let messageLogName = dateToFilename(/* @__PURE__ */ new Date());
486
- let initialMessages;
487
- let initialForkNumber;
488
- if (!startNew && !inputPrompt) {
489
- try {
490
- const logs = await loadLogList(CACHE_PATHS.messages());
491
- for (const log of logs) {
492
- try {
493
- const loadedMessages = await loadMessagesFromLog(
494
- log.fullPath,
495
- tools
496
- );
497
- if (loadedMessages && loadedMessages.length > 0) {
498
- initialMessages = loadedMessages;
499
- messageLogName = log.date;
500
- initialForkNumber = getNextAvailableLogForkNumber(
501
- log.date,
502
- log.forkNumber ?? 1,
503
- 0
504
- );
505
- break;
506
- }
507
- } catch {
508
- continue;
509
- }
510
- }
511
- } catch {
512
- }
513
- }
518
+ const messageLogName = dateToFilename(/* @__PURE__ */ new Date());
519
+ const shouldAutoResume = !startNew && !inputPrompt;
514
520
  {
515
- const { render } = await import("ink");
516
- const { REPL } = await import("../screens/REPL.js");
521
+ const { render } = await inkPromise;
522
+ const { REPL } = await replPromise;
517
523
  await prepareTerminalForREPL();
518
524
  render(
519
525
  /* @__PURE__ */ React.createElement(
@@ -523,18 +529,18 @@ ${commandList}`
523
529
  debug,
524
530
  initialPrompt: inputPrompt,
525
531
  messageLogName,
526
- initialForkNumber,
527
- initialMessages,
528
532
  shouldShowPromptInput: true,
529
533
  verbose,
530
534
  tools,
531
535
  safeMode,
532
536
  safetyMode,
533
- mcpClients,
537
+ mcpClients: [],
534
538
  isDefaultModel,
535
- initialUpdateVersion: updateInfo.version,
536
- initialUpdateCommands: updateInfo.commands,
537
- fallbackMode: renderContext?.fallbackMode
539
+ initialUpdateVersion: null,
540
+ initialUpdateCommands: null,
541
+ enableArchitect: enableArchitect ?? getCurrentProjectConfig().enableArchitectTool,
542
+ fallbackMode: renderContext?.fallbackMode,
543
+ autoResume: shouldAutoResume
538
544
  }
539
545
  ),
540
546
  renderContext
@@ -860,8 +866,12 @@ Run "${PRODUCT_COMMAND}" to start using your configured setup.`
860
866
  for (const [name, server] of Object.entries(servers)) {
861
867
  if (server.type === "sse") {
862
868
  console.log(`${name}: ${server.url} (SSE)`);
869
+ } else if (server.type === "http") {
870
+ console.log(`${name}: ${server.url} (HTTP)`);
863
871
  } else {
864
- console.log(`${name}: ${server.command} ${server.args.join(" ")}`);
872
+ console.log(
873
+ `${name}: ${server.command} ${(server.args || []).join(" ")}`
874
+ );
865
875
  }
866
876
  }
867
877
  }
@@ -920,10 +930,13 @@ Run "${PRODUCT_COMMAND}" to start using your configured setup.`
920
930
  if (server.type === "sse") {
921
931
  console.log(` Type: sse`);
922
932
  console.log(` URL: ${server.url}`);
933
+ } else if (server.type === "http") {
934
+ console.log(` Type: http`);
935
+ console.log(` URL: ${server.url}`);
923
936
  } else {
924
937
  console.log(` Type: stdio`);
925
938
  console.log(` Command: ${server.command}`);
926
- console.log(` Args: ${server.args.join(" ")}`);
939
+ console.log(` Args: ${(server.args || []).join(" ")}`);
927
940
  if (server.env) {
928
941
  console.log(" Environment:");
929
942
  for (const [key, value] of Object.entries(server.env)) {