claude-flow
Version:
Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration
1,199 lines (1,198 loc) • 168 kB
JavaScript
/**
* V3 CLI Hooks Command
* Self-learning hooks system for intelligent workflow automation
*/
import { output } from '../output.js';
import { confirm } from '../prompt.js';
import { callMCPTool, MCPClientError } from '../mcp-client.js';
import { storeCommand } from './transfer-store.js';
// Hook types
const HOOK_TYPES = [
{ value: 'pre-edit', label: 'Pre-Edit', hint: 'Get context before editing files' },
{ value: 'post-edit', label: 'Post-Edit', hint: 'Record editing outcomes' },
{ value: 'pre-command', label: 'Pre-Command', hint: 'Assess risk before commands' },
{ value: 'post-command', label: 'Post-Command', hint: 'Record command outcomes' },
{ value: 'route', label: 'Route', hint: 'Route tasks to optimal agents' },
{ value: 'explain', label: 'Explain', hint: 'Explain routing decisions' }
];
// Agent routing options
const AGENT_TYPES = [
'coder', 'researcher', 'tester', 'reviewer', 'architect',
'security-architect', 'security-auditor', 'memory-specialist',
'swarm-specialist', 'performance-engineer', 'core-architect',
'test-architect', 'coordinator', 'analyst', 'optimizer'
];
// Pre-edit subcommand
const preEditCommand = {
name: 'pre-edit',
description: 'Get context and agent suggestions before editing a file',
options: [
{
name: 'file',
short: 'f',
description: 'File path to edit',
type: 'string',
required: false
},
{
name: 'operation',
short: 'o',
description: 'Type of edit operation (create, update, delete, refactor)',
type: 'string',
default: 'update'
},
{
name: 'context',
short: 'c',
description: 'Additional context about the edit',
type: 'string'
}
],
examples: [
{ command: 'claude-flow hooks pre-edit -f src/utils.ts', description: 'Get context before editing' },
{ command: 'claude-flow hooks pre-edit -f src/api.ts -o refactor', description: 'Pre-edit with operation type' }
],
action: async (ctx) => {
// Default file to 'unknown' for backward compatibility (env var may be empty)
const filePath = ctx.args[0] || ctx.flags.file || 'unknown';
const operation = ctx.flags.operation || 'update';
output.printInfo(`Analyzing context for: ${output.highlight(filePath)}`);
try {
// Call MCP tool for pre-edit hook
const result = await callMCPTool('hooks_pre-edit', {
filePath,
operation,
context: ctx.flags.context,
includePatterns: true,
includeRisks: true,
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.printBox([
`File: ${result.filePath}`,
`Operation: ${result.operation}`,
`Type: ${result.context.fileType}`,
`Exists: ${result.context.fileExists ? 'Yes' : 'No'}`
].join('\n'), 'File Context');
if (result.context.suggestedAgents.length > 0) {
output.writeln();
output.writeln(output.bold('Suggested Agents'));
output.printList(result.context.suggestedAgents.map(a => output.highlight(a)));
}
if (result.context.relatedFiles.length > 0) {
output.writeln();
output.writeln(output.bold('Related Files'));
output.printList(result.context.relatedFiles.slice(0, 5).map(f => output.dim(f)));
}
if (result.context.patterns.length > 0) {
output.writeln();
output.writeln(output.bold('Learned Patterns'));
output.printTable({
columns: [
{ key: 'pattern', header: 'Pattern', width: 40 },
{ key: 'confidence', header: 'Confidence', width: 12, align: 'right', format: (v) => `${(Number(v) * 100).toFixed(1)}%` }
],
data: result.context.patterns
});
}
if (result.context.risks.length > 0) {
output.writeln();
output.writeln(output.bold(output.error('Potential Risks')));
output.printList(result.context.risks.map(r => output.warning(r)));
}
if (result.recommendations.length > 0) {
output.writeln();
output.writeln(output.bold('Recommendations'));
output.printList(result.recommendations.map(r => output.success(`• ${r}`)));
}
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Pre-edit hook failed: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Post-edit subcommand
const postEditCommand = {
name: 'post-edit',
description: 'Record editing outcome for learning',
options: [
{
name: 'file',
short: 'f',
description: 'File path that was edited',
type: 'string',
required: false
},
{
name: 'success',
short: 's',
description: 'Whether the edit was successful',
type: 'boolean',
required: false
},
{
name: 'outcome',
short: 'o',
description: 'Outcome description',
type: 'string'
},
{
name: 'metrics',
short: 'm',
description: 'Performance metrics (e.g., "time:500ms,quality:0.95")',
type: 'string'
}
],
examples: [
{ command: 'claude-flow hooks post-edit -f src/utils.ts --success true', description: 'Record successful edit' },
{ command: 'claude-flow hooks post-edit -f src/api.ts --success false -o "Type error"', description: 'Record failed edit' }
],
action: async (ctx) => {
// Default file to 'unknown' for backward compatibility (env var may be empty)
const filePath = ctx.args[0] || ctx.flags.file || 'unknown';
// Default success to true for backward compatibility (PostToolUse = success, PostToolUseFailure = failure)
const success = ctx.flags.success !== undefined ? ctx.flags.success : true;
output.printInfo(`Recording outcome for: ${output.highlight(filePath)}`);
try {
// Parse metrics if provided
const metrics = {};
if (ctx.flags.metrics) {
const metricsStr = ctx.flags.metrics;
metricsStr.split(',').forEach(pair => {
const [key, value] = pair.split(':');
if (key && value) {
metrics[key.trim()] = parseFloat(value);
}
});
}
// Call MCP tool for post-edit hook
const result = await callMCPTool('hooks_post-edit', {
filePath,
success,
outcome: ctx.flags.outcome,
metrics,
timestamp: Date.now(),
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.printSuccess(`Outcome recorded for ${filePath}`);
if (result.learningUpdates) {
output.writeln();
output.writeln(output.bold('Learning Updates'));
output.printTable({
columns: [
{ key: 'metric', header: 'Metric', width: 25 },
{ key: 'value', header: 'Value', width: 15, align: 'right' }
],
data: [
{ metric: 'Patterns Updated', value: result.learningUpdates.patternsUpdated },
{ metric: 'Confidence Adjusted', value: result.learningUpdates.confidenceAdjusted },
{ metric: 'New Patterns', value: result.learningUpdates.newPatterns }
]
});
}
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Post-edit hook failed: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Pre-command subcommand
const preCommandCommand = {
name: 'pre-command',
description: 'Assess risk before executing a command',
options: [
{
name: 'command',
short: 'c',
description: 'Command to execute',
type: 'string',
required: true
},
{
name: 'dry-run',
short: 'd',
description: 'Only analyze, do not execute',
type: 'boolean',
default: true
}
],
examples: [
{ command: 'claude-flow hooks pre-command -c "rm -rf dist"', description: 'Assess command risk' },
{ command: 'claude-flow hooks pre-command -c "npm install lodash"', description: 'Check package install' }
],
action: async (ctx) => {
const command = ctx.args[0] || ctx.flags.command;
if (!command) {
output.printError('Command is required. Use --command or -c flag.');
return { success: false, exitCode: 1 };
}
output.printInfo(`Analyzing command: ${output.highlight(command)}`);
try {
// Call MCP tool for pre-command hook
const result = await callMCPTool('hooks_pre-command', {
command,
includeAlternatives: true,
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
// Risk level indicator
let riskIndicator;
switch (result.riskLevel) {
case 'critical':
riskIndicator = output.error('CRITICAL');
break;
case 'high':
riskIndicator = output.error('HIGH');
break;
case 'medium':
riskIndicator = output.warning('MEDIUM');
break;
default:
riskIndicator = output.success('LOW');
}
output.printBox([
`Risk Level: ${riskIndicator}`,
`Should Proceed: ${result.shouldProceed ? output.success('Yes') : output.error('No')}`
].join('\n'), 'Risk Assessment');
if (result.risks.length > 0) {
output.writeln();
output.writeln(output.bold('Identified Risks'));
output.printTable({
columns: [
{ key: 'type', header: 'Type', width: 15 },
{ key: 'severity', header: 'Severity', width: 10 },
{ key: 'description', header: 'Description', width: 40 }
],
data: result.risks
});
}
if (result.safeAlternatives && result.safeAlternatives.length > 0) {
output.writeln();
output.writeln(output.bold('Safe Alternatives'));
output.printList(result.safeAlternatives.map(a => output.success(a)));
}
if (result.recommendations.length > 0) {
output.writeln();
output.writeln(output.bold('Recommendations'));
output.printList(result.recommendations);
}
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Pre-command hook failed: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Post-command subcommand
const postCommandCommand = {
name: 'post-command',
description: 'Record command execution outcome',
options: [
{
name: 'command',
short: 'c',
description: 'Command that was executed',
type: 'string',
required: true
},
{
name: 'success',
short: 's',
description: 'Whether the command succeeded',
type: 'boolean',
required: false
},
{
name: 'exit-code',
short: 'e',
description: 'Command exit code',
type: 'number',
default: 0
},
{
name: 'duration',
short: 'd',
description: 'Execution duration in milliseconds',
type: 'number'
}
],
examples: [
{ command: 'claude-flow hooks post-command -c "npm test" --success true', description: 'Record successful test run' },
{ command: 'claude-flow hooks post-command -c "npm build" --success false -e 1', description: 'Record failed build' }
],
action: async (ctx) => {
const command = ctx.args[0] || ctx.flags.command;
// Default success to true for backward compatibility
const success = ctx.flags.success !== undefined ? ctx.flags.success : true;
if (!command) {
output.printError('Command is required. Use --command or -c flag.');
return { success: false, exitCode: 1 };
}
output.printInfo(`Recording command outcome: ${output.highlight(command)}`);
try {
// Call MCP tool for post-command hook
const result = await callMCPTool('hooks_post-command', {
command,
success,
exitCode: ctx.flags.exitCode || 0,
duration: ctx.flags.duration,
timestamp: Date.now(),
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.printSuccess('Command outcome recorded');
if (result.learningUpdates) {
output.writeln();
output.writeln(output.dim(`Patterns updated: ${result.learningUpdates.commandPatternsUpdated}`));
output.writeln(output.dim(`Risk assessment: ${result.learningUpdates.riskAssessmentUpdated ? 'Updated' : 'No change'}`));
}
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Post-command hook failed: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Route subcommand
const routeCommand = {
name: 'route',
description: 'Route task to optimal agent using learned patterns',
options: [
{
name: 'task',
short: 't',
description: 'Task description',
type: 'string',
required: true
},
{
name: 'context',
short: 'c',
description: 'Additional context',
type: 'string'
},
{
name: 'top-k',
short: 'K',
description: 'Number of top agent suggestions',
type: 'number',
default: 3
}
],
examples: [
{ command: 'claude-flow hooks route -t "Fix authentication bug"', description: 'Route task to optimal agent' },
{ command: 'claude-flow hooks route -t "Optimize database queries" -K 5', description: 'Get top 5 suggestions' }
],
action: async (ctx) => {
const task = ctx.args[0] || ctx.flags.task;
const topK = ctx.flags.topK || 3;
if (!task) {
output.printError('Task description is required. Use --task or -t flag.');
return { success: false, exitCode: 1 };
}
output.printInfo(`Routing task: ${output.highlight(task)}`);
try {
// Call MCP tool for routing
const result = await callMCPTool('hooks_route', {
task,
context: ctx.flags.context,
topK,
includeEstimates: true,
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
// Show routing method info
if (result.routing) {
output.writeln();
output.writeln(output.bold('Routing Method'));
const methodDisplay = result.routing.method.startsWith('semantic')
? output.success(`${result.routing.method} (${result.routing.backend || 'semantic'})`)
: 'keyword';
output.printList([
`Method: ${methodDisplay}`,
result.routing.backend ? `Backend: ${result.routing.backend}` : null,
`Latency: ${result.routing.latencyMs.toFixed(3)}ms`,
result.matchedPattern ? `Matched Pattern: ${result.matchedPattern}` : null,
].filter(Boolean));
// Show semantic matches if available
if (result.semanticMatches && result.semanticMatches.length > 0) {
output.writeln();
output.writeln(output.dim('Semantic Matches:'));
result.semanticMatches.forEach(m => {
output.writeln(` ${m.pattern}: ${(m.score * 100).toFixed(1)}%`);
});
}
}
output.writeln();
output.printBox([
`Agent: ${output.highlight(result.primaryAgent.type)}`,
`Confidence: ${(result.primaryAgent.confidence * 100).toFixed(1)}%`,
`Reason: ${result.primaryAgent.reason}`
].join('\n'), 'Primary Recommendation');
if (result.alternativeAgents.length > 0) {
output.writeln();
output.writeln(output.bold('Alternative Agents'));
output.printTable({
columns: [
{ key: 'type', header: 'Agent Type', width: 20 },
{ key: 'confidence', header: 'Confidence', width: 12, align: 'right', format: (v) => `${(Number(v) * 100).toFixed(1)}%` },
{ key: 'reason', header: 'Reason', width: 35 }
],
data: result.alternativeAgents
});
}
if (result.estimatedMetrics) {
output.writeln();
output.writeln(output.bold('Estimated Metrics'));
output.printList([
`Success Probability: ${(result.estimatedMetrics.successProbability * 100).toFixed(1)}%`,
`Estimated Duration: ${result.estimatedMetrics.estimatedDuration}`,
`Complexity: ${result.estimatedMetrics.complexity.toUpperCase()}`
]);
}
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Routing failed: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Explain subcommand
const explainCommand = {
name: 'explain',
description: 'Explain routing decision with transparency',
options: [
{
name: 'task',
short: 't',
description: 'Task description',
type: 'string',
required: true
},
{
name: 'agent',
short: 'a',
description: 'Agent type to explain',
type: 'string'
},
{
name: 'verbose',
short: 'v',
description: 'Verbose explanation',
type: 'boolean',
default: false
}
],
examples: [
{ command: 'claude-flow hooks explain -t "Fix authentication bug"', description: 'Explain routing decision' },
{ command: 'claude-flow hooks explain -t "Optimize queries" -a coder --verbose', description: 'Verbose explanation for specific agent' }
],
action: async (ctx) => {
const task = ctx.args[0] || ctx.flags.task;
if (!task) {
output.printError('Task description is required. Use --task or -t flag.');
return { success: false, exitCode: 1 };
}
output.printInfo(`Explaining routing for: ${output.highlight(task)}`);
try {
// Call MCP tool for explanation
const result = await callMCPTool('hooks_explain', {
task,
agent: ctx.flags.agent,
verbose: ctx.flags.verbose || false,
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.writeln(output.bold('Decision Explanation'));
output.writeln();
output.writeln(result.explanation);
output.writeln();
output.printBox([
`Agent: ${output.highlight(result.decision.agent)}`,
`Confidence: ${(result.decision.confidence * 100).toFixed(1)}%`
].join('\n'), 'Final Decision');
if (result.decision.reasoning.length > 0) {
output.writeln();
output.writeln(output.bold('Reasoning Steps'));
output.printList(result.decision.reasoning.map((r, i) => `${i + 1}. ${r}`));
}
if (result.factors.length > 0) {
output.writeln();
output.writeln(output.bold('Decision Factors'));
output.printTable({
columns: [
{ key: 'factor', header: 'Factor', width: 20 },
{ key: 'weight', header: 'Weight', width: 10, align: 'right', format: (v) => `${(Number(v) * 100).toFixed(0)}%` },
{ key: 'value', header: 'Value', width: 10, align: 'right', format: (v) => Number(v).toFixed(2) },
{ key: 'impact', header: 'Impact', width: 25 }
],
data: result.factors
});
}
if (result.patterns.length > 0 && ctx.flags.verbose) {
output.writeln();
output.writeln(output.bold('Matched Patterns'));
result.patterns.forEach((p, i) => {
output.writeln();
output.writeln(`${i + 1}. ${output.highlight(p.pattern)} (${(p.matchScore * 100).toFixed(1)}% match)`);
if (p.examples.length > 0) {
output.printList(p.examples.slice(0, 3).map(e => output.dim(` ${e}`)));
}
});
}
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Explanation failed: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Pretrain subcommand
const pretrainCommand = {
name: 'pretrain',
description: 'Bootstrap intelligence from repository (4-step pipeline + embeddings)',
options: [
{
name: 'path',
short: 'p',
description: 'Repository path',
type: 'string',
default: '.'
},
{
name: 'depth',
short: 'd',
description: 'Analysis depth (shallow, medium, deep)',
type: 'string',
default: 'medium',
choices: ['shallow', 'medium', 'deep']
},
{
name: 'skip-cache',
description: 'Skip cached analysis',
type: 'boolean',
default: false
},
{
name: 'with-embeddings',
description: 'Index documents for semantic search during pretraining',
type: 'boolean',
default: true
},
{
name: 'embedding-model',
description: 'ONNX embedding model',
type: 'string',
default: 'all-MiniLM-L6-v2',
choices: ['all-MiniLM-L6-v2', 'all-mpnet-base-v2']
},
{
name: 'file-types',
description: 'File extensions to index (comma-separated)',
type: 'string',
default: 'ts,js,py,md,json'
}
],
examples: [
{ command: 'claude-flow hooks pretrain', description: 'Pretrain with embeddings indexing' },
{ command: 'claude-flow hooks pretrain -p ../my-project --depth deep', description: 'Deep analysis of specific project' },
{ command: 'claude-flow hooks pretrain --no-with-embeddings', description: 'Skip embedding indexing' },
{ command: 'claude-flow hooks pretrain --file-types ts,tsx,js', description: 'Index only TypeScript/JS files' }
],
action: async (ctx) => {
const repoPath = ctx.flags.path || '.';
const depth = ctx.flags.depth || 'medium';
const withEmbeddings = ctx.flags['with-embeddings'] !== false && ctx.flags.withEmbeddings !== false;
const embeddingModel = (ctx.flags['embedding-model'] || ctx.flags.embeddingModel || 'all-MiniLM-L6-v2');
const fileTypes = (ctx.flags['file-types'] || ctx.flags.fileTypes || 'ts,js,py,md,json');
output.writeln();
output.writeln(output.bold('Pretraining Intelligence (4-Step Pipeline + Embeddings)'));
output.writeln();
const steps = [
{ name: 'RETRIEVE', desc: 'Top-k memory injection with MMR diversity' },
{ name: 'JUDGE', desc: 'LLM-as-judge trajectory evaluation' },
{ name: 'DISTILL', desc: 'Extract strategy memories from trajectories' },
{ name: 'CONSOLIDATE', desc: 'Dedup, detect contradictions, prune old patterns' }
];
// Add embedding steps if enabled
if (withEmbeddings) {
steps.push({ name: 'EMBED', desc: `Index documents with ${embeddingModel} (ONNX)` }, { name: 'HYPERBOLIC', desc: 'Project to Poincaré ball for hierarchy preservation' });
}
const spinner = output.createSpinner({ text: 'Starting pretraining...', spinner: 'dots' });
try {
spinner.start();
// Display progress for each step
for (const step of steps) {
spinner.setText(`${step.name}: ${step.desc}`);
await new Promise(resolve => setTimeout(resolve, 800));
}
// Call MCP tool for pretraining
const result = await callMCPTool('hooks_pretrain', {
path: repoPath,
depth,
skipCache: ctx.flags.skipCache || false,
withEmbeddings,
embeddingModel,
fileTypes: fileTypes.split(',').map((t) => t.trim()),
});
spinner.succeed('Pretraining completed');
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
// Base stats
const tableData = [
{ metric: 'Files Analyzed', value: result.stats.filesAnalyzed },
{ metric: 'Patterns Extracted', value: result.stats.patternsExtracted },
{ metric: 'Strategies Learned', value: result.stats.strategiesLearned },
{ metric: 'Trajectories Evaluated', value: result.stats.trajectoriesEvaluated },
{ metric: 'Contradictions Resolved', value: result.stats.contradictionsResolved },
];
// Add embedding stats if available
if (withEmbeddings && result.stats.documentsIndexed !== undefined) {
tableData.push({ metric: 'Documents Indexed', value: result.stats.documentsIndexed }, { metric: 'Embeddings Generated', value: result.stats.embeddingsGenerated || 0 }, { metric: 'Hyperbolic Projections', value: result.stats.hyperbolicProjections || 0 });
}
tableData.push({ metric: 'Duration', value: `${(result.duration / 1000).toFixed(1)}s` });
output.printTable({
columns: [
{ key: 'metric', header: 'Metric', width: 30 },
{ key: 'value', header: 'Value', width: 15, align: 'right' }
],
data: tableData
});
output.writeln();
output.printSuccess('Repository intelligence bootstrapped successfully');
if (withEmbeddings) {
output.writeln(output.dim(' Semantic search enabled: Use "embeddings search -q <query>" to search'));
}
output.writeln(output.dim(' Next step: Run "claude-flow hooks build-agents" to generate optimized configs'));
return { success: true, data: result };
}
catch (error) {
spinner.fail('Pretraining failed');
if (error instanceof MCPClientError) {
output.printError(`Pretraining error: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Build agents subcommand
const buildAgentsCommand = {
name: 'build-agents',
description: 'Generate optimized agent configs from pretrain data',
options: [
{
name: 'output',
short: 'o',
description: 'Output directory for agent configs',
type: 'string',
default: './agents'
},
{
name: 'focus',
short: 'f',
description: 'Focus area (v3-implementation, security, performance, all)',
type: 'string',
default: 'all'
},
{
name: 'config-format',
description: 'Config format (yaml, json)',
type: 'string',
default: 'yaml',
choices: ['yaml', 'json']
}
],
examples: [
{ command: 'claude-flow hooks build-agents', description: 'Build all agent configs' },
{ command: 'claude-flow hooks build-agents --focus security -o ./config/agents', description: 'Build security-focused configs' }
],
action: async (ctx) => {
const output_dir = ctx.flags.output || './agents';
const focus = ctx.flags.focus || 'all';
const configFormat = ctx.flags.configFormat || 'yaml';
output.printInfo(`Building agent configs (focus: ${output.highlight(focus)})`);
const spinner = output.createSpinner({ text: 'Generating configs...', spinner: 'dots' });
try {
spinner.start();
// Call MCP tool for building agents
const result = await callMCPTool('hooks_build-agents', {
outputDir: output_dir,
focus,
format: configFormat,
includePretrained: true,
});
spinner.succeed(`Generated ${result.agents.length} agent configs`);
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.writeln(output.bold('Generated Agent Configs'));
output.printTable({
columns: [
{ key: 'type', header: 'Agent Type', width: 20 },
{ key: 'configFile', header: 'Config File', width: 30 },
{ key: 'capabilities', header: 'Capabilities', width: 10, align: 'right', format: (v) => String(Array.isArray(v) ? v.length : 0) }
],
data: result.agents
});
output.writeln();
output.printTable({
columns: [
{ key: 'metric', header: 'Metric', width: 30 },
{ key: 'value', header: 'Value', width: 15, align: 'right' }
],
data: [
{ metric: 'Configs Generated', value: result.stats.configsGenerated },
{ metric: 'Patterns Applied', value: result.stats.patternsApplied },
{ metric: 'Optimizations Included', value: result.stats.optimizationsIncluded }
]
});
output.writeln();
output.printSuccess(`Agent configs saved to ${output_dir}`);
return { success: true, data: result };
}
catch (error) {
spinner.fail('Agent config generation failed');
if (error instanceof MCPClientError) {
output.printError(`Build agents error: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Metrics subcommand
const metricsCommand = {
name: 'metrics',
description: 'View learning metrics dashboard',
options: [
{
name: 'period',
short: 'p',
description: 'Time period (1h, 24h, 7d, 30d, all)',
type: 'string',
default: '24h'
},
{
name: 'v3-dashboard',
description: 'Show V3 performance dashboard',
type: 'boolean',
default: false
},
{
name: 'category',
short: 'c',
description: 'Metric category (patterns, agents, commands, performance)',
type: 'string'
}
],
examples: [
{ command: 'claude-flow hooks metrics', description: 'View 24h metrics' },
{ command: 'claude-flow hooks metrics --period 7d --v3-dashboard', description: 'V3 metrics for 7 days' }
],
action: async (ctx) => {
const period = ctx.flags.period || '24h';
const v3Dashboard = ctx.flags.v3Dashboard;
output.writeln();
output.writeln(output.bold(`Learning Metrics Dashboard (${period})`));
output.writeln();
try {
// Call MCP tool for metrics
const result = await callMCPTool('hooks_metrics', {
period,
includeV3: v3Dashboard,
category: ctx.flags.category,
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
// Patterns section
output.writeln(output.bold('📊 Pattern Learning'));
output.printTable({
columns: [
{ key: 'metric', header: 'Metric', width: 25 },
{ key: 'value', header: 'Value', width: 20, align: 'right' }
],
data: [
{ metric: 'Total Patterns', value: result.patterns.total },
{ metric: 'Successful', value: output.success(String(result.patterns.successful)) },
{ metric: 'Failed', value: output.error(String(result.patterns.failed)) },
{ metric: 'Avg Confidence', value: `${(result.patterns.avgConfidence * 100).toFixed(1)}%` }
]
});
output.writeln();
// Agent routing section
output.writeln(output.bold('🤖 Agent Routing'));
output.printTable({
columns: [
{ key: 'metric', header: 'Metric', width: 25 },
{ key: 'value', header: 'Value', width: 20, align: 'right' }
],
data: [
{ metric: 'Routing Accuracy', value: `${(result.agents.routingAccuracy * 100).toFixed(1)}%` },
{ metric: 'Total Routes', value: result.agents.totalRoutes },
{ metric: 'Top Agent', value: output.highlight(result.agents.topAgent) }
]
});
output.writeln();
// Command execution section
output.writeln(output.bold('⚡ Command Execution'));
output.printTable({
columns: [
{ key: 'metric', header: 'Metric', width: 25 },
{ key: 'value', header: 'Value', width: 20, align: 'right' }
],
data: [
{ metric: 'Total Executed', value: result.commands.totalExecuted },
{ metric: 'Success Rate', value: `${(result.commands.successRate * 100).toFixed(1)}%` },
{ metric: 'Avg Risk Score', value: result.commands.avgRiskScore.toFixed(2) }
]
});
if (v3Dashboard && result.performance) {
const p = result.performance;
output.writeln();
output.writeln(output.bold('🚀 V3 Performance Gains'));
output.printList([
`Flash Attention: ${output.success(p.flashAttention ?? 'N/A')}`,
`Memory Reduction: ${output.success(p.memoryReduction ?? 'N/A')}`,
`Search Improvement: ${output.success(p.searchImprovement ?? 'N/A')}`,
`Token Reduction: ${output.success(p.tokenReduction ?? 'N/A')}`
]);
}
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Metrics error: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Pattern Store command (imported from transfer-store.ts)
// storeCommand is imported at the top
// Transfer from project subcommand
const transferFromProjectCommand = {
name: 'from-project',
aliases: ['project'],
description: 'Transfer patterns from another project',
options: [
{
name: 'source',
short: 's',
description: 'Source project path',
type: 'string',
required: true
},
{
name: 'filter',
short: 'f',
description: 'Filter patterns by type',
type: 'string'
},
{
name: 'min-confidence',
short: 'm',
description: 'Minimum confidence threshold (0-1)',
type: 'number',
default: 0.7
}
],
examples: [
{ command: 'claude-flow hooks transfer from-project -s ../old-project', description: 'Transfer all patterns' },
{ command: 'claude-flow hooks transfer from-project -s ../prod --filter security -m 0.9', description: 'Transfer high-confidence security patterns' }
],
action: async (ctx) => {
const sourcePath = ctx.args[0] || ctx.flags.source;
const minConfidence = ctx.flags.minConfidence || 0.7;
if (!sourcePath) {
output.printError('Source project path is required. Use --source or -s flag.');
return { success: false, exitCode: 1 };
}
output.printInfo(`Transferring patterns from: ${output.highlight(sourcePath)}`);
const spinner = output.createSpinner({ text: 'Analyzing source patterns...', spinner: 'dots' });
try {
spinner.start();
// Call MCP tool for transfer
const result = await callMCPTool('hooks_transfer', {
sourcePath,
filter: ctx.flags.filter,
minConfidence,
mergeStrategy: 'keep-highest-confidence',
});
spinner.succeed(`Transferred ${result.transferred.total} patterns`);
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.writeln(output.bold('Transfer Summary'));
output.printTable({
columns: [
{ key: 'category', header: 'Category', width: 25 },
{ key: 'count', header: 'Count', width: 15, align: 'right' }
],
data: [
{ category: 'Total Transferred', count: output.success(String(result.transferred.total)) },
{ category: 'Skipped (Low Confidence)', count: result.skipped.lowConfidence },
{ category: 'Skipped (Duplicates)', count: result.skipped.duplicates },
{ category: 'Skipped (Conflicts)', count: result.skipped.conflicts }
]
});
if (Object.keys(result.transferred.byType).length > 0) {
output.writeln();
output.writeln(output.bold('By Pattern Type'));
output.printTable({
columns: [
{ key: 'type', header: 'Type', width: 20 },
{ key: 'count', header: 'Count', width: 15, align: 'right' }
],
data: Object.entries(result.transferred.byType).map(([type, count]) => ({ type, count }))
});
}
output.writeln();
output.printList([
`Avg Confidence: ${(result.stats.avgConfidence * 100).toFixed(1)}%`,
`Avg Age: ${result.stats.avgAge}`
]);
return { success: true, data: result };
}
catch (error) {
spinner.fail('Transfer failed');
if (error instanceof MCPClientError) {
output.printError(`Transfer error: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Parent transfer command combining all transfer methods
const transferCommand = {
name: 'transfer',
description: 'Transfer patterns and plugins via IPFS-based decentralized registry',
subcommands: [storeCommand, transferFromProjectCommand],
examples: [
{ command: 'claude-flow hooks transfer store list', description: 'List patterns from registry' },
{ command: 'claude-flow hooks transfer store search -q routing', description: 'Search patterns' },
{ command: 'claude-flow hooks transfer store download -p seraphine-genesis', description: 'Download pattern' },
{ command: 'claude-flow hooks transfer store publish', description: 'Publish pattern to registry' },
{ command: 'claude-flow hooks transfer from-project -s ../other-project', description: 'Transfer from project' },
],
action: async () => {
output.writeln();
output.writeln(output.bold('Pattern Transfer System'));
output.writeln(output.dim('Decentralized pattern sharing via IPFS'));
output.writeln();
output.writeln('Subcommands:');
output.printList([
`${output.highlight('store')} - Pattern marketplace (list, search, download, publish)`,
`${output.highlight('from-project')} - Transfer patterns from another project`,
]);
output.writeln();
output.writeln(output.bold('IPFS-Based Features:'));
output.printList([
'Decentralized registry via IPNS for discoverability',
'Content-addressed storage for integrity',
'Ed25519 signatures for verification',
'Anonymization levels: minimal, standard, strict, paranoid',
'Trust levels: unverified, community, verified, official',
]);
output.writeln();
output.writeln('Run "claude-flow hooks transfer <subcommand> --help" for details');
return { success: true };
}
};
// List subcommand
const listCommand = {
name: 'list',
aliases: ['ls'],
description: 'List all registered hooks',
options: [
{
name: 'enabled',
short: 'e',
description: 'Show only enabled hooks',
type: 'boolean',
default: false
},
{
name: 'type',
short: 't',
description: 'Filter by hook type',
type: 'string'
}
],
action: async (ctx) => {
try {
// Call MCP tool for list
const result = await callMCPTool('hooks_list', {
enabled: ctx.flags.enabled || undefined,
type: ctx.flags.type || undefined,
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.writeln(output.bold('Registered Hooks'));
output.writeln();
if (result.hooks.length === 0) {
output.printInfo('No hooks found matching criteria');
return { success: true, data: result };
}
output.printTable({
columns: [
{ key: 'name', header: 'Name', width: 20 },
{ key: 'type', header: 'Type', width: 15 },
{ key: 'enabled', header: 'Enabled', width: 10, format: (v) => v ? output.success('Yes') : output.dim('No') },
{ key: 'priority', header: 'Priority', width: 10, align: 'right' },
{ key: 'executionCount', header: 'Executions', width: 12, align: 'right' },
{ key: 'lastExecuted', header: 'Last Executed', width: 20, format: (v) => v ? new Date(String(v)).toLocaleString() : 'Never' }
],
data: result.hooks
});
output.writeln();
output.printInfo(`Total: ${result.total} hooks`);
return { success: true, data: result };
}
catch (error) {
if (error instanceof MCPClientError) {
output.printError(`Failed to list hooks: ${error.message}`);
}
else {
output.printError(`Unexpected error: ${String(error)}`);
}
return { success: false, exitCode: 1 };
}
}
};
// Pre-task subcommand
const preTaskCommand = {
name: 'pre-task',
description: 'Record task start and get agent suggestions',
options: [
{
name: 'task-id',
short: 'i',
description: 'Unique task identifier',
type: 'string',
required: true
},
{
name: 'description',
short: 'd',
description: 'Task description',
type: 'string',
required: true
},
{
name: 'auto-spawn',
short: 'a',
description: 'Auto-spawn suggested agents',
type: 'boolean',
default: false
}
],
examples: [
{ command: 'claude-flow hooks pre-task -i task-123 -d "Fix auth bug"', description: 'Record task start' },
{ command: 'claude-flow hooks pre-task -i task-456 -d "Implement feature" --auto-spawn', description: 'With auto-spawn' }
],
action: async (ctx) => {
const taskId = ctx.flags.taskId;
const description = ctx.args[0] || ctx.flags.description;
if (!taskId || !description) {
output.printError('Task ID and description are required.');
return { success: false, exitCode: 1 };
}
output.printInfo(`Starting task: ${output.highlight(taskId)}`);
try {
const result = await callMCPTool('hooks_pre-task', {
taskId,
description,
autoSpawn: ctx.flags.autoSpawn || false,
timestamp: Date.now(),
});
if (ctx.flags.format === 'json') {
output.printJson(result);
return { success: true, data: result };
}
output.writeln();
output.printBox([
`Task ID: ${result.taskId}`,
`Description: ${result.description}`,
`Complexity: ${result.complexity.toUpperCase()}`,
`Est. Duration: ${result.estimatedDuration}`
].join('\n'), 'Task Registered');
if (result.suggestedAgents.length > 0) {