groove-dev 0.24.16 → 0.25.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.
@@ -1563,8 +1563,15 @@ Keep responses concise. Help them think, don't lecture them about the system the
1563
1563
  return res.json({ exists: false, agents: [] });
1564
1564
  }
1565
1565
  try {
1566
- const agents = JSON.parse(readFileSync(teamPath, 'utf8'));
1567
- res.json({ exists: true, agents: Array.isArray(agents) ? agents : [] });
1566
+ const raw = JSON.parse(readFileSync(teamPath, 'utf8'));
1567
+ // Support both old format (bare array) and new format ({ projectDir, agents })
1568
+ if (Array.isArray(raw)) {
1569
+ res.json({ exists: true, agents: raw });
1570
+ } else if (raw && Array.isArray(raw.agents)) {
1571
+ res.json({ exists: true, agents: raw.agents, projectDir: raw.projectDir || null });
1572
+ } else {
1573
+ res.json({ exists: false, agents: [] });
1574
+ }
1568
1575
  } catch {
1569
1576
  res.json({ exists: false, agents: [] });
1570
1577
  }
@@ -1576,28 +1583,51 @@ Keep responses concise. Help them think, don't lecture them about the system the
1576
1583
  return res.status(404).json({ error: 'No recommended team found. Run a planner first.' });
1577
1584
  }
1578
1585
  try {
1579
- const agents = JSON.parse(readFileSync(teamPath, 'utf8'));
1580
- if (!Array.isArray(agents) || agents.length === 0) {
1586
+ const raw = JSON.parse(readFileSync(teamPath, 'utf8'));
1587
+
1588
+ // Support both old format (bare array) and new format ({ projectDir, agents })
1589
+ let agentConfigs;
1590
+ let projectDir = null;
1591
+ if (Array.isArray(raw)) {
1592
+ agentConfigs = raw;
1593
+ } else if (raw && Array.isArray(raw.agents)) {
1594
+ agentConfigs = raw.agents;
1595
+ projectDir = raw.projectDir || null;
1596
+ } else {
1597
+ return res.status(400).json({ error: 'Invalid recommended team format' });
1598
+ }
1599
+
1600
+ if (agentConfigs.length === 0) {
1581
1601
  return res.status(400).json({ error: 'Recommended team is empty' });
1582
1602
  }
1583
1603
 
1584
- const defaultDir = daemon.config?.defaultWorkingDir || undefined;
1604
+ const baseDir = daemon.config?.defaultWorkingDir || daemon.projectDir;
1585
1605
  const defaultTeamId = daemon.teams.getDefault()?.id || null;
1586
1606
 
1607
+ // If planner specified a project directory, create it and use it as workingDir
1608
+ let projectWorkingDir = baseDir;
1609
+ if (projectDir) {
1610
+ // Sanitize: kebab-case, no path traversal
1611
+ const safeName = String(projectDir).replace(/[^a-zA-Z0-9_-]/g, '-').slice(0, 64);
1612
+ projectWorkingDir = resolve(baseDir, safeName);
1613
+ mkdirSync(projectWorkingDir, { recursive: true });
1614
+ console.log(`[Groove] Project directory: ${projectWorkingDir}`);
1615
+ }
1616
+
1587
1617
  // Separate phase 1 (builders) and phase 2 (QC/finisher)
1588
- const phase1 = agents.filter((a) => !a.phase || a.phase === 1);
1589
- let phase2 = agents.filter((a) => a.phase === 2);
1618
+ const phase1 = agentConfigs.filter((a) => !a.phase || a.phase === 1);
1619
+ let phase2 = agentConfigs.filter((a) => a.phase === 2);
1590
1620
 
1591
1621
  // Safety net: if planner forgot the QC agent, auto-add one
1592
1622
  if (phase2.length === 0 && phase1.length >= 2) {
1593
1623
  phase2 = [{
1594
1624
  name: 'qc-agent',
1595
1625
  role: 'fullstack', phase: 2, scope: [],
1596
- prompt: 'QC Senior Dev: All builder agents have completed. Audit their changes for correctness, fix any issues, run tests, build the project, commit all changes, and launch the dev server. Output the localhost URL where the app can be accessed.',
1626
+ prompt: 'QC Senior Dev: All builder agents have completed. Audit their changes for correctness, fix any issues, run tests, build the project, commit all changes, and launch the dev server. Output the localhost URL where the app can be accessed. IMPORTANT: Do NOT delete files from other projects or directories outside this project.',
1597
1627
  }];
1598
1628
  }
1599
1629
 
1600
- // Spawn phase 1 agents immediately
1630
+ // Spawn phase 1 agents immediately — all scoped to projectWorkingDir
1601
1631
  const spawned = [];
1602
1632
  const failed = [];
1603
1633
  const phase1Ids = [];
@@ -1610,7 +1640,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
1610
1640
  provider: config.provider || 'claude-code',
1611
1641
  model: config.model || 'auto',
1612
1642
  permission: config.permission || 'auto',
1613
- workingDir: config.workingDir || defaultDir,
1643
+ workingDir: config.workingDir || projectWorkingDir,
1614
1644
  name: config.name || undefined,
1615
1645
  });
