@pheem49/mint 1.5.5 → 1.6.1

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 (222) hide show
  1. package/.codex +0 -0
  2. package/.github/FUNDING.yml +2 -0
  3. package/.github/workflows/ci.yml +45 -0
  4. package/.github/workflows/release.yml +79 -0
  5. package/Cargo.lock +5792 -0
  6. package/Cargo.toml +32 -0
  7. package/README.md +387 -353
  8. package/assets/icon.png +0 -0
  9. package/bin/mint +0 -0
  10. package/crates/mint-cli/Cargo.toml +23 -0
  11. package/crates/mint-cli/src/agent.rs +851 -0
  12. package/crates/mint-cli/src/gmail.rs +216 -0
  13. package/crates/mint-cli/src/image.rs +142 -0
  14. package/crates/mint-cli/src/main.rs +2837 -0
  15. package/crates/mint-cli/src/mcp.rs +63 -0
  16. package/crates/mint-cli/src/onboard.rs +1149 -0
  17. package/crates/mint-cli/src/setup.rs +390 -0
  18. package/crates/mint-cli/src/skills.rs +8 -0
  19. package/crates/mint-cli/src/updater.rs +279 -0
  20. package/crates/mint-core/Cargo.toml +22 -0
  21. package/crates/mint-core/src/agent_loop.rs +94 -0
  22. package/crates/mint-core/src/api_server.rs +991 -0
  23. package/crates/mint-core/src/channels.rs +248 -0
  24. package/crates/mint-core/src/chat.rs +895 -0
  25. package/crates/mint-core/src/code_tools.rs +729 -0
  26. package/crates/mint-core/src/config.rs +368 -0
  27. package/crates/mint-core/src/files.rs +159 -0
  28. package/crates/mint-core/src/knowledge.rs +541 -0
  29. package/crates/mint-core/src/lib.rs +84 -0
  30. package/crates/mint-core/src/mcp.rs +273 -0
  31. package/crates/mint-core/src/memory.rs +673 -0
  32. package/crates/mint-core/src/orchestration.rs +2157 -0
  33. package/crates/mint-core/src/pictures.rs +314 -0
  34. package/crates/mint-core/src/plugins.rs +727 -0
  35. package/crates/mint-core/src/safety.rs +416 -0
  36. package/crates/mint-core/src/semantic.rs +254 -0
  37. package/crates/mint-core/src/shell.rs +317 -0
  38. package/crates/mint-core/src/skills.rs +71 -0
  39. package/crates/mint-core/src/symbols.rs +157 -0
  40. package/crates/mint-core/src/tasks.rs +308 -0
  41. package/crates/mint-core/src/tts.rs +92 -0
  42. package/crates/mint-core/src/weather.rs +93 -0
  43. package/crates/mint-core/src/web_search.rs +200 -0
  44. package/crates/mint-core/src/workflows.rs +81 -0
  45. package/crates/mint-core/tests/mcp_stdio.rs +45 -0
  46. package/crates/mint-core/tests/memory_persistence.rs +172 -0
  47. package/crates/mint-core/tests/pictures_storage.rs +14 -0
  48. package/crates/mint-core/tests/task_lifecycle.rs +87 -0
  49. package/package.json +35 -99
  50. package/src/bin/index.js +16 -0
  51. package/src/renderer/index-web.html +17 -0
  52. package/src/renderer/index.html +17 -0
  53. package/src/renderer/public/Live2DCubismCore.js +9 -0
  54. package/src/renderer/public/assets/icon.png +0 -0
  55. package/src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.model3.json +36 -0
  56. package/src/renderer/src/App.tsx +33 -0
  57. package/src/renderer/src/calculator.ts +47 -0
  58. package/src/renderer/src/components/ChatPanel.tsx +1598 -0
  59. package/src/renderer/src/components/DashboardSidebar.tsx +358 -0
  60. package/src/renderer/src/components/Live2DStage.tsx +374 -0
  61. package/src/renderer/src/components/MintDashboard.tsx +950 -0
  62. package/src/renderer/src/components/ModelPanel.tsx +154 -0
  63. package/src/renderer/src/components/PicturesLibrary.tsx +46 -0
  64. package/src/renderer/src/components/ProactiveGlow.tsx +19 -0
  65. package/src/renderer/src/components/ScreenPicker.tsx +579 -0
  66. package/src/renderer/src/components/SettingsWindow.tsx +1467 -0
  67. package/src/renderer/src/components/SpotlightWindow.tsx +280 -0
  68. package/src/renderer/src/components/WidgetWindow.tsx +36 -0
  69. package/src/renderer/src/components/WorkspacePanel.tsx +268 -0
  70. package/src/{UI → renderer/src/css}/settings.css +69 -16
  71. package/src/renderer/src/css/spotlight.css +113 -0
  72. package/src/renderer/src/css/styles.css +3722 -0
  73. package/src/renderer/src/css/widget.css +185 -0
  74. package/src/renderer/src/env.d.ts +116 -0
  75. package/src/renderer/src/index.css +379 -0
  76. package/src/renderer/src/main.tsx +13 -0
  77. package/src/renderer/src/tauri.ts +996 -0
  78. package/src/renderer/src-web/App.tsx +25 -0
  79. package/src/renderer/src-web/calculator.ts +47 -0
  80. package/src/renderer/src-web/components/ChatPanel.tsx +1662 -0
  81. package/src/renderer/src-web/components/DashboardSidebar.tsx +242 -0
  82. package/src/renderer/src-web/components/MintDashboard.tsx +763 -0
  83. package/src/renderer/src-web/components/PicturesLibrary.tsx +73 -0
  84. package/src/renderer/src-web/components/SettingsWindow.tsx +1500 -0
  85. package/src/renderer/src-web/css/settings.css +1100 -0
  86. package/src/{UI → renderer/src-web/css}/spotlight.css +4 -4
  87. package/src/{UI → renderer/src-web/css}/styles.css +1055 -159
  88. package/src/{UI → renderer/src-web/css}/widget.css +2 -2
  89. package/src/renderer/src-web/env.d.ts +107 -0
  90. package/src/renderer/src-web/index.css +379 -0
  91. package/src/renderer/src-web/main.tsx +13 -0
  92. package/src/renderer/src-web/tauri.ts +983 -0
  93. package/tsconfig.json +30 -0
  94. package/vite.config.ts +33 -0
  95. package/vite.config.web.ts +51 -0
  96. package/GUIDE_TH.md +0 -125
  97. package/assets/Agent_Mint.png +0 -0
  98. package/assets/CLI_Screen.png +0 -0
  99. package/assets/Settings.png +0 -0
  100. package/benchmark_ai.js +0 -71
  101. package/install.ps1 +0 -64
  102. package/install.sh +0 -54
  103. package/main.js +0 -139
  104. package/mint-cli-logic.js +0 -3
  105. package/mint-cli.js +0 -410
  106. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +0 -47
  107. package/models/Shiroko_Model/Shiroko//342/232/241/351/253/230/344/272/256/342/232/241/344/275/277/347/224/250/346/225/231/347/250/213/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.txt +0 -23
  108. package/preload-picker.js +0 -11
  109. package/preload-settings.js +0 -11
  110. package/preload.js +0 -41
  111. package/scripts/install_linux_desktop_entry.js +0 -48
  112. package/src/AI_Brain/Gemini_API.js +0 -813
  113. package/src/AI_Brain/agent_orchestrator.js +0 -73
  114. package/src/AI_Brain/autonomous_brain.js +0 -179
  115. package/src/AI_Brain/behavior_memory.js +0 -135
  116. package/src/AI_Brain/headless_agent.js +0 -143
  117. package/src/AI_Brain/knowledge_base.js +0 -349
  118. package/src/AI_Brain/memory_store.js +0 -662
  119. package/src/AI_Brain/proactive_engine.js +0 -172
  120. package/src/AI_Brain/provider_adapter.js +0 -365
  121. package/src/Automation_Layer/browser_automation.js +0 -149
  122. package/src/Automation_Layer/file_operations.js +0 -286
  123. package/src/Automation_Layer/open_app.js +0 -85
  124. package/src/Automation_Layer/open_website.js +0 -38
  125. package/src/CLI/approval_handler.js +0 -47
  126. package/src/CLI/chat_router.js +0 -247
  127. package/src/CLI/chat_ui.js +0 -1159
  128. package/src/CLI/cli_colors.js +0 -115
  129. package/src/CLI/cli_formatters.js +0 -94
  130. package/src/CLI/code_agent.js +0 -1667
  131. package/src/CLI/code_session_memory.js +0 -62
  132. package/src/CLI/gmail_auth.js +0 -210
  133. package/src/CLI/image_input.js +0 -90
  134. package/src/CLI/intent_detectors.js +0 -181
  135. package/src/CLI/interactive_chat.js +0 -658
  136. package/src/CLI/list_features.js +0 -64
  137. package/src/CLI/onboarding.js +0 -416
  138. package/src/CLI/repo_summarizer.js +0 -282
  139. package/src/CLI/semantic_code_search.js +0 -312
  140. package/src/CLI/skill_manager.js +0 -41
  141. package/src/CLI/slash_command_handler.js +0 -418
  142. package/src/CLI/symbol_indexer.js +0 -231
  143. package/src/CLI/updater.js +0 -230
  144. package/src/CLI/workspace_manager.js +0 -90
  145. package/src/Channels/brave_search_bridge.js +0 -35
  146. package/src/Channels/discord_bridge.js +0 -66
  147. package/src/Channels/google_search_bridge.js +0 -38
  148. package/src/Channels/line_bridge.js +0 -60
  149. package/src/Channels/slack_bridge.js +0 -48
  150. package/src/Channels/telegram_bridge.js +0 -41
  151. package/src/Channels/whatsapp_bridge.js +0 -57
  152. package/src/Command_Parser/parser.js +0 -45
  153. package/src/Plugins/dev_tools.js +0 -41
  154. package/src/Plugins/discord.js +0 -20
  155. package/src/Plugins/docker.js +0 -47
  156. package/src/Plugins/gmail.js +0 -251
  157. package/src/Plugins/google_calendar.js +0 -252
  158. package/src/Plugins/mcp_manager.js +0 -95
  159. package/src/Plugins/notion.js +0 -256
  160. package/src/Plugins/obsidian.js +0 -54
  161. package/src/Plugins/plugin_manager.js +0 -81
  162. package/src/Plugins/spotify.js +0 -173
  163. package/src/Plugins/system_metrics.js +0 -31
  164. package/src/Plugins/system_monitor.js +0 -72
  165. package/src/System/action_executor.js +0 -178
  166. package/src/System/bridge_manager.js +0 -76
  167. package/src/System/chat_history_manager.js +0 -83
  168. package/src/System/config_manager.js +0 -194
  169. package/src/System/custom_workflows.js +0 -163
  170. package/src/System/daemon_manager.js +0 -67
  171. package/src/System/google_tts_urls.js +0 -51
  172. package/src/System/granular_automation.js +0 -157
  173. package/src/System/ipc_handlers.js +0 -332
  174. package/src/System/notifications.js +0 -23
  175. package/src/System/optional_require.js +0 -23
  176. package/src/System/picture_store.js +0 -109
  177. package/src/System/proactive_loop.js +0 -153
  178. package/src/System/safety_manager.js +0 -273
  179. package/src/System/sandbox_runner.js +0 -182
  180. package/src/System/screen_capture.js +0 -175
  181. package/src/System/smart_context.js +0 -227
  182. package/src/System/system_automation.js +0 -162
  183. package/src/System/system_events.js +0 -79
  184. package/src/System/system_info.js +0 -125
  185. package/src/System/task_manager.js +0 -222
  186. package/src/System/tool_registry.js +0 -293
  187. package/src/System/window_manager.js +0 -220
  188. package/src/UI/floating.css +0 -80
  189. package/src/UI/floating.html +0 -17
  190. package/src/UI/floating.js +0 -67
  191. package/src/UI/live2d_manager.js +0 -600
  192. package/src/UI/preload-floating.js +0 -7
  193. package/src/UI/preload-spotlight.js +0 -11
  194. package/src/UI/preload-widget.js +0 -5
  195. package/src/UI/proactive-glow.html +0 -42
  196. package/src/UI/renderer.js +0 -2127
  197. package/src/UI/screenPicker.html +0 -214
  198. package/src/UI/screenPicker.js +0 -262
  199. package/src/UI/settings.html +0 -577
  200. package/src/UI/settings.js +0 -770
  201. package/src/UI/spotlight.html +0 -23
  202. package/src/UI/spotlight.js +0 -185
  203. package/src/UI/widget.html +0 -29
  204. package/src/UI/widget.js +0 -10
  205. /package/{models → src/renderer/public/models}/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
  206. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/apron.exp3.json} +0 -0
  207. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/catfilter.exp3.json} +0 -0
  208. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/click.exp3.json} +0 -0
  209. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/dazed.exp3.json} +0 -0
  210. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253/347/234/274/347/217/240/346/221/207/346/231/203.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/dazedeyes.exp3.json} +0 -0
  211. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/glasses.exp3.json} +0 -0
  212. /package/{models → src/renderer/public/models}/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +0 -0
  213. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/pen.exp3.json} +0 -0
  214. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/photo.exp3.json} +0 -0
  215. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_00.png} +0 -0
  216. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_01.png} +0 -0
  217. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_02.png} +0 -0
  218. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_03.png} +0 -0
  219. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.cdi3.json} +0 -0
  220. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.moc3} +0 -0
  221. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.physics3.json} +0 -0
  222. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.vtube.json} +0 -0
