groove-dev 0.25.4 → 0.25.5
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/node_modules/@groove-dev/gui/dist/assets/{index-DtW5ej1k.js → index-feaBOh4i.js} +127 -127
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +32 -1
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +39 -2
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +5 -1
- package/package.json +1 -1
- package/packages/gui/dist/assets/{index-DtW5ej1k.js → index-feaBOh4i.js} +127 -127
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/src/components/agents/agent-chat.jsx +32 -1
- package/packages/gui/src/components/agents/spawn-wizard.jsx +39 -2
- package/packages/gui/src/views/agents.jsx +5 -1
|
@@ -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-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-feaBOh4i.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">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
2
|
import { useState, useRef, useEffect } from 'react';
|
|
3
|
-
import { Send, Loader2, MessageSquare, HelpCircle, ArrowRight } from 'lucide-react';
|
|
3
|
+
import { Send, Loader2, MessageSquare, HelpCircle, ArrowRight, Paperclip } from 'lucide-react';
|
|
4
4
|
import { useGrooveStore } from '../../stores/groove';
|
|
5
5
|
import { cn } from '../../lib/cn';
|
|
6
6
|
import { Avatar } from '../ui/avatar';
|
|
@@ -108,8 +108,10 @@ export function AgentChat({ agent }) {
|
|
|
108
108
|
|
|
109
109
|
const [input, setInput] = useState('');
|
|
110
110
|
const [sending, setSending] = useState(false);
|
|
111
|
+
const [attachedFiles, setAttachedFiles] = useState([]);
|
|
111
112
|
const scrollRef = useRef(null);
|
|
112
113
|
const inputRef = useRef(null);
|
|
114
|
+
const fileInputRef = useRef(null);
|
|
113
115
|
|
|
114
116
|
useEffect(() => {
|
|
115
117
|
if (scrollRef.current) {
|
|
@@ -117,10 +119,23 @@ export function AgentChat({ agent }) {
|
|
|
117
119
|
}
|
|
118
120
|
}, [chatHistory.length, activityLog.length]);
|
|
119
121
|
|
|
122
|
+
function handleFileSelect(e) {
|
|
123
|
+
const files = Array.from(e.target.files || []);
|
|
124
|
+
if (files.length === 0) return;
|
|
125
|
+
// Use webkitRelativePath or name — files from input have path info
|
|
126
|
+
const paths = files.map((f) => f.name);
|
|
127
|
+
setAttachedFiles((prev) => [...prev, ...paths]);
|
|
128
|
+
// Also add file paths to the prompt so the agent knows about them
|
|
129
|
+
const pathList = files.map((f) => f.name).join(', ');
|
|
130
|
+
setInput((prev) => prev + (prev ? '\n' : '') + `[Attached files: ${pathList}]`);
|
|
131
|
+
e.target.value = '';
|
|
132
|
+
}
|
|
133
|
+
|
|
120
134
|
async function handleSend() {
|
|
121
135
|
const text = input.trim();
|
|
122
136
|
if (!text || sending) return;
|
|
123
137
|
setInput('');
|
|
138
|
+
setAttachedFiles([]);
|
|
124
139
|
setSending(true);
|
|
125
140
|
try {
|
|
126
141
|
if (text.startsWith('?')) {
|
|
@@ -192,6 +207,22 @@ export function AgentChat({ agent }) {
|
|
|
192
207
|
</div>
|
|
193
208
|
|
|
194
209
|
<div className="flex items-end gap-2">
|
|
210
|
+
{/* File import */}
|
|
211
|
+
<input
|
|
212
|
+
ref={fileInputRef}
|
|
213
|
+
type="file"
|
|
214
|
+
multiple
|
|
215
|
+
accept=".pdf,.png,.jpg,.jpeg,.gif,.svg,.csv,.txt,.md,.json,.yaml,.yml,.docx,.pptx,.xlsx"
|
|
216
|
+
onChange={handleFileSelect}
|
|
217
|
+
className="hidden"
|
|
218
|
+
/>
|
|
219
|
+
<button
|
|
220
|
+
onClick={() => fileInputRef.current?.click()}
|
|
221
|
+
className="w-10 h-10 flex items-center justify-center rounded-xl text-text-4 hover:text-text-1 hover:bg-surface-3 transition-colors cursor-pointer"
|
|
222
|
+
title="Attach file"
|
|
223
|
+
>
|
|
224
|
+
<Paperclip size={16} />
|
|
225
|
+
</button>
|
|
195
226
|
<textarea
|
|
196
227
|
ref={inputRef}
|
|
197
228
|
value={input}
|
|
@@ -11,7 +11,9 @@ 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,
|
|
14
15
|
} from 'lucide-react';
|
|
16
|
+
import { api } from '../../lib/api';
|
|
15
17
|
|
|
16
18
|
const ROLE_PRESETS = [
|
|
17
19
|
{ id: 'planner', label: 'Planner', desc: 'Plans the team and tasks', icon: Rocket, tier: 'Heavy' },
|
|
@@ -46,15 +48,20 @@ export function SpawnWizard() {
|
|
|
46
48
|
const [model, setModel] = useState('');
|
|
47
49
|
const [prompt, setPrompt] = useState('');
|
|
48
50
|
const [providers, setProviders] = useState([]);
|
|
51
|
+
const [installedSkills, setInstalledSkills] = useState([]);
|
|
52
|
+
const [selectedSkills, setSelectedSkills] = useState([]);
|
|
49
53
|
const [spawning, setSpawning] = useState(false);
|
|
50
54
|
|
|
51
55
|
useEffect(() => {
|
|
52
56
|
if (open) {
|
|
53
57
|
fetchProviders().then((data) => {
|
|
54
|
-
// API returns array directly
|
|
55
58
|
setProviders(Array.isArray(data) ? data : data.providers || []);
|
|
56
59
|
}).catch(() => {});
|
|
60
|
+
api.get('/skills/installed').then((data) => {
|
|
61
|
+
setInstalledSkills(Array.isArray(data) ? data : []);
|
|
62
|
+
}).catch(() => {});
|
|
57
63
|
setRole(''); setCustomRole(''); setName(''); setProvider(''); setModel(''); setPrompt('');
|
|
64
|
+
setSelectedSkills([]);
|
|
58
65
|
}
|
|
59
66
|
}, [open, fetchProviders]);
|
|
60
67
|
|
|
@@ -69,10 +76,11 @@ export function SpawnWizard() {
|
|
|
69
76
|
try {
|
|
70
77
|
const config = {
|
|
71
78
|
role: selectedRole,
|
|
72
|
-
...(name && { name: name.replace(/\s+/g, '-') }),
|
|
79
|
+
...(name && { name: name.replace(/\s+/g, '-') }),
|
|
73
80
|
...(provider && { provider }),
|
|
74
81
|
...(model && { model }),
|
|
75
82
|
...(prompt && { prompt }),
|
|
83
|
+
...(selectedSkills.length > 0 && { skills: selectedSkills }),
|
|
76
84
|
};
|
|
77
85
|
await spawnAgent(config);
|
|
78
86
|
closeDetail();
|
|
@@ -202,6 +210,35 @@ export function SpawnWizard() {
|
|
|
202
210
|
</div>
|
|
203
211
|
)}
|
|
204
212
|
|
|
213
|
+
{/* Skills */}
|
|
214
|
+
{installedSkills.length > 0 && (
|
|
215
|
+
<div className="space-y-1.5">
|
|
216
|
+
<label className="text-xs font-medium text-text-2 font-sans">Skills</label>
|
|
217
|
+
<div className="flex flex-wrap gap-1.5">
|
|
218
|
+
{installedSkills.map((skill) => {
|
|
219
|
+
const active = selectedSkills.includes(skill.id);
|
|
220
|
+
return (
|
|
221
|
+
<button
|
|
222
|
+
key={skill.id}
|
|
223
|
+
onClick={() => setSelectedSkills((prev) =>
|
|
224
|
+
active ? prev.filter((s) => s !== skill.id) : [...prev, skill.id]
|
|
225
|
+
)}
|
|
226
|
+
className={cn(
|
|
227
|
+
'inline-flex items-center gap-1 px-2 py-1 rounded text-2xs font-sans transition-colors cursor-pointer',
|
|
228
|
+
active
|
|
229
|
+
? 'bg-accent/15 text-accent border border-accent/30'
|
|
230
|
+
: 'bg-surface-0 text-text-2 border border-border-subtle hover:border-border',
|
|
231
|
+
)}
|
|
232
|
+
>
|
|
233
|
+
<Sparkles size={10} />
|
|
234
|
+
{skill.name || skill.id}
|
|
235
|
+
</button>
|
|
236
|
+
);
|
|
237
|
+
})}
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
)}
|
|
241
|
+
|
|
205
242
|
<Textarea
|
|
206
243
|
label="Prompt (optional)"
|
|
207
244
|
value={prompt}
|
|
@@ -216,6 +216,10 @@ function AgentTreeInner() {
|
|
|
216
216
|
const occupied = new Set();
|
|
217
217
|
const posKey = (x, y) => `${Math.round(x / 100)},${Math.round(y / 100)}`;
|
|
218
218
|
|
|
219
|
+
// Mark root node position as occupied
|
|
220
|
+
const rootPos = saved[ROOT_ID] || { x: 0, y: 0 };
|
|
221
|
+
occupied.add(posKey(rootPos.x, rootPos.y));
|
|
222
|
+
|
|
219
223
|
// First pass: place agents with saved positions
|
|
220
224
|
const pending = [];
|
|
221
225
|
agents.forEach((agent, i) => {
|
|
@@ -239,7 +243,7 @@ function AgentTreeInner() {
|
|
|
239
243
|
const col = index % MAX_PER_ROW;
|
|
240
244
|
const totalInRow = Math.min(agents.length - row * MAX_PER_ROW, MAX_PER_ROW);
|
|
241
245
|
const offsetX = -((totalInRow - 1) * NODE_X_GAP) / 2;
|
|
242
|
-
let pos = { x: offsetX + col * NODE_X_GAP, y:
|
|
246
|
+
let pos = { x: offsetX + col * NODE_X_GAP, y: NODE_Y_GAP + row * NODE_Y_GAP };
|
|
243
247
|
|
|
244
248
|
// If position is occupied, shift down until we find empty space
|
|
245
249
|
while (occupied.has(posKey(pos.x, pos.y))) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.5",
|
|
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)",
|