groove-dev 0.12.5 → 0.12.7

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.
@@ -4,7 +4,7 @@
4
4
  import express from 'express';
5
5
  import { resolve, dirname } from 'path';
6
6
  import { fileURLToPath } from 'url';
7
- import { existsSync, readFileSync } from 'fs';
7
+ import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
8
8
  import { listProviders } from './providers/index.js';
9
9
  import { validateAgentConfig } from './validate.js';
10
10
 
@@ -365,6 +365,52 @@ export function createApi(app, daemon) {
365
365
  res.json(daemon.adaptive.getAllProfiles());
366
366
  });
367
367
 
368
+ // --- Directory Browser ---
369
+
370
+ app.get('/api/browse', (req, res) => {
371
+ const relPath = req.query.path || '';
372
+
373
+ // Security: no absolute paths, no traversal
374
+ if (relPath.startsWith('/') || relPath.includes('..') || relPath.includes('\0')) {
375
+ return res.status(400).json({ error: 'Invalid path' });
376
+ }
377
+
378
+ const fullPath = relPath ? resolve(daemon.projectDir, relPath) : daemon.projectDir;
379
+
380
+ // Must stay within project directory
381
+ if (!fullPath.startsWith(daemon.projectDir)) {
382
+ return res.status(400).json({ error: 'Path outside project' });
383
+ }
384
+
385
+ if (!existsSync(fullPath)) {
386
+ return res.status(404).json({ error: 'Directory not found' });
387
+ }
388
+
389
+ try {
390
+ const entries = readdirSync(fullPath, { withFileTypes: true })
391
+ .filter((e) => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules')
392
+ .sort((a, b) => a.name.localeCompare(b.name))
393
+ .map((e) => {
394
+ const childPath = relPath ? `${relPath}/${e.name}` : e.name;
395
+ // Check if this dir has subdirectories (for expand arrow)
396
+ let hasChildren = false;
397
+ try {
398
+ hasChildren = readdirSync(resolve(fullPath, e.name), { withFileTypes: true })
399
+ .some((c) => c.isDirectory() && !c.name.startsWith('.') && c.name !== 'node_modules');
400
+ } catch { /* unreadable */ }
401
+ return { name: e.name, path: childPath, hasChildren };
402
+ });
403
+
404
+ res.json({
405
+ current: relPath || '.',
406
+ parent: relPath ? relPath.split('/').slice(0, -1).join('/') : null,
407
+ dirs: entries,
408
+ });
409
+ } catch (err) {
410
+ res.status(500).json({ error: err.message });
411
+ }
412
+ });
413
+
368
414
  // --- Codebase Indexer ---
369
415
 
370
416
  app.get('/api/indexer', (req, res) => {
@@ -25,9 +25,33 @@ export function isFirstRun(grooveDir) {
25
25
  export function printWelcome(port, host = '127.0.0.1', firstRun = false) {
26
26
  const providers = listProviders();
27
27
  const installed = providers.filter((p) => p.installed);
28
+ const notInstalled = providers.filter((p) => !p.installed);
28
29
 
29
30
  console.log('');
30
- console.log(' GROOVE');
31
+ console.log(' ┌─────────────────────────────────────┐');
32
+ console.log(' │ Welcome to GROOVE │');
33
+ console.log(' │ Agent orchestration for AI coding │');
34
+ console.log(' └─────────────────────────────────────┘');
35
+ console.log('');
36
+
37
+ if (installed.length > 0) {
38
+ console.log(` Providers (${installed.length} ready):`);
39
+ for (const p of installed) {
40
+ console.log(` ✓ ${p.name}`);
41
+ }
42
+ } else {
43
+ console.log(' No AI providers detected.');
44
+ console.log(' Install at least one: npm i -g @anthropic-ai/claude-code');
45
+ }
46
+
47
+ if (notInstalled.length > 0) {
48
+ console.log('');
49
+ console.log(' Available to install:');
50
+ for (const p of notInstalled) {
51
+ console.log(` · ${p.name.padEnd(18)} ${p.installCommand}`);
52
+ }
53
+ }
54
+
31
55
  console.log('');
32
56
 
33
57
  const isRemote = host !== '127.0.0.1';
@@ -72,18 +96,6 @@ export function printWelcome(port, host = '127.0.0.1', firstRun = false) {
72
96
 
73
97
  console.log(` Stop: groove stop (or Ctrl+C)`);
74
98
  console.log(` Docs: https://docs.groovedev.ai`);
75
-
76
- // Show providers only on first run or if none installed
77
- if (firstRun || installed.length === 0) {
78
- console.log('');
79
- if (installed.length > 0) {
80
- console.log(` Providers: ${installed.map((p) => p.name).join(', ')}`);
81
- } else {
82
- console.log(' No AI providers detected.');
83
- console.log(' Install one: npm i -g @anthropic-ai/claude-code');
84
- }
85
- }
86
-
87
99
  console.log('');
88
100
  }
89
101