groove-dev 0.27.155 → 0.27.156

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/CLAUDE.md CHANGED
@@ -295,10 +295,3 @@ Audit-driven release. Multi-agent orchestration system with 7 coordination layer
295
295
  - Dashboard: routing donut, cache panel, context health gauges
296
296
  - Monitor/QC agent mode (stay active, loop)
297
297
  - Distribution: demo video, HN launch, Twitter content
298
-
299
- <!-- GROOVE:START -->
300
- ## GROOVE Orchestration (auto-injected)
301
- Active agents: 0
302
- See AGENTS_REGISTRY.md for full agent state.
303
- **Memory policy:** GROOVE manages project memory automatically. Do not read or write MEMORY.md or .groove/memory/ files directly.
304
- <!-- GROOVE:END -->
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/cli",
3
- "version": "0.27.155",
3
+ "version": "0.27.156",
4
4
  "description": "GROOVE CLI — manage AI coding agents from your terminal",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/daemon",
3
- "version": "0.27.155",
3
+ "version": "0.27.156",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
1
  // FSL-1.1-Apache-2.0 — see LICENSE
2
2
  import { resolve, sep, isAbsolute, basename } from 'path';
3
3
  import { existsSync, readFileSync, readdirSync, statSync, writeFileSync, mkdirSync, unlinkSync, renameSync, rmSync, createReadStream, realpathSync } from 'fs';
4
- import { execFile, execFileSync } from 'child_process';
4
+ import { execFile, execFileSync, spawn } from 'child_process';
5
5
  import { homedir } from 'os';
6
6
  import { lookup as mimeLookup } from '../mimetypes.js';
7
7
 
@@ -331,15 +331,37 @@ export function registerFileRoutes(app, daemon) {
331
331
  }
332
332
  });
333
333
 
334
- // Download a file (serves raw with Content-Disposition)
334
+ // Download a file or folder (folders are streamed as zip)
335
335
  app.get('/api/files/download', (req, res) => {
336
336
  const relPath = req.query.path;
337
337
  const result = validateFilePath(relPath, getEditorRoot(daemon));
338
338
  if (result.error) return res.status(400).json({ error: result.error });
339
- if (!existsSync(result.fullPath)) return res.status(404).json({ error: 'File not found' });
339
+ if (!existsSync(result.fullPath)) return res.status(404).json({ error: 'Not found' });
340
340
 
341
341
  const stat = statSync(result.fullPath);
342
- if (stat.isDirectory()) return res.status(400).json({ error: 'Cannot download a directory' });
342
+
343
+ if (stat.isDirectory()) {
344
+ const folderName = basename(result.fullPath);
345
+ res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(folderName)}.zip"`);
346
+ res.setHeader('Content-Type', 'application/zip');
347
+
348
+ const zipProc = spawn('zip', [
349
+ '-r', '-q', '-',
350
+ relPath,
351
+ '-x', `${relPath}/.git/*`,
352
+ '-x', `${relPath}/node_modules/*`,
353
+ ], {
354
+ cwd: getEditorRoot(daemon),
355
+ stdio: ['ignore', 'pipe', 'pipe'],
356
+ });
357
+
358
+ zipProc.stdout.pipe(res);
359
+ zipProc.stderr.on('data', () => {});
360
+ zipProc.on('error', () => {
361
+ if (!res.headersSent) res.status(500).json({ error: 'Failed to create zip' });
362
+ });
363
+ return;
364
+ }
343
365
 
344
366
  const name = basename(result.fullPath);
345
367
  const mime = mimeLookup(name) || 'application/octet-stream';