/** * V3 Guidance Provider * * Generates Claude-visible guidance output from ReasoningBank patterns. * Outputs plain text (exit 0) or JSON with additionalContext. * * @module @claude-flow/hooks/reasoningbank/guidance-provider */ import { ReasoningBank } from './index.js'; /** * Security patterns to block */ const BLOCKED_PATTERNS = ['.env', '.pem', '.key', 'credentials', 'secret', 'password']; const WARNED_PATTERNS = ['prod', 'production', 'live', 'deploy']; /** * Dangerous commands to block */ const DANGEROUS_COMMANDS = [ 'rm -rf', 'drop database', 'truncate', 'push.*--force|--force.*push', 'reset --hard', 'format c:', ]; /** * Risky commands to warn about */ const RISKY_COMMANDS = ['npm publish', 'git push', 'deploy', 'kubectl apply']; /** * Guidance Provider class * * Converts ReasoningBank patterns into Claude-visible guidance. */ export class GuidanceProvider { reasoningBank; constructor(reasoningBank) { this.reasoningBank = reasoningBank || new ReasoningBank(); } /** * Initialize the provider */ async initialize() { await this.reasoningBank.initialize(); } /** * Generate session start context * Returns plain text that Claude will see */ async generateSessionContext() { const stats = this.reasoningBank.getStats(); const lines = [ '## V3 Development Context', '', '**Architecture**: Domain-Driven Design with 15 @claude-flow modules', '**Priority**: Security-first (CVE-1, CVE-2, CVE-3 remediation)', '', '**Performance Targets**:', '- HNSW search: 150x-12,500x faster (<1ms)', '- Flash Attention: 2.49x-7.47x speedup', '- Memory: 50-75% reduction', '', '**Active Patterns**:', '- Use TDD London School (mock-first)', '- Event sourcing for state changes', '- agentic-flow@alpha as core foundation', '- Bounded contexts with clear interfaces', '', '**Code Quality Rules**:', '- Files under 500 lines', '- No hardcoded secrets', '- Input validation at boundaries', '- Typed interfaces for all public APIs', '', `**Learned Patterns**: ${stats.shortTermCount + stats.longTermCount} available`, `**Avg Search Time**: ${stats.avgSearchTime.toFixed(2)}ms`, ]; return lines.join('\n'); } /** * Generate user prompt context * Returns plain text guidance based on prompt analysis */ async generatePromptContext(prompt) { const guidance = await this.reasoningBank.generateGuidance({ event: 'pre-route', timestamp: new Date(), routing: { task: prompt }, }); const lines = []; // Add domain-specific guidance if (guidance.recommendations.length > 0) { const domains = this.detectDomains(prompt); for (const domain of domains) { lines.push(`**${this.capitalize(domain)} Guidance**:`); for (const rec of guidance.recommendations.slice(0, 5)) { lines.push(`- ${rec}`); } lines.push(''); } } // Add relevant patterns if (guidance.patterns.length > 0) { lines.push('**Relevant Learned Patterns**:'); for (const { pattern, similarity } of guidance.patterns.slice(0, 3)) { lines.push(`- ${pattern.strategy} (${(similarity * 100).toFixed(0)}% match)`); } } return lines.join('\n'); } /** * Generate pre-edit guidance * Returns JSON for Claude hook system */ async generatePreEditGuidance(filePath) { // Security checks - block sensitive files for (const pattern of BLOCKED_PATTERNS) { if (filePath.toLowerCase().includes(pattern)) { return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: `Security: Cannot edit ${pattern} files directly. Use environment variables instead.`, }, }; } } // Warn about production files for (const pattern of WARNED_PATTERNS) { if (filePath.toLowerCase().includes(pattern)) { return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'ask', permissionDecisionReason: `This appears to be a ${pattern} file. Confirm this edit is intentional.`, }, }; } } // Generate file-specific guidance from patterns const guidance = await this.reasoningBank.generateGuidance({ event: 'pre-edit', timestamp: new Date(), file: { path: filePath, operation: 'modify' }, }); let contextGuidance = ''; // File type specific guidance if (/test|spec/i.test(filePath)) { contextGuidance = 'Testing file: Use TDD London School patterns. Mock dependencies, test behavior not implementation.'; } else if (/security|auth/i.test(filePath)) { contextGuidance = 'Security module: Validate inputs, use parameterized queries, no hardcoded secrets.'; } else if (/memory|cache/i.test(filePath)) { contextGuidance = 'Memory module: Consider HNSW indexing, batch operations, proper cleanup.'; } else if (/swarm|coordinator/i.test(filePath)) { contextGuidance = 'Swarm module: Use event-driven communication, handle failures gracefully.'; } else if (/\.ts$/.test(filePath)) { contextGuidance = 'TypeScript: Use strict types, avoid any, export interfaces for public APIs.'; } // Add pattern-based guidance if (guidance.patterns.length > 0) { const patternHints = guidance.patterns .slice(0, 2) .map(p => p.pattern.strategy) .join('; '); contextGuidance += ` Similar patterns: ${patternHints}`; } if (contextGuidance) { return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'allow', additionalContext: contextGuidance, }, }; } return { decision: 'allow' }; } /** * Generate post-edit feedback * Returns JSON with quality feedback */ async generatePostEditFeedback(filePath, fileContent) { const issues = []; if (fileContent) { // Check for console.log in non-test files if (!/test|spec/i.test(filePath) && fileContent.includes('console.log')) { issues.push('Remove console.log statements (use proper logging)'); } // Check for TODO/FIXME if (/TODO|FIXME|HACK/i.test(fileContent)) { issues.push('Address TODO/FIXME comments before committing'); } // Check for any type in TypeScript if (/\.ts$/.test(filePath) && /:\s*any\b/.test(fileContent)) { issues.push("Replace 'any' types with specific types"); } // Check file size const lines = fileContent.split('\n').length; if (lines > 500) { issues.push(`File exceeds 500 lines (${lines}). Consider splitting.`); } // Check for hardcoded secrets if (/password\s*=\s*['"][^'"]+['"]|api[_-]?key\s*=\s*['"][^'"]+['"]/i.test(fileContent)) { issues.push('Possible hardcoded secret detected. Use environment variables.'); } } // Store the edit as a pattern await this.reasoningBank.storePattern(`Edit: ${filePath}`, 'code', { operation: 'modify', issues: issues.length, }); if (issues.length > 0) { return { decision: 'allow', reason: `Edit completed. Review suggestions:\n- ${issues.join('\n- ')}`, hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: `Quality check found items to address:\n- ${issues.join('\n- ')}`, }, }; } return { decision: 'allow' }; } /** * Generate pre-command guidance * Returns JSON with risk assessment */ async generatePreCommandGuidance(command) { // Block dangerous commands for (const pattern of DANGEROUS_COMMANDS) { if (new RegExp(pattern, 'i').test(command)) { return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: `Destructive command blocked: ${pattern}. Use safer alternatives.`, }, }; } } // Warn about risky commands for (const pattern of RISKY_COMMANDS) { if (command.toLowerCase().includes(pattern)) { return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'ask', permissionDecisionReason: `This command has external effects (${pattern}). Confirm before proceeding.`, }, }; } } // Guide test commands if (/npm test|vitest|jest|pnpm test/i.test(command)) { return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'allow', additionalContext: 'Running tests. If failures occur, fix them before proceeding.', }, }; } // Guide build commands if (/npm run build|tsc|pnpm build/i.test(command)) { return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'allow', additionalContext: 'Building project. Watch for type errors. All must pass before commit.', }, }; } return { decision: 'allow' }; } /** * Generate task routing guidance * Returns plain text with agent recommendation */ async generateRoutingGuidance(task) { const routing = await this.reasoningBank.routeTask(task); const lines = [ `**Recommended Agent**: ${routing.agent}`, `**Confidence**: ${routing.confidence}%`, `**Reasoning**: ${routing.reasoning}`, '', ]; if (routing.alternatives.length > 0) { lines.push('**Alternatives**:'); for (const alt of routing.alternatives) { lines.push(`- ${alt.agent} (${alt.confidence}%)`); } lines.push(''); } if (routing.historicalPerformance) { lines.push('**Historical Performance**:'); lines.push(`- Success rate: ${(routing.historicalPerformance.successRate * 100).toFixed(0)}%`); lines.push(`- Avg quality: ${(routing.historicalPerformance.avgQuality * 100).toFixed(0)}%`); lines.push(`- Similar tasks: ${routing.historicalPerformance.taskCount}`); } lines.push(''); lines.push(`Use Task tool with subagent_type="${routing.agent}" for optimal results.`); return lines.join('\n'); } /** * Generate stop check * Returns exit code 2 + stderr if work incomplete */ async generateStopCheck() { const stats = this.reasoningBank.getStats(); // Check if there are uncommitted patterns if (stats.shortTermCount > 10) { return { shouldStop: false, reason: `${stats.shortTermCount} patterns not yet consolidated. Run consolidation before stopping.`, }; } return { shouldStop: true }; } // ===== Helper Methods ===== detectDomains(text) { const domains = []; const lowerText = text.toLowerCase(); if (/security|auth|password|token|secret|cve|vuln/i.test(lowerText)) { domains.push('security'); } if (/test|spec|mock|coverage|tdd|assert/i.test(lowerText)) { domains.push('testing'); } if (/perf|optim|fast|slow|memory|cache|speed/i.test(lowerText)) { domains.push('performance'); } if (/architect|design|ddd|domain|refactor|struct/i.test(lowerText)) { domains.push('architecture'); } if (/fix|bug|error|issue|broken|fail|debug/i.test(lowerText)) { domains.push('debugging'); } return domains; } capitalize(s) { return s.charAt(0).toUpperCase() + s.slice(1); } } // Export singleton export const guidanceProvider = new GuidanceProvider(); //# sourceMappingURL=guidance-provider.js.map