@ian2018cs/agenthub 0.1.63 → 0.1.65

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.
@@ -551,6 +551,76 @@ router.get('/preview', async (req, res) => {
551
551
  }
552
552
  });
553
553
 
554
+ /**
555
+ * GET /api/agents/project-files
556
+ * List files in a project directory for manual file selection when submitting an Agent.
557
+ */
558
+ const AGENT_SKIP_DIRS = new Set([
559
+ 'node_modules', 'dist', 'build', '.git', '.svn', '.hg',
560
+ '__pycache__', '.pytest_cache', '.venv', 'venv',
561
+ '.next', '.nuxt', 'coverage', '.cache',
562
+ '.claude', '.codex', '.gemini', 'data'
563
+ ]);
564
+ const MAX_PROJECT_FILES = 500;
565
+ const MAX_DEPTH = 5;
566
+
567
+ router.get('/project-files', async (req, res) => {
568
+ try {
569
+ const userUuid = req.user?.uuid;
570
+ if (!userUuid) return res.status(401).json({ error: 'User authentication required' });
571
+
572
+ const { projectKey } = req.query;
573
+ if (!projectKey) return res.status(400).json({ error: 'projectKey is required' });
574
+
575
+ const config = await loadProjectConfig(userUuid);
576
+ const entry = config[projectKey];
577
+ if (!entry) return res.status(404).json({ error: 'Project not found' });
578
+ const projectPath = entry.originalPath || projectKey.replace(/-/g, '/');
579
+
580
+ const files = [];
581
+ let truncated = false;
582
+
583
+ async function walk(dir, depth) {
584
+ if (depth > MAX_DEPTH || truncated) return;
585
+ let entries;
586
+ try {
587
+ entries = await fs.readdir(dir, { withFileTypes: true });
588
+ } catch { return; }
589
+
590
+ for (const entry of entries) {
591
+ if (truncated) return;
592
+ if (entry.isSymbolicLink()) continue;
593
+ if (entry.name.startsWith('.')) continue;
594
+
595
+ const fullPath = path.join(dir, entry.name);
596
+
597
+ if (entry.isDirectory()) {
598
+ if (AGENT_SKIP_DIRS.has(entry.name)) continue;
599
+ await walk(fullPath, depth + 1);
600
+ } else if (entry.isFile()) {
601
+ if (entry.name === 'CLAUDE.md') continue;
602
+ const relPath = path.relative(projectPath, fullPath);
603
+ let size = 0;
604
+ try { size = (await fs.stat(fullPath)).size; } catch {}
605
+ files.push({ path: relPath, size });
606
+ if (files.length >= MAX_PROJECT_FILES) {
607
+ truncated = true;
608
+ return;
609
+ }
610
+ }
611
+ }
612
+ }
613
+
614
+ await walk(projectPath, 0);
615
+ files.sort((a, b) => a.path.localeCompare(b.path));
616
+
617
+ res.json({ files, truncated });
618
+ } catch (error) {
619
+ console.error('Error listing project files:', error);
620
+ res.status(500).json({ error: 'Failed to list project files', details: error.message });
621
+ }
622
+ });
623
+
554
624
  /**
555
625
  * GET /api/agents/check-name/:name
556
626
  * Check if an agent name already exists in the repo, and if there's a pending conflict.