UNPKG

claude-flow-novice

Version:

Claude Flow Novice - Advanced orchestration platform for multi-agent AI workflows with CFN Loop architecture Includes Local RuVector Accelerator and all CFN skills for complete functionality.

366 lines (360 loc) 14.6 kB
/** * CLI Agent Context Builder * * Builds natural language system prompts for CLI-spawned agents. * Converts JSON context from Redis into readable markdown format. * * Phase 1: System Prompts Enhancement (Sprint 2) * - Load CLAUDE.md (project rules) * - Load agent markdown template * - Format epic/phase/success criteria as natural language * - Build comprehensive system prompt for API execution */ import fs from 'fs/promises'; import path from 'path'; /** * Load CLAUDE.md project rules */ async function loadProjectRules() { try { // Try current working directory const cwdPath = path.join(process.cwd(), 'CLAUDE.md'); const content = await fs.readFile(cwdPath, 'utf-8'); return content; } catch (error) { console.warn('[cli-agent-context] Could not load CLAUDE.md:', error); return ''; } } /** * Load agent markdown template */ async function loadAgentTemplate(agentType) { try { // Search in .claude/agents/ subdirectories const searchPaths = [ path.join(process.cwd(), '.claude', 'agents', 'cfn-dev-team', 'coordinators', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'cfn-dev-team', 'developers', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'cfn-dev-team', 'testers', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'cfn-dev-team', 'specialists', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'cfn-dev-team', 'frontend', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'cfn-dev-team', 'security', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'cfn-dev-team', 'reviewers', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'cfn-dev-team', 'reviewers', 'quality', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'coordinators', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'core-agents', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'developers', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'specialists', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'testers', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'planners', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'frontend', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'security', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', 'custom', `${agentType}.md`), path.join(process.cwd(), '.claude', 'agents', `${agentType}.md`) ]; for (const searchPath of searchPaths){ try { const content = await fs.readFile(searchPath, 'utf-8'); return content; } catch { // Continue to next path } } console.warn(`[cli-agent-context] Could not find agent template: ${agentType}.md`); return ''; } catch (error) { console.warn('[cli-agent-context] Error loading agent template:', error); return ''; } } /** * Parse JSON string safely */ function parseJSON(jsonString, fallback) { if (!jsonString || jsonString.trim() === '' || jsonString === '(nil)') { return fallback; } try { return JSON.parse(jsonString); } catch (error) { console.warn('[cli-agent-context] Failed to parse JSON:', error); return fallback; } } /** * Format epic context as natural language */ function formatEpicContext(epic) { if (!epic.epicGoal && !epic.inScope && !epic.outOfScope) { return ''; } const sections = []; sections.push('## Epic Context'); sections.push(''); if (epic.epicGoal) { sections.push('**Epic Goal:**'); sections.push(epic.epicGoal); sections.push(''); } if (epic.riskProfile) { sections.push(`**Risk Profile:** ${epic.riskProfile}`); sections.push(''); } if (epic.inScope && epic.inScope.length > 0) { sections.push('**In Scope:**'); for (const item of epic.inScope){ sections.push(`- ${item}`); } sections.push(''); } if (epic.outOfScope && epic.outOfScope.length > 0) { sections.push('**Out of Scope:**'); for (const item of epic.outOfScope){ sections.push(`- ${item}`); } sections.push(''); } if (epic.phases && epic.phases.length > 0) { sections.push('**Phases:**'); for(let i = 0; i < epic.phases.length; i++){ sections.push(`${i + 1}. ${epic.phases[i]}`); } sections.push(''); } if (epic.stakeholders && epic.stakeholders.length > 0) { sections.push(`**Stakeholders:** ${epic.stakeholders.join(', ')}`); sections.push(''); } if (epic.timeline) { sections.push('**Timeline:**'); if (epic.timeline.start) sections.push(`- Start: ${epic.timeline.start}`); if (epic.timeline.end) sections.push(`- End: ${epic.timeline.end}`); if (epic.timeline.milestones && epic.timeline.milestones.length > 0) { sections.push('- Milestones:'); for (const milestone of epic.timeline.milestones){ sections.push(` - ${milestone.phase}: ${milestone.date}`); } } sections.push(''); } return sections.join('\n'); } /** * Format phase context as natural language */ function formatPhaseContext(phase) { if (!phase.currentPhase && !phase.deliverables) { return ''; } const sections = []; sections.push('## Current Phase'); sections.push(''); if (phase.currentPhase) { sections.push(`**Phase:** ${phase.currentPhase}`); if (phase.phaseNumber) { sections.push(`**Phase Number:** ${phase.phaseNumber}`); } sections.push(''); } if (phase.dependencies && phase.dependencies.length > 0) { sections.push('**Dependencies:**'); for (const dep of phase.dependencies){ sections.push(`- ${dep}`); } sections.push(''); } if (phase.deliverables && phase.deliverables.length > 0) { sections.push('**Deliverables:**'); for (const deliverable of phase.deliverables){ sections.push(`- ${deliverable}`); } sections.push(''); } if (phase.blockers && phase.blockers.length > 0) { sections.push('**Current Blockers:**'); for (const blocker of phase.blockers){ sections.push(`- ${blocker}`); } sections.push(''); } if (phase.resources) { sections.push('**Resources:**'); if (phase.resources.agentCount) sections.push(`- Agents: ${phase.resources.agentCount}`); if (phase.resources.estimatedDuration) sections.push(`- Duration: ${phase.resources.estimatedDuration} hours`); if (phase.resources.costBudget) sections.push(`- Budget: $${phase.resources.costBudget.toFixed(2)}`); sections.push(''); } return sections.join('\n'); } /** * Format success criteria as natural language */ function formatSuccessCriteria(criteria) { // Check if any criteria fields are present if (!criteria.acceptanceCriteria && !criteria.gateThreshold && !criteria.consensusThreshold && !criteria.qualityGates && !criteria.definitionOfDone && !criteria.nonFunctionalRequirements) { return ''; } const sections = []; sections.push('## Success Criteria'); sections.push(''); if (criteria.acceptanceCriteria && criteria.acceptanceCriteria.length > 0) { sections.push('**Acceptance Criteria:**'); for (const criterion of criteria.acceptanceCriteria){ sections.push(`- ${criterion}`); } sections.push(''); } if (criteria.gateThreshold || criteria.consensusThreshold) { sections.push('**Quality Gates:**'); if (criteria.gateThreshold) { sections.push(`- Gate Threshold (Loop 3): ${(criteria.gateThreshold * 100).toFixed(0)}%`); } if (criteria.consensusThreshold) { sections.push(`- Consensus Threshold (Loop 2): ${(criteria.consensusThreshold * 100).toFixed(0)}%`); } sections.push(''); } if (criteria.qualityGates) { sections.push('**Quality Metrics:**'); if (criteria.qualityGates.testCoverage) { sections.push(`- Test Coverage: ${criteria.qualityGates.testCoverage}%`); } if (criteria.qualityGates.securityScore) { sections.push(`- Security Score: ${(criteria.qualityGates.securityScore * 100).toFixed(0)}%`); } if (criteria.qualityGates.performanceBudget) { sections.push(`- Performance Budget: ${criteria.qualityGates.performanceBudget}ms`); } sections.push(''); } if (criteria.definitionOfDone && criteria.definitionOfDone.length > 0) { sections.push('**Definition of Done:**'); for (const item of criteria.definitionOfDone){ sections.push(`- [ ] ${item}`); } sections.push(''); } if (criteria.nonFunctionalRequirements && criteria.nonFunctionalRequirements.length > 0) { sections.push('**Non-Functional Requirements:**'); for (const req of criteria.nonFunctionalRequirements){ sections.push(`- ${req}`); } sections.push(''); } return sections.join('\n'); } /** * Format iteration context */ function formatIterationContext(iteration, taskId) { if (!iteration || iteration === 1) { return ''; } return ` ## Current Iteration This is **iteration ${iteration}** of your task. **Previous Iterations:** You have completed ${iteration - 1} iteration${iteration > 2 ? 's' : ''} before this one. **Your Goal:** Address feedback from previous iterations and improve the quality of your work. **Feedback Access:** Check Redis for iteration feedback: \`\`\`bash redis-cli get "swarm:${taskId}:\${AGENT_ID}:feedback:iteration-${iteration}" \`\`\` `; } /** * Build comprehensive system prompt for CLI agent * * Combines: * - Project rules (CLAUDE.md) * - Agent markdown template * - Epic context (formatted) * - Phase context (formatted) * - Success criteria (formatted) * - Iteration context */ export async function buildCLIAgentSystemPrompt(options) { console.log('[cli-agent-context] Building system prompt...'); const sections = []; // 1. Load and include CLAUDE.md console.log('[cli-agent-context] Loading CLAUDE.md...'); const projectRules = await loadProjectRules(); if (projectRules) { sections.push('# Project Rules (CLAUDE.md)'); sections.push(''); sections.push(projectRules); sections.push(''); sections.push('---'); sections.push(''); } // 2. Load and include agent markdown template console.log(`[cli-agent-context] Loading agent template: ${options.agentType}`); const agentTemplate = await loadAgentTemplate(options.agentType); if (agentTemplate) { sections.push(`# Agent Definition: ${options.agentType}`); sections.push(''); sections.push(agentTemplate); sections.push(''); sections.push('---'); sections.push(''); } // 3. Parse and format epic context const epicContext = parseJSON(options.epicContext, {}); const formattedEpic = formatEpicContext(epicContext); if (formattedEpic) { sections.push(formattedEpic); sections.push('---'); sections.push(''); } // 4. Parse and format phase context const phaseContext = parseJSON(options.phaseContext, {}); const formattedPhase = formatPhaseContext(phaseContext); if (formattedPhase) { sections.push(formattedPhase); sections.push('---'); sections.push(''); } // 5. Parse and format success criteria const successCriteria = parseJSON(options.successCriteria, {}); const formattedCriteria = formatSuccessCriteria(successCriteria); if (formattedCriteria) { sections.push(formattedCriteria); sections.push('---'); sections.push(''); } // 6. Format iteration context const iterationContext = formatIterationContext(options.iteration, options.taskId); if (iterationContext) { sections.push(iterationContext); sections.push('---'); sections.push(''); } // 7. Add execution reminder sections.push('## Execution Instructions'); sections.push(''); sections.push('You are executing as a CLI-spawned agent with full project context.'); sections.push('Follow the agent definition, project rules, and success criteria exactly.'); sections.push(''); sections.push('**Remember:**'); sections.push('- Respect scope boundaries (in-scope vs out-of-scope)'); sections.push('- Meet acceptance criteria and quality gates'); sections.push('- Follow CFN Loop protocol if task-id is provided'); sections.push('- Report confidence score when complete'); sections.push(''); const fullPrompt = sections.join('\n'); console.log(`[cli-agent-context] System prompt built: ${fullPrompt.length} characters`); console.log(`[cli-agent-context] - Project rules: ${projectRules ? 'included' : 'not found'}`); console.log(`[cli-agent-context] - Agent template: ${agentTemplate ? 'included' : 'not found'}`); console.log(`[cli-agent-context] - Epic context: ${formattedEpic ? 'formatted' : 'none'}`); console.log(`[cli-agent-context] - Phase context: ${formattedPhase ? 'formatted' : 'none'}`); console.log(`[cli-agent-context] - Success criteria: ${formattedCriteria ? 'formatted' : 'none'}`); return fullPrompt; } /** * Helper: Load context from environment variables * * Used by agent executor to load context from env vars set by cfn-spawn */ export function loadContextFromEnv() { return { agentType: process.env.AGENT_TYPE || 'unknown', taskId: process.env.TASK_ID, iteration: process.env.ITERATION ? parseInt(process.env.ITERATION, 10) : 1, epicContext: process.env.EPIC_CONTEXT, phaseContext: process.env.PHASE_CONTEXT, successCriteria: process.env.SUCCESS_CRITERIA }; } //# sourceMappingURL=cli-agent-context.js.map