groove-dev 0.16.4 → 0.17.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 (34) hide show
  1. package/README.md +18 -16
  2. package/node_modules/@groove-dev/daemon/integrations-registry.json +417 -0
  3. package/node_modules/@groove-dev/daemon/src/api.js +204 -0
  4. package/node_modules/@groove-dev/daemon/src/index.js +9 -0
  5. package/node_modules/@groove-dev/daemon/src/integrations.js +475 -0
  6. package/node_modules/@groove-dev/daemon/src/introducer.js +23 -0
  7. package/node_modules/@groove-dev/daemon/src/process.js +59 -0
  8. package/node_modules/@groove-dev/daemon/src/registry.js +2 -1
  9. package/node_modules/@groove-dev/daemon/src/scheduler.js +336 -0
  10. package/node_modules/@groove-dev/daemon/src/validate.js +10 -0
  11. package/node_modules/@groove-dev/gui/dist/assets/index-CEf7nLM2.js +156 -0
  12. package/node_modules/@groove-dev/gui/dist/index.html +1 -1
  13. package/node_modules/@groove-dev/gui/src/App.jsx +6 -0
  14. package/node_modules/@groove-dev/gui/src/components/SpawnPanel.jsx +98 -7
  15. package/node_modules/@groove-dev/gui/src/views/IntegrationsStore.jsx +1171 -0
  16. package/node_modules/@groove-dev/gui/src/views/ScheduleManager.jsx +614 -0
  17. package/package.json +2 -2
  18. package/packages/daemon/integrations-registry.json +417 -0
  19. package/packages/daemon/src/api.js +204 -0
  20. package/packages/daemon/src/index.js +9 -0
  21. package/packages/daemon/src/integrations.js +475 -0
  22. package/packages/daemon/src/introducer.js +23 -0
  23. package/packages/daemon/src/process.js +59 -0
  24. package/packages/daemon/src/registry.js +2 -1
  25. package/packages/daemon/src/scheduler.js +336 -0
  26. package/packages/daemon/src/validate.js +10 -0
  27. package/packages/gui/dist/assets/index-CEf7nLM2.js +156 -0
  28. package/packages/gui/dist/index.html +1 -1
  29. package/packages/gui/src/App.jsx +6 -0
  30. package/packages/gui/src/components/SpawnPanel.jsx +98 -7
  31. package/packages/gui/src/views/IntegrationsStore.jsx +1171 -0
  32. package/packages/gui/src/views/ScheduleManager.jsx +614 -0
  33. package/node_modules/@groove-dev/gui/dist/assets/index-B_VHpncx.js +0 -153
  34. package/packages/gui/dist/assets/index-B_VHpncx.js +0 -153
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>GROOVE</title>
7
- <script type="module" crossorigin src="/assets/index-B_VHpncx.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-CEf7nLM2.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-BhjOFLBc.css">
9
9
  </head>
10
10
  <body>
@@ -12,13 +12,17 @@ import TeamSelector from './components/TeamSelector';
12
12
  import CommandCenter from './views/CommandCenter';
13
13
  import ApprovalQueue from './components/ApprovalQueue';
14
14
  import SkillsMarketplace from './views/SkillsMarketplace';
15
+ import IntegrationsStore from './views/IntegrationsStore';
16
+ import ScheduleManager from './views/ScheduleManager';
15
17
  import FileEditor from './views/FileEditor';
16
18
 
17
19
  const TABS = [
18
20
  { id: 'agents', label: 'Agents' },
19
21
  { id: 'editor', label: 'Editor' },
22
+ { id: 'integrations', label: 'Integrations' },
20
23
  { id: 'skills', label: 'Skills' },
21
24
  { id: 'stats', label: 'Stats' },
25
+ { id: 'schedules', label: 'Schedules' },
22
26
  { id: 'teams', label: 'Teams' },
23
27
  { id: 'approvals', label: 'Approvals' },
24
28
  ];
@@ -143,8 +147,10 @@ export default function App() {
143
147
  !hasAgents ? <EmptyState /> : <AgentTree />
144
148
  )}
145
149
  {activeTab === 'editor' && <FileEditor />}
150
+ {activeTab === 'integrations' && <IntegrationsStore />}
146
151
  {activeTab === 'skills' && <SkillsMarketplace />}
147
152
  {activeTab === 'stats' && <CommandCenter />}
153
+ {activeTab === 'schedules' && <ScheduleManager />}
148
154
  {activeTab === 'teams' && <TeamSelector />}
149
155
  {activeTab === 'approvals' && <ApprovalQueue />}
150
156
  </main>
