@pheem49/mint 1.8.0 → 1.8.2

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 (200) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/Cargo.lock +34 -3
  3. package/Cargo.toml +2 -2
  4. package/README.md +20 -6
  5. package/Release_Note.md +129 -0
  6. package/assets/Mint_CLI.png +0 -0
  7. package/bin/mint +0 -0
  8. package/crates/mint-cli/Cargo.toml +1 -0
  9. package/crates/mint-cli/src/agent.rs +242 -54
  10. package/crates/mint-cli/src/main.rs +397 -74
  11. package/crates/mint-core/src/agent_loop.rs +1 -0
  12. package/crates/mint-core/src/api_server.rs +64 -2
  13. package/crates/mint-core/src/browser.rs +456 -0
  14. package/crates/mint-core/src/channels.rs +1 -0
  15. package/crates/mint-core/src/chat.rs +3 -0
  16. package/crates/mint-core/src/config.rs +130 -1
  17. package/crates/mint-core/src/lib.rs +19 -0
  18. package/crates/mint-core/src/orchestration.rs +317 -12
  19. package/crates/mint-core/src/skills.rs +113 -4
  20. package/crates/mint-core/src/tts.rs +3 -1
  21. package/crates/mint-core/src/workflows.rs +1 -22
  22. package/flatpak/com.pheem49.mint.desktop +9 -0
  23. package/flatpak/com.pheem49.mint.metainfo.xml +35 -0
  24. package/flatpak/com.pheem49.mint.yaml +52 -0
  25. package/package.json +1 -1
  26. package/shared-modules/.github/workflows/ci.yml +120 -0
  27. package/shared-modules/.github/workflows/update.yaml +28 -0
  28. package/shared-modules/CODEOWNERS +23 -0
  29. package/shared-modules/README.md +79 -0
  30. package/shared-modules/SDL/README.md +3 -0
  31. package/shared-modules/SDL/SDL-1.2.15.json +51 -0
  32. package/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch +118 -0
  33. package/shared-modules/SDL/SDL_image-1.2.12.json +25 -0
  34. package/shared-modules/SDL/SDL_mixer-1.2.12.json +27 -0
  35. package/shared-modules/SDL/SDL_net-1.2.8.json +19 -0
  36. package/shared-modules/SDL/SDL_pango-0.1.2.json +26 -0
  37. package/shared-modules/SDL/SDL_ttf-2.0.11.json +28 -0
  38. package/shared-modules/SDL/libsdl-1.2.15-strict-prototypes.patch +316 -0
  39. package/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch +24 -0
  40. package/shared-modules/SDL/sdl-libx11-build.patch +59 -0
  41. package/shared-modules/SDL/sdl-sysloadso-buffer-length.patch +27 -0
  42. package/shared-modules/SDL/sdl12-compat.json +22 -0
  43. package/shared-modules/SDL2/SDL2_image.json +27 -0
  44. package/shared-modules/SDL2/SDL2_ttf.json +29 -0
  45. package/shared-modules/cld2/CMakeLists.txt +157 -0
  46. package/shared-modules/cld2/cld2.json +24 -0
  47. package/shared-modules/clutter/clutter.json +72 -0
  48. package/shared-modules/dbus-glib/dbus-glib.json +29 -0
  49. package/shared-modules/glew/glew.json +36 -0
  50. package/shared-modules/glu/glu-9.json +12 -0
  51. package/shared-modules/gtk2/arc-gtk2-theme-Replace-Inkscape-dependency-with-rsvg-convert.patch +180 -0
  52. package/shared-modules/gtk2/gtk2-common-themes.json +360 -0
  53. package/shared-modules/gtk2/gtk2-fix-crash-in-show-uri.patch +30 -0
  54. package/shared-modules/gtk2/gtk2-gcc14.patch +281 -0
  55. package/shared-modules/gtk2/gtk2-gcc15.patch +250 -0
  56. package/shared-modules/gtk2/gtk2-murrine-c99.patch +34 -0
  57. package/shared-modules/gtk2/gtk2-use-adwaita-theme.patch +80 -0
  58. package/shared-modules/gtk2/gtk2.json +120 -0
  59. package/shared-modules/gtk2/ibus-gcc15.patch +13 -0
  60. package/shared-modules/gtk2/murrine-engine-fix-crash.patch +12 -0
  61. package/shared-modules/gtk2/readme.md +10 -0
  62. package/shared-modules/intltool/intltool-0.51.json +20 -0
  63. package/shared-modules/intltool/intltool-perl5.26-regex-fixes.patch +59 -0
  64. package/shared-modules/lame/0001-Build-xmm_quantize_sub.c-with-msse.patch +34 -0
  65. package/shared-modules/lame/0002-Include-GTK-1-autoconf-directives-in-build-system.patch +222 -0
  66. package/shared-modules/lame/0003-Patch-out-remaining-ansi2knr.patch +70 -0
  67. package/shared-modules/lame/0004-initscr-is-not-used-anywhere-in-lame-sourcetree.patch +41 -0
  68. package/shared-modules/lame/0005-Update-autoconf.patch +81 -0
  69. package/shared-modules/lame/lame-3.99.5.json +32 -0
  70. package/shared-modules/libappindicator/libappindicator-gtk2-12.10.json +140 -0
  71. package/shared-modules/libappindicator/libappindicator-gtk3-12.10.json +140 -0
  72. package/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json +140 -0
  73. package/shared-modules/libappindicator/patches/0001-libappindicator-ftbfs.patch +20 -0
  74. package/shared-modules/libappindicator/patches/0002-libappindicator-no-python.patch +888 -0
  75. package/shared-modules/libappindicator/patches/0003-libappindicator-fix-crash-from-incorrect-signal-emission.patch +74 -0
  76. package/shared-modules/libappindicator/patches/0004-libappindicator-fix-crash-iterating-icon-themes.patch +11 -0
  77. package/shared-modules/libayatana-appindicator/0001-Fix-HAVE_VALGRIND-AM_CONDITIONAL.patch +51 -0
  78. package/shared-modules/libayatana-appindicator/0001-Make-introspection-configurable.patch +44 -0
  79. package/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json +121 -0
  80. package/shared-modules/libcanberra/0001-gtk-Don-t-assume-all-GdkDisplays-are-GdkX11Displays-.patch +123 -0
  81. package/shared-modules/libcanberra/libcanberra.json +44 -0
  82. package/shared-modules/libmad/libmad.json +19 -0
  83. package/shared-modules/libsecret/libsecret.json +23 -0
  84. package/shared-modules/libsoup/libsoup-2.4.json +14 -0
  85. package/shared-modules/libusb/libusb.json +27 -0
  86. package/shared-modules/linux-audio/dssi.json +31 -0
  87. package/shared-modules/linux-audio/fftw3f-static.json +44 -0
  88. package/shared-modules/linux-audio/fftw3f.json +42 -0
  89. package/shared-modules/linux-audio/fluidsynth2-static.json +30 -0
  90. package/shared-modules/linux-audio/fluidsynth2.json +19 -0
  91. package/shared-modules/linux-audio/ladspa.json +26 -0
  92. package/shared-modules/linux-audio/lash.json +35 -0
  93. package/shared-modules/linux-audio/libinstpatch.json +16 -0
  94. package/shared-modules/linux-audio/liblo-static.json +22 -0
  95. package/shared-modules/linux-audio/liblo.json +17 -0
  96. package/shared-modules/linux-audio/lilv-static.json +94 -0
  97. package/shared-modules/linux-audio/lilv.json +82 -0
  98. package/shared-modules/linux-audio/lrdf.json +57 -0
  99. package/shared-modules/linux-audio/lv2.json +21 -0
  100. package/shared-modules/linux-audio/patches/lash/0001-configure-remove-aclocal-m4.patch +29 -0
  101. package/shared-modules/linux-audio/patches/lash/0002-Fix-build-with-recent-gcc.patch +73 -0
  102. package/shared-modules/linux-audio/patches/lash/0003-Fix-linking.patch +136 -0
  103. package/shared-modules/linux-audio/patches/raptor-libxml.patch +31 -0
  104. package/shared-modules/linux-audio/patches/stk-install.sh +54 -0
  105. package/shared-modules/linux-audio/stk.json +28 -0
  106. package/shared-modules/lua5.1/0004-Fix-stack-overflow-in-vararg-functions.patch +21 -0
  107. package/shared-modules/lua5.1/lua-5.1.5-so.patch +43 -0
  108. package/shared-modules/lua5.1/lua-5.1.5.json +42 -0
  109. package/shared-modules/lua5.3/lua-5.3.5.json +41 -0
  110. package/shared-modules/lua5.3/lua.pc.in +8 -0
  111. package/shared-modules/lua5.3/luaconf.patch +47 -0
  112. package/shared-modules/lua5.3/makefile-add-liblua.so-rule.patch +47 -0
  113. package/shared-modules/lua5.4/lua-5.4.json +41 -0
  114. package/shared-modules/lua5.4/lua.pc.in +8 -0
  115. package/shared-modules/lua5.4/luaconf.patch +34 -0
  116. package/shared-modules/lua5.4/makefile-add-liblua.so-rule.patch +42 -0
  117. package/shared-modules/luajit/luajit.json +30 -0
  118. package/shared-modules/lzo/lzo.json +27 -0
  119. package/shared-modules/mac/mac.json +18 -0
  120. package/shared-modules/physfs/physfs.json +26 -0
  121. package/shared-modules/pygame/audiofile-gcc6.patch +21 -0
  122. package/shared-modules/pygame/cython-fixes.patch +34 -0
  123. package/shared-modules/pygame/fluidsynth-no-rawmidi.patch +69 -0
  124. package/shared-modules/pygame/glib.patch +25 -0
  125. package/shared-modules/pygame/pygame1-python2.json +157 -0
  126. package/shared-modules/pygtk/pygtk-c99.patch +15 -0
  127. package/shared-modules/pygtk/pygtk.json +117 -0
  128. package/shared-modules/pygtk/pygtk.patch +39 -0
  129. package/shared-modules/python2.7/python-2.7.json +48 -0
  130. package/shared-modules/smpeg/smpeg-0.4.5.json +50 -0
  131. package/shared-modules/smpeg/smpeg-am-prog-as.patch +12 -0
  132. package/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch +17 -0
  133. package/shared-modules/smpeg/smpeg-gcc6.patch +40 -0
  134. package/shared-modules/smpeg/smpeg-no-gtk.patch +37 -0
  135. package/shared-modules/squashfs-tools/squashfs-tools.json +25 -0
  136. package/shared-modules/update-checker.sh +50 -0
  137. package/shared-modules/vorbisgain/0001-temp_files.patch +75 -0
  138. package/shared-modules/vorbisgain/0002-errno.patch +33 -0
  139. package/shared-modules/vorbisgain/0003-manpage.patch +248 -0
  140. package/shared-modules/vorbisgain/0004-vorbisgain_mtime.patch +183 -0
  141. package/shared-modules/vorbisgain/0005-double_fclose.patch +21 -0
  142. package/shared-modules/vorbisgain/0006-manpage_hyphens.patch +56 -0
  143. package/shared-modules/vorbisgain/0007-recursively_spelling.patch +14 -0
  144. package/shared-modules/vorbisgain/0008-manpage_recursion_mistake.patch +23 -0
  145. package/shared-modules/vorbisgain/0009-hardening.patch +13 -0
  146. package/shared-modules/vorbisgain/0010-fclose.patch +13 -0
  147. package/shared-modules/vorbisgain/README.md +23 -0
  148. package/shared-modules/vorbisgain/vorbisgain_0.37-2.json +29 -0
  149. package/src/renderer/shared/agentProgress.ts +145 -0
  150. package/src/renderer/shared/calculator.ts +52 -0
  151. package/src/renderer/shared/components/AgentActivityDrawer.tsx +72 -0
  152. package/src/renderer/shared/components/AgentActivityTable.tsx +66 -0
  153. package/src/renderer/shared/components/ApprovalCard.tsx +93 -0
  154. package/src/renderer/shared/components/ChatCodeBlock.tsx +75 -0
  155. package/src/renderer/shared/components/ThinkingBlock.tsx +101 -0
  156. package/src/renderer/shared/constants/models.ts +91 -0
  157. package/src/renderer/shared/platform.ts +82 -0
  158. package/src/renderer/shared/types.ts +140 -0
  159. package/src/renderer/shared/utils/agentActivity.ts +187 -0
  160. package/src/renderer/shared/utils/approval.ts +1 -0
  161. package/src/renderer/shared/utils/approval.tsx +64 -0
  162. package/src/renderer/shared/utils/markdown.tsx +347 -0
  163. package/src/renderer/shared/utils/providers.ts +36 -0
  164. package/src/renderer/shared/utils/speech.ts +216 -0
  165. package/src/renderer/shared/utils/ui.ts +147 -0
  166. package/src/renderer/src/agentProgress.ts +11 -39
  167. package/src/renderer/src/calculator.ts +3 -46
  168. package/src/renderer/src/components/ChatPanel.tsx +230 -1202
  169. package/src/renderer/src/components/ImageStudioPanel.tsx +11 -1
  170. package/src/renderer/src/components/MintDashboard.tsx +63 -171
  171. package/src/renderer/src/components/PicturesLibrary.tsx +32 -8
  172. package/src/renderer/src/components/Settings/AgentsTab.tsx +362 -0
  173. package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -22
  174. package/src/renderer/src/components/Settings/PluginsTab.tsx +221 -41
  175. package/src/renderer/src/components/SettingsWindow.tsx +54 -12
  176. package/src/renderer/src/components/WorkspacePanel.tsx +66 -9
  177. package/src/renderer/src/css/image-studio.css +6 -0
  178. package/src/renderer/src/css/styles.css +148 -11
  179. package/src/renderer/src/index.css +12 -1
  180. package/src/renderer/src/tauri.ts +169 -143
  181. package/src/renderer/src-web/agentProgress.ts +11 -39
  182. package/src/renderer/src-web/calculator.ts +3 -46
  183. package/src/renderer/src-web/components/ChatPanel.tsx +223 -1199
  184. package/src/renderer/src-web/components/ImageStudioPanel.tsx +11 -1
  185. package/src/renderer/src-web/components/MintDashboard.tsx +31 -172
  186. package/src/renderer/src-web/components/PicturesLibrary.tsx +23 -29
  187. package/src/renderer/src-web/components/Settings/AgentsTab.tsx +362 -0
  188. package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -22
  189. package/src/renderer/src-web/components/Settings/PluginsTab.tsx +221 -41
  190. package/src/renderer/src-web/components/SettingsWindow.tsx +54 -12
  191. package/src/renderer/src-web/css/image-studio.css +6 -0
  192. package/src/renderer/src-web/css/styles.css +184 -12
  193. package/src/renderer/src-web/index.css +12 -1
  194. package/src/renderer/src-web/tauri.ts +204 -104
  195. package/src-tauri/src/browser.rs +1 -141
  196. package/src-tauri/src/headless.rs +3 -2
  197. package/src-tauri/src/lib.rs +254 -59
  198. package/src-tauri/src/webhooks.rs +1 -0
  199. package/src-tauri/src/workflows.rs +57 -4
  200. package/src-tauri/tauri.conf.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import React, { useState, useEffect } from 'react'
