groove-dev 0.26.38 → 0.27.0
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.
- package/CHANGELOG.md +59 -0
- package/CLAUDE.md +24 -19
- package/node_modules/@groove-dev/cli/bin/groove.js +2 -0
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/cli/src/commands/nuke.js +16 -4
- package/node_modules/@groove-dev/cli/src/commands/stop.js +17 -2
- package/node_modules/@groove-dev/daemon/integrations-registry.json +681 -75
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/adaptive.js +23 -25
- package/node_modules/@groove-dev/daemon/src/api.js +346 -22
- package/node_modules/@groove-dev/daemon/src/classifier.js +53 -6
- package/node_modules/@groove-dev/daemon/src/firstrun.js +14 -1
- package/node_modules/@groove-dev/daemon/src/gateways/manager.js +2 -2
- package/node_modules/@groove-dev/daemon/src/index.js +28 -4
- package/node_modules/@groove-dev/daemon/src/integrations.js +215 -14
- package/node_modules/@groove-dev/daemon/src/introducer.js +84 -11
- package/node_modules/@groove-dev/daemon/src/journalist.js +43 -1
- package/node_modules/@groove-dev/daemon/src/lockmanager.js +60 -0
- package/node_modules/@groove-dev/daemon/src/mcp-manager.js +270 -0
- package/node_modules/@groove-dev/daemon/src/memory.js +370 -0
- package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
- package/node_modules/@groove-dev/daemon/src/process.js +141 -9
- package/node_modules/@groove-dev/daemon/src/registry.js +1 -1
- package/node_modules/@groove-dev/daemon/src/rotator.js +334 -31
- package/node_modules/@groove-dev/daemon/src/router.js +43 -0
- package/node_modules/@groove-dev/daemon/src/tokentracker.js +70 -18
- package/node_modules/@groove-dev/daemon/src/validate.js +5 -13
- package/node_modules/@groove-dev/daemon/templates/groove-slides.cjs +306 -0
- package/node_modules/@groove-dev/daemon/test/classifier.test.js +3 -5
- package/node_modules/@groove-dev/daemon/test/lockmanager.test.js +64 -0
- package/node_modules/@groove-dev/daemon/test/memory.test.js +252 -0
- package/node_modules/@groove-dev/daemon/test/rotator.test.js +108 -0
- package/node_modules/@groove-dev/daemon/test/router.test.js +64 -0
- package/node_modules/@groove-dev/daemon/test/slides-engine.test.js +230 -0
- package/node_modules/@groove-dev/daemon/test/tokentracker.test.js +78 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-DjORRpF0.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-eCrVowF0.js +652 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -4
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +26 -17
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +22 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +53 -21
- package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +132 -90
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +212 -1
- package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +6 -2
- package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +495 -174
- package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +12 -2
- package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +55 -0
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +24 -19
- package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/marketplace/integration-wizard.jsx +391 -61
- package/node_modules/@groove-dev/gui/src/components/marketplace/marketplace-card.jsx +29 -7
- package/node_modules/@groove-dev/gui/src/lib/format.js +0 -6
- package/node_modules/@groove-dev/gui/src/lib/hooks/use-dashboard.js +23 -5
- package/node_modules/@groove-dev/gui/src/stores/groove.js +59 -9
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +84 -10
- package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +24 -21
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +153 -85
- package/package.json +2 -8
- package/packages/cli/bin/groove.js +2 -0
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/commands/nuke.js +16 -4
- package/packages/cli/src/commands/stop.js +17 -2
- package/packages/daemon/integrations-registry.json +681 -75
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/adaptive.js +23 -25
- package/packages/daemon/src/api.js +346 -22
- package/packages/daemon/src/classifier.js +53 -6
- package/packages/daemon/src/firstrun.js +14 -1
- package/packages/daemon/src/gateways/manager.js +2 -2
- package/packages/daemon/src/index.js +28 -4
- package/packages/daemon/src/integrations.js +215 -14
- package/packages/daemon/src/introducer.js +84 -11
- package/packages/daemon/src/journalist.js +43 -1
- package/packages/daemon/src/lockmanager.js +60 -0
- package/packages/daemon/src/mcp-manager.js +270 -0
- package/packages/daemon/src/memory.js +370 -0
- package/packages/daemon/src/pm.js +1 -1
- package/packages/daemon/src/process.js +141 -9
- package/packages/daemon/src/registry.js +1 -1
- package/packages/daemon/src/rotator.js +334 -31
- package/packages/daemon/src/router.js +43 -0
- package/packages/daemon/src/tokentracker.js +70 -18
- package/packages/daemon/src/validate.js +5 -13
- package/packages/daemon/templates/groove-slides.cjs +306 -0
- package/packages/gui/dist/assets/index-DjORRpF0.css +1 -0
- package/packages/gui/dist/assets/index-eCrVowF0.js +652 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -4
- package/packages/gui/src/components/agents/agent-chat.jsx +26 -17
- package/packages/gui/src/components/agents/agent-config.jsx +22 -1
- package/packages/gui/src/components/agents/agent-feed.jsx +53 -21
- package/packages/gui/src/components/agents/agent-node.jsx +132 -90
- package/packages/gui/src/components/agents/spawn-wizard.jsx +212 -1
- package/packages/gui/src/components/dashboard/cache-ring.jsx +6 -2
- package/packages/gui/src/components/dashboard/intel-panel.jsx +495 -174
- package/packages/gui/src/components/dashboard/kpi-card.jsx +12 -2
- package/packages/gui/src/components/dashboard/team-burn-panel.jsx +55 -0
- package/packages/gui/src/components/layout/activity-bar.jsx +3 -3
- package/packages/gui/src/components/layout/app-shell.jsx +24 -19
- package/packages/gui/src/components/layout/command-palette.jsx +2 -2
- package/packages/gui/src/components/marketplace/integration-wizard.jsx +391 -61
- package/packages/gui/src/components/marketplace/marketplace-card.jsx +29 -7
- package/packages/gui/src/lib/format.js +0 -6
- package/packages/gui/src/lib/hooks/use-dashboard.js +23 -5
- package/packages/gui/src/stores/groove.js +59 -9
- package/packages/gui/src/views/agents.jsx +84 -10
- package/packages/gui/src/views/dashboard.jsx +24 -21
- package/packages/gui/src/views/marketplace.jsx +153 -85
- package/node_modules/@groove-dev/gui/dist/assets/index-CEFKgLGB.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-CaKBNWcK.js +0 -638
- package/node_modules/@groove-dev/gui/dist/groove-logo-short.png +0 -0
- package/node_modules/@groove-dev/gui/dist/groove-logo.png +0 -0
- package/node_modules/@groove-dev/gui/public/groove-logo-short.png +0 -0
- package/node_modules/@groove-dev/gui/public/groove-logo.png +0 -0
- package/node_modules/@groove-dev/gui/src/components/ui/dropdown-menu.jsx +0 -60
- package/node_modules/@groove-dev/gui/src/lib/hooks/use-media-query.js +0 -18
- package/node_modules/@radix-ui/react-dropdown-menu/LICENSE +0 -21
- package/node_modules/@radix-ui/react-dropdown-menu/README.md +0 -3
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.d.mts +0 -97
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.d.ts +0 -97
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.js +0 -337
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.js.map +0 -7
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.mjs +0 -305
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.mjs.map +0 -7
- package/node_modules/@radix-ui/react-dropdown-menu/package.json +0 -75
- package/node_modules/@radix-ui/react-popover/LICENSE +0 -21
- package/node_modules/@radix-ui/react-popover/README.md +0 -3
- package/node_modules/@radix-ui/react-popover/dist/index.d.mts +0 -85
- package/node_modules/@radix-ui/react-popover/dist/index.d.ts +0 -85
- package/node_modules/@radix-ui/react-popover/dist/index.js +0 -352
- package/node_modules/@radix-ui/react-popover/dist/index.js.map +0 -7
- package/node_modules/@radix-ui/react-popover/dist/index.mjs +0 -320
- package/node_modules/@radix-ui/react-popover/dist/index.mjs.map +0 -7
- package/node_modules/@radix-ui/react-popover/package.json +0 -82
- package/node_modules/@radix-ui/react-separator/LICENSE +0 -21
- package/node_modules/@radix-ui/react-separator/README.md +0 -3
- package/node_modules/@radix-ui/react-separator/dist/index.d.mts +0 -21
- package/node_modules/@radix-ui/react-separator/dist/index.d.ts +0 -21
- package/node_modules/@radix-ui/react-separator/dist/index.js +0 -65
- package/node_modules/@radix-ui/react-separator/dist/index.js.map +0 -7
- package/node_modules/@radix-ui/react-separator/dist/index.mjs +0 -32
- package/node_modules/@radix-ui/react-separator/dist/index.mjs.map +0 -7
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/LICENSE +0 -21
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/README.md +0 -3
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.d.mts +0 -52
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.d.ts +0 -52
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.js +0 -80
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.js.map +0 -7
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.mjs +0 -47
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.mjs.map +0 -7
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/package.json +0 -69
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/LICENSE +0 -21
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/README.md +0 -3
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.d.mts +0 -22
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.d.ts +0 -22
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.js +0 -152
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.js.map +0 -7
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.mjs +0 -119
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.mjs.map +0 -7
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/package.json +0 -64
- package/node_modules/@radix-ui/react-separator/package.json +0 -69
- package/packages/gui/dist/assets/index-CEFKgLGB.css +0 -1
- package/packages/gui/dist/assets/index-CaKBNWcK.js +0 -638
- package/packages/gui/dist/groove-logo-short.png +0 -0
- package/packages/gui/dist/groove-logo.png +0 -0
- package/packages/gui/public/groove-logo-short.png +0 -0
- package/packages/gui/public/groove-logo.png +0 -0
- package/packages/gui/src/components/ui/dropdown-menu.jsx +0 -60
- package/packages/gui/src/lib/hooks/use-media-query.js +0 -18
|
@@ -17,7 +17,7 @@ import { api } from '../lib/api';
|
|
|
17
17
|
import { useToast } from '../lib/hooks/use-toast';
|
|
18
18
|
import { fmtNum, timeAgo } from '../lib/format';
|
|
19
19
|
import { useGrooveStore } from '../stores/groove';
|
|
20
|
-
import { IntegrationWizard } from '../components/marketplace/integration-wizard';
|
|
20
|
+
import { IntegrationWizard, GoogleWorkspaceWizard } from '../components/marketplace/integration-wizard';
|
|
21
21
|
import {
|
|
22
22
|
ChevronLeft, ChevronDown, Sparkles, Plug, LogIn, LogOut,
|
|
23
23
|
User, Upload, Package, Download, ShoppingBag, RefreshCw, Trash2,
|
|
@@ -275,12 +275,15 @@ function SkillsBrowse() {
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
// ── Integrations Browse ──────────────────────────────────
|
|
278
|
+
const GOOGLE_IDS = new Set(['gmail', 'google-calendar', 'google-drive', 'google-docs', 'google-sheets', 'google-slides']);
|
|
279
|
+
|
|
278
280
|
function IntegrationsBrowse() {
|
|
279
281
|
const [items, setItems] = useState([]);
|
|
280
282
|
const [loading, setLoading] = useState(true);
|
|
281
283
|
const [search, setSearch] = useState('');
|
|
282
284
|
const [selectedIntegration, setSelectedIntegration] = useState(null);
|
|
283
285
|
const [showWizard, setShowWizard] = useState(false);
|
|
286
|
+
const [showGoogleWizard, setShowGoogleWizard] = useState(false);
|
|
284
287
|
|
|
285
288
|
const fetchItems = () => {
|
|
286
289
|
setLoading(true);
|
|
@@ -292,6 +295,11 @@ function IntegrationsBrowse() {
|
|
|
292
295
|
|
|
293
296
|
useEffect(() => { fetchItems(); }, [search]);
|
|
294
297
|
|
|
298
|
+
// Split items: Google services get grouped, rest stay as individual cards
|
|
299
|
+
const googleItems = items.filter((i) => GOOGLE_IDS.has(i.id));
|
|
300
|
+
const otherItems = items.filter((i) => !GOOGLE_IDS.has(i.id));
|
|
301
|
+
const googleInstalledCount = googleItems.filter((i) => i.installed).length;
|
|
302
|
+
|
|
295
303
|
function handleCardClick(item) {
|
|
296
304
|
setSelectedIntegration(item);
|
|
297
305
|
setShowWizard(true);
|
|
@@ -300,10 +308,13 @@ function IntegrationsBrowse() {
|
|
|
300
308
|
function handleWizardClose() {
|
|
301
309
|
setShowWizard(false);
|
|
302
310
|
setSelectedIntegration(null);
|
|
303
|
-
// Refresh list to pick up install/uninstall changes
|
|
304
311
|
fetchItems();
|
|
305
312
|
}
|
|
306
313
|
|
|
314
|
+
// Check if search matches any Google service
|
|
315
|
+
const googleMatchesSearch = !search || 'google workspace gmail calendar drive docs sheets slides'
|
|
316
|
+
.includes(search.toLowerCase());
|
|
317
|
+
|
|
307
318
|
return (
|
|
308
319
|
<ScrollArea className="h-full">
|
|
309
320
|
<div className="px-5 py-4">
|
|
@@ -312,17 +323,42 @@ function IntegrationsBrowse() {
|
|
|
312
323
|
<SearchBar value={search} onChange={setSearch} placeholder="Search integrations..." />
|
|
313
324
|
</div>
|
|
314
325
|
<div className="flex-1" />
|
|
315
|
-
<span className="text-2xs text-text-4 font-mono flex-shrink-0">
|
|
326
|
+
<span className="text-2xs text-text-4 font-mono flex-shrink-0">
|
|
327
|
+
{otherItems.length + (googleItems.length > 0 ? 1 : 0)}
|
|
328
|
+
</span>
|
|
316
329
|
</div>
|
|
317
330
|
|
|
318
331
|
<div className="mt-4 grid gap-3" style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(240px, 1fr))' }}>
|
|
319
|
-
{loading
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
332
|
+
{loading ? (
|
|
333
|
+
Array.from({ length: 6 }).map((_, i) => <SkillCardSkeleton key={i} />)
|
|
334
|
+
) : (
|
|
335
|
+
<>
|
|
336
|
+
{/* Google Workspace card */}
|
|
337
|
+
{googleItems.length > 0 && googleMatchesSearch && (
|
|
338
|
+
<MarketplaceCard
|
|
339
|
+
key="google-workspace"
|
|
340
|
+
item={{
|
|
341
|
+
id: 'google-workspace',
|
|
342
|
+
name: 'Google Workspace',
|
|
343
|
+
description: `Gmail, Calendar, Drive, Docs, Sheets, Slides — one sign-in for all`,
|
|
344
|
+
category: 'productivity',
|
|
345
|
+
tags: ['google', 'email', 'calendar', 'drive', 'docs'],
|
|
346
|
+
verified: 'mcp-official',
|
|
347
|
+
installed: googleInstalledCount > 0,
|
|
348
|
+
_installedCount: googleInstalledCount,
|
|
349
|
+
}}
|
|
350
|
+
onClick={() => setShowGoogleWizard(true)}
|
|
351
|
+
/>
|
|
352
|
+
)}
|
|
353
|
+
{/* Other integrations */}
|
|
354
|
+
{otherItems.map((item) => (
|
|
355
|
+
<MarketplaceCard key={item.id} item={item} onClick={() => handleCardClick(item)} />
|
|
356
|
+
))}
|
|
357
|
+
</>
|
|
358
|
+
)}
|
|
323
359
|
</div>
|
|
324
360
|
|
|
325
|
-
{!loading &&
|
|
361
|
+
{!loading && otherItems.length === 0 && googleItems.length === 0 && (
|
|
326
362
|
<div className="text-center py-16 text-text-4 font-sans text-sm">No integrations found.</div>
|
|
327
363
|
)}
|
|
328
364
|
</div>
|
|
@@ -332,6 +368,12 @@ function IntegrationsBrowse() {
|
|
|
332
368
|
open={showWizard}
|
|
333
369
|
onClose={handleWizardClose}
|
|
334
370
|
/>
|
|
371
|
+
|
|
372
|
+
<GoogleWorkspaceWizard
|
|
373
|
+
integrations={items}
|
|
374
|
+
open={showGoogleWizard}
|
|
375
|
+
onClose={() => { setShowGoogleWizard(false); fetchItems(); }}
|
|
376
|
+
/>
|
|
335
377
|
</ScrollArea>
|
|
336
378
|
);
|
|
337
379
|
}
|
|
@@ -341,15 +383,24 @@ function MyLibrary() {
|
|
|
341
383
|
const authenticated = useGrooveStore((s) => s.marketplaceAuthenticated);
|
|
342
384
|
const login = useGrooveStore((s) => s.marketplaceLogin);
|
|
343
385
|
const [purchases, setPurchases] = useState([]);
|
|
344
|
-
const [
|
|
386
|
+
const [installedSkills, setInstalledSkills] = useState([]);
|
|
387
|
+
const [installedIntegrations, setInstalledIntegrations] = useState([]);
|
|
345
388
|
const [loading, setLoading] = useState(true);
|
|
346
|
-
const [
|
|
389
|
+
const [libraryFilter, setLibraryFilter] = useState('all');
|
|
390
|
+
const [librarySearch, setLibrarySearch] = useState('');
|
|
391
|
+
const [selectedSkill, setSelectedSkill] = useState(null);
|
|
392
|
+
const [wizardIntegration, setWizardIntegration] = useState(null);
|
|
393
|
+
const [showWizard, setShowWizard] = useState(false);
|
|
347
394
|
const toast = useToast();
|
|
348
395
|
const fileRef = useRef(null);
|
|
349
396
|
|
|
350
397
|
const refreshInstalled = async () => {
|
|
351
|
-
const
|
|
352
|
-
|
|
398
|
+
const [skillsData, intData] = await Promise.all([
|
|
399
|
+
api.get('/skills/installed').catch(() => []),
|
|
400
|
+
api.get('/integrations/installed').catch(() => []),
|
|
401
|
+
]);
|
|
402
|
+
setInstalledSkills(Array.isArray(skillsData) ? skillsData : skillsData.skills || []);
|
|
403
|
+
setInstalledIntegrations(Array.isArray(intData) ? intData : intData.integrations || []);
|
|
353
404
|
};
|
|
354
405
|
|
|
355
406
|
useEffect(() => {
|
|
@@ -357,32 +408,14 @@ function MyLibrary() {
|
|
|
357
408
|
Promise.all([
|
|
358
409
|
authenticated ? api.get('/auth/purchases').then((d) => d.purchases || []).catch(() => []) : Promise.resolve([]),
|
|
359
410
|
api.get('/skills/installed').then((d) => Array.isArray(d) ? d : d.skills || []).catch(() => []),
|
|
360
|
-
|
|
411
|
+
api.get('/integrations/installed').then((d) => Array.isArray(d) ? d : d.integrations || []).catch(() => []),
|
|
412
|
+
]).then(([p, s, i]) => {
|
|
361
413
|
setPurchases(p);
|
|
362
|
-
|
|
414
|
+
setInstalledSkills(s);
|
|
415
|
+
setInstalledIntegrations(i);
|
|
363
416
|
}).finally(() => setLoading(false));
|
|
364
417
|
}, [authenticated]);
|
|
365
418
|
|
|
366
|
-
async function handleUpdate(s) {
|
|
367
|
-
setBusyId(s.id);
|
|
368
|
-
try {
|
|
369
|
-
await api.post(`/skills/${s.id}/update`);
|
|
370
|
-
toast.success(`${s.name || s.id} updated`);
|
|
371
|
-
await refreshInstalled();
|
|
372
|
-
} catch (err) { toast.error('Update failed', err.message); }
|
|
373
|
-
setBusyId(null);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
async function handleUninstall(s) {
|
|
377
|
-
setBusyId(s.id);
|
|
378
|
-
try {
|
|
379
|
-
await api.delete(`/skills/${s.id}`);
|
|
380
|
-
toast.success(`${s.name || s.id} uninstalled`);
|
|
381
|
-
await refreshInstalled();
|
|
382
|
-
} catch (err) { toast.error('Uninstall failed', err.message); }
|
|
383
|
-
setBusyId(null);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
419
|
async function handleImport(e) {
|
|
387
420
|
const file = e.target.files?.[0];
|
|
388
421
|
if (!file) return;
|
|
@@ -398,6 +431,35 @@ function MyLibrary() {
|
|
|
398
431
|
e.target.value = '';
|
|
399
432
|
}
|
|
400
433
|
|
|
434
|
+
if (selectedSkill) {
|
|
435
|
+
return <SkillDetail skill={selectedSkill} onBack={() => setSelectedSkill(null)} />;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const searchLower = librarySearch.toLowerCase();
|
|
439
|
+
const filteredSkills = (libraryFilter === 'all' || libraryFilter === 'skills')
|
|
440
|
+
? installedSkills.filter((s) => !searchLower || (s.name || s.id || '').toLowerCase().includes(searchLower) || (s.description || '').toLowerCase().includes(searchLower))
|
|
441
|
+
: [];
|
|
442
|
+
const filteredIntegrations = (libraryFilter === 'all' || libraryFilter === 'integrations')
|
|
443
|
+
? installedIntegrations.filter((i) => !searchLower || (i.name || i.id || '').toLowerCase().includes(searchLower) || (i.description || '').toLowerCase().includes(searchLower))
|
|
444
|
+
: [];
|
|
445
|
+
|
|
446
|
+
const allItems = [
|
|
447
|
+
...filteredSkills.map((s) => ({ ...s, _type: 'skill' })),
|
|
448
|
+
...filteredIntegrations.map((i) => ({ ...i, _type: 'integration' })),
|
|
449
|
+
];
|
|
450
|
+
|
|
451
|
+
const filters = [
|
|
452
|
+
{ id: 'all', label: 'All' },
|
|
453
|
+
{ id: 'skills', label: 'Skills' },
|
|
454
|
+
{ id: 'integrations', label: 'Integrations' },
|
|
455
|
+
];
|
|
456
|
+
|
|
457
|
+
const emptyMessages = {
|
|
458
|
+
all: 'No skills or integrations installed yet. Visit the Marketplace to get started.',
|
|
459
|
+
skills: 'No skills installed.',
|
|
460
|
+
integrations: 'No integrations installed.',
|
|
461
|
+
};
|
|
462
|
+
|
|
401
463
|
if (loading) {
|
|
402
464
|
return (
|
|
403
465
|
<div className="p-5 space-y-3">
|
|
@@ -408,9 +470,28 @@ function MyLibrary() {
|
|
|
408
470
|
|
|
409
471
|
return (
|
|
410
472
|
<ScrollArea className="h-full">
|
|
411
|
-
<div className="px-5 py-4 space-y-
|
|
412
|
-
{/*
|
|
413
|
-
<div className="flex items-center gap-3">
|
|
473
|
+
<div className="px-5 py-4 space-y-5">
|
|
474
|
+
{/* Search + filter pills + import */}
|
|
475
|
+
<div className="flex items-center gap-3 flex-wrap">
|
|
476
|
+
<div className="w-72">
|
|
477
|
+
<SearchBar value={librarySearch} onChange={setLibrarySearch} placeholder="Search library..." />
|
|
478
|
+
</div>
|
|
479
|
+
<div className="flex items-center gap-1">
|
|
480
|
+
{filters.map((f) => (
|
|
481
|
+
<button
|
|
482
|
+
key={f.id}
|
|
483
|
+
onClick={() => setLibraryFilter(f.id)}
|
|
484
|
+
className={`px-3 py-1.5 text-xs font-semibold font-sans rounded-full cursor-pointer select-none transition-colors ${
|
|
485
|
+
libraryFilter === f.id
|
|
486
|
+
? 'bg-accent/15 text-accent border border-accent/25'
|
|
487
|
+
: 'text-text-3 hover:text-text-1 border border-transparent hover:border-border-subtle'
|
|
488
|
+
}`}
|
|
489
|
+
>
|
|
490
|
+
{f.label}
|
|
491
|
+
</button>
|
|
492
|
+
))}
|
|
493
|
+
</div>
|
|
494
|
+
<div className="flex-1" />
|
|
414
495
|
<input ref={fileRef} type="file" accept=".md" onChange={handleImport} className="hidden" />
|
|
415
496
|
<Button
|
|
416
497
|
variant="secondary"
|
|
@@ -419,9 +500,9 @@ function MyLibrary() {
|
|
|
419
500
|
className="gap-1.5"
|
|
420
501
|
>
|
|
421
502
|
<Upload size={13} />
|
|
422
|
-
Import .md
|
|
503
|
+
Import .md
|
|
423
504
|
</Button>
|
|
424
|
-
<span className="text-2xs text-text-4 font-
|
|
505
|
+
<span className="text-2xs text-text-4 font-mono flex-shrink-0">{allItems.length}</span>
|
|
425
506
|
</div>
|
|
426
507
|
|
|
427
508
|
{/* Purchases */}
|
|
@@ -465,53 +546,40 @@ function MyLibrary() {
|
|
|
465
546
|
</div>
|
|
466
547
|
)}
|
|
467
548
|
|
|
468
|
-
{/*
|
|
469
|
-
|
|
470
|
-
<
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
<
|
|
484
|
-
{
|
|
485
|
-
</
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
className="p-1.5 rounded text-text-3 hover:text-accent hover:bg-accent/10 cursor-pointer transition-colors disabled:opacity-50"
|
|
496
|
-
>
|
|
497
|
-
<RefreshCw size={12} className={busyId === s.id ? 'animate-spin' : ''} />
|
|
498
|
-
</button>
|
|
499
|
-
<button
|
|
500
|
-
onClick={() => handleUninstall(s)}
|
|
501
|
-
disabled={busyId === s.id}
|
|
502
|
-
title="Uninstall"
|
|
503
|
-
className="p-1.5 rounded text-text-3 hover:text-error hover:bg-error/10 cursor-pointer transition-colors disabled:opacity-50"
|
|
504
|
-
>
|
|
505
|
-
<Trash2 size={12} />
|
|
506
|
-
</button>
|
|
507
|
-
</div>
|
|
508
|
-
<Badge variant="accent" className="text-2xs flex-shrink-0">Installed</Badge>
|
|
509
|
-
</div>
|
|
510
|
-
))}
|
|
511
|
-
</div>
|
|
512
|
-
)}
|
|
513
|
-
</div>
|
|
549
|
+
{/* Card grid */}
|
|
550
|
+
{allItems.length > 0 ? (
|
|
551
|
+
<div className="grid gap-3" style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(240px, 1fr))' }}>
|
|
552
|
+
{allItems.map((item) => item._type === 'skill' ? (
|
|
553
|
+
<MarketplaceCard
|
|
554
|
+
key={`skill-${item.id}`}
|
|
555
|
+
item={{ ...item, installed: true }}
|
|
556
|
+
onClick={() => setSelectedSkill(item)}
|
|
557
|
+
/>
|
|
558
|
+
) : (
|
|
559
|
+
<MarketplaceCard
|
|
560
|
+
key={`int-${item.id}`}
|
|
561
|
+
item={{ ...item, installed: true }}
|
|
562
|
+
onClick={() => { setWizardIntegration(item); setShowWizard(true); }}
|
|
563
|
+
statusBadge={
|
|
564
|
+
<Badge variant={item.configured ? 'success' : 'warning'} className="text-2xs">
|
|
565
|
+
{item.configured ? 'Active' : 'Not configured'}
|
|
566
|
+
</Badge>
|
|
567
|
+
}
|
|
568
|
+
/>
|
|
569
|
+
))}
|
|
570
|
+
</div>
|
|
571
|
+
) : (
|
|
572
|
+
<div className="text-center py-16 text-text-4 font-sans text-sm">
|
|
573
|
+
{emptyMessages[libraryFilter]}
|
|
574
|
+
</div>
|
|
575
|
+
)}
|
|
514
576
|
</div>
|
|
577
|
+
|
|
578
|
+
<IntegrationWizard
|
|
579
|
+
integration={wizardIntegration}
|
|
580
|
+
open={showWizard}
|
|
581
|
+
onClose={() => { setShowWizard(false); setWizardIntegration(null); refreshInstalled(); }}
|
|
582
|
+
/>
|
|
515
583
|
</ScrollArea>
|
|
516
584
|
);
|
|
517
585
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. Local model agent engine (GGUF/Ollama/llama-server), HuggingFace model browser, 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, any local model.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -52,15 +52,9 @@
|
|
|
52
52
|
"dev:daemon": "npm run dev -w packages/daemon",
|
|
53
53
|
"dev:gui": "npm run dev -w packages/gui",
|
|
54
54
|
"build": "npm run build -w packages/gui",
|
|
55
|
-
"build:gui": "npm run build -w packages/gui",
|
|
56
55
|
"test": "node --test packages/daemon/test/*.test.js",
|
|
57
|
-
"prepublishOnly": "npm run build
|
|
56
|
+
"prepublishOnly": "npm run build"
|
|
58
57
|
},
|
|
59
|
-
"bundledDependencies": [
|
|
60
|
-
"@groove-dev/daemon",
|
|
61
|
-
"@groove-dev/cli",
|
|
62
|
-
"@groove-dev/gui"
|
|
63
|
-
],
|
|
64
58
|
"bundleDependencies": [
|
|
65
59
|
"@groove-dev/daemon",
|
|
66
60
|
"@groove-dev/cli",
|
|
@@ -39,6 +39,7 @@ program
|
|
|
39
39
|
program
|
|
40
40
|
.command('stop')
|
|
41
41
|
.description('Stop the GROOVE daemon')
|
|
42
|
+
.option('-f, --force', 'Stop even if agents are still running (destroys their work)')
|
|
42
43
|
.action(stop);
|
|
43
44
|
|
|
44
45
|
program
|
|
@@ -69,6 +70,7 @@ program
|
|
|
69
70
|
program
|
|
70
71
|
.command('nuke')
|
|
71
72
|
.description('Kill all agents and stop the daemon')
|
|
73
|
+
.option('-f, --force', 'Required when agents are still running')
|
|
72
74
|
.action(nuke);
|
|
73
75
|
|
|
74
76
|
program
|
|
@@ -4,14 +4,26 @@
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { apiCall } from '../client.js';
|
|
6
6
|
|
|
7
|
-
export async function nuke() {
|
|
7
|
+
export async function nuke(options = {}) {
|
|
8
|
+
let status;
|
|
9
|
+
try {
|
|
10
|
+
status = await apiCall('GET', '/api/status');
|
|
11
|
+
} catch {
|
|
12
|
+
console.log(chalk.yellow('No running daemon found.'));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (status.running > 0 && !options.force) {
|
|
17
|
+
console.error(chalk.red(`Refusing to nuke: ${status.running} agent(s) still running.`));
|
|
18
|
+
console.error(chalk.dim(' Nuke will kill every agent in every team and stop the daemon.'));
|
|
19
|
+
console.error(chalk.dim(' Re-run with "groove nuke --force" to confirm.'));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
8
23
|
try {
|
|
9
24
|
console.log(chalk.yellow('Nuking all agents...'));
|
|
10
25
|
await apiCall('DELETE', '/api/agents');
|
|
11
|
-
|
|
12
|
-
const status = await apiCall('GET', '/api/status');
|
|
13
26
|
process.kill(status.pid, 'SIGTERM');
|
|
14
|
-
|
|
15
27
|
console.log(chalk.green('All agents killed. Daemon stopped.'));
|
|
16
28
|
} catch {
|
|
17
29
|
console.log(chalk.yellow('No running daemon found.'));
|
|
@@ -4,9 +4,24 @@
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { apiCall } from '../client.js';
|
|
6
6
|
|
|
7
|
-
export async function stop() {
|
|
7
|
+
export async function stop(options = {}) {
|
|
8
|
+
let status;
|
|
9
|
+
try {
|
|
10
|
+
status = await apiCall('GET', '/api/status');
|
|
11
|
+
} catch {
|
|
12
|
+
console.log(chalk.yellow('No running daemon found.'));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (status.running > 0 && !options.force) {
|
|
17
|
+
console.error(chalk.red(`Refusing to stop: ${status.running} agent(s) still running.`));
|
|
18
|
+
console.error(chalk.dim(' Stopping the daemon destroys all running agents in every team.'));
|
|
19
|
+
console.error(chalk.dim(' Kill specific agents first with "groove kill <id>",'));
|
|
20
|
+
console.error(chalk.dim(' or override with "groove stop --force".'));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
8
24
|
try {
|
|
9
|
-
const status = await apiCall('GET', '/api/status');
|
|
10
25
|
process.kill(status.pid, 'SIGTERM');
|
|
11
26
|
console.log(chalk.green('GROOVE daemon stopped.'));
|
|
12
27
|
} catch {
|