@@ -6,13 +6,21 @@ import { useGrooveStore } from '../stores/groove';
6
6
  import DirPicker from './DirPicker';
7
7
 
8
8
  const ROLE_PRESETS = [
9
- { id: 'backend', label: 'Backend', desc: 'APIs, server logic, database', scope: ['src/api/**', 'src/server/**', 'src/lib/**', 'src/db/**'] },
10
- { id: 'frontend', label: 'Frontend', desc: 'UI components, views, styles', scope: ['src/components/**', 'src/views/**', 'src/pages/**', 'src/styles/**'] },
11
- { id: 'fullstack', label: 'Fullstack', desc: 'Full codebase access', scope: [] },
12
- { id: 'planner', label: 'Planner', desc: 'Architecture, research, planning', scope: [] },
13
- { id: 'testing', label: 'Testing', desc: 'Tests, specs, coverage', scope: ['tests/**', 'test/**', '**/*.test.*', '**/*.spec.*'] },
14
- { id: 'devops', label: 'DevOps', desc: 'Docker, CI/CD, infra', scope: ['Dockerfile*', 'docker-compose*', '.github/**', 'infra/**'] },
15
- { id: 'docs', label: 'Docs', desc: 'Documentation, READMEs', scope: ['docs/**', '*.md'] },
9
+ // Coding roles
10
+ { id: 'backend', label: 'Backend', desc: 'APIs, server logic, database', scope: ['src/api/**', 'src/server/**', 'src/lib/**', 'src/db/**'], category: 'coding' },
11
+ { id: 'frontend', label: 'Frontend', desc: 'UI components, views, styles', scope: ['src/components/**', 'src/views/**', 'src/pages/**', 'src/styles/**'], category: 'coding' },
12
+ { id: 'fullstack', label: 'Fullstack', desc: 'Full codebase access', scope: [], category: 'coding' },
13
+ { id: 'planner', label: 'Planner', desc: 'Architecture, research, planning', scope: [], category: 'coding' },
14
+ { id: 'testing', label: 'Testing', desc: 'Tests, specs, coverage', scope: ['tests/**', 'test/**', '**/*.test.*', '**/*.spec.*'], category: 'coding' },
15
+ { id: 'devops', label: 'DevOps', desc: 'Docker, CI/CD, infra', scope: ['Dockerfile*', 'docker-compose*', '.github/**', 'infra/**'], category: 'coding' },
16
+ { id: 'docs', label: 'Docs', desc: 'Documentation, READMEs', scope: ['docs/**', '*.md'], category: 'coding' },
17
+ // Business roles
18
+ { id: 'cmo', label: 'CMO', desc: 'Marketing, social media, content', scope: [], category: 'business', integrations: ['slack', 'brave-search'] },
19
+ { id: 'cfo', label: 'CFO', desc: 'Finance, billing, revenue', scope: [], category: 'business', integrations: ['stripe', 'google-drive'] },
20
+ { id: 'ea', label: 'EA', desc: 'Scheduling, email, comms', scope: [], category: 'business', integrations: ['gmail', 'google-calendar', 'slack'] },
21
+ { id: 'support', label: 'Support', desc: 'Customer support, triage', scope: [], category: 'business', integrations: ['slack', 'discord'] },
22
+ { id: 'analyst', label: 'Analyst', desc: 'Data analysis, reporting', scope: [], category: 'business', integrations: ['postgres', 'google-drive'] },
23
+ { id: 'home', label: 'Home', desc: 'Smart home automation', scope: [], category: 'business', integrations: ['home-assistant'] },
16
24
  ];
17
25
 