2
2
  import { DEFAULT_CONFIG } from '../SettingsWindow'
3
- import { listLearnedSkills, addLearnedSkill, deleteLearnedSkill, LearnedSkill } from '../../tauri'
3
+ import { listLearnedSkills, addLearnedSkill, deleteLearnedSkill, LearnedSkill, detectSystemTools, DetectedTools } from '../../tauri'
4
4
 
5
5
  interface PluginsTabProps {
6
6
  config: typeof DEFAULT_CONFIG
@@ -40,14 +40,49 @@ export default function PluginsTab({
40
40
  const [skillsLoading, setSkillsLoading] = useState(false)
41
41
  const [skillsError, setSkillsError] = useState('')
42
42
 
43
- // Load skills on mount
43
+ // Local state for auto-detected tools
44
+ const [detectedTools, setDetectedTools] = useState<DetectedTools>({
45
+ docker: false,
46
+ git: false,
47
+ gh: false,
48
+ node: false
49
+ })
50
+ const [detecting, setDetecting] = useState(false)
51
+
52
+ // Load skills and detect tools on mount
44
53
  useEffect(() => {
45
54
  fetchSkills()
55
+ detectTools()
46
56
  }, [])
47
57
 
58
+ const detectTools = async () => {
59
+ setDetecting(true)
60
+ try {
61
+ const tools = await detectSystemTools()
62
+ setDetectedTools(tools)
63
+ } catch (e) {
64
+ console.error("Failed to detect tools:", e)
65
+ } finally {
66
+ setDetecting(false)
67
+ }
68
+ }
69
+
70
+ const handleEnableTool = (name: string, command: string, args: string[]) => {
71
+ const updatedMcp = {
72
+ ...(config.mcpServers || {}),
73
+ [name]: {
74
+ command,
75
+ args,
76
+ env: {}
77
+ }
78
+ }
79
+ updateField('mcpServers', updatedMcp)
80
+ }
81
+
48
82
  const fetchSkills = async () => {
49
83
  try {
50
- const list = await listLearnedSkills()
84
+ const activeWorkspace = window.localStorage.getItem('mint:last-workspace-path') || undefined
85
+ const list = await listLearnedSkills(activeWorkspace)
51
86
  setSkills(list)
52
87
  } catch (e) {
53
88
  console.error("Failed to load skills:", e)
@@ -122,6 +157,79 @@ export default function PluginsTab({
122
157
  }
123
158
  ]
124
159
 
160
+ const mcpListItems: Array<{
161
+ name: string
162
+ command: string
163
+ args: string[]
164
+ icon: string
165
+ isEnabled: boolean
166
+ isConfigured: boolean
167
+ description: string
168
+ }> = []
169
+
170
+ Object.entries(config.mcpServers || {}).forEach(([name, srv]: [string, any]) => {
171
+ let icon = '⚙';
172
+ if (name === 'docker') icon = '🐳';
173
+ else if (name === 'git' || name === 'gitkraken') icon = '🐙';
174
+ else if (name === 'github') icon = '🐱';
175
+ else if (name === 'node') icon = '🟢';
176
+
177
+ mcpListItems.push({
178
+ name,
179
+ command: srv.command,
180
+ args: srv.args || [],
181
+ icon,
182
+ isEnabled: true,
183
+ isConfigured: true,
184
+ description: `Command: ${srv.command} ${(srv.args || []).join(' ')}`
185
+ });
186
+ });
187
+
188
+ if (detectedTools.docker && !config.mcpServers?.docker) {
189
+ mcpListItems.push({
190
+ name: 'docker',
191
+ command: 'npx',
192
+ args: ['-y', '@proxeus/mcp-docker-server'],
193
+ icon: '🐳',
194
+ isEnabled: false,
195
+ isConfigured: false,
196
+ description: 'Docker MCP Server (Discovered)'
197
+ });
198
+ }
199
+ if (detectedTools.git && !config.mcpServers?.git) {
200
+ mcpListItems.push({
201
+ name: 'git',
202
+ command: 'npx',
203
+ args: ['-y', '@modelcontextprotocol/server-git'],
204
+ icon: '🐙',
205
+ isEnabled: false,
206
+ isConfigured: false,
207
+ description: 'Git MCP Server (Discovered)'
208
+ });
209
+ }
210
+ if (detectedTools.gh && !config.mcpServers?.github) {
211
+ mcpListItems.push({
212
+ name: 'github',
213
+ command: 'npx',
214
+ args: ['-y', '@modelcontextprotocol/server-github'],
215
+ icon: '🐱',
216
+ isEnabled: false,
217
+ isConfigured: false,
218
+ description: 'GitHub MCP Server (Discovered)'
219
+ });
220
+ }
221
+ if (detectedTools.node && !config.mcpServers?.node) {
222
+ mcpListItems.push({
223
+ name: 'node',
224
+ command: 'npx',
225
+ args: ['-y', '@modelcontextprotocol/server-node'],
226
+ icon: '🟢',
227
+ isEnabled: false,
228
+ isConfigured: false,
229
+ description: 'NodeJS Runtime MCP Server (Discovered)'
230
+ });
231
+ }
232
+
125
233
  return (
126
234
  <div className="tab-pane active" style={{ display: 'flex', flexDirection: 'column', gap: '24px' }}>
127
235
  {/* ── Learned AI Skills ── */}
@@ -154,7 +262,7 @@ export default function PluginsTab({
154
262
  {skills.map((s) => (
155
263
  <div className="skill-card" key={s.id} style={{
156
264
  display: 'flex',
157
- alignItems: 'flex-start',
265
+ alignItems: 'center',
158
266
  justifyContent: 'space-between',
159
267
  padding: '16px 20px',
160
268
  border: '1px solid var(--border)',
@@ -163,7 +271,7 @@ export default function PluginsTab({
163
271
  gap: '16px'
164
272
  }}>
165
273
  <div style={{ flex: 1, minWidth: 0 }}>
166
- <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '6px' }}>
274
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
167
275
  <span className="badge" style={{
168
276
  background: 'var(--accent)',
169
277
  color: '#fff',
@@ -172,24 +280,31 @@ export default function PluginsTab({
172
280
  padding: '2px 8px',
173
281
  borderRadius: '4px'
174
282
  }}>{s.name}</span>
283
+ {s.location && (
284
+ <span className="location-badge" style={{
285
+ background: s.location === 'workspace'
286
+ ? 'rgba(16, 185, 129, 0.15)'
287
+ : s.location === 'global'
288
+ ? 'rgba(59, 130, 246, 0.15)'
289
+ : 'rgba(139, 92, 246, 0.15)',
290
+ color: s.location === 'workspace'
291
+ ? '#10b981'
292
+ : s.location === 'global'
293
+ ? '#3b82f6'
294
+ : '#8b5cf6',
295
+ fontSize: '0.7rem',
296
+ fontWeight: '600',
297
+ padding: '2px 6px',
298
+ borderRadius: '4px',
299
+ textTransform: 'capitalize'
300
+ }}>
301
+ {s.location === 'database' ? 'Taught' : s.location}
302
+ </span>
303
+ )}
175
304
  <span style={{ fontSize: '0.8rem', color: 'var(--text-muted)', textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap' }}>
176
305
  Source: {s.sourcePath}
177
306
  </span>
178
307
  </div>
179
- <div style={{
180
- fontSize: '0.85rem',
181
- color: 'var(--text-main)',
182
- whiteSpace: 'pre-wrap',
183
- maxHeight: '120px',
184
- overflowY: 'auto',
185
- background: 'rgba(0,0,0,0.12)',
186
- padding: '10px 14px',
187
- borderRadius: '8px',
188
- fontFamily: 'monospace',
189
- lineHeight: '1.4'
190
- }}>
191
- {s.content}
192
- </div>
193
308
  </div>
194
309
  <button
195
310
  className="btn btn-danger"
@@ -255,33 +370,98 @@ export default function PluginsTab({
255
370
  </div>
256
371
 
257
372
  <div className="mcp-list">
258
- {Object.entries(config.mcpServers || {}).map(([name, srv]: [string, any]) => (
259
- <div className="plugin-card" key={name} style={{
260
- display: 'flex',
261
- alignItems: 'center',
262
- justifyContent: 'space-between',
263
- padding: '16px 20px',
264
- border: '1px solid var(--border)',
265
- borderRadius: '12px',
266
- background: 'var(--surface-bg)',
267
- marginBottom: '10px'
268
- }}>
269
- <div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
270
- <div className="plugin-icon" style={{ fontSize: '1.5rem', width: '40px', height: '40px', display: 'grid', placeItems: 'center', background: 'var(--surface-strong)', borderRadius: '10px' }}>⚙</div>
271
- <div className="plugin-info">
272
- <div className="plugin-name" style={{ fontWeight: '600', color: 'var(--text-main)' }}>{name}</div>
273
- <div className="plugin-desc" style={{ fontFamily: 'monospace', fontSize: '0.8rem', color: 'var(--text-muted)' }}>
274
- Command: {srv.command} {srv.args?.join(' ')}
373
+ {mcpListItems.length === 0 ? (
374
+ <div style={{ padding: '20px', textAlign: 'center', color: 'var(--text-muted)' }}>
375
+ No MCP servers configured or discovered.
376
+ </div>
377
+ ) : (
378
+ mcpListItems.map((item) => (
379
+ <div
380
+ className="plugin-card"
381
+ key={item.name}
382
+ style={{
383
+ display: 'flex',
384
+ alignItems: 'center',
385
+ justifyContent: 'space-between',
386
+ padding: '16px 20px',
387
+ border: '1px solid var(--border)',
388
+ borderRadius: '12px',
389
+ background: item.isConfigured ? 'var(--surface-bg)' : 'rgba(16, 185, 129, 0.03)',
390
+ opacity: item.isConfigured ? 1 : 0.65,
391
+ marginBottom: '10px',
392
+ transition: 'all 0.2s'
393
+ }}
394
+ >
395
+ <div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
396
+ <div className="plugin-icon" style={{ fontSize: '1.5rem', width: '40px', height: '40px', display: 'grid', placeItems: 'center', background: 'var(--surface-strong)', borderRadius: '10px' }}>
397
+ {item.icon}
398
+ </div>
399
+ <div className="plugin-info">
400
+ <div className="plugin-name" style={{ fontWeight: '600', color: 'var(--text-main)' }}>
401
+ {item.name} {!item.isConfigured && <span style={{ fontSize: '0.75rem', padding: '2px 6px', background: 'rgba(16, 185, 129, 0.15)', color: '#10b981', borderRadius: '4px', marginLeft: '6px' }}>Discovered</span>}
402
+ </div>
403
+ <div className="plugin-desc" style={{ fontFamily: 'monospace', fontSize: '0.8rem', color: 'var(--text-muted)' }}>
404
+ {item.description}
405
+ </div>
275
406
  </div>
276
407
  </div>
408
+ <div className="plugin-actions" style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
409
+ {item.isConfigured && (
410
+ <button
411
+ className="btn-icon-danger"
412
+ onClick={() => handleRemoveMcpServer(item.name)}
413
+ style={{
414
+ background: 'transparent',
415
+ border: 'none',
416
+ color: '#ef4444',
417
+ cursor: 'pointer',
418
+ padding: '6px',
419
+ display: 'flex',
420
+ alignItems: 'center',
421
+ justifyContent: 'center',
422
+ borderRadius: '6px',
423
+ transition: 'background 0.2s'
424
+ }}
425
+ title="Delete MCP Server"
426
+ >
427
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
428
+ <polyline points="3 6 5 6 21 6"></polyline>
429
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
430
+ <line x1="10" y1="11" x2="10" y2="17"></line>
431
+ <line x1="14" y1="11" x2="14" y2="17"></line>
432
+ </svg>
433
+ </button>
434
+ )}
435
+ <label className="settings-toggle-switch">
436
+ <input
437
+ type="checkbox"
438
+ checked={item.isEnabled}
439
+ onChange={(e) => {
440
+ if (e.target.checked) {
441
+ handleEnableTool(item.name, item.command, item.args);
442
+ } else {
443
+ handleRemoveMcpServer(item.name);
444
+ }
445
+ }}
446
+ />
447
+ <span className="settings-toggle-slider" />
448
+ </label>
449
+ </div>
277
450
  </div>
278
- <div className="plugin-actions">
279
- <button className="btn btn-danger" onClick={() => handleRemoveMcpServer(name)} style={{ padding: '6px 12px', fontSize: '0.8rem', height: '34px' }}>Remove</button>
280
- </div>
281
- </div>
282
- ))}
451
+ ))
452
+ )}
283
453
  </div>
284
454
 
455
+ {/* Save settings alert */}
456
+ {((detectedTools.docker && !config.mcpServers?.docker) ||
457
+ (detectedTools.git && !config.mcpServers?.git) ||
458
+ (detectedTools.gh && !config.mcpServers?.github) ||
459
+ (detectedTools.node && !config.mcpServers?.node)) && (
460
+ <div style={{ marginTop: '12px', padding: '12px 16px', background: 'rgba(255,193,7,0.1)', border: '1px solid rgba(255,193,7,0.3)', borderRadius: '8px', fontSize: '0.8rem', color: '#ffb300', fontWeight: '500', display: 'flex', alignItems: 'center', gap: '8px' }}>
461
+ <span>⚠️ After toggling a plugin, please click the "Save Settings" button at the bottom of the window to persist changes.</span>
462
+ </div>
463
+ )}
464
+
285
465
  <div className="add-mcp-box" style={{
286
466
  marginTop: '20px',
287
467
  padding: '20px',
@@ -6,6 +6,7 @@ import AudioTab from './Settings/AudioTab'
6
6
  import AutomationTab from './Settings/AutomationTab'
7
7
  import ThemeTab from './Settings/ThemeTab'
8
8
  import PluginsTab from './Settings/PluginsTab'
9
+ import AgentsTab from './Settings/AgentsTab'
9
10
 
10
11
  export const DEFAULT_CONFIG = {
11
12
  theme: 'dark',
@@ -30,7 +31,7 @@ export const DEFAULT_CONFIG = {
30
31
  proactiveCooldown: 120,
31
32
  enableVoiceReply: true,
32
33
  enableCustomWorkflows: true,
33
- enableAgentCollaboration: true,
34
+ enableAgentCollaboration: false,
34
35
  ttsProvider: 'google',
35
36
  ttsVolume: 1.0,
36
37
  ttsSpeed: 1.0,
@@ -91,10 +92,40 @@ export const DEFAULT_CONFIG = {
91
92
  imageGenProvider: 'gemini' as 'gemini' | 'dalle' | 'stability' | 'ideogram' | 'replicate',
92
93
  stabilityApiKey: '',
93
94
  ideogramApiKey: '',
94
- replicateApiKey: ''
95
+ replicateApiKey: '',
96
+ // Multi-Agent Configuration
97
+ agents: [
98
+ {
99
+ id: 'planner',
100
+ name: 'Planner',
101
+ provider: 'gemini',
102
+ model: 'gemini-2.5-flash',
103
+ apiKey: '',
104
+ systemInstruction: 'You are the Planner agent. Your task is to analyze the user request, inspect the workspace structure, and design a step-by-step implementation plan. Create or update the implementation_plan.md file to document your proposal.',
105
+ enabled: true
106
+ },
107
+ {
108
+ id: 'coder',
109
+ name: 'Coder',
110
+ provider: 'gemini',
111
+ model: 'gemini-2.5-flash',
112
+ apiKey: '',
113
+ systemInstruction: 'You are the Coder agent. Your task is to implement the changes specified in the approved implementation plan. Read relevant files, write clean and efficient code, and run terminal commands to build or verify. Stay focused on execution.',
114
+ enabled: true
115
+ },
116
+ {
117
+ id: 'reviewer',
118
+ name: 'Reviewer',
119
+ provider: 'gemini',
120
+ model: 'gemini-2.5-flash',
121
+ apiKey: '',
122
+ systemInstruction: 'You are the Reviewer agent. Your task is to verify the code modifications made by the Coder. Run the automated tests, check lint errors, and ensure the implementation is correct and complete.',
123
+ enabled: true
124
+ }
125
+ ]
95
126
  }
96
127
 
97
- type TabType = 'sect-general' | 'sect-audio' | 'sect-automation' | 'sect-theme' | 'sect-plugins' | 'sect-shortcuts' | 'sect-memory'
128
+ type TabType = 'sect-general' | 'sect-audio' | 'sect-automation' | 'sect-theme' | 'sect-plugins' | 'sect-shortcuts' | 'sect-memory' | 'sect-agents'
98
129
 
99
130
  export const GEMINI_MODELS = [
100
131
  'gemini-2.5-flash',
@@ -157,15 +188,7 @@ export const LOCAL_MODELS = [
157
188
  'lmstudio-community/gemma-2-9b-it-GGUF'
158
189
  ]
159
190
 
160
- export const OLLAMA_MODELS = [
161
- 'llama3:latest',
162
- 'llama3.1:latest',
163
- 'llama3.2:latest',
164
- 'gemma2:latest',
165
- 'mistral:latest',
166
- 'phi3:latest',
167
- 'qwen2.5:latest'
168
- ]
191
+ export const OLLAMA_MODELS: string[] = []
169
192
 
170
193
  export default function SettingsWindow() {
171
194
  const [activeTab, setActiveTab] = useState<TabType>('sect-general')
@@ -594,6 +617,17 @@ export default function SettingsWindow() {
594
617
  </span>
595
618
  <strong>Plugins</strong>
596
619
  </button>
620
+ <button className={`tab-btn ${activeTab === 'sect-agents' ? 'active' : ''}`} onClick={() => setActiveTab('sect-agents')}>
621
+ <span style={{ display: 'inline-flex', alignItems: 'center' }}>
622
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
623
+ <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
624
+ <circle cx="9" cy="7" r="4"></circle>
625
+ <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
626
+ <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
627
+ </svg>
628
+ </span>
629
+ <strong>Multi-Agent (Beta)</strong>
630
+ </button>
597
631
  </nav>
598
632
 
599
633
  <div className="settings-content">
@@ -674,6 +708,14 @@ export default function SettingsWindow() {
674
708
  handleConnectPlugin={handleConnectPlugin}
675
709
  />
676
710
  )}
711
+
712
+ {activeTab === 'sect-agents' && (
713
+ <AgentsTab
714
+ config={config}
715
+ updateField={updateField}
716
+ dynamicOllamaModels={dynamicOllamaModels}
717
+ />
718
+ )}
677
719
  </div>
678
720
  </main>
679
721
 
@@ -1,5 +1,5 @@
1
- import { useEffect, useState } from 'react'
2
- import { getWorkspaceTree, type WorkspaceTreeEntry } from '../tauri'
1
+ import React, { useEffect, useState } from 'react'
2
+ import { getWorkspaceTree, type WorkspaceTreeEntry, createWorkspaceFile, createWorkspaceFolder, deleteWorkspaceItem } from '../tauri'
3
3
  import folderIcon from 'material-icon-theme/icons/folder.svg?url'
4
4
  import folderOpenIcon from 'material-icon-theme/icons/folder-open.svg?url'
5
5
  import folderComponentsIcon from 'material-icon-theme/icons/folder-components.svg?url'
@@ -147,7 +147,7 @@ function materialFileIcon(name: string, fileExtension: string) {
147
147
  return FILE_ICONS_BY_NAME[name.toLowerCase()] || FILE_ICONS_BY_EXTENSION[fileExtension] || documentIcon
148
148
  }
149
149
 
150
- function TreeNode({ entry, level }: { entry: WorkspaceTreeEntry; level: number; key?: string }) {
150
+ function TreeNode({ entry, level, onRefresh, workspacePath }: { entry: WorkspaceTreeEntry; level: number; onRefresh: () => void; workspacePath: string; key?: string }) {
151
151
  const [open, setOpen] = useState(level < 1)
152
152
  const isDirectory = entry.kind === 'directory'
153
153
  const hasChildren = entry.children.length > 0
@@ -156,6 +156,20 @@ function TreeNode({ entry, level }: { entry: WorkspaceTreeEntry; level: number;
156
156
  const materialIcon = isDirectory ? materialFolderIcon(entry.name, open) : materialFileIcon(entry.name, fileExtension)
157
157
  const dragText = `@${entry.path}`
158
158
 
159
+ const handleContextMenu = async (event: React.MouseEvent) => {
160
+ event.preventDefault()
161
+ const confirmed = confirm(`Are you sure you want to delete "${entry.name}"?`)
162
+ if (!confirmed) return
163
+
164
+ try {
165
+ const absolutePath = `${workspacePath}/${entry.path}`
166
+ await deleteWorkspaceItem(absolutePath)
167
+ onRefresh()
168
+ } catch (err) {
169
+ alert(`Failed to delete item: ${err instanceof Error ? err.message : String(err)}`)
170
+ }
171
+ }
172
+
159
173
  return (
160
174
  <div className="workspace-tree-node">
161
175
  <button
@@ -163,6 +177,7 @@ function TreeNode({ entry, level }: { entry: WorkspaceTreeEntry; level: number;
163
177
  className={`workspace-tree-row ${isDirectory ? 'is-directory' : 'is-file'}`}
164
178
  style={{ paddingLeft: `${level * 16 + 8}px` }}
165
179
  onClick={() => isDirectory && hasChildren && setOpen((current) => !current)}
180
+ onContextMenu={handleContextMenu}
166
181
  title={entry.path}
167
182
  draggable
168
183
  onDragStart={(event) => {
@@ -186,7 +201,7 @@ function TreeNode({ entry, level }: { entry: WorkspaceTreeEntry; level: number;
186
201
  {isDirectory && open && hasChildren && (
187
202
  <div className="workspace-tree-children">
188
203
  {entry.children.map((child) => (
189
- <TreeNode key={child.path} entry={child} level={level + 1} />
204
+ <TreeNode key={child.path} entry={child} level={level + 1} onRefresh={onRefresh} workspacePath={workspacePath} />
190
205
  ))}
191
206
  </div>
192
207
  )}
@@ -217,11 +232,52 @@ export default function WorkspacePanel({ agentMode, sending, workspacePath, onEn
217
232
 
218
233
  useEffect(() => {
219
234
  refresh()
235
+
236
+ // Refresh when the window gains focus (e.g., switching back from VS Code)
237
+ const handleFocus = () => {
238
+ refresh()
239
+ }
240
+ window.addEventListener('focus', handleFocus)
241
+
242
+ // Poll every 15 seconds to catch edits/updates in real-time
243
+ const interval = setInterval(() => {
244
+ refresh()
245
+ }, 15000)
246
+
247
+ return () => {
248
+ window.removeEventListener('focus', handleFocus)
249
+ clearInterval(interval)
250
+ }
220
251
  }, [workspacePath])
221
252
 
222
- const startWorkspacePrompt = () => {
223
- onEnableAgentMode()
224
- onSetMessage('Review this workspace and help plan next steps')
253
+ const handleCreateFile = async () => {
254
+ if (!workspacePath.trim()) return
255
+ const name = prompt('Enter name of new file:')
256
+ if (!name || !name.trim()) return
257
+
258
+ try {
259
+ setError('')
260
+ const fullPath = `${workspacePath}/${name.trim()}`
261
+ await createWorkspaceFile(fullPath)
262
+ await refresh()
263
+ } catch (err) {
264
+ setError(err instanceof Error ? err.message : String(err))
265
+ }
266
+ }
267
+
268
+ const handleCreateFolder = async () => {
269
+ if (!workspacePath.trim()) return
270
+ const name = prompt('Enter name of new folder:')
271
+ if (!name || !name.trim()) return
272
+
273
+ try {
274
+ setError('')
275
+ const fullPath = `${workspacePath}/${name.trim()}`
276
+ await createWorkspaceFolder(fullPath)
277
+ await refresh()
278
+ } catch (err) {
279
+ setError(err instanceof Error ? err.message : String(err))
280
+ }
225
281
  }
226
282
 
227
283
  return (
@@ -237,7 +293,8 @@ export default function WorkspacePanel({ agentMode, sending, workspacePath, onEn
237
293
  </header>
238
294
 
239
295
  <div className="workspace-panel-actions">
240
- <button type="button" onClick={startWorkspacePrompt}>Use Agent</button>
296
+ <button type="button" onClick={handleCreateFile} disabled={!workspacePath.trim()}>New File</button>
297
+ <button type="button" onClick={handleCreateFolder} disabled={!workspacePath.trim()}>New Folder</button>
241
298
  <button type="button" onClick={refresh} disabled={!workspacePath.trim()}>Refresh</button>
242
299
  </div>
243
300
 
@@ -255,7 +312,7 @@ export default function WorkspacePanel({ agentMode, sending, workspacePath, onEn
255
312
  </div>
256
313
  <div className="workspace-tree">
257
314
  {tree.children.map((entry) => (
258
- <TreeNode key={entry.path} entry={entry} level={0} />
315
+ <TreeNode key={entry.path} entry={entry} level={0} onRefresh={refresh} workspacePath={workspacePath} />
259
316
  ))}
260
317
  </div>
261
318
  </>
@@ -725,3 +725,9 @@
725
725
  overflow-y: auto;
726
726
  }
727
727
  }
728
+
729
+ @media (max-width: 760px) {
730
+ .img-studio {
731
+ left: 0 !important;
732
+ }
733
+ }