claudeboard 3.1.0 → 3.1.1

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 (2) hide show
  1. package/package.json +4 -1
  2. package/src/orchestrator.js +142 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudeboard",
3
- "version": "3.1.0",
3
+ "version": "3.1.1",
4
4
  "description": "Visual orchestrator for Claude Code agent teams",
5
5
  "bin": {
6
6
  "claudeboard": "bin/cli.js"
@@ -32,5 +32,8 @@
32
32
  "express": "^4.18.2",
33
33
  "open": "^10.1.0",
34
34
  "ws": "^8.16.0"
35
+ },
36
+ "devDependencies": {
37
+ "supabase": "^2.78.1"
35
38
  }
36
39
  }
@@ -755,10 +755,149 @@ function runQA() {
755
755
  spawnQAAgent(getTasks());
756
756
  }
757
757
 
758
- // Upload PRD from a markdown file sends it through the orchestrator to generate tasks
758
+ // Upload PRD: uses a dedicated planner agent (bypasses the 10k conversational limit)
759
759
  function uploadPRD(content) {
760
- const trimmed = content.slice(0, 20000);
761
- sendMessage(`Tengo mi PRD listo. Analizalo y creá las tareas necesarias para implementarlo:\n\n${trimmed}`);
760
+ spawnPRDPlannerAgent(content);
761
+ }
762
+
763
+ // Dedicated PRD planner agent — decomposes a full PRD into granular tasks
764
+ function spawnPRDPlannerAgent(rawContent) {
765
+ // Save the full PRD to disk first
766
+ try { savePRD(rawContent); } catch { /* non-fatal */ }
767
+ if (broadcast) broadcast({ type: 'prd:generated' });
768
+ if (broadcast) broadcast({ type: 'orchestrator:thinking' });
769
+
770
+ const projectPath = getProjectPath();
771
+ let plannerBuffer = '';
772
+ let tasksParsed = false;
773
+
774
+ // Collect project context to help Claude understand the tech stack
775
+ let projectCtx = '';
776
+ try {
777
+ const ctx = scanProject(projectPath);
778
+ if (ctx) {
779
+ const lines = [];
780
+ if (ctx.techStack.length) lines.push(`Stack detectado: ${ctx.techStack.join(', ')}`);
781
+ if (ctx.pkgDescription) lines.push(`Descripción: ${ctx.pkgDescription}`);
782
+ if (ctx.fileTree) lines.push(`Árbol de archivos:\n${ctx.fileTree.slice(0, 1500)}`);
783
+ if (ctx.existingPrd) lines.push(`PRD anterior:\n${ctx.existingPrd.slice(0, 500)}`);
784
+ projectCtx = lines.length ? `\n\nContexto del proyecto:\n${lines.join('\n')}` : '';
785
+ }
786
+ } catch { /* ignore */ }
787
+
788
+ // Cap PRD at 180 KB — well above any realistic PRD
789
+ const prdContent = rawContent.slice(0, 180 * 1024);
790
+
791
+ const prompt = `Sos el arquitecto de software de ClaudeBoard. Tu único trabajo es leer el PRD completo y descomponerlo en TODAS las tareas atómicas necesarias para construir el proyecto de cero a producción.${projectCtx}
792
+
793
+ ━━━ REGLAS DE GRANULARIDAD ━━━
794
+ • Cada tarea = 1 unidad de trabajo concreto que un agente puede completar solo (1 endpoint, 1 tabla, 1 componente, 1 página, 1 integración, etc.)
795
+ • Tamaño justo: ni mega-tareas ("hacer todo el backend") ni micro-tareas triviales de 2 líneas
796
+ • Para un proyecto mediano esperás 30–60 tareas. Para uno grande, 60–150 tareas. No te limites.
797
+ • Cada tarea debe poder ejecutarse de forma independiente con el contexto del handoff del agente anterior
798
+
799
+ ━━━ ORDEN DE CONSTRUCCIÓN (seguir esta secuencia) ━━━
800
+ 1. Setup del proyecto (estructura, configs, variables de entorno, dependencias)
801
+ 2. Base de datos (cada tabla/schema/migración es una tarea separada)
802
+ 3. Autenticación y permisos
803
+ 4. Backend — APIs y lógica de negocio (un endpoint o módulo por tarea)
804
+ 5. Integraciones externas (pagos, emails, storage, webhooks, etc.)
805
+ 6. Frontend — layout, componentes base y sistema de diseño
806
+ 7. Frontend — páginas y features (una por tarea)
807
+ 8. Lógica de tiempo real / subscripciones si aplica
808
+ 9. Testing y validación
809
+ 10. Deploy, CI/CD y variables de producción
810
+
811
+ ━━━ FORMATO EXACTO (sin texto antes ni después del bloque) ━━━
812
+ <TASKS>
813
+ <TASK>
814
+ title: [verbo + módulo concreto — ej: "Crear tabla profiles con RLS en Supabase"]
815
+ description: [qué crear o modificar exactamente, qué archivos tocar, qué debe funcionar al terminar — todo en una sola línea]
816
+ successCriteria: [cómo verificar que está hecho — en una sola línea]
817
+ priority: high|medium|low
818
+ </TASK>
819
+ </TASKS>
820
+
821
+ ━━━ PRD COMPLETO A PLANIFICAR ━━━
822
+ ${prdContent}`;
823
+
824
+ const proc = spawnClaude(prompt, projectPath);
825
+
826
+ proc.stdout.on('data', (data) => {
827
+ const chunk = data.toString('utf-8');
828
+ plannerBuffer += chunk;
829
+ // Stream progress to chat so the user sees something happening
830
+ if (broadcast) broadcast({ type: 'orchestrator:chunk', chunk });
831
+ });
832
+
833
+ proc.stderr.on('data', () => { /* swallow */ });
834
+
835
+ proc.on('close', () => {
836
+ if (!tasksParsed) {
837
+ tasksParsed = true;
838
+ const created = parsePlannerTasks(plannerBuffer);
839
+ if (broadcast) broadcast({ type: 'orchestrator:done' });
840
+ if (created.length > 0) {
841
+ if (broadcast) broadcast({
842
+ type: 'orchestrator:chunk',
843
+ chunk: `\n\n✅ **PRD procesado:** ${created.length} tareas creadas en el board. Los agentes arrancan ahora.`,
844
+ });
845
+ } else {
846
+ if (broadcast) broadcast({
847
+ type: 'orchestrator:chunk',
848
+ chunk: '\n\n⚠️ No se pudieron extraer tareas del PRD. Intentá pegarlo en el chat directamente.',
849
+ });
850
+ }
851
+ }
852
+ });
853
+
854
+ proc.on('error', (err) => {
855
+ console.error('[prd-planner] spawn error:', err.message);
856
+ if (broadcast) broadcast({ type: 'orchestrator:error', message: 'Error al procesar el PRD. ¿Está el CLI de Claude instalado?' });
857
+ });
858
+ }
859
+
860
+ // Parse tasks from planner output and enqueue them
861
+ function parsePlannerTasks(buffer) {
862
+ let taskDefs = [];
863
+
864
+ const taskBlocks = [...buffer.matchAll(/<TASK>([\s\S]*?)<\/TASK>/g)];
865
+ if (taskBlocks.length > 0) {
866
+ taskDefs = taskBlocks.map(m => {
867
+ const block = m[1];
868
+ const get = (key) => {
869
+ const match = block.match(new RegExp(`^${key}:\\s*(.+)$`, 'mi'));
870
+ return match ? match[1].trim() : '';
871
+ };
872
+ return {
873
+ title: get('title'),
874
+ description: get('description'),
875
+ successCriteria: get('successCriteria'),
876
+ priority: get('priority') || 'medium',
877
+ };
878
+ }).filter(t => t.title);
879
+ }
880
+
881
+ if (taskDefs.length === 0) return [];
882
+
883
+ const created = taskDefs.map(t => createTask(t));
884
+ if (broadcast) broadcast({ type: 'tasks:created', tasks: created });
885
+ notify('tasks:created', { taskTitle: null, status: 'created' });
886
+ qaAgentRan = false;
887
+
888
+ // Check if any task requires Supabase credentials
889
+ const needsSupabase = taskDefs.some(isSupabaseTask);
890
+ if (needsSupabase && !awaitingSupabaseCreds) {
891
+ awaitingSupabaseCreds = true;
892
+ if (broadcast) broadcast({
893
+ type: 'supabase:preflight',
894
+ taskTitles: created.map(t => t.title),
895
+ });
896
+ } else {
897
+ processQueue();
898
+ }
899
+
900
+ return created;
762
901
  }
763
902
 
764
903
  // Called when the user submits Supabase credentials from the modal