1616
1646
  validated.teamId = defaultTeamId;
@@ -1623,7 +1653,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
1623
1653
  }
1624
1654
  }
1625
1655
 
1626
- // If there are phase 2 agents, register them for auto-spawn on phase 1 completion
1656
+ // Phase 2 agents also scoped to projectWorkingDir
1627
1657
  if (phase2.length > 0 && phase1Ids.length > 0) {
1628
1658
  daemon._pendingPhase2 = daemon._pendingPhase2 || [];
1629
1659
  daemon._pendingPhase2.push({
@@ -1632,7 +1662,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
1632
1662
  role: c.role, scope: c.scope || [], prompt: c.prompt || '',
1633
1663
  provider: c.provider || 'claude-code', model: c.model || 'auto',
1634
1664
  permission: c.permission || 'auto',
1635
- workingDir: c.workingDir || defaultDir,
1665
+ workingDir: c.workingDir || projectWorkingDir,
1636
1666
  name: c.name || undefined,
1637
1667
  teamId: defaultTeamId,
1638
1668
  })),
@@ -1641,9 +1671,9 @@ Keep responses concise. Help them think, don't lecture them about the system the
1641
1671
 
1642
1672
  daemon.audit.log('team.launch', {
1643
1673
  phase1: spawned.length, phase2Pending: phase2.length, failed: failed.length,
1644
- agents: spawned.map((a) => a.role),
1674
+ agents: spawned.map((a) => a.role), projectDir: projectDir || null,
1645
1675
  });
1646
- res.json({ launched: spawned.length, phase2Pending: phase2.length, agents: spawned, failed });
1676
+ res.json({ launched: spawned.length, phase2Pending: phase2.length, agents: spawned, failed, projectDir: projectDir || null });
1647
1677
  } catch (err) {
1648
1678
  res.status(500).json({ error: err.message });
1649
1679
  }
@@ -127,6 +127,16 @@ export class Introducer {
127
127
  lines.push(`4. Clear your entry from \`.groove/coordination.md\` when done`);
128
128
  }
129
129
 
130
+ // File safety — prevent agents from deleting files they didn't create
131
+ lines.push('');
132
+ lines.push(`## File Safety`);
133
+ lines.push('');
134
+ lines.push(`CRITICAL: NEVER delete files you did not create in this session. Do NOT remove files from other projects, previous work, or unrelated directories.`);
135
+ if (newAgent.workingDir) {
136
+ lines.push(`Your working directory is \`${newAgent.workingDir}\`. Stay inside it. Do NOT modify or delete files outside this directory.`);
137
+ }
138
+ lines.push(`If you see files that seem unrelated to your task, leave them alone — they belong to another project or agent.`);
139
+
130
140
  // Memory containment — prevent agents from reading/writing auto-memory