18
26
  const PERMISSION_LEVELS = [
@@ -43,11 +51,14 @@ export default function SpawnPanel() {
43
51
  const [showDirPicker, setShowDirPicker] = useState(false);
44
52
  const [installedSkills, setInstalledSkills] = useState([]);
45
53
  const [selectedSkills, setSelectedSkills] = useState([]);
54
+ const [installedIntegrations, setInstalledIntegrations] = useState([]);
55
+ const [selectedIntegrations, setSelectedIntegrations] = useState([]);
46
56
 
47
57
  useEffect(() => {
48
58
  fetchProviders();
49
59
  fetchWorkspaces();
50
60
  fetchInstalledSkills();
61
+ fetchInstalledIntegrations();
51
62
  }, []);
52
63
 
53
64
  async function fetchProviders() {
@@ -72,12 +83,36 @@ export default function SpawnPanel() {
72
83
  } catch { /* ignore */ }
73
84
  }
74
85
 
86
+ async function fetchInstalledIntegrations() {
87
+ try {
88
+ const res = await fetch('/api/integrations/installed');
89
+ setInstalledIntegrations(await res.json());
90
+ } catch { /* ignore */ }
91
+ }
92
+
75
93
  function toggleSkill(skillId) {
76
94
  setSelectedSkills((prev) =>
77
95
  prev.includes(skillId) ? prev.filter((s) => s !== skillId) : [...prev, skillId]
78
96
  );
79
97
  }
80
98
 
99
+ function toggleIntegration(integrationId) {
100
+ setSelectedIntegrations((prev) =>
101
+ prev.includes(integrationId) ? prev.filter((s) => s !== integrationId) : [...prev, integrationId]
102
+ );
103
+ }
104
+
105
+ // Auto-select integrations when a business role is chosen
106
+ useEffect(() => {
107
+ const preset = ROLE_PRESETS.find((p) => p.id === role);
108
+ if (preset?.integrations && installedIntegrations.length > 0) {
109
+ const autoSelect = preset.integrations.filter((id) =>
110
+ installedIntegrations.some((i) => i.id === id && i.configured)
111
+ );
112
+ setSelectedIntegrations(autoSelect);
113
+ }
114
+ }, [role, installedIntegrations]);
115
+
81
116
  const selectedPreset = ROLE_PRESETS.find((p) => p.id === role);
82
117
  const effectiveScope = role === 'custom'
83
118
  ? scope
@@ -144,6 +179,7 @@ export default function SpawnPanel() {
144
179
  permission,
145
180
  ...(workingDir.trim() ? { workingDir: workingDir.trim() } : {}),
146
181
  ...(selectedSkills.length > 0 ? { skills: selectedSkills } : {}),
182
+ ...(selectedIntegrations.length > 0 ? { integrations: selectedIntegrations } : {}),
147
183
  });
148
184
  closeDetail();
149
185
  } catch (err) {
@@ -298,6 +334,61 @@ export default function SpawnPanel() {
298
334
  ))}
299
335
  </div>
300
336
 
337
+ {/* Integrations picker */}
338
+ {installedIntegrations.length > 0 && (
339
+ <>
340
+ <div style={styles.label}>INTEGRATIONS</div>
341
+ <div style={styles.skillsGrid}>
342
+ {installedIntegrations.map((item) => {
343
+ const active = selectedIntegrations.includes(item.id);
344
+ const ready = item.configured;
345
+ return (
346
+ <button
347
+ key={item.id}
348
+ type="button"
349
+ onClick={() => ready && toggleIntegration(item.id)}
350
+ title={ready ? item.description : 'Configure credentials first'}
351
+ style={{
352
+ ...styles.skillBtn,
353
+ borderColor: active ? 'var(--accent)' : !ready ? 'var(--amber)' : 'var(--border)',
354
+ background: active ? 'rgba(51, 175, 188, 0.08)' : 'var(--bg-surface)',
355
+ opacity: ready ? 1 : 0.5,
356
+ cursor: ready ? 'pointer' : 'not-allowed',
357
+ }}
358
+ >
359
+ <span style={{
360
+ ...styles.skillIcon,
361
+ background: active ? 'var(--accent)' : !ready ? 'var(--amber)' : 'var(--bg-active)',
362
+ color: active ? 'var(--bg-base)' : 'var(--text-dim)',
363
+ }}>
364
+ {(item.name || '?').charAt(0)}
365
+ </span>
366
+ <div style={{ flex: 1, minWidth: 0 }}>
367
+ <div style={{
368
+ fontSize: 11, fontWeight: 600,
369
+ color: active ? 'var(--text-bright)' : 'var(--text-primary)',
370
+ }}>
371
+ {item.name}
372
+ </div>
373
+ <div style={{ fontSize: 9, color: ready ? 'var(--green)' : 'var(--amber)' }}>
374
+ {ready ? 'connected' : 'needs setup'}
375
+ </div>
376
+ </div>
377
+ {active && (
378
+ <span style={{ fontSize: 10, color: 'var(--accent)', flexShrink: 0 }}>{'\u2713'}</span>
379
+ )}
380
+ </button>
381
+ );
382
+ })}
383
+ </div>
384
+ {selectedIntegrations.length > 0 && (
385
+ <div style={styles.hint}>
386
+ {selectedIntegrations.length} integration{selectedIntegrations.length !== 1 ? 's' : ''} will provide MCP tools to this agent
387
+ </div>
388
+ )}
389
+ </>
390
+ )}
391
+
301
392
  {/* Skills picker */}
302
393
  {installedSkills.length > 0 && (
303
394
  <>