groove-dev 0.25.8 → 0.25.10

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.
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" type="image/png" href="/favicon.png" />
7
7
  <title>Groove GUI</title>
8
- <script type="module" crossorigin src="/assets/index-DVW2lYyr.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-Cg1mJi9s.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/vendor-C0HXlhrU.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/reactflow-BQPfi37R.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/codemirror-BBL3i_JW.js">
@@ -11,7 +11,7 @@ import {
11
11
  Server, Monitor, Code2, TestTube, Cloud, FileText,
12
12
  Shield, Database, Megaphone, Calculator, UserCheck,
13
13
  Headphones, BarChart3, Rocket, ChevronDown, Pen, Presentation,
14
- Sparkles, X, Search,
14
+ Sparkles, X, Search, AlertTriangle,
15
15
  } from 'lucide-react';
16
16
  import { api } from '../../lib/api';
17
17
  import { Dialog, DialogContent } from '../ui/dialog';
@@ -67,7 +67,15 @@ export function SpawnWizard() {
67
67
  useEffect(() => {
68
68
  if (open) {
69
69
  fetchProviders().then((data) => {
70
- setProviders(Array.isArray(data) ? data : data.providers || []);
70
+ const list = Array.isArray(data) ? data : data.providers || [];
71
+ setProviders(list);
72
+ // Auto-select first installed provider
73
+ const installed = list.filter((p) => p.installed);
74
+ if (installed.length > 0 && !provider) {
75
+ const priority = ['claude-code', 'gemini', 'codex', 'ollama'];
76
+ const best = priority.find((pid) => installed.some((p) => p.id === pid)) || installed[0].id;
77
+ setProvider(best);
78
+ }
71
79
  }).catch(() => {});
72
80
  api.get('/skills/installed').then((data) => {
73
81
  setInstalledSkills(Array.isArray(data) ? data : []);
@@ -329,7 +337,13 @@ export function SpawnWizard() {
329
337
 
330
338
  {/* Sticky footer */}
331
339
  <div className="border-t border-border-subtle px-5 py-4 bg-surface-1">
332
- {selectedRole && (
340
+ {installedProviders.length === 0 && providers.length > 0 && (
341
+ <div className="flex items-center gap-2 mb-3 px-3 py-2 rounded-md bg-warning/8 border border-warning/20">
342
+ <AlertTriangle size={13} className="text-warning flex-shrink-0" />
343
+ <span className="text-2xs font-sans text-text-2">No AI providers installed. Install Claude Code, Gemini CLI, Codex, or Ollama to spawn agents.</span>
344
+ </div>
345
+ )}
346
+ {selectedRole && installedProviders.length > 0 && (
333
347
  <div className="flex items-center gap-2 mb-3 text-xs text-text-3 font-sans">
334
348
  <span>Spawning</span>
335
349
  <Badge variant="accent">{selectedRole}</Badge>
@@ -341,7 +355,7 @@ export function SpawnWizard() {
341
355
  variant="primary"
342
356
  size="lg"
343
357
  onClick={handleSpawn}
344
- disabled={!selectedRole || spawning}
358
+ disabled={!selectedRole || spawning || installedProviders.length === 0}
345
359
  className="w-full"
346
360
  >
347
361
  {spawning ? 'Spawning...' : 'Spawn Agent'}
@@ -120,7 +120,15 @@ export const useGrooveStore = create((set, get) => ({
120
120
  if (arr.length > 200) timeline[agent.id] = arr.slice(-200);
121
121
  }
122
122
  }
123
- set({ agents: msg.data, tokenTimeline: timeline, hydrated: true });
123
+ // Only replace agents array if something meaningful changed
124
+ // (prevents React Flow tree flicker on every lastActivity update)
125
+ const prev = get().agents;
126
+ const changed = msg.data.length !== prev.length || msg.data.some((a, i) => {
127
+ const p = prev[i];
128
+ return !p || p.id !== a.id || p.status !== a.status || p.tokensUsed !== a.tokensUsed
129
+ || p.contextUsage !== a.contextUsage || p.name !== a.name || p.model !== a.model;
130
+ });
131
+ set({ agents: changed ? msg.data : prev, tokenTimeline: timeline, hydrated: true });
124
132
  break;
125
133
  }
126
134
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groove-dev",
3
- "version": "0.25.8",
3
+ "version": "0.25.10",
4
4
  "description": "Open-source agent orchestration layer — the AI company OS. MCP integrations (Slack, Gmail, Stripe, 15+), agent scheduling (cron), business roles (CMO, CFO, EA). GUI dashboard, multi-agent coordination, zero cold-start, infinite sessions. Works with Claude Code, Codex, Gemini CLI, Ollama.",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
@@ -165,12 +165,33 @@ export class ProcessManager {
165
165
  }
166
166
  }
167
167
 
168
- // Validate provider exists and is installed
169
- const provider = getProvider(config.provider || 'claude-code');
168
+ // Resolve provider auto-detect best installed if not specified
169
+ let providerName = config.provider;
170
+ if (!providerName) {
171
+ const { getInstalledProviders } = await import('./providers/index.js');
172
+ const installed = getInstalledProviders();
173
+ if (installed.length === 0) {
174
+ throw new Error('No AI providers installed. Install Claude Code, Gemini CLI, Codex, or Ollama first.');
175
+ }
176
+ // Priority: claude-code > gemini > codex > ollama
177
+ const priority = ['claude-code', 'gemini', 'codex', 'ollama'];
178
+ const best = priority.find((p) => installed.some((i) => i.id === p)) || installed[0].id;
179
+ providerName = best;
180
+ }
181
+
182
+ const provider = getProvider(providerName);
170
183
  if (!provider) {
171
- throw new Error(`Unknown provider: ${config.provider}`);
184
+ throw new Error(`Unknown provider: ${providerName}`);
172
185
  }
173
186
  if (!provider.constructor.isInstalled()) {
187
+ // Try to find any installed provider as fallback
188
+ const { getInstalledProviders } = await import('./providers/index.js');
189
+ const installed = getInstalledProviders();
190
+ if (installed.length > 0) {
191
+ throw new Error(
192
+ `${provider.constructor.displayName} is not installed. Available providers: ${installed.map((p) => p.name).join(', ')}`
193
+ );
194
+ }
174
195
  throw new Error(
175
196
  `${provider.constructor.displayName} is not installed. Run: ${provider.constructor.installCommand()}`
176
197
  );
@@ -183,7 +204,7 @@ export class ProcessManager {
183
204
  // Register the agent in the registry
184
205
  const agent = registry.add({
185
206
  ...config,
186
- provider: config.provider || 'claude-code',
207
+ provider: providerName,
187
208
  model: isAutoRouted ? null : config.model, // Set after routing
188
209
  });
189
210