131
141
  // which can contain stale context from unrelated sessions in the same dir
132
142
  lines.push('');
@@ -74,15 +74,33 @@ After completing your plan, you MUST do two things:
74
74
  1. Write your team recommendation as a clear summary in your output so the user can review it.
75
75
 
76
76
  2. Save a machine-readable team config to .groove/recommended-team.json using this EXACT format:
77
- [
78
- { "role": "frontend", "phase": 1, "scope": ["src/components/**", "src/views/**"], "prompt": "Build the frontend: [specific tasks]" },
79
- { "role": "backend", "phase": 1, "scope": ["src/api/**", "src/server/**"], "prompt": "Build the backend: [specific tasks]" },
80
- { "role": "fullstack", "phase": 2, "scope": [], "prompt": "QC Senior Dev: Audit all changes from phase 1 agents. Verify correctness, fix issues, run tests, build the project, commit, and launch. Output the localhost URL." }
81
- ]
77
+
78
+ For NEW projects (building something from scratch):
79
+ {
80
+ "projectDir": "my-project-name",
81
+ "agents": [
82
+ { "role": "frontend", "phase": 1, "scope": ["src/components/**", "src/views/**"], "prompt": "Build the frontend: [specific tasks]" },
83
+ { "role": "backend", "phase": 1, "scope": ["src/api/**", "src/server/**"], "prompt": "Build the backend: [specific tasks]" },
84
+ { "role": "fullstack", "phase": 2, "scope": [], "prompt": "QC Senior Dev: Audit all changes from phase 1 agents. Verify correctness, fix issues, run tests, build the project, commit, and launch. Output the localhost URL." }
85
+ ]
86
+ }
87
+
88
+ For EXISTING codebases (modifying/extending an existing project):
89
+ {
90
+ "agents": [
91
+ { "role": "frontend", "phase": 1, "scope": ["src/components/**"], "prompt": "Update the frontend: [specific tasks]" },
92
+ { "role": "fullstack", "phase": 2, "scope": [], "prompt": "QC Senior Dev: Audit all changes, fix issues, run tests, build, commit, and launch." }
93
+ ]
94
+ }
95
+
96
+ PROJECT DIRECTORY RULES:
97
+ - For NEW projects: ALWAYS include "projectDir" with a short, clean directory name (kebab-case, e.g. "cat-website", "landing-page", "api-service"). All agents will be spawned inside this directory so each project stays isolated.
98
+ - For EXISTING codebases: Do NOT include "projectDir". Agents work in the current repo root. You can tell an existing codebase by the presence of package.json, .git, or established source directories.
99
+ - NEVER mix projects. Each new project gets its own directory.
82
100
 
83
101
  MANDATORY RULES — NEVER SKIP THESE:
84
102
 
85
- 1. The LAST entry in the array MUST be: { "role": "fullstack", "phase": 2, ... }
103
+ 1. The LAST entry in the agents array MUST be: { "role": "fullstack", "phase": 2, ... }
86
104
  This is the QC Senior Dev. It auto-spawns after all other agents finish.
87
105
  Its prompt: audit changes, fix issues, run tests, build, commit, launch.
88
106
  NEVER omit this agent. Every team needs a QC.
@@ -93,7 +111,7 @@ MANDATORY RULES — NEVER SKIP THESE:
93
111
 
94
112
  4. Set appropriate scopes. Write detailed prompts so each agent knows exactly what to build.
95
113
 
96
- 5. If the project is a monorepo, set "workingDir" for agents that need specific subdirectories.
114
+ 5. NEVER instruct any agent to delete files from other projects or clean up unrelated code. Each agent must ONLY create and modify files relevant to its assigned tasks.
97
115
 
98
116
  IMPORTANT: Do not use markdown formatting like ** or ### in your output. Write in plain text with clean formatting. Use line breaks, dashes, and indentation for structure.
99
117