groove-dev 0.27.91 → 0.27.93

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 (70) hide show
  1. package/node_modules/@groove-dev/cli/package.json +1 -1
  2. package/node_modules/@groove-dev/daemon/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/src/api.js +228 -3
  4. package/node_modules/@groove-dev/daemon/src/introducer.js +42 -0
  5. package/node_modules/@groove-dev/daemon/src/process.js +5 -1
  6. package/node_modules/@groove-dev/daemon/src/providers/base.js +4 -0
  7. package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +8 -0
  8. package/node_modules/@groove-dev/daemon/src/providers/codex.js +33 -4
  9. package/node_modules/@groove-dev/daemon/src/providers/gemini.js +14 -1
  10. package/node_modules/@groove-dev/daemon/src/providers/grok.js +8 -1
  11. package/node_modules/@groove-dev/daemon/src/providers/local.js +8 -1
  12. package/node_modules/@groove-dev/daemon/src/tunnel-manager.js +74 -5
  13. package/node_modules/@groove-dev/daemon/src/validate.js +22 -1
  14. package/node_modules/@groove-dev/gui/dist/assets/{codemirror-BBL3i_JW.js → codemirror-CFF1Lrnz.js} +10 -10
  15. package/node_modules/@groove-dev/gui/dist/assets/index-Bo6AeNmM.css +1 -0
  16. package/node_modules/@groove-dev/gui/dist/assets/index-VB4_k5Pz.js +8653 -0
  17. package/node_modules/@groove-dev/gui/dist/index.html +3 -3
  18. package/node_modules/@groove-dev/gui/package.json +1 -1
  19. package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +26 -44
  20. package/node_modules/@groove-dev/gui/src/components/agents/agent-file-tree.jsx +29 -28
  21. package/node_modules/@groove-dev/gui/src/components/agents/workspace-mode.jsx +53 -143
  22. package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +3 -30
  23. package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +163 -153
  24. package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +15 -5
  25. package/node_modules/@groove-dev/gui/src/components/chat/conversation-list.jsx +26 -17
  26. package/node_modules/@groove-dev/gui/src/components/editor/code-editor.jsx +32 -26
  27. package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +5 -1
  28. package/node_modules/@groove-dev/gui/src/components/settings/remote-server-card.jsx +9 -5
  29. package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +5 -1
  30. package/node_modules/@groove-dev/gui/src/components/ui/slider.jsx +50 -0
  31. package/node_modules/@groove-dev/gui/src/stores/groove.js +151 -12
  32. package/node_modules/@groove-dev/gui/src/views/agents.jsx +720 -38
  33. package/package.json +1 -1
  34. package/packages/cli/package.json +1 -1
  35. package/packages/daemon/package.json +1 -1
  36. package/packages/daemon/src/api.js +228 -3
  37. package/packages/daemon/src/introducer.js +42 -0
  38. package/packages/daemon/src/process.js +5 -1
  39. package/packages/daemon/src/providers/base.js +4 -0
  40. package/packages/daemon/src/providers/claude-code.js +8 -0
  41. package/packages/daemon/src/providers/codex.js +33 -4
  42. package/packages/daemon/src/providers/gemini.js +14 -1
  43. package/packages/daemon/src/providers/grok.js +8 -1
  44. package/packages/daemon/src/providers/local.js +8 -1
  45. package/packages/daemon/src/tunnel-manager.js +74 -5
  46. package/packages/daemon/src/validate.js +22 -1
  47. package/packages/gui/dist/assets/{codemirror-BBL3i_JW.js → codemirror-CFF1Lrnz.js} +10 -10
  48. package/packages/gui/dist/assets/index-Bo6AeNmM.css +1 -0
  49. package/packages/gui/dist/assets/index-VB4_k5Pz.js +8653 -0
  50. package/packages/gui/dist/index.html +3 -3
  51. package/packages/gui/package.json +1 -1
  52. package/packages/gui/src/components/agents/agent-chat.jsx +26 -44
  53. package/packages/gui/src/components/agents/agent-file-tree.jsx +29 -28
  54. package/packages/gui/src/components/agents/workspace-mode.jsx +53 -143
  55. package/packages/gui/src/components/chat/chat-header.jsx +3 -30
  56. package/packages/gui/src/components/chat/chat-input.jsx +163 -153
  57. package/packages/gui/src/components/chat/chat-view.jsx +15 -5
  58. package/packages/gui/src/components/chat/conversation-list.jsx +26 -17
  59. package/packages/gui/src/components/editor/code-editor.jsx +32 -26
  60. package/packages/gui/src/components/settings/quick-connect.jsx +5 -1
  61. package/packages/gui/src/components/settings/remote-server-card.jsx +9 -5
  62. package/packages/gui/src/components/settings/ssh-wizard.jsx +5 -1
  63. package/packages/gui/src/components/ui/slider.jsx +50 -0
  64. package/packages/gui/src/stores/groove.js +151 -12
  65. package/packages/gui/src/views/agents.jsx +720 -38
  66. package/workspace.png +0 -0
  67. package/node_modules/@groove-dev/gui/dist/assets/index-D4vJ_1ET.css +0 -1
  68. package/node_modules/@groove-dev/gui/dist/assets/index-MLIZRMj1.js +0 -8642
  69. package/packages/gui/dist/assets/index-D4vJ_1ET.css +0 -1
  70. package/packages/gui/dist/assets/index-MLIZRMj1.js +0 -8642
