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