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
560 lines • 30.3 kB
JavaScript
/**
* V3 CLI Guidance Command
* Guidance Control Plane - compile, retrieve, enforce, optimize
*/
import { output } from '../output.js';
// compile subcommand
const compileCommand = {
name: 'compile',
description: 'Compile CLAUDE.md into a policy bundle (constitution + shards + manifest)',
options: [
{ name: 'root', short: 'r', type: 'string', description: 'Root guidance file path', default: './CLAUDE.md' },
{ name: 'local', short: 'l', type: 'string', description: 'Local guidance overlay file path' },
{ name: 'output', short: 'o', type: 'string', description: 'Output directory for compiled bundle' },
{ name: 'json', type: 'boolean', description: 'Output as JSON', default: 'false' },
],
examples: [
{ command: 'claude-flow guidance compile', description: 'Compile default CLAUDE.md' },
{ command: 'claude-flow guidance compile -r ./CLAUDE.md -l ./CLAUDE.local.md', description: 'Compile with local overlay' },
{ command: 'claude-flow guidance compile --json', description: 'Output compiled bundle as JSON' },
],
action: async (ctx) => {
const rootPath = ctx.flags.root || './CLAUDE.md';
const localPath = ctx.flags.local;
const jsonOutput = ctx.flags.json === true;
output.writeln();
output.writeln(output.bold('Guidance Compiler'));
output.writeln(output.dim('─'.repeat(50)));
try {
const { readFile } = await import('node:fs/promises');
const { existsSync } = await import('node:fs');
if (!existsSync(rootPath)) {
output.writeln(output.error(`Root guidance file not found: ${rootPath}`));
return { success: false, message: `File not found: ${rootPath}` };
}
const rootContent = await readFile(rootPath, 'utf-8');
let localContent;
if (localPath && existsSync(localPath)) {
localContent = await readFile(localPath, 'utf-8');
}
const { GuidanceCompiler } = await import('@claude-flow/guidance/compiler');
const compiler = new GuidanceCompiler();
const bundle = compiler.compile(rootContent, localContent);
if (jsonOutput) {
output.writeln(JSON.stringify(bundle, null, 2));
}
else {
output.writeln(output.success('Compiled successfully'));
output.writeln();
output.writeln(` Constitution rules: ${output.bold(String(bundle.constitution.rules.length))}`);
output.writeln(` Constitution hash: ${output.dim(bundle.constitution.hash)}`);
output.writeln(` Shard count: ${output.bold(String(bundle.shards.length))}`);
output.writeln(` Total rules: ${output.bold(String(bundle.manifest.totalRules))}`);
output.writeln(` Compiled at: ${output.dim(new Date(bundle.manifest.compiledAt).toISOString())}`);
if (localContent) {
output.writeln(` Local overlay: ${output.success('applied')}`);
}
output.writeln();
output.writeln(output.dim('Rule summary:'));
for (const rule of bundle.manifest.rules.slice(0, 10)) {
const risk = rule.riskClass === 'critical' ? output.error(rule.riskClass) :
rule.riskClass === 'high' ? output.warning(rule.riskClass) :
output.dim(rule.riskClass);
output.writeln(` ${output.bold(rule.id)} [${risk}] ${rule.source.slice(0, 60)}${rule.source.length > 60 ? '...' : ''}`);
}
if (bundle.manifest.rules.length > 10) {
output.writeln(output.dim(` ... and ${bundle.manifest.rules.length - 10} more`));
}
}
return { success: true, data: bundle };
}
catch (error) {
const msg = error instanceof Error ? error.message : String(error);
output.writeln(output.error(`Compilation failed: ${msg}`));
return { success: false, message: msg };
}
},
};
// retrieve subcommand
const retrieveCommand = {
name: 'retrieve',
description: 'Retrieve task-relevant guidance shards for a given task description',
options: [
{ name: 'task', short: 't', type: 'string', description: 'Task description', required: true },
{ name: 'root', short: 'r', type: 'string', description: 'Root guidance file path', default: './CLAUDE.md' },
{ name: 'local', short: 'l', type: 'string', description: 'Local overlay file path' },
{ name: 'max-shards', short: 'n', type: 'number', description: 'Maximum number of shards to retrieve', default: '5' },
{ name: 'intent', short: 'i', type: 'string', description: 'Override detected intent' },
{ name: 'json', type: 'boolean', description: 'Output as JSON', default: 'false' },
],
examples: [
{ command: 'claude-flow guidance retrieve -t "Fix SQL injection in user search"', description: 'Retrieve guidance for a security task' },
{ command: 'claude-flow guidance retrieve -t "Add unit tests" -n 3', description: 'Retrieve top 3 shards for testing' },
],
action: async (ctx) => {
const task = ctx.flags.task;
const rootPath = ctx.flags.root || './CLAUDE.md';
const localPath = ctx.flags.local;
const maxShards = parseInt(ctx.flags['max-shards'] || '5', 10);
const intentOverride = ctx.flags.intent;
const jsonOutput = ctx.flags.json === true;
if (!task) {
output.writeln(output.error('Task description is required (-t "...")'));
return { success: false, message: 'Missing task description' };
}
output.writeln();
output.writeln(output.bold('Guidance Retriever'));
output.writeln(output.dim('─'.repeat(50)));
try {
const { readFile } = await import('node:fs/promises');
const { existsSync } = await import('node:fs');
const { GuidanceCompiler } = await import('@claude-flow/guidance/compiler');
const { ShardRetriever, HashEmbeddingProvider } = await import('@claude-flow/guidance/retriever');
if (!existsSync(rootPath)) {
output.writeln(output.error(`Root guidance file not found: ${rootPath}`));
return { success: false, message: `File not found: ${rootPath}` };
}
const rootContent = await readFile(rootPath, 'utf-8');
let localContent;
if (localPath && existsSync(localPath)) {
localContent = await readFile(localPath, 'utf-8');
}
const compiler = new GuidanceCompiler();
const bundle = compiler.compile(rootContent, localContent);
const retriever = new ShardRetriever(new HashEmbeddingProvider(128));
await retriever.loadBundle(bundle);
const result = await retriever.retrieve({
taskDescription: task,
maxShards,
intent: intentOverride,
});
if (jsonOutput) {
output.writeln(JSON.stringify(result, null, 2));
}
else {
output.writeln(` Detected intent: ${output.bold(result.detectedIntent)}`);
output.writeln(` Retrieval time: ${output.dim(result.latencyMs + 'ms')}`);
output.writeln(` Constitution: ${output.bold(String(result.constitution.rules.length))} rules`);
output.writeln(` Shards: ${output.bold(String(result.shards.length))} retrieved`);
output.writeln();
if (result.shards.length > 0) {
output.writeln(output.dim('Retrieved shards:'));
for (const shard of result.shards) {
output.writeln(` ${output.bold(shard.shard.rule.id)} [${shard.shard.rule.riskClass}] ${shard.shard.rule.text.slice(0, 60)}`);
}
}
output.writeln();
output.writeln(output.dim('Policy text preview:'));
const lines = result.policyText.split('\n').slice(0, 15);
for (const line of lines) {
output.writeln(` ${line}`);
}
if (result.policyText.split('\n').length > 15) {
output.writeln(output.dim(' ...'));
}
}
return { success: true, data: result };
}
catch (error) {
const msg = error instanceof Error ? error.message : String(error);
output.writeln(output.error(`Retrieval failed: ${msg}`));
return { success: false, message: msg };
}
},
};
// gates subcommand
const gatesCommand = {
name: 'gates',
description: 'Evaluate enforcement gates against a command or content',
options: [
{ name: 'command', short: 'c', type: 'string', description: 'Command to evaluate' },
{ name: 'content', type: 'string', description: 'Content to check for secrets' },
{ name: 'tool', short: 't', type: 'string', description: 'Tool name to check against allowlist' },
{ name: 'json', type: 'boolean', description: 'Output as JSON', default: 'false' },
],
examples: [
{ command: 'claude-flow guidance gates -c "rm -rf /tmp"', description: 'Check if a command is destructive' },
{ command: 'claude-flow guidance gates --content "api_key=sk-abc123..."', description: 'Check content for secrets' },
],
action: async (ctx) => {
const command = ctx.flags.command;
const content = ctx.flags.content;
const tool = ctx.flags.tool;
const jsonOutput = ctx.flags.json === true;
output.writeln();
output.writeln(output.bold('Enforcement Gates'));
output.writeln(output.dim('─'.repeat(50)));
try {
const { EnforcementGates } = await import('@claude-flow/guidance/gates');
const gates = new EnforcementGates();
const results = [];
if (command) {
const gateResults = gates.evaluateCommand(command);
results.push({ type: 'command', result: gateResults });
}
if (content) {
const secretResult = gates.evaluateSecrets(content);
results.push({ type: 'secrets', result: secretResult });
}
if (tool) {
const toolResult = gates.evaluateToolAllowlist(tool);
results.push({ type: 'tool-allowlist', result: toolResult });
}
if (results.length === 0) {
output.writeln(output.warning('No input provided. Use -c, --content, or -t to evaluate.'));
return { success: false, message: 'No input' };
}
if (jsonOutput) {
output.writeln(JSON.stringify(results, null, 2));
}
else {
for (const { type, result } of results) {
output.writeln(` ${output.bold(type)}:`);
if (result === null) {
output.writeln(` ${output.success('ALLOW')} - No gate triggered`);
}
else if (Array.isArray(result)) {
if (result.length === 0) {
output.writeln(` ${output.success('ALLOW')} - All gates passed`);
}
else {
for (const r of result) {
const color = r.decision === 'block' ? output.error.bind(output) :
r.decision === 'require-confirmation' ? output.warning.bind(output) :
output.dim.bind(output);
output.writeln(` ${color(r.decision.toUpperCase())} [${r.gateName}] ${r.reason}`);
if (r.remediation) {
output.writeln(` Remediation: ${output.dim(r.remediation)}`);
}
}
}
}
else {
const color = result.decision === 'block' ? output.error.bind(output) :
result.decision === 'require-confirmation' ? output.warning.bind(output) :
output.dim.bind(output);
output.writeln(` ${color(result.decision.toUpperCase())} [${result.gateName}] ${result.reason}`);
if (result.remediation) {
output.writeln(` Remediation: ${output.dim(result.remediation)}`);
}
}
}
}
return { success: true, data: results };
}
catch (error) {
const msg = error instanceof Error ? error.message : String(error);
output.writeln(output.error(`Gate evaluation failed: ${msg}`));
return { success: false, message: msg };
}
},
};
// status subcommand
const statusCommand = {
name: 'status',
description: 'Show guidance control plane status and metrics',
options: [
{ name: 'json', type: 'boolean', description: 'Output as JSON', default: 'false' },
],
action: async (ctx) => {
const jsonOutput = ctx.flags.json === true;
output.writeln();
output.writeln(output.bold('Guidance Control Plane Status'));
output.writeln(output.dim('─'.repeat(50)));
try {
const { existsSync } = await import('node:fs');
const rootExists = existsSync('./CLAUDE.md');
const localExists = existsSync('./CLAUDE.local.md');
const statusData = {
rootGuidance: rootExists ? 'found' : 'not found',
localOverlay: localExists ? 'found' : 'not configured',
dataDir: existsSync('./.claude-flow/guidance') ? 'exists' : 'not created',
};
if (jsonOutput) {
output.writeln(JSON.stringify(statusData, null, 2));
}
else {
output.writeln(` Root guidance: ${rootExists ? output.success('CLAUDE.md found') : output.warning('CLAUDE.md not found')}`);
output.writeln(` Local overlay: ${localExists ? output.success('CLAUDE.local.md found') : output.dim('not configured')}`);
output.writeln(` Data directory: ${statusData.dataDir === 'exists' ? output.success('exists') : output.dim('not created')}`);
if (rootExists) {
const { readFile } = await import('node:fs/promises');
const { GuidanceCompiler } = await import('@claude-flow/guidance/compiler');
const rootContent = await readFile('./CLAUDE.md', 'utf-8');
const compiler = new GuidanceCompiler();
const bundle = compiler.compile(rootContent);
output.writeln();
output.writeln(output.dim('Compiled bundle:'));
output.writeln(` Constitution rules: ${output.bold(String(bundle.constitution.rules.length))}`);
output.writeln(` Shard count: ${output.bold(String(bundle.shards.length))}`);
output.writeln(` Total rules: ${output.bold(String(bundle.manifest.totalRules))}`);
output.writeln(` Hash: ${output.dim(bundle.constitution.hash)}`);
}
}
return { success: true, data: statusData };
}
catch (error) {
const msg = error instanceof Error ? error.message : String(error);
output.writeln(output.error(`Status check failed: ${msg}`));
return { success: false, message: msg };
}
},
};
// optimize subcommand
const optimizeCommand = {
name: 'optimize',
description: 'Analyze and optimize a CLAUDE.md file for structure, coverage, and enforceability',
options: [
{ name: 'root', short: 'r', type: 'string', description: 'Root guidance file path', default: './CLAUDE.md' },
{ name: 'local', short: 'l', type: 'string', description: 'Local overlay file path' },
{ name: 'apply', short: 'a', type: 'boolean', description: 'Apply optimizations to the file', default: 'false' },
{ name: 'context-size', short: 's', type: 'string', description: 'Target context size: compact, standard, full', default: 'standard' },
{ name: 'target-score', type: 'number', description: 'Target composite score (0-100)', default: '90' },
{ name: 'max-iterations', type: 'number', description: 'Maximum optimization iterations', default: '5' },
{ name: 'json', type: 'boolean', description: 'Output as JSON', default: 'false' },
],
examples: [
{ command: 'claude-flow guidance optimize', description: 'Analyze current CLAUDE.md and show suggestions' },
{ command: 'claude-flow guidance optimize --apply', description: 'Apply optimizations to CLAUDE.md' },
{ command: 'claude-flow guidance optimize -s compact --apply', description: 'Optimize for compact context window' },
{ command: 'claude-flow guidance optimize --target-score 95', description: 'Optimize until score reaches 95' },
],
action: async (ctx) => {
const rootPath = ctx.flags.root || './CLAUDE.md';
const localPath = ctx.flags.local;
const applyChanges = ctx.flags.apply === true;
const contextSize = (ctx.flags['context-size'] || 'standard');
const targetScore = parseInt(ctx.flags['target-score'] || '90', 10);
const maxIterations = parseInt(ctx.flags['max-iterations'] || '5', 10);
const jsonOutput = ctx.flags.json === true;
output.writeln();
output.writeln(output.bold('Guidance Optimizer'));
output.writeln(output.dim('─'.repeat(50)));
try {
const { readFile, writeFile } = await import('node:fs/promises');
const { existsSync } = await import('node:fs');
if (!existsSync(rootPath)) {
output.writeln(output.error(`Root guidance file not found: ${rootPath}`));
return { success: false, message: `File not found: ${rootPath}` };
}
const rootContent = await readFile(rootPath, 'utf-8');
let localContent;
if (localPath && existsSync(localPath)) {
localContent = await readFile(localPath, 'utf-8');
}
// Step 1: Analyze current state
const { analyze, formatReport, optimizeForSize, formatBenchmark } = await import('@claude-flow/guidance/analyzer');
const analysis = analyze(rootContent, localContent);
if (jsonOutput && !applyChanges) {
output.writeln(JSON.stringify(analysis, null, 2));
return { success: true, data: analysis };
}
// Show current analysis
output.writeln(formatReport(analysis));
output.writeln();
if (analysis.compositeScore >= targetScore) {
output.writeln(output.success(`Score ${analysis.compositeScore}/100 already meets target ${targetScore}. No optimization needed.`));
return { success: true, data: analysis };
}
// Step 2: Run optimization
output.writeln(output.dim(`Optimizing (target: ${targetScore}, context: ${contextSize}, max iterations: ${maxIterations})...`));
const result = optimizeForSize(rootContent, {
contextSize,
localContent,
maxIterations,
targetScore,
});
if (jsonOutput) {
output.writeln(JSON.stringify({
before: analysis,
after: result.benchmark.after,
delta: result.benchmark.delta,
steps: result.appliedSteps,
}, null, 2));
return { success: true, data: result };
}
// Show benchmark comparison
output.writeln();
output.writeln(formatBenchmark(result.benchmark));
output.writeln();
if (result.appliedSteps.length > 0) {
output.writeln(`Applied ${output.bold(String(result.appliedSteps.length))} optimization steps:`);
for (const step of result.appliedSteps) {
output.writeln(` ${output.success('+')} ${step}`);
}
output.writeln();
}
// Step 3: Apply if requested
if (applyChanges) {
await writeFile(rootPath, result.optimized, 'utf-8');
output.writeln(output.success(`Optimized CLAUDE.md written to ${rootPath}`));
output.writeln(` Before: ${analysis.compositeScore}/100 (${analysis.grade})`);
output.writeln(` After: ${result.benchmark.after.compositeScore}/100 (${result.benchmark.after.grade})`);
output.writeln(` Delta: ${result.benchmark.delta >= 0 ? '+' : ''}${result.benchmark.delta}`);
}
else {
output.writeln(output.warning('Dry run - use --apply to write changes.'));
output.writeln(` Projected: ${analysis.compositeScore} → ${result.benchmark.after.compositeScore}/100`);
}
return { success: true, data: result };
}
catch (error) {
const msg = error instanceof Error ? error.message : String(error);
output.writeln(output.error(`Optimization failed: ${msg}`));
return { success: false, message: msg };
}
},
};
// ab-test subcommand
const abTestCommand = {
name: 'ab-test',
description: 'Run A/B behavioral comparison between two CLAUDE.md versions',
options: [
{ name: 'config-a', short: 'a', type: 'string', description: 'Path to Config A (baseline CLAUDE.md). Defaults to no guidance.' },
{ name: 'config-b', short: 'b', type: 'string', description: 'Path to Config B (candidate CLAUDE.md)', default: './CLAUDE.md' },
{ name: 'tasks', short: 't', type: 'string', description: 'Path to custom task JSON file (array of ABTask objects)' },
{ name: 'work-dir', short: 'w', type: 'string', description: 'Working directory for test execution' },
{ name: 'json', type: 'boolean', description: 'Output as JSON', default: 'false' },
],
examples: [
{ command: 'claude-flow guidance ab-test', description: 'Run default A/B test (no guidance vs ./CLAUDE.md)' },
{ command: 'claude-flow guidance ab-test -a old.md -b new.md', description: 'Compare two CLAUDE.md versions' },
{ command: 'claude-flow guidance ab-test --tasks custom-tasks.json', description: 'Run with custom test tasks' },
{ command: 'claude-flow guidance ab-test --json', description: 'Output full report as JSON' },
],
action: async (ctx) => {
const configAPath = ctx.flags['config-a'];
const configBPath = ctx.flags['config-b'] || './CLAUDE.md';
const tasksPath = ctx.flags.tasks;
const workDir = ctx.flags['work-dir'];
const jsonOutput = ctx.flags.json === true;
output.writeln();
output.writeln(output.bold('A/B Behavioral Benchmark'));
output.writeln(output.dim('─'.repeat(50)));
try {
const { readFile } = await import('node:fs/promises');
const { existsSync } = await import('node:fs');
const { abBenchmark, getDefaultABTasks } = await import('@claude-flow/guidance/analyzer');
// Load Config B (candidate) content
if (!existsSync(configBPath)) {
output.writeln(output.error(`Config B file not found: ${configBPath}`));
return { success: false, message: `File not found: ${configBPath}` };
}
const configBContent = await readFile(configBPath, 'utf-8');
// Optionally load Config A for display context
let configALabel = 'No control plane (baseline)';
if (configAPath) {
if (!existsSync(configAPath)) {
output.writeln(output.error(`Config A file not found: ${configAPath}`));
return { success: false, message: `File not found: ${configAPath}` };
}
configALabel = configAPath;
}
// Load custom tasks if provided
let customTasks;
if (tasksPath) {
if (!existsSync(tasksPath)) {
output.writeln(output.error(`Tasks file not found: ${tasksPath}`));
return { success: false, message: `File not found: ${tasksPath}` };
}
const tasksJson = await readFile(tasksPath, 'utf-8');
customTasks = JSON.parse(tasksJson);
output.writeln(` Custom tasks: ${output.bold(String(customTasks.length))} loaded from ${tasksPath}`);
}
output.writeln(` Config A: ${output.dim(configALabel)}`);
output.writeln(` Config B: ${output.dim(configBPath)}`);
output.writeln(` Tasks: ${output.dim(customTasks ? `${customTasks.length} custom` : `${getDefaultABTasks().length} default`)}`);
output.writeln();
output.writeln(output.dim('Running benchmark...'));
// Run the A/B benchmark
const report = await abBenchmark(configBContent, {
tasks: customTasks,
workDir,
});
if (jsonOutput) {
output.writeln(JSON.stringify({
configA: { label: configALabel, metrics: report.configA.metrics },
configB: { label: configBPath, metrics: report.configB.metrics },
compositeDelta: report.compositeDelta,
classDeltas: report.classDeltas,
categoryShift: report.categoryShift,
taskResults: {
configA: report.configA.taskResults.map(r => ({
taskId: r.taskId, taskClass: r.taskClass, passed: r.passed,
violations: r.violations.length, toolCalls: r.toolCalls,
})),
configB: report.configB.taskResults.map(r => ({
taskId: r.taskId, taskClass: r.taskClass, passed: r.passed,
violations: r.violations.length, toolCalls: r.toolCalls,
})),
},
}, null, 2));
return { success: true, data: report };
}
// Print formatted report
output.writeln(report.report);
output.writeln();
// Summary verdict
const delta = report.compositeDelta;
if (delta > 0.05) {
output.writeln(output.success(`Config B is better (+${delta} composite delta)`));
}
else if (delta < -0.05) {
output.writeln(output.error(`Config B is worse (${delta} composite delta)`));
}
else {
output.writeln(output.warning(`No significant difference (${delta} composite delta)`));
}
if (report.categoryShift) {
output.writeln(output.success('Category shift detected: 3+ task classes improved by 20%+'));
}
return { success: true, data: report };
}
catch (error) {
const msg = error instanceof Error ? error.message : String(error);
output.writeln(output.error(`A/B benchmark failed: ${msg}`));
return { success: false, message: msg };
}
},
};
// Main guidance command
export const guidanceCommand = {
name: 'guidance',
description: 'Guidance Control Plane - compile, retrieve, enforce, and optimize guidance rules',
aliases: ['guide', 'policy'],
subcommands: [
compileCommand,
retrieveCommand,
gatesCommand,
statusCommand,
optimizeCommand,
abTestCommand,
],
options: [],
examples: [
{ command: 'claude-flow guidance compile', description: 'Compile CLAUDE.md into policy bundle' },
{ command: 'claude-flow guidance retrieve -t "Fix auth bug"', description: 'Retrieve relevant guidance' },
{ command: 'claude-flow guidance gates -c "rm -rf /"', description: 'Check enforcement gates' },
{ command: 'claude-flow guidance status', description: 'Show control plane status' },
{ command: 'claude-flow guidance optimize', description: 'Analyze and optimize CLAUDE.md' },
{ command: 'claude-flow guidance ab-test', description: 'Run A/B behavioral comparison' },
],
action: async (ctx) => {
output.writeln();
output.writeln(output.bold('Guidance Control Plane'));
output.writeln(output.dim('─'.repeat(50)));
output.writeln();
output.writeln('Available subcommands:');
output.writeln(` ${output.bold('compile')} Compile CLAUDE.md into policy bundle`);
output.writeln(` ${output.bold('retrieve')} Retrieve task-relevant guidance shards`);
output.writeln(` ${output.bold('gates')} Evaluate enforcement gates`);
output.writeln(` ${output.bold('status')} Show control plane status`);
output.writeln(` ${output.bold('optimize')} Analyze and optimize CLAUDE.md`);
output.writeln(` ${output.bold('ab-test')} Run A/B behavioral comparison`);
output.writeln();
output.writeln(output.dim('Use claude-flow guidance <subcommand> --help for details'));
return { success: true };
},
};
export default guidanceCommand;
//# sourceMappingURL=guidance.js.map