@@ -0,0 +1,50 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+ import { cn } from '../../lib/cn';
3
+
4
+ export function TuningSlider({
5
+ label, value, onChange, min = 0, max = 100, step = 1,
6
+ formatValue, displayValue, disabled, className,
7
+ }) {
8
+ const pct = ((value - min) / (max - min)) * 100;
9
+ const fmt = displayValue || formatValue;
10
+ const display = typeof fmt === 'function' ? fmt(value) : (typeof fmt === 'string' ? fmt : value);
11
+
12
+ return (
13
+ <div className={cn('flex items-center gap-3 h-10', disabled && 'opacity-40 pointer-events-none', className)}>
14
+ <span className="text-xs text-text-2 font-sans w-28 shrink-0">{label}</span>
15
+ <span className="text-2xs text-text-4 font-mono w-6 text-right shrink-0">{min}</span>
16
+ <div className="relative flex-1 flex items-center group">
17
+ <div className="absolute inset-y-0 flex items-center w-full pointer-events-none">
18
+ <div className="w-full h-1.5 rounded-full bg-surface-5 overflow-hidden">
19
+ <div
20
+ className="h-full rounded-full bg-accent transition-all"
21
+ style={{ width: `${pct}%` }}
22
+ />
23
+ </div>
24
+ </div>
25
+ <input
26
+ type="range"
27
+ min={min}
28
+ max={max}
29
+ step={step}
30
+ value={value}
31
+ disabled={disabled}
32
+ onChange={(e) => onChange(Number(e.target.value))}
33
+ className="relative w-full h-4 appearance-none bg-transparent cursor-pointer
34
+ [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4
35
+ [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-accent [&::-webkit-slider-thumb]:border-2
36
+ [&::-webkit-slider-thumb]:border-surface-1 [&::-webkit-slider-thumb]:shadow-[0_0_6px_rgba(51,175,188,0.4)]
37
+ [&::-webkit-slider-thumb]:hover:shadow-[0_0_10px_rgba(51,175,188,0.6)]
38
+ [&::-webkit-slider-thumb]:transition-shadow
39
+ [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:rounded-full
40
+ [&::-moz-range-thumb]:bg-accent [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-surface-1
41
+ [&::-moz-range-thumb]:shadow-[0_0_6px_rgba(51,175,188,0.4)]
42
+ [&::-moz-range-track]:bg-transparent
43
+ disabled:cursor-not-allowed"
44
+ />
45
+ </div>
46
+ <span className="text-2xs text-text-4 font-mono w-6 shrink-0">{max}</span>
47
+ <span className="text-xs text-accent font-mono font-semibold w-10 text-right shrink-0">{display}</span>
48
+ </div>
49
+ );
50
+ }
@@ -62,6 +62,17 @@ export const useGrooveStore = create((set, get) => ({
62
62
  previewChat: [],
63
63
  previewIterating: false,
64
64
 
65
+ // ── Team Launch Config (set during planner spawn, cascades to team) ──
66
+ teamLaunchConfig: null, // { provider, model, reasoningEffort, temperature, verbosity }
67
+
68
+ // ── Team Builder ──────────────────────────────────────────
69
+ teamBuilderOpen: false,
70
+ teamBuilderRoles: [],
71
+ teamBuilderSettings: { provider: null, model: null, reasoningEffort: 50, temperature: 0.5 },
72
+ teamBuilderTask: '',
73
+ teamBuilderLaunchMode: 'plan',
74
+ teamTemplates: { builtIn: [], custom: [] },
75
+
65
76
  // ── Navigation ────────────────────────────────────────────
66
77
  activeView: 'agents', // 'agents' | 'editor' | 'dashboard' | 'marketplace' | 'teams' | 'settings' | 'preview'
67
78
  detailPanel: null, // null | { type: 'agent', agentId } | { type: 'spawn' } | { type: 'journalist' }
@@ -428,6 +439,19 @@ export const useGrooveStore = create((set, get) => ({
428
439
  set({ activityLog: log });
429
440
  persistJSON('groove:activityLog', log);
430
441
  }
442
+
443
+ // Open-on-write: auto-open files the agent writes in workspace mode
444
+ if (get().workspaceMode && Array.isArray(data.data)) {
445
+ const WRITE_TOOLS = new Set(['Write', 'Edit', 'write_file', 'edit_file', 'create_file']);
446
+ for (const block of data.data) {
447
+ if (block.type !== 'tool_use' || !WRITE_TOOLS.has(block.name)) continue;
448
+ const filePath = block.input?.file_path || block.input?.path;
449
+ if (filePath && agentId === get().workspaceAgentId) {
450
+ const relPath = filePath.replace(/^\/[^/]+.*?\/groove\//, '');
451
+ get().openFile(relPath);
452
+ }
453
+ }
454
+ }
431
455
  break;
432
456
  }
433
457
 
@@ -1537,9 +1561,18 @@ export const useGrooveStore = create((set, get) => ({
1537
1561
  async launchRecommendedTeam(modifiedAgents) {
1538
1562
  try {
1539
1563
  const teamId = get().recommendedTeam?.teamId || null;
1564
+ const tlc = get().teamLaunchConfig;
1540
1565
  set({ recommendedTeam: null }); // Dismiss modal immediately
1541
1566
  get().addToast('info', 'Launching team...');
1542
- const body = { ...(modifiedAgents && { agents: modifiedAgents }), ...(teamId && { teamId }) };
1567
+ const body = {
1568
+ ...(modifiedAgents && { agents: modifiedAgents }),
1569
+ ...(teamId && { teamId }),
1570
+ ...(tlc?.provider && { teamProvider: tlc.provider }),
1571
+ ...(tlc?.model && { teamModel: tlc.model }),
1572
+ ...(tlc?.reasoningEffort != null && { teamReasoningEffort: tlc.reasoningEffort }),
1573
+ ...(tlc?.temperature != null && { teamTemperature: tlc.temperature }),
1574
+ ...(tlc?.verbosity != null && { teamVerbosity: tlc.verbosity }),
1575
+ };
1543
1576
  const result = await api.post('/recommended-team/launch', body);
1544
1577
  const totalOk = (result.launched || 0) + (result.reused || 0);
1545
1578
  const failures = result.failed || [];
@@ -1577,6 +1610,104 @@ export const useGrooveStore = create((set, get) => ({
1577
1610
  }
1578
1611
  },
1579
1612
 
1613
+ // ── Team Builder ──────────────────────────────────────────
1614
+
1615
+ openTeamBuilder() { set({ teamBuilderOpen: true }); },
1616
+ closeTeamBuilder() {
1617
+ set({
1618
+ teamBuilderOpen: false,
1619
+ teamBuilderRoles: [],
1620
+ teamBuilderSettings: { provider: null, model: null, reasoningEffort: 50, temperature: 0.5 },
1621
+ teamBuilderTask: '',
1622
+ teamBuilderLaunchMode: 'plan',
1623
+ });
1624
+ },
1625
+ addTeamBuilderRole(role) {
1626
+ set((s) => ({
1627
+ teamBuilderRoles: [...s.teamBuilderRoles, {
1628
+ role, name: '', provider: null, model: null,
1629
+ reasoningEffort: null, temperature: null, prompt: '',
1630
+ }],
1631
+ }));
1632
+ },
1633
+ removeTeamBuilderRole(index) {
1634
+ set((s) => ({ teamBuilderRoles: s.teamBuilderRoles.filter((_, i) => i !== index) }));
1635
+ },
1636
+ updateTeamBuilderRole(index, updates) {
1637
+ set((s) => ({
1638
+ teamBuilderRoles: s.teamBuilderRoles.map((r, i) => i === index ? { ...r, ...updates } : r),
1639
+ }));
1640
+ },
1641
+ applyTemplate(template) {
1642
+ set({
1643
+ teamBuilderRoles: (template.roles || []).map((r) => ({
1644
+ role: typeof r === 'string' ? r : r.role,
1645
+ name: '', provider: null, model: null,
1646
+ reasoningEffort: null, temperature: null, prompt: '',
1647
+ })),
1648
+ });
1649
+ },
1650
+ setTeamBuilderSettings(settings) {
1651
+ set((s) => ({ teamBuilderSettings: { ...s.teamBuilderSettings, ...settings } }));
1652
+ },
1653
+ setTeamBuilderTask(task) { set({ teamBuilderTask: task }); },
1654
+ setTeamBuilderLaunchMode(mode) { set({ teamBuilderLaunchMode: mode }); },
1655
+
1656
+ async fetchTeamTemplates() {
1657
+ try {
1658
+ const data = await api.get('/team-templates');
1659
+ set({ teamTemplates: data });
1660
+ } catch { /* endpoint may not exist yet */ }
1661
+ },
1662
+
1663
+ async saveTeamTemplate(name) {
1664
+ try {
1665
+ const { teamBuilderRoles, teamBuilderSettings } = get();
1666
+ await api.post('/team-templates', {
1667
+ name,
1668
+ roles: teamBuilderRoles.map((r) => r.role),
1669
+ settings: teamBuilderSettings,
1670
+ });
1671
+ get().addToast('success', `Template "${name}" saved`);
1672
+ get().fetchTeamTemplates();
1673
+ } catch (err) {
1674
+ get().addToast('error', 'Failed to save template', err.message);
1675
+ }
1676
+ },
1677
+
1678
+ async deleteTeamTemplate(name) {
1679
+ try {
1680
+ await api.delete(`/team-templates/${encodeURIComponent(name)}`);
1681
+ get().addToast('info', `Template "${name}" deleted`);
1682
+ get().fetchTeamTemplates();
1683
+ } catch (err) {
1684
+ get().addToast('error', 'Failed to delete template', err.message);
1685
+ }
1686
+ },
1687
+
1688
+ async launchTeamBuilder() {
1689
+ const { teamBuilderRoles, teamBuilderSettings, teamBuilderTask, teamBuilderLaunchMode, activeTeamId } = get();
1690
+ if (teamBuilderRoles.length === 0) return;
1691
+ try {
1692
+ get().addToast('info', 'Launching team...');
1693
+ const body = {
1694
+ task: teamBuilderTask,
1695
+ roles: teamBuilderRoles,
1696
+ settings: teamBuilderSettings,
1697
+ launchMode: teamBuilderLaunchMode,
1698
+ teamId: activeTeamId,
1699
+ };
1700
+ const result = await api.post('/team-builder/launch', body);
1701
+ const count = result.launched || result.agents?.length || 0;
1702
+ get().addToast('success', `Launched ${count} agent${count !== 1 ? 's' : ''}`);
1703
+ get().closeTeamBuilder();
1704
+ return result;
1705
+ } catch (err) {
1706
+ get().addToast('error', 'Team launch failed', err.message);
1707
+ throw err;
1708
+ }
1709
+ },
1710
+
1580
1711
  // ── GitHub Repo Import ────────────────────────────────────
1581
1712
 
1582
1713
  async fetchImportedRepos() {
@@ -1946,10 +2077,13 @@ export const useGrooveStore = create((set, get) => ({
1946
2077
  get().addChatMessage(id, 'user', message, false);
1947
2078
  set((s) => ({ thinkingAgents: new Set([...s.thinkingAgents, id]) }));
1948
2079
 
1949
- // Snapshot per-agent state before the async call a WebSocket state broadcast
1950
- // can arrive before the HTTP response returns and prune chatHistory[id], losing
1951
- // the user's message. The snapshot guarantees the transfer to the new agent ID
1952
- // always has the full history.
2080
+ // Auto-attach active file context when in workspace mode
2081
+ let enriched = message;
2082
+ if (get().workspaceMode && get().workspaceAgentId === id && get().editorActiveFile) {
2083
+ const filePath = get().editorActiveFile;
2084
+ enriched = `[Active file: ${filePath}]\n\n${message}`;
2085
+ }
2086
+
1953
2087
  const snapshot = {
1954
2088
  chatHistory: [...(get().chatHistory[id] || [])],
1955
2089
  activityLog: [...(get().activityLog[id] || [])],
@@ -1957,7 +2091,7 @@ export const useGrooveStore = create((set, get) => ({
1957
2091
  };
1958
2092
 
1959
2093
  try {
1960
- const data = await api.post(`/agents/${encodeURIComponent(id)}/instruct`, { message });
2094
+ const data = await api.post(`/agents/${encodeURIComponent(id)}/instruct`, { message: enriched });
1961
2095
 
1962
2096
  if (data.status === 'message_sent') {
1963
2097
  return data;
@@ -2364,18 +2498,23 @@ export const useGrooveStore = create((set, get) => ({
2364
2498
  set({ workspaceMode: on });
2365
2499
  localStorage.setItem('groove:workspaceMode', String(on));
2366
2500
  if (on) {
2367
- get().closeDetail();
2368
- }
2369
- if (on && !get().workspaceAgentId) {
2370
2501
  const teamAgents = get().agents.filter((a) => a.teamId === get().activeTeamId);
2371
- const selected = get().detailPanel?.type === 'agent' ? get().detailPanel.agentId : null;
2372
- const running = teamAgents.find((a) => a.status === 'running');
2373
- set({ workspaceAgentId: selected || running?.id || teamAgents[0]?.id || null });
2502
+ const current = get().workspaceAgentId;
2503
+ const belongsToTeam = current && teamAgents.some((a) => a.id === current);
2504
+ if (!belongsToTeam) {
2505
+ const selected = get().detailPanel?.type === 'agent' ? get().detailPanel.agentId : null;
2506
+ const selectedInTeam = selected && teamAgents.some((a) => a.id === selected);
2507
+ const running = teamAgents.find((a) => a.status === 'running');
2508
+ set({ workspaceAgentId: (selectedInTeam ? selected : null) || running?.id || teamAgents[0]?.id || null });
2509
+ }
2510
+ const agentId = get().workspaceAgentId;
2511
+ if (agentId) get().selectAgent(agentId);
2374
2512
  }
2375
2513
  },
2376
2514
 
2377
2515
  setWorkspaceAgent(id) {
2378
2516
  set({ workspaceAgentId: id });
2517
+ if (id) get().selectAgent(id);
2379
2518
  },
2380
2519
 
2381
2520
  captureSnapshot(path, content) {