@@ -1,658 +0,0 @@
1
- 'use strict';
2
-
3
- const crypto = require('crypto');
4
- const { colors, exitWithGoodbye } = require('./cli_colors');
5
- const { splitResponseSentences } = require('./cli_formatters');
6
- const {
7
- isRepoSummaryRequest,
8
- parseRepoSummaryArgs,
9
- isSymbolIndexRequest,
10
- parseSymbolIndexArgs,
11
- isSemanticCodeSearchRequest,
12
- parseSemanticCodeArgs,
13
- extractSemanticCodeQuery
14
- } = require('./intent_detectors');
15
- const { handleSlashCommandUI } = require('./slash_command_handler');
16
- const { createChatUI } = require('./chat_ui');
17
- const { loadImageAsDataUri, loadClipboardImageAsDataUri } = require('./image_input');
18
- const { summarizeRepository, formatRepoSummary } = require('./repo_summarizer');
19
- const { buildSymbolIndex, formatSymbolIndex } = require('./symbol_indexer');
20
- const {
21
- indexSemanticCode,
22
- searchSemanticCode,
23
- formatSemanticCodeIndex,
24
- formatSemanticCodeSearch
25
- } = require('./semantic_code_search');
26
- const { handleChat, getChatTranscript } = require('../AI_Brain/Gemini_API');
27
- const agentOrchestrator = require('../AI_Brain/agent_orchestrator');
28
- const systemMonitor = require('../Plugins/system_monitor');
29
- const workspaceManager = require('./workspace_manager');
30
- const { executeCodeTask } = require('./code_agent');
31
- const { resetChat } = require('../AI_Brain/Gemini_API');
32
- const { saveChatImages } = require('../System/picture_store');
33
-
34
- const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
35
-
36
- function createSessionStats() {
37
- return {
38
- sessionId: crypto.randomUUID(),
39
- startedAt: Date.now(),
40
- activeStartedAt: null,
41
- agentActiveMs: 0,
42
- toolCalls: { total: 0, success: 0, failed: 0 },
43
- modelUsage: {}
44
- };
45
- }
46
-
47
- function addUsageRow(stats, row = {}) {
48
- const provider = row.provider || 'unknown';
49
- const model = row.model || 'unknown';
50
- const key = `${provider}:${model}`;
51
- if (!stats.modelUsage[key]) {
52
- stats.modelUsage[key] = {
53
- provider,
54
- model,
55
- requests: 0,
56
- inputTokens: 0,
57
- cacheReads: 0,
58
- outputTokens: 0,
59
- reasoningTokens: 0,
60
- totalTokens: 0
61
- };
62
- }
63
-
64
- const target = stats.modelUsage[key];
65
- target.requests += Number(row.requests) || 1;
66
- target.inputTokens += Number(row.inputTokens) || 0;
67
- target.cacheReads += Number(row.cacheReads) || 0;
68
- target.outputTokens += Number(row.outputTokens) || 0;
69
- target.reasoningTokens += Number(row.reasoningTokens) || 0;
70
- target.totalTokens += Number(row.totalTokens) || 0;
71
- }
72
-
73
- function normalizeProviderUsage(providerInfo = {}) {
74
- const usage = providerInfo.usage;
75
- if (Array.isArray(usage)) return usage;
76
- if (!usage || typeof usage !== 'object') {
77
- return [{
78
- provider: providerInfo.provider,
79
- model: providerInfo.model,
80
- requests: 1
81
- }];
82
- }
83
-
84
- return [{
85
- provider: providerInfo.provider,
86
- model: providerInfo.model,
87
- requests: 1,
88
- inputTokens: usage.promptTokenCount || usage.prompt_tokens || usage.input_tokens,
89
- cacheReads: usage.cachedContentTokenCount ||
90
- (usage.prompt_tokens_details && usage.prompt_tokens_details.cached_tokens) ||
91
- usage.cache_read_input_tokens,
92
- outputTokens: usage.candidatesTokenCount || usage.completion_tokens || usage.output_tokens,
93
- reasoningTokens: usage.thoughtsTokenCount ||
94
- (usage.completion_tokens_details && usage.completion_tokens_details.reasoning_tokens),
95
- totalTokens: usage.totalTokenCount || usage.total_tokens
96
- }];
97
- }
98
-
99
- function recordProviderInfo(stats, providerInfo) {
100
- if (!providerInfo) return;
101
- for (const row of normalizeProviderUsage(providerInfo)) {
102
- addUsageRow(stats, row);
103
- }
104
- }
105
-
106
- function markAgentActive(stats, active) {
107
- const now = Date.now();
108
- if (active && !stats.activeStartedAt) {
109
- stats.activeStartedAt = now;
110
- return;
111
- }
112
- if (!active && stats.activeStartedAt) {
113
- stats.agentActiveMs += now - stats.activeStartedAt;
114
- stats.activeStartedAt = null;
115
- }
116
- }
117
-
118
- function buildExitSummary(stats) {
119
- const activeMs = stats.agentActiveMs + (stats.activeStartedAt ? Date.now() - stats.activeStartedAt : 0);
120
- const total = stats.toolCalls.total;
121
- return {
122
- message: 'Goodbye! See you again.',
123
- sessionId: stats.sessionId,
124
- toolCalls: {
125
- ...stats.toolCalls,
126
- successRate: total ? (stats.toolCalls.success / total) * 100 : 0
127
- },
128
- wallMs: Date.now() - stats.startedAt,
129
- agentActiveMs: activeMs,
130
- modelUsage: Object.values(stats.modelUsage),
131
- quotaHint: 'Use /models to view model quota information'
132
- };
133
- }
134
-
135
- // ---------------------------------------------------------------------------
136
- // Internal helpers
137
- // ---------------------------------------------------------------------------
138
-
139
- /**
140
- * Streams response text sentence-by-sentence into the TUI.
141
- */
142
- async function streamAssistantSentences(text, appendMessage, metadata = {}, streamMessage = null) {
143
- const sentences = splitResponseSentences(text);
144
- const chunks = sentences.filter(s => String(s || '').trim());
145
-
146
- if (typeof streamMessage === 'function') {
147
- const stream = streamMessage(metadata);
148
- for (let i = 0; i < chunks.length; i++) {
149
- stream.appendChunk(chunks[i]);
150
- if (i < chunks.length - 1) await sleep(90);
151
- }
152
- stream.finalize();
153
- return;
154
- }
155
-
156
- for (let i = 0; i < chunks.length; i++) {
157
- appendMessage('assistant', chunks[i], i === 0 ? metadata : {});
158
- if (i < chunks.length - 1) await sleep(90);
159
- }
160
- }
161
-
162
- /**
163
- * Runs a timer that increments seconds and calls setThinking every 1s.
164
- * Returns a cancel function.
165
- */
166
- function startThinkingTimer(setThinking) {
167
- let seconds = 0;
168
- setThinking(true, seconds);
169
- const timer = setInterval(() => {
170
- seconds++;
171
- setThinking(true, seconds);
172
- }, 1000);
173
- return () => clearInterval(timer);
174
- }
175
-
176
- // ---------------------------------------------------------------------------
177
- // Local tool message senders
178
- // ---------------------------------------------------------------------------
179
-
180
- async function sendRepoSummaryMessage({ rawArgs = '', appendMessage, streamMessage, setThinking }) {
181
- const formatErr = (err) => err && err.message ? err.message : String(err || 'Unknown error');
182
- try {
183
- if (typeof setThinking === 'function') setThinking(false);
184
- const opts = parseRepoSummaryArgs(rawArgs);
185
- const summary = summarizeRepository(opts.targetPath);
186
- const responseText = opts.json ? JSON.stringify(summary, null, 2) : formatRepoSummary(summary);
187
- await streamAssistantSentences(responseText, appendMessage, {}, streamMessage);
188
- return responseText;
189
- } catch (err) {
190
- if (typeof setThinking === 'function') setThinking(false);
191
- appendMessage('error', formatErr(err));
192
- return '';
193
- }
194
- }
195
-
196
- async function sendSymbolIndexMessage({ rawArgs = '', appendMessage, streamMessage, setThinking }) {
197
- const formatErr = (err) => err && err.message ? err.message : String(err || 'Unknown error');
198
- try {
199
- if (typeof setThinking === 'function') setThinking(false);
200
- const opts = parseSymbolIndexArgs(rawArgs);
201
- const index = buildSymbolIndex(opts.targetPath);
202
- const responseText = opts.json
203
- ? JSON.stringify(index, null, 2)
204
- : formatSymbolIndex(index, { limit: opts.limit });
205
- await streamAssistantSentences(responseText, appendMessage, {}, streamMessage);
206
- return responseText;
207
- } catch (err) {
208
- if (typeof setThinking === 'function') setThinking(false);
209
- appendMessage('error', formatErr(err));
210
- return '';
211
- }
212
- }
213
-
214
- async function sendSemanticCodeMessage({ rawArgs = '', appendMessage, streamMessage, setThinking, appendCodeStep }) {
215
- const formatErr = (err) => err && err.message ? err.message : String(err || 'Unknown error');
216
- const opts = parseSemanticCodeArgs(rawArgs);
217
- try {
218
- if (typeof setThinking === 'function') setThinking(true, 0);
219
- let responseText;
220
-
221
- if (opts.mode === 'index') {
222
- const index = await indexSemanticCode(opts.targetPath, {
223
- onProgress: (info) => {
224
- if (typeof appendCodeStep === 'function' &&
225
- (info.current === 1 || info.current === info.total || info.current % 25 === 0)) {
226
- appendCodeStep({ action: 'semantic_code_index', target: `${info.current}/${info.total} ${info.file}` });
227
- }
228
- }
229
- });
230
- responseText = opts.json ? JSON.stringify(index, null, 2) : formatSemanticCodeIndex(index);
231
- } else {
232
- if (!opts.query) throw new Error('Usage: /semantic-code search <query>');
233
- const results = await searchSemanticCode(opts.query, opts.targetPath, { topK: opts.topK });
234
- responseText = opts.json ? JSON.stringify(results, null, 2) : formatSemanticCodeSearch(results);
235
- }
236
-
237
- if (typeof setThinking === 'function') setThinking(false);
238
- await streamAssistantSentences(responseText, appendMessage, {}, streamMessage);
239
- return responseText;
240
- } catch (err) {
241
- if (typeof setThinking === 'function') setThinking(false);
242
- appendMessage('error', formatErr(err));
243
- return '';
244
- }
245
- }
246
-
247
- function hasAllImageLabels(message = '', imageCount = 0) {
248
- const text = String(message || '');
249
- for (let index = 1; index <= imageCount; index++) {
250
- if (!text.includes(`[Image #${index}]`)) return false;
251
- }
252
- return imageCount > 0;
253
- }
254
-
255
- function formatImageDisplayMessage(message = '', labels = '', imageCount = 0) {
256
- if (!labels) return message;
257
- return hasAllImageLabels(message, imageCount) ? message : `${message}\n${labels}`;
258
- }
259
-
260
- async function sendImageMessage({ images, image, prompt, appendMessage, streamMessage, setThinking, appendCodeStep, stats }) {
261
- const formatErr = (err) => err && err.message ? err.message : String(err || 'Unknown error');
262
- const imageList = images || (image ? [image] : []);
263
- const message = prompt || 'Analyze this image.';
264
- const labels = imageList.map((_, i) => `[Image #${i + 1}]`).join(' ');
265
- const displayMessage = formatImageDisplayMessage(message, labels, imageList.length);
266
-
267
- appendMessage('user', displayMessage);
268
- if (appendCodeStep) {
269
- appendCodeStep({
270
- thought: imageList.length > 1
271
- ? `Analyzing ${imageList.length} attached images before answering.`
272
- : 'Analyzing the attached image before answering.'
273
- });
274
- }
275
-
276
- const cancelTimer = startThinkingTimer(setThinking);
277
- if (stats) markAgentActive(stats, true);
278
- try {
279
- const imageDataUris = imageList.map(item => item.dataUri);
280
- const result = await handleChat(message, imageDataUris, null);
281
- try {
282
- saveChatImages(imageDataUris, { source: 'cli', message });
283
- } catch (saveError) {
284
- console.error('[Pictures] Failed to save CLI image:', saveError.message);
285
- }
286
- cancelTimer();
287
- setThinking(false);
288
- if (stats) markAgentActive(stats, false);
289
-
290
- const responseText = result.response || '';
291
- if (stats) recordProviderInfo(stats, result.providerInfo);
292
- await streamAssistantSentences(responseText, appendMessage, { providerInfo: result.providerInfo }, streamMessage);
293
- return { responseText, labels, imageList };
294
- } catch (err) {
295
- cancelTimer();
296
- setThinking(false);
297
- if (stats) markAgentActive(stats, false);
298
- appendMessage('error', formatErr(err));
299
- return { responseText: '', labels, imageList };
300
- }
301
- }
302
-
303
- // ---------------------------------------------------------------------------
304
- // Agent task execution (shared by onSubmit + initial message)
305
- // ---------------------------------------------------------------------------
306
-
307
- async function runAgentTask(text, { appendMessage, streamMessage, setThinking, requestApproval, askUser, setMode, appendCodeStep }, sharedState) {
308
- const formatErr = (err) => err && err.message ? err.message : String(err || 'Unknown error');
309
- const transcript = await getChatTranscript();
310
- const contextualHistory = sharedState.recentImageContextText
311
- ? [...transcript, { sender: 'system', text: sharedState.recentImageContextText, timestamp: new Date().toISOString() }]
312
- : transcript;
313
-
314
- if (setMode) setMode('Agent');
315
- const cancelTimer = startThinkingTimer(setThinking);
316
- markAgentActive(sharedState.stats, true);
317
-
318
- try {
319
- const config = require('../System/config_manager').readConfig();
320
- const availableProviders = require('../System/config_manager').getAvailableProviders(config);
321
- const preferredProvider = require('./code_agent')._helpers.selectSupportedCodeProvider(config, availableProviders);
322
- let streamedFinalSummary = false;
323
-
324
- const result = await executeCodeTask(text, {
325
- cwd: process.cwd(),
326
- requestApproval,
327
- askUser,
328
- provider: preferredProvider,
329
- history: contextualHistory,
330
- signal: sharedState.abortController?.signal,
331
- onProgress: (info) => {
332
- if (info && info.phase === 'tool_call') {
333
- sharedState.stats.toolCalls.total += 1;
334
- if (info.status === 'success') sharedState.stats.toolCalls.success += 1;
335
- else sharedState.stats.toolCalls.failed += 1;
336
- }
337
- if (appendCodeStep) appendCodeStep(info);
338
- },
339
- onFinalSummary: async (info) => {
340
- cancelTimer();
341
- setThinking(false);
342
- markAgentActive(sharedState.stats, false);
343
- recordProviderInfo(sharedState.stats, info.providerInfo);
344
- streamedFinalSummary = true;
345
- await streamAssistantSentences(info.summary, appendMessage, { providerInfo: info.providerInfo }, streamMessage);
346
- }
347
- });
348
-
349
- cancelTimer();
350
- setThinking(false);
351
- markAgentActive(sharedState.stats, false);
352
- sharedState.lastResponseText = result.summary;
353
- if (!streamedFinalSummary) {
354
- recordProviderInfo(sharedState.stats, result.providerInfo);
355
- await streamAssistantSentences(result.summary, appendMessage, { providerInfo: result.providerInfo }, streamMessage);
356
- }
357
- } catch (err) {
358
- cancelTimer();
359
- setThinking(false);
360
- markAgentActive(sharedState.stats, false);
361
- appendMessage('error', formatErr(err));
362
- } finally {
363
- if (setMode) setMode('Agent');
364
- }
365
- }
366
-
367
- // ---------------------------------------------------------------------------
368
- // Public: startInteractiveChat
369
- // ---------------------------------------------------------------------------
370
-
371
- /**
372
- * Starts the interactive TUI chat session.
373
- *
374
- * @param {string|null} initialMessage Optional first message (from CLI arg).
375
- * @param {{ imagePath?: string }} options
376
- */
377
- async function startInteractiveChat(initialMessage = null, options = {}) {
378
- const formatErr = (err) => err && err.message ? err.message : String(err || 'Unknown error');
379
-
380
- // Shared mutable state between onSubmit closures
381
- const sharedState = {
382
- lastResponseText: '',
383
- recentImageContextText: '',
384
- isBusy: false,
385
- abortController: null,
386
- stats: createSessionStats()
387
- };
388
-
389
- // -----------------------------------------------------------------------
390
- let ui;
391
- ui = await createChatUI({
392
- onPasteImage: async () => {
393
- try {
394
- const image = loadClipboardImageAsDataUri();
395
- return { label: image.path, image };
396
- } catch (err) {
397
- throw new Error(formatErr(err));
398
- }
399
- },
400
-
401
- onCancel: () => {
402
- if (sharedState.abortController) {
403
- sharedState.abortController.abort();
404
- }
405
- },
406
-
407
- onSubmit: async (text, submitOptions = {}) => {
408
- if (sharedState.isBusy) {
409
- ui.appendMessage('system', 'Mint is still working on the previous request. Please wait for it to finish before sending another command.');
410
- return;
411
- }
412
- sharedState.isBusy = true;
413
- sharedState.abortController = new AbortController();
414
-
415
- const {
416
- appendMessage, streamMessage, setThinking, updateStatusModel,
417
- copyLastResponse, requestApproval, setMode, appendCodeStep,
418
- updateWorkspace, askUser, attachImage, setInputText,
419
- setPendingPasteText, setFastMode, toggleFastMode, getFastMode
420
- } = ui;
421
-
422
- try {
423
- // ── Image submission ────────────────────────────────────────
424
- if (submitOptions.images && submitOptions.images.length > 0) {
425
- const images = submitOptions.images.map(item => item.image || item);
426
- const { responseText, labels, imageList } = await sendImageMessage({
427
- images,
428
- prompt: text.trim() || 'Analyze this image.',
429
- appendMessage, streamMessage, setThinking, appendCodeStep,
430
- stats: sharedState.stats
431
- });
432
- sharedState.lastResponseText = responseText;
433
- if (responseText) {
434
- sharedState.recentImageContextText = [
435
- `Recent image context: the user attached ${imageList.length} image(s) labelled ${labels || '[Image #1]'}.`,
436
- 'The terminal UI displays image attachments as labels only; it does not render thumbnails inside the chat.',
437
- `Assistant response to those image(s): ${responseText}`
438
- ].join('\n');
439
- }
440
- return;
441
- }
442
-
443
- // ── Slash commands ──────────────────────────────────────────
444
- if (text.startsWith('/')) {
445
- if (text.startsWith('/agent')) {
446
- const aArgs = text.split(' ');
447
- if (aArgs[1] === 'list') {
448
- appendMessage('system', `Available Agents: ${agentOrchestrator.listAgents().join(', ')}`);
449
- } else if (aArgs[1]) {
450
- const success = agentOrchestrator.setAgent(aArgs[1]);
451
- if (success) {
452
- const agent = agentOrchestrator.getCurrentAgent();
453
- appendMessage('system', `Switched to Agent: ${agent.icon} ${agent.name}`);
454
- updateStatusModel(agent.name);
455
- resetChat();
456
- } else {
457
- appendMessage('error', `Agent "${aArgs[1]}" not found. Try /agent list`);
458
- }
459
- } else {
460
- const agent = agentOrchestrator.getCurrentAgent();
461
- appendMessage('system', `Current Agent: ${agent.icon} ${agent.name}\nUsage: /agent <type> or /agent list`);
462
- }
463
- return;
464
- }
465
-
466
- if (text.startsWith('/stats')) {
467
- appendMessage('system', '📊 Fetching system statistics...');
468
- const stats = await systemMonitor.execute('stats');
469
- appendMessage('system', stats);
470
- return;
471
- }
472
-
473
- if (text.startsWith('/workspace')) {
474
- const wArgs = text.split(' ');
475
- const subCmd = wArgs[1];
476
- if (subCmd === 'add') {
477
- const name = wArgs[2];
478
- const wsPath = wArgs[3] || '.';
479
- const instructions = wArgs.slice(4).join(' ');
480
- if (!name) {
481
- appendMessage('error', 'Usage: /workspace add <name> [path] [instructions]');
482
- } else {
483
- workspaceManager.addWorkspace(name, wsPath, instructions);
484
- appendMessage('system', `Workspace "${name}" registered at ${require('path').resolve(wsPath)}`);
485
- resetChat();
486
- }
487
- } else if (subCmd === 'list') {
488
- const all = workspaceManager.listWorkspaces();
489
- let listMsg = 'Registered Workspaces:\n';
490
- for (const n in all) listMsg += `- ${n}: ${all[n].path}\n`;
491
- appendMessage('system', Object.keys(all).length ? listMsg : 'No workspaces registered.');
492
- } else if (subCmd === 'remove') {
493
- const name = wArgs[2];
494
- if (workspaceManager.removeWorkspace(name)) {
495
- appendMessage('system', `Removed workspace "${name}"`);
496
- resetChat();
497
- } else {
498
- appendMessage('error', `Workspace "${name}" not found.`);
499
- }
500
- } else if (subCmd === 'use' || subCmd === 'switch') {
501
- const name = wArgs[2];
502
- const all = workspaceManager.listWorkspaces();
503
- if (all[name]) {
504
- const newPath = all[name].path;
505
- try {
506
- process.chdir(newPath);
507
- updateWorkspace(newPath);
508
- appendMessage('system', `✓ Switched to workspace "${name}" at ${newPath}`);
509
- resetChat();
510
- } catch (e) {
511
- appendMessage('error', `Failed to change directory: ${e.message}`);
512
- }
513
- } else {
514
- appendMessage('error', `Workspace "${name}" not found. Try /workspace list`);
515
- }
516
- } else {
517
- const ws = workspaceManager.getWorkspaceByPath(process.cwd());
518
- appendMessage('system', ws
519
- ? `Current Workspace: ${ws.name}\nPath: ${ws.path}`
520
- : `Not currently in a registered workspace.\nActive Path: ${process.cwd()}\nUsage: /workspace <add|use|list|remove>`);
521
- }
522
- return;
523
- }
524
-
525
- if (text.startsWith('/review')) {
526
- if (!sharedState.lastResponseText) {
527
- appendMessage('error', 'Nothing to review yet. Get a response first.');
528
- return;
529
- }
530
- agentOrchestrator.setAgent('reviewer');
531
- appendMessage('system', '⚖️ Requesting second-pass review from Mint Reviewer...');
532
- text = `Please review this previous response and provide a critique:\n\n${sharedState.lastResponseText}`;
533
- } else {
534
- if (!text.startsWith('/image') && !text.startsWith('/paste')) {
535
- appendMessage('user', text);
536
- }
537
- const slashResult = await handleSlashCommandUI(
538
- text, appendMessage, updateStatusModel, copyLastResponse,
539
- setThinking, requestApproval, setMode, appendCodeStep, updateWorkspace, {
540
- sendImageMessage: (args) => sendImageMessage({ ...args, stats: sharedState.stats }),
541
- formatErrorMessage: formatErr,
542
- attachImage, setInputText, setPendingPasteText,
543
- setFastMode, toggleFastMode, getFastMode,
544
- sendRepoSummaryMessage, sendSymbolIndexMessage,
545
- sendSemanticCodeMessage, streamAssistantSentences,
546
- streamMessage
547
- }
548
- );
549
- if (slashResult && slashResult.lastResponseText) {
550
- sharedState.lastResponseText = slashResult.lastResponseText;
551
- }
552
- return;
553
- }
554
- }
555
-
556
- appendMessage('user', text);
557
-
558
- // ── Local tool shortcuts (natural language) ─────────────────
559
- if (isRepoSummaryRequest(text)) {
560
- const r = await sendRepoSummaryMessage({ appendMessage, streamMessage, setThinking });
561
- sharedState.lastResponseText = r;
562
- return;
563
- }
564
- if (isSymbolIndexRequest(text)) {
565
- const r = await sendSymbolIndexMessage({ appendMessage, streamMessage, setThinking });
566
- sharedState.lastResponseText = r;
567
- return;
568
- }
569
- if (isSemanticCodeSearchRequest(text)) {
570
- const query = extractSemanticCodeQuery(text);
571
- const r = await sendSemanticCodeMessage({
572
- rawArgs: `search ${query}`,
573
- appendMessage, streamMessage, setThinking, appendCodeStep
574
- });
575
- sharedState.lastResponseText = r;
576
- return;
577
- }
578
-
579
- // ── Default to guarded Code Agent ───────────────────────────
580
- await runAgentTask(text, {
581
- appendMessage, streamMessage, setThinking,
582
- requestApproval, askUser, setMode, appendCodeStep
583
- }, sharedState);
584
- } finally {
585
- sharedState.isBusy = false;
586
- sharedState.abortController = null;
587
- }
588
- },
589
-
590
- onExit: () => {
591
- if (ui && typeof ui.unmount === 'function') ui.unmount();
592
- exitWithGoodbye(0, buildExitSummary(sharedState.stats));
593
- }
594
- });
595
-
596
- // ── Handle initial CLI --image option ───────────────────────────────────
597
- if (options.imagePath) {
598
- const { appendMessage, streamMessage, setThinking, appendCodeStep } = ui;
599
- const image = loadImageAsDataUri(options.imagePath);
600
- const prompt = initialMessage || 'Analyze this image.';
601
- const { responseText, labels, imageList } = await sendImageMessage({
602
- images: [image], prompt, appendMessage, streamMessage, setThinking, appendCodeStep,
603
- stats: sharedState.stats
604
- });
605
- sharedState.lastResponseText = responseText;
606
- if (responseText) {
607
- sharedState.recentImageContextText = [
608
- `Recent image context: the user attached ${imageList.length} image(s) labelled ${labels || '[Image #1]'}.`,
609
- 'The terminal UI displays image attachments as labels only; it does not render thumbnails inside the chat.',
610
- `Assistant response to those image(s): ${responseText}`
611
- ].join('\n');
612
- }
613
- return;
614
- }
615
-
616
- // ── Handle initial CLI message argument ─────────────────────────────────
617
- if (initialMessage) {
618
- const { appendMessage, streamMessage, setThinking, requestApproval, setMode, appendCodeStep, askUser } = ui;
619
- appendMessage('user', initialMessage);
620
-
621
- if (isRepoSummaryRequest(initialMessage)) {
622
- sharedState.lastResponseText = await sendRepoSummaryMessage({ appendMessage, streamMessage, setThinking });
623
- return;
624
- }
625
- if (isSymbolIndexRequest(initialMessage)) {
626
- sharedState.lastResponseText = await sendSymbolIndexMessage({ appendMessage, streamMessage, setThinking });
627
- return;
628
- }
629
- if (isSemanticCodeSearchRequest(initialMessage)) {
630
- const query = extractSemanticCodeQuery(initialMessage);
631
- sharedState.lastResponseText = await sendSemanticCodeMessage({
632
- rawArgs: `search ${query}`,
633
- appendMessage, streamMessage, setThinking, appendCodeStep
634
- });
635
- return;
636
- }
637
-
638
- sharedState.isBusy = true;
639
- sharedState.abortController = new AbortController();
640
- try {
641
- await runAgentTask(initialMessage, {
642
- appendMessage, streamMessage, setThinking,
643
- requestApproval, askUser, setMode, appendCodeStep
644
- }, sharedState);
645
- } finally {
646
- sharedState.isBusy = false;
647
- sharedState.abortController = null;
648
- }
649
- }
650
- }
651
-
652
- module.exports = {
653
- startInteractiveChat,
654
- _helpers: {
655
- hasAllImageLabels,
656
- formatImageDisplayMessage
657
- }
658
- };