sf-agent-framework
Version:
AI Agent Orchestration Framework for Salesforce Development - Two-phase architecture with 70% context reduction
1,391 lines (1,217 loc) ⢠61.2 kB
JavaScript
#!/usr/bin/env node
const { Command } = require('commander');
const path = require('path');
const { execSync } = require('child_process');
const { version } = require('../package.json');
const program = new Command();
program
.name('sf-agent')
.description('SF-Agent Framework CLI for building and managing agents')
.version(version);
program
.command('install')
.description('Install SF-Agent Framework agents and tools interactively')
.option('-f, --full', 'Install complete SF-Agent Framework')
.option('-d, --directory <path>', 'Installation directory')
.option(
'-i, --ide <ide...>',
'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, cline, gemini, github-copilot, other)'
)
.action(async (options) => {
try {
// Use child_process to run the installer - this is more reliable for npm packages
const installerPath = path.join(__dirname, 'installer', 'bin', 'sf-agent.js');
// Build the command
let cmd = `node "${installerPath}" install`;
if (options.full) cmd += ' --full';
if (options.directory) cmd += ` --directory "${options.directory}"`;
if (options.ide && options.ide.length > 0) {
cmd += ` --ide ${options.ide.join(' ')}`;
}
// Execute the installer
execSync(cmd, { stdio: 'inherit' });
} catch (error) {
console.error('Installation failed:', error.message);
process.exit(1);
}
});
// Story-based context engineering commands
program
.command('shard <document>')
.description('Shard planning document into story-sized pieces')
.option('-o, --output <dir>', 'Output directory', 'docs/sharded')
.option('-v, --verbose', 'Verbose output')
.action(async (document, options) => {
try {
const sharderPath = path.join(__dirname, 'document-sharder.js');
const cmd = `node "${sharderPath}" "${document}" -o "${options.output}"${options.verbose ? ' -v' : ''}`;
execSync(cmd, { stdio: 'inherit' });
} catch (error) {
console.error('Sharding failed:', error.message);
process.exit(1);
}
});
program
.command('story <action> [storyId]')
.description('Manage story queue and context')
.option('-d, --dir <directory>', 'Story directory', 'docs/stories')
.action(async (action, storyId, options) => {
try {
const queuePath = path.join(__dirname, 'story-queue-manager.js');
let cmd = `node "${queuePath}" ${action}`;
if (storyId) cmd += ` ${storyId}`;
if (options.dir) cmd += ` -d "${options.dir}"`;
execSync(cmd, { stdio: 'inherit' });
} catch (error) {
console.error('Story management failed:', error.message);
process.exit(1);
}
});
program
.command('validate-story <storyId>')
.description('Validate story has complete context for development')
.option('--fix', 'Apply auto-fix suggestions')
.option('--block', 'Block story if invalid')
.action(async (storyId, options) => {
console.log(`Validating story ${storyId}...`);
// Story validation logic would go here
console.log('Story validation complete');
});
program
.command('phase <phase>')
.description('Switch between planning and development phases')
.action(async (phase) => {
if (!['planning', 'development'].includes(phase)) {
console.error('Phase must be either "planning" or "development"');
process.exit(1);
}
console.log(`Switching to ${phase} phase...`);
console.log(`Context limit: ${phase === 'planning' ? '128k' : '32k'} tokens`);
console.log(`Agent mode: ${phase === 'planning' ? 'Rich' : 'Lean'}`);
});
program
.command('workflow [workflowId]')
.description('Start an interactive workflow session')
.option('-d, --dir <directory>', 'Workflow directory', 'sf-core/workflows')
.option(
'-o, --output <directory>',
'Output directory for session files',
'docs/workflow-sessions'
)
.option('-v, --verbose', 'Verbose output')
.action(async (workflowId, options) => {
try {
const InteractiveWorkflowPlanner = require('./lib/interactive-workflow-planner');
const planner = new InteractiveWorkflowPlanner(process.cwd());
if (!workflowId) {
// List available workflows
console.log('\nš Available Workflows:\n');
const workflows = await planner.listWorkflows();
if (workflows.length === 0) {
console.log('No workflows found. Creating default interactive workflow...');
workflowId = 'interactive-project-setup';
} else {
workflows.forEach((w) => console.log(` - ${w}`));
console.log('\nRun: sf-agent workflow <workflow-name> to start\n');
return;
}
}
// Start the interactive workflow
const result = await planner.startInteractiveWorkflow(workflowId);
// Display summary
console.log('\nā
Workflow Completed!\n');
console.log('Summary:', JSON.stringify(result, null, 2));
// Save session results if output directory specified
if (options.output) {
const fs = require('fs').promises;
const outputPath = path.join(options.output, `${workflowId}-${Date.now()}.json`);
await fs.mkdir(options.output, { recursive: true });
await fs.writeFile(outputPath, JSON.stringify(result, null, 2));
console.log(`\nSession saved to: ${outputPath}`);
}
} catch (error) {
console.error('Workflow execution failed:', error.message);
process.exit(1);
}
});
program
.command('handoff <action> [handoffId]')
.description('Manage agent handoffs (create, accept, complete, status)')
.option('--from <agent>', 'Source agent (for create)')
.option('--to <agent>', 'Target agent (for create)')
.option('--agent <agent>', 'Agent ID (for accept)')
.option('--artifacts <paths...>', 'Artifact paths')
.option('--notes <text>', 'Notes')
.option('--next <agent>', 'Next agent in chain (for complete)')
.action(async (action, handoffId, options) => {
try {
const AgentCollaborationProtocol = require('./lib/agent-collaboration-protocol');
const protocol = new AgentCollaborationProtocol(process.cwd());
await protocol.initialize();
switch (action) {
case 'create':
if (!options.from || !options.to) {
console.error('Error: --from and --to are required for create');
process.exit(1);
}
const handoff = await protocol.createHandoff({
from: options.from,
to: options.to,
artifacts: options.artifacts || [],
context: { notes: options.notes },
});
console.log(`\nā
Handoff created: ${handoff.id}`);
console.log(`From: ${handoff.from} ā To: ${handoff.to}`);
break;
case 'accept':
if (!handoffId || !options.agent) {
console.error('Error: handoffId and --agent are required for accept');
process.exit(1);
}
const accepted = await protocol.acceptHandoff(handoffId, options.agent);
console.log(`\nā
Handoff ${handoffId} accepted by ${options.agent}`);
console.log(`Artifacts loaded: ${accepted.artifacts.length}`);
break;
case 'complete':
if (!handoffId || !options.agent) {
console.error('Error: handoffId and --agent are required for complete');
process.exit(1);
}
const deliverables =
options.artifacts?.map((a) => ({ name: a, content: `Deliverable: ${a}` })) || [];
const completed = await protocol.completeHandoff(handoffId, options.agent, deliverables);
console.log(`\nā
Handoff ${handoffId} completed`);
console.log(`Deliverables: ${deliverables.length}`);
break;
case 'status':
if (handoffId) {
const status = protocol.getHandoffStatus(handoffId);
console.log(`\nHandoff ${handoffId}:`);
console.log(JSON.stringify(status, null, 2));
} else {
const handoffs = protocol.listActiveHandoffs(options.agent);
console.log(`\nActive Handoffs${options.agent ? ` for ${options.agent}` : ''}:`);
handoffs.forEach((h) => {
console.log(` - ${h.id}: ${h.from} ā ${h.to} (${h.status})`);
});
}
break;
case 'metrics':
const metrics = protocol.getCollaborationMetrics();
console.log('\nš Collaboration Metrics:');
console.log(JSON.stringify(metrics, null, 2));
break;
default:
console.error(`Unknown action: ${action}`);
console.log('Available actions: create, accept, complete, status, metrics');
process.exit(1);
}
} catch (error) {
console.error('Handoff operation failed:', error.message);
process.exit(1);
}
});
program
.command('update')
.description('Update existing SF-Agent installation')
.option('--force', 'Force update, overwriting modified files')
.option('--dry-run', 'Show what would be updated without making changes')
.action(async (options) => {
try {
// Use child_process to run the installer - this is more reliable for npm packages
const installerPath = path.join(__dirname, 'installer', 'bin', 'sf-agent.js');
// Build the command
let cmd = `node "${installerPath}" update`;
if (options.force) cmd += ' --force';
if (options.dryRun) cmd += ' --dry-run';
// Execute the installer
execSync(cmd, { stdio: 'inherit' });
} catch (error) {
console.error('Update failed:', error.message);
process.exit(1);
}
});
program
.command('build')
.description('Build web bundles for agents and teams')
.option('-a, --agents-only', 'Build only agent bundles')
.option('-t, --teams-only', 'Build only team bundles')
.option('--no-clean', 'Skip cleaning output directories')
.action(async (options) => {
const WebBuilder = require('./builders/web-builder');
// Use package root directory instead of current working directory
const packageRoot = path.resolve(__dirname, '..');
const builder = new WebBuilder({
rootDir: packageRoot,
});
try {
if (options.clean) {
console.log('Cleaning output directories...');
await builder.cleanOutputDirs();
}
if (!options.teamsOnly) {
console.log('Building agent bundles...');
await builder.buildAgents();
}
if (!options.agentsOnly) {
console.log('Building team bundles...');
await builder.buildTeams();
}
console.log('Build completed successfully!');
} catch (error) {
console.error('Build failed:', error.message);
process.exit(1);
}
});
program
.command('list:agents')
.description('List all available agents')
.action(async () => {
const WebBuilder = require('./builders/web-builder');
// Use package root directory instead of current working directory
const packageRoot = path.resolve(__dirname, '..');
const builder = new WebBuilder({ rootDir: packageRoot });
const agents = await builder.resolver.listAgents();
console.log('Available agents:');
agents.forEach((agent) => console.log(` - ${agent}`));
});
program
.command('validate')
.description('Validate agent and team configurations')
.action(async () => {
const WebBuilder = require('./builders/web-builder');
// Use package root directory instead of current working directory
const packageRoot = path.resolve(__dirname, '..');
const builder = new WebBuilder({ rootDir: packageRoot });
try {
// Validate by attempting to build all agents and teams
const agents = await builder.resolver.listAgents();
const teams = await builder.resolver.listTeams();
console.log('Validating agents...');
for (const agent of agents) {
await builder.resolver.resolveAgentDependencies(agent);
console.log(` ā ${agent}`);
}
console.log('\nValidating teams...');
for (const team of teams) {
await builder.resolver.resolveTeamDependencies(team);
console.log(` ā ${team}`);
}
console.log('\nAll configurations are valid!');
} catch (error) {
console.error('Validation failed:', error.message);
process.exit(1);
}
});
program
.command('upgrade')
.description('Upgrade a SF-Agent-Method V3 project to V4')
.option('-p, --project <path>', 'Path to V3 project (defaults to current directory)')
.option('--dry-run', 'Show what would be changed without making changes')
.option('--no-backup', 'Skip creating backup (not recommended)')
.action(async (options) => {
const V3ToV4Upgrader = require('./upgraders/v3-to-v4-upgrader');
const upgrader = new V3ToV4Upgrader();
await upgrader.upgrade({
projectPath: options.project,
dryRun: options.dryRun,
backup: options.backup,
});
});
program
.command('mcp:setup')
.description('Set up MCP (Model Context Protocol) for your IDE')
.option(
'-i, --ide <ide>',
'Specific IDE to configure (cursor, claude-code, windsurf, github-copilot, vscode)'
)
.option('-a, --all', 'Configure MCP for all supported IDEs')
.option('-u, --universal', 'Create universal MCP configuration file')
.action(async (options) => {
const IdeSetup = require('./installer/lib/ide-setup');
const projectDir = process.cwd();
try {
if (options.universal || options.all) {
console.log('Creating universal MCP configuration...');
await IdeSetup.createUniversalMCPConfig(projectDir);
}
if (options.all) {
console.log('\nConfiguring MCP for all supported IDEs...');
const ides = ['cursor', 'claude-code', 'windsurf', 'github-copilot', 'vscode'];
for (const ide of ides) {
console.log(`\nConfiguring ${ide}...`);
switch (ide) {
case 'cursor':
await IdeSetup.setupCursorMCP(projectDir);
break;
case 'claude-code':
await IdeSetup.setupClaudeCodeMCP(projectDir);
break;
case 'windsurf':
await IdeSetup.setupWindsurfMCP(projectDir);
break;
case 'github-copilot':
await IdeSetup.setupGitHubCopilotMCP(projectDir);
break;
case 'vscode':
await IdeSetup.setupVSCodeMCP(projectDir);
break;
}
}
} else if (options.ide) {
console.log(`Configuring MCP for ${options.ide}...`);
switch (options.ide) {
case 'cursor':
await IdeSetup.setupCursorMCP(projectDir);
break;
case 'claude-code':
await IdeSetup.setupClaudeCodeMCP(projectDir);
break;
case 'windsurf':
await IdeSetup.setupWindsurfMCP(projectDir);
break;
case 'github-copilot':
await IdeSetup.setupGitHubCopilotMCP(projectDir);
break;
case 'vscode':
await IdeSetup.setupVSCodeMCP(projectDir);
break;
default:
console.error(`Unsupported IDE: ${options.ide}`);
console.log('Supported IDEs: cursor, claude-code, windsurf, github-copilot, vscode');
process.exit(1);
}
} else {
console.log('Please specify an IDE with --ide or use --all for all IDEs');
console.log('Example: npm run mcp:setup -- --ide cursor');
console.log('Example: npm run mcp:setup -- --all');
}
console.log('\nā MCP setup complete!');
console.log('See sf-core/mcp/MCP-IDE-GUIDE.md for usage instructions');
} catch (error) {
console.error('MCP setup failed:', error.message);
process.exit(1);
}
});
program
.command('mcp:list')
.description('List all available MCP servers in the project')
.action(async () => {
const IdeSetup = require('./installer/lib/ide-setup');
const projectDir = process.cwd();
const servers = await IdeSetup.discoverMCPServers(projectDir);
if (servers.length === 0) {
console.log('No MCP servers found in the project.');
console.log('To add MCP servers, create .js files in:');
console.log(' - .sf-core/mcp/servers/');
return;
}
console.log('Available MCP servers:');
servers.forEach((server) => {
console.log(`\n ${server.packName}/${server.serverName}`);
console.log(` Path: ${server.relativePath}`);
});
console.log(`\nTotal: ${servers.length} MCP servers found`);
});
program
.command('metrics:record <eventName> [data]')
.description('Record a metric event')
.action((eventName, data) => {
const { recordEvent } = require('./metrics-tracker');
let parsedData = null;
if (data) {
try {
parsedData = JSON.parse(data);
} catch (error) {
console.error('Invalid JSON data:', error.message);
process.exit(1);
}
}
recordEvent(eventName, parsedData);
});
program
.command('metrics:report')
.description('Generate a metrics report')
.action(() => {
const { generateReport } = require('./metrics-tracker');
generateReport();
});
program
.command('visualize:workflow <file>')
.description('Generate a Mermaid diagram for a workflow YAML file')
.action((file) => {
const visualizeWorkflow = require('./workflow-visualizer');
visualizeWorkflow(file);
});
program
.command('create-agent')
.description('Create a new agent interactively')
.action(() => {
try {
execSync('node tools/agent-creator.js', { stdio: 'inherit' });
} catch (error) {
console.error('Agent creation failed:', error.message);
process.exit(1);
}
});
program
.command('flatten')
.description('Flatten the codebase into a single XML file')
.action(() => {
try {
console.log('Flattening codebase...');
execSync('node tools/codebase-flattener.js', { stdio: 'inherit' });
console.log('Codebase flattened successfully!');
} catch (error) {
console.error('Codebase flattening failed:', error.message);
process.exit(1);
}
});
// Memory system commands
program
.command('memory <action>')
.description('Manage context memory system (init, store, retrieve, stats, patterns)')
.option('--query <text>', 'Query for retrieval')
.option('--type <type>', 'Context type')
.option('--agent <agent>', 'Agent name')
.option('--content <text>', 'Content to store')
.option('--limit <number>', 'Limit results', '10')
.action(async (action, options) => {
try {
const ContextMemorySystem = require('./lib/context-memory-system');
const memory = new ContextMemorySystem(process.cwd());
switch (action) {
case 'init':
await memory.initialize();
console.log('ā
Memory system initialized');
console.log(`Session ID: ${memory.currentSession.id}`);
break;
case 'store':
await memory.initialize();
const contextId = await memory.storeContext({
type: options.type || 'general',
agent: options.agent || 'user',
content: options.content || 'Test context',
metadata: {},
});
console.log(`ā
Context stored: ${contextId}`);
break;
case 'retrieve':
await memory.initialize();
const contexts = await memory.retrieveContext(options.query || '', {
limit: parseInt(options.limit),
});
console.log(`\nš Retrieved ${contexts.length} contexts:`);
contexts.forEach((c, i) => {
console.log(`\n${i + 1}. ${c.id} (${c.type})`);
console.log(` Agent: ${c.agent}`);
console.log(` Importance: ${c.importance}`);
});
break;
case 'stats':
await memory.initialize();
const stats = memory.getMemoryStats();
console.log('\nš Memory Statistics:');
console.log(JSON.stringify(stats, null, 2));
break;
case 'patterns':
await memory.initialize();
const patterns = memory.getLearnedPatterns(options.type);
console.log(`\nš Learned Patterns${options.type ? ` (${options.type})` : ''}:`);
patterns.forEach((p, i) => {
console.log(`\n${i + 1}. ${p.pattern.type}`);
console.log(` Frequency: ${p.frequency}`);
console.log(` First seen: ${p.first_seen}`);
});
break;
case 'end':
await memory.initialize();
const session = await memory.endSession();
console.log(`ā
Session ended: ${session.id}`);
break;
default:
console.error(`Unknown action: ${action}`);
console.log('Available actions: init, store, retrieve, stats, patterns, end');
}
} catch (error) {
console.error('Memory operation failed:', error.message);
process.exit(1);
}
});
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// WORKFLOW TRACK COMMANDS (v4.5.0)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
program
.command('workflow:init')
.description('Initialize a workflow with intelligent track selection (Quick/Balanced/Enterprise)')
.option('-s, --skip-analysis', 'Skip complexity analysis and choose track manually')
.option('-t, --track <track>', 'Force specific track (quick, balanced, enterprise)')
.option('--non-interactive', 'Run in non-interactive mode with defaults')
.action(async (options) => {
try {
const WorkflowInitializer = require('../sf-core/workflows/workflow-initializer');
const initializer = new WorkflowInitializer(process.cwd());
if (options.track) {
// Force specific track
console.log(`\nš Initializing ${options.track.toUpperCase()} workflow...\n`);
const result = await initializer.initializeWorkflow(options.track, {
skipAnalysis: true,
nonInteractive: options.nonInteractive,
});
console.log(`\nā
Workflow initialized: ${result.workflowId}`);
console.log(`Track: ${result.track}`);
console.log(`Session file: ${result.sessionFile}`);
} else {
// Interactive initialization with analysis
const result = await initializer.initialize({
skipAnalysis: options.skipAnalysis,
nonInteractive: options.nonInteractive,
});
console.log('\nā
Workflow initialized successfully!');
console.log(`\nWorkflow ID: ${result.workflowId}`);
console.log(`Track: ${result.track}`);
console.log(`Complexity: ${result.complexity.score}/100 (${result.complexity.category})`);
console.log(`Estimated time: ${result.estimatedTime}`);
console.log(`Session file: ${result.sessionFile}\n`);
}
} catch (error) {
console.error('ā Workflow initialization failed:', error.message);
process.exit(1);
}
});
program
.command('workflow:status')
.description('Check current workflow status and progress')
.option('-v, --verbose', 'Show detailed status')
.action(async (options) => {
try {
const WorkflowInitializer = require('../sf-core/workflows/workflow-initializer');
const initializer = new WorkflowInitializer(process.cwd());
const status = await initializer.getCurrentWorkflowStatus();
if (!status) {
console.log('\nš No active workflow found.');
console.log('Run "sf-agent workflow:init" to start a new workflow.\n');
return;
}
console.log('\nš Current Workflow Status:\n');
console.log(`Workflow ID: ${status.workflowId}`);
console.log(`Track: ${status.track} (${status.trackName})`);
console.log(`Phase: ${status.currentPhase}`);
console.log(`Progress: ${status.progress}%`);
console.log(`Started: ${status.startTime}`);
if (options.verbose && status.phases) {
console.log('\nPhases:');
status.phases.forEach((phase) => {
const icon = phase.completed ? 'ā
' : phase.active ? 'ā³' : 'ā';
console.log(` ${icon} ${phase.name} ${phase.completed ? '(completed)' : ''}`);
});
}
if (status.nextSteps && status.nextSteps.length > 0) {
console.log('\nNext Steps:');
status.nextSteps.forEach((step, i) => {
console.log(` ${i + 1}. ${step}`);
});
}
console.log('');
} catch (error) {
console.error('ā Failed to get workflow status:', error.message);
process.exit(1);
}
});
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// CONFIGURATION MANAGEMENT COMMANDS (v4.5.0)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
program
.command('config:init')
.description('Initialize update-safe user configuration')
.action(async () => {
try {
const UpdateSafeConfig = require('../sf-core/utils/update-safe-config');
const configManager = new UpdateSafeConfig(process.cwd());
console.log('\nš§ Initializing update-safe configuration...\n');
await configManager.initialize();
console.log('\nā
Configuration initialized successfully!');
console.log('\nUser config location: .sf-agent/user-config.json');
console.log('Preferences location: .sf-agent/preferences.json');
console.log('Agent overrides: .sf-agent/agent-overrides/\n');
} catch (error) {
console.error('ā Configuration initialization failed:', error.message);
process.exit(1);
}
});
program
.command('config:upgrade')
.description('Upgrade configuration while preserving user customizations')
.option('--version <version>', 'Target framework version', '4.5.0')
.option('--no-backup', 'Skip backup (not recommended)')
.action(async (options) => {
try {
const UpdateSafeConfig = require('../sf-core/utils/update-safe-config');
const configManager = new UpdateSafeConfig(process.cwd());
console.log(`\nš Upgrading configuration to v${options.version}...\n`);
const result = await configManager.preserveUserSettings(options.version);
console.log('\nā
Configuration upgraded successfully!');
if (result.backupLocation) {
console.log(`Backup location: ${result.backupLocation}`);
}
console.log(`Framework version: ${result.version}\n`);
} catch (error) {
console.error('ā Configuration upgrade failed:', error.message);
process.exit(1);
}
});
program
.command('config:validate')
.description('Validate user configuration')
.action(async () => {
try {
const UpdateSafeConfig = require('../sf-core/utils/update-safe-config');
const configManager = new UpdateSafeConfig(process.cwd());
console.log('\nš Validating configuration...\n');
const validation = await configManager.validateUserConfig();
if (validation.valid) {
console.log('ā
Configuration is valid!\n');
} else {
console.log('ā Configuration has issues:\n');
validation.issues.forEach((issue) => {
console.log(` ā ${issue}`);
});
}
if (validation.warnings.length > 0) {
console.log('\nā ļø Warnings:');
validation.warnings.forEach((warning) => {
console.log(` ā ļø ${warning}`);
});
}
console.log('');
} catch (error) {
console.error('ā Configuration validation failed:', error.message);
process.exit(1);
}
});
program
.command('config:reset')
.description('Reset configuration to defaults')
.option('--no-backup', 'Skip backup before reset (not recommended)')
.action(async (options) => {
try {
const UpdateSafeConfig = require('../sf-core/utils/update-safe-config');
const configManager = new UpdateSafeConfig(process.cwd());
console.log('\nā ļø This will reset your configuration to defaults.\n');
await configManager.resetToDefaults({ backupFirst: options.backup });
console.log('ā
Configuration reset complete!\n');
} catch (error) {
console.error('ā Configuration reset failed:', error.message);
process.exit(1);
}
});
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// AGENT ROLE MATRIX COMMANDS (v4.5.0)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
program
.command('agents:roles')
.description('Display agent role matrix and collaboration patterns')
.option(
'-c, --category <category>',
'Filter by category (product, architecture, development, quality, deployment, data, leadership)'
)
.option('-p, --pattern <pattern>', 'Show specific collaboration pattern')
.action(async (options) => {
try {
const fs = require('fs-extra');
const yaml = require('js-yaml');
const rolePath = path.join(process.cwd(), 'sf-core', 'config', 'agent-role-matrix.yaml');
const roleMatrix = yaml.load(await fs.readFile(rolePath, 'utf8'));
console.log('\nš¤ Agent Role Matrix\n');
console.log(`Framework Version: ${roleMatrix.version}`);
console.log(`Last Updated: ${roleMatrix.last_updated}\n`);
if (options.category) {
// Show specific category
const category = roleMatrix.agent_roles[options.category];
if (!category) {
console.error(`ā Category not found: ${options.category}`);
console.log('\nAvailable categories:', Object.keys(roleMatrix.agent_roles).join(', '));
process.exit(1);
}
console.log(`Category: ${options.category.toUpperCase()}\n`);
category.primary_agents.forEach((agent) => {
console.log(` ⢠${agent.id}`);
console.log(` Expertise: ${agent.expertise}`);
console.log(` Context: ${agent.context}`);
console.log(` Responsibilities: ${agent.responsibilities.join(', ')}`);
console.log('');
});
} else if (options.pattern) {
// Show collaboration pattern
const pattern = roleMatrix.collaboration_patterns[options.pattern];
if (!pattern) {
console.error(`ā Pattern not found: ${options.pattern}`);
console.log(
'\nAvailable patterns:',
Object.keys(roleMatrix.collaboration_patterns).join(', ')
);
process.exit(1);
}
console.log(`Collaboration Pattern: ${options.pattern}\n`);
console.log(`Complexity: ${pattern.complexity}`);
console.log(`Duration: ${pattern.duration}\n`);
console.log('Sequence:');
pattern.sequence.forEach((step, i) => {
console.log(` ${i + 1}. ${step.phase}`);
console.log(` Agents: ${step.agents.join(', ')}`);
if (step.deliverables) {
console.log(` Deliverables: ${step.deliverables.join(', ')}`);
}
});
console.log('');
} else {
// Show overview
console.log('Agent Categories:\n');
Object.keys(roleMatrix.agent_roles).forEach((category) => {
const count = roleMatrix.agent_roles[category].primary_agents.length;
console.log(` ⢠${category}: ${count} agents`);
});
console.log('\nCollaboration Patterns:\n');
Object.keys(roleMatrix.collaboration_patterns).forEach((pattern) => {
const p = roleMatrix.collaboration_patterns[pattern];
console.log(` ⢠${pattern} (${p.complexity}, ${p.duration})`);
});
console.log('\nš” Use --category <name> to see agents in a category');
console.log('š” Use --pattern <name> to see a collaboration pattern\n');
}
} catch (error) {
console.error('ā Failed to load agent role matrix:', error.message);
process.exit(1);
}
});
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// PREFERENCE MANAGEMENT COMMANDS (v4.5.0)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
program
.command('preference:get [category] [key]')
.description('Get preference value(s)')
.action(async (category, key) => {
try {
const UpdateSafeConfig = require('../sf-core/utils/update-safe-config');
const configManager = new UpdateSafeConfig(process.cwd());
const preferences = await configManager.loadPreferences();
if (!preferences) {
console.log('\nā ļø No preferences found. Run "sf-agent config:init" first.\n');
return;
}
if (!category) {
// Show all preferences
console.log('\nāļø All Preferences:\n');
console.log(JSON.stringify(preferences, null, 2));
console.log('');
} else if (!key) {
// Show category
if (!preferences[category]) {
console.error(`ā Category not found: ${category}`);
console.log('\nAvailable categories:', Object.keys(preferences).join(', '));
process.exit(1);
}
console.log(`\nāļø ${category} Preferences:\n`);
console.log(JSON.stringify(preferences[category], null, 2));
console.log('');
} else {
// Show specific preference
if (!preferences[category] || preferences[category][key] === undefined) {
console.error(`ā Preference not found: ${category}.${key}`);
process.exit(1);
}
console.log(`\n${category}.${key} = ${JSON.stringify(preferences[category][key])}\n`);
}
} catch (error) {
console.error('ā Failed to get preference:', error.message);
process.exit(1);
}
});
program
.command('preference:set <category> <key> <value>')
.description('Set preference value')
.action(async (category, key, value) => {
try {
const UpdateSafeConfig = require('../sf-core/utils/update-safe-config');
const configManager = new UpdateSafeConfig(process.cwd());
// Parse value (handle boolean, number, string)
let parsedValue = value;
if (value === 'true') parsedValue = true;
else if (value === 'false') parsedValue = false;
else if (!isNaN(value)) parsedValue = parseFloat(value);
await configManager.updatePreference(category, key, parsedValue);
} catch (error) {
console.error('ā Failed to set preference:', error.message);
process.exit(1);
}
});
program
.command('agent:override <agentId>')
.description('Create or edit agent override file')
.option('-c, --content <content>', 'Override content')
.option('-f, --file <file>', 'Load content from file')
.action(async (agentId, options) => {
try {
const UpdateSafeConfig = require('../sf-core/utils/update-safe-config');
const fs = require('fs-extra');
const configManager = new UpdateSafeConfig(process.cwd());
let content = '';
if (options.file) {
content = await fs.readFile(options.file, 'utf8');
} else if (options.content) {
content = options.content;
} else {
console.log('\nš Creating agent override template...\n');
content = `# ${agentId} Override
This is a custom override for the ${agentId} agent.
## Custom Instructions
Add your custom instructions here...
## Modified Capabilities
List any capability modifications...
## Notes
- This override will be preserved through framework updates
- Override last modified: ${new Date().toISOString()}
`;
}
await configManager.createAgentOverride(agentId, content);
console.log(`\nš” Edit at: .sf-agent/agent-overrides/${agentId}.md\n`);
} catch (error) {
console.error('ā Failed to create agent override:', error.message);
process.exit(1);
}
});
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// PHASE 2: SCALE-ADAPTIVE FEATURES (v4.5.0)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Document Sharding Commands
program
.command('shard:create <documentPath>')
.description('Shard a document into context-aware chunks (90% token savings)')
.option(
'-s, --strategy <strategy>',
'Sharding strategy (heading-based, function-based, section-based, paragraph-based)'
)
.option(
'-p, --priority <priority>',
'Default priority (critical, high, medium, low, archive)',
'medium'
)
.action(async (documentPath, options) => {
try {
const DocumentSharder = require('../sf-core/utils/document-sharder');
const sharder = new DocumentSharder(process.cwd());
await sharder.initialize();
const result = await sharder.shardDocument(documentPath, {
strategy: options.strategy,
priority: options.priority,
});
console.log('\nā
Document sharded successfully!\n');
console.log(`Shards created: ${result.shards.length}`);
console.log(`Original size: ${result.originalTokens.toLocaleString()} tokens`);
console.log(
`Token savings: ${result.savingsPercentage}% (${result.tokensSaved.toLocaleString()} tokens)\n`
);
} catch (error) {
console.error('ā Sharding failed:', error.message);
process.exit(1);
}
});
program
.command('shard:load <documentPath>')
.description('Load shards by priority')
.option('-p, --priority <level>', 'Max priority level to load (1-5)', '3')
.option('-t, --tokens <max>', 'Maximum tokens to load', '10000')
.action(async (documentPath, options) => {
try {
const DocumentSharder = require('../sf-core/utils/document-sharder');
const sharder = new DocumentSharder(process.cwd());
await sharder.initialize();
const result = await sharder.loadShardsByPriority(
documentPath,
parseInt(options.priority),
parseInt(options.tokens)
);
console.log('\nš Shards loaded:\n');
console.log(`Loaded: ${result.loadedCount} of ${result.totalAvailable} shards`);
console.log(`Tokens: ${result.tokensLoaded.toLocaleString()}\n`);
if (result.shards.length > 0) {
console.log('Loaded shards:');
result.shards.forEach((shard, i) => {
console.log(` ${i + 1}. Priority ${shard.priority} - ${shard.tokens} tokens`);
});
console.log('');
}
} catch (error) {
console.error('ā Load failed:', error.message);
process.exit(1);
}
});
program
.command('shard:stats')
.description('Show sharding statistics and savings')
.action(async () => {
try {
const DocumentSharder = require('../sf-core/utils/document-sharder');
const sharder = new DocumentSharder(process.cwd());
await sharder.initialize();
const stats = await sharder.getStats();
console.log('\nš Sharding Statistics\n');
console.log(`Total documents: ${stats.totalDocuments}`);
console.log(`Total shards: ${stats.totalShards}`);
console.log(`Original tokens: ${stats.totalOriginalTokens.toLocaleString()}`);
console.log(`Typical load tokens: ${stats.totalShardedTokens.toLocaleString()}`);
console.log(`Token savings: ${stats.savingsPercentage}%\n`);
if (stats.documents.length > 0) {
console.log('Sharded documents:');
stats.documents.forEach((doc, i) => {
console.log(` ${i + 1}. ${doc.path}`);
console.log(` Shards: ${doc.shardCount}`);
console.log(` Original: ${doc.originalTokens.toLocaleString()} tokens`);
});
console.log('');
}
} catch (error) {
console.error('ā Failed to get stats:', error.message);
process.exit(1);
}
});
program
.command('shard:cleanup <documentPath>')
.description('Remove shards for a document')
.action(async (documentPath) => {
try {
const DocumentSharder = require('../sf-core/utils/document-sharder');
const sharder = new DocumentSharder(process.cwd());
await sharder.initialize();
await sharder.cleanup(documentPath);
console.log('ā
Shards cleaned up successfully\n');
} catch (error) {
console.error('ā Cleanup failed:', error.message);
process.exit(1);
}
});
// Context Budget Commands
program
.command('budget:init <model>')
.description(
'Initialize context budget for model (claude_opus_4_5, gpt_5_1_instant, gemini_3_pro)'
)
.option('-p, --phase <type>', 'Phase type (standard, planning, coding)', 'standard')
.action(async (model, options) => {
try {
const ContextBudgetManager = require('../sf-core/utils/context-budget-manager');
const budgetManager = new ContextBudgetManager(process.cwd());
await budgetManager.initialize();
const budget = budgetManager.allocateBudget(model, options.phase);
console.log('ā
Budget initialized successfully\n');
} catch (error) {
console.error('ā Budget initialization failed:', error.message);
process.exit(1);
}
});
program
.command('budget:status')
.description('Check current budget status and usage')
.action(async () => {
try {
const ContextBudgetManager = require('../sf-core/utils/context-budget-manager');
const budgetManager = new ContextBudgetManager(process.cwd());
await budgetManager.initialize();
const status = budgetManager.checkBudgetStatus();
const summary = budgetManager.getLoadedContentSummary();
console.log('\nš° Context Budget Status\n');
console.log(`Status: ${status.status.toUpperCase()}`);
console.log(`Usage: ${status.usagePercent}%`);
console.log(`Allocated: ${status.allocated.toLocaleString()} tokens`);
console.log(`Used: ${status.used.toLocaleString()} tokens`);
console.log(`Available: ${status.available.toLocaleString()} tokens\n`);
console.log('Loaded content:');
console.log(` Total items: ${summary.total.count}`);
console.log(
` Critical: ${summary.byPriority.critical.count} items (${summary.byPriority.critical.tokens.toLocaleString()} tokens)`
);
console.log(
` High: ${summary.byPriority.high.count} items (${summary.byPriority.high.tokens.toLocaleString()} tokens)`
);
console.log(
` Medium: ${summary.byPriority.medium.count} items (${summary.byPriority.medium.tokens.toLocaleString()} tokens)`
);
console.log(
` Low: ${summary.byPriority.low.count} items (${summary.byPriority.low.tokens.toLocaleString()} tokens)\n`
);
} catch (error) {
console.error('ā Failed to get budget status:', error.message);
process.exit(1);
}
});
program
.command('budget:optimize')
.description('Optimize context to free up budget')
.option(
'-s, --strategy <strategy>',
'Optimization strategy (auto, remove-low-priority, shard-large)',
'auto'
)
.action(async (options) => {
try {
const ContextBudgetManager = require('../sf-core/utils/context-budget-manager');
const budgetManager = new ContextBudgetManager(process.cwd());
await budgetManager.initialize();
const result = await budgetManager.optimizeContext(options.strategy);
console.log('\nā
Context optimized!\n');
result.optimizations.forEach((opt) => {
console.log(` ⢠${opt.action}`);
if (opt.tokensFreed) {
console.log(` Tokens freed: ${opt.tokensFreed.toLocaleString()}`);
}
if (opt.files) {
console.log(` Files to shard: ${opt.files.length}`);
}
});
console.log(`\nNew status: ${result.newStatus.status.toUpperCase()}`);
console.log(`Available: ${result.newStatus.available.toLocaleString()} tokens\n`);
} catch (error) {
console.error('ā Optimization failed:', error.message);
process.exit(1);
}
});
program
.command('budget:recommend')
.description('Get budget optimization recommendations')
.action(async () => {
try {
const ContextBudgetManager = require('../sf-core/utils/context-budget-manager');
const budgetManager = new ContextBudgetManager(process.cwd());
await budgetManager.initialize();
const result = await budgetManager.getRecommendations();
console.log('\nš” Budget Recommendations\n');
console.log(`Current status: ${result.status.toUpperCase()}\n`);
if (result.recommendations.length === 0) {
console.log('No recommendations - budget is healthy!\n');
return;
}
result.recommendations.forEach((rec, i) => {
console.log(`${i + 1}. [${rec.priority.toUpperCase()}] ${rec.action}`);
console.log(` ${rec.description}`);
console.log(` Impact: ${rec.impact}`);
if (rec.commands) {
console.log(` Actions:`);
rec.commands.forEach((cmd) => console.log(` - ${cmd}`));
}
console.log('');
});
} catch (error) {
console.error('ā Failed to get recommendations:', error.message);
process.exit(1);
}
});
// Scale-Adaptive Workflow Commands
program
.command('adaptive:start <track>')
.description('Start scale-adaptive workflow')
.option('-c, --complexity <score>', 'Project complexity (0-100)', '50')
.option('--token-budget <tokens>', 'Token budget override')
.option('--time-budget <minutes>', 'Time budget override')
.action(async (track, options) => {
try {
const ScaleAdaptiveEngine = require('../sf-core/workflows/scale-adaptive-engine');
const engine = new ScaleAdaptiveEngine(process.cwd());
await engine.initialize();
const projectInfo = {
complexity: parseInt(options.complexity),
requiresHighQuality: options.quality === 'high',
};
const budgets = {};
if (options.tokenBudget) budgets.tokens = parseInt(options.tokenBudget);
if (options.timeBudget) budgets.time = parseInt(options.timeBudget);
const session = await engine.startWorkflow(track, projectInfo, budgets);
console.log('ā
Adaptive workflow started successfully!\n');
} catch (error) {
console.error('ā Failed to start workflow:', error.message);
process.exit(1);
}
});
program
.command('adaptive:status')
.description('Get scale-adaptive workflow status')
.action(async () => {
try {
const ScaleAdaptiveEngine = require('../sf-core/workflows/scale-adaptive-engine');
const engine = new ScaleAdaptiveEngine(process.cwd());
const metrics = engine.getMetrics();
console.log('\nā” Scale-Adaptive Workflow Status\n');
console.log(`Current track: ${metrics.currentTrack}`);
console.log(`Current phase: ${metrics.currentPhase}`);
console.log(`Adaptations: ${metrics.adaptations}\n`);
console.log('Budget status:');
console.log(` Tokens: ${metrics.budgets.tokens.percentUsed.toFixed(1)}% used`);
console.log(` Time: ${metrics.budgets.time.percentUsed.toFixed(1)}% used`);
console.log(` Cost: ${metrics.budgets.cost.percentUsed.toFixed(1)}% used\n`);
if (metrics.adaptationHistory.length > 0) {
console.log('Recent adaptations:');
metrics.adaptationHistory.slice(-5).forEach((adapt, i) => {
console.log(` ${i + 1}. ${adapt.reason} ā ${adapt.action}`);
if (adapt.toTrack) {
console.log(` Track: ${adapt.fromTrack} ā ${adapt.toTrack}`);
}
});
console.log('');
}
} catch (error) {
console.error('ā Failed to get status:', error.message);
process.exit(1);
}
});
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// PHASE 3: ADVANCED FEATURES (v4.5.0)
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
// Reflection Engine Commands
program
.command('reflect <output>')
.description('Run reflection on agent output (self-correction)')
.option('-a, --agent <agent>', 'Agent name', 'unknown')
.option('-t, --threshold <score>', 'Quality threshold (0-1)', '0.8')
.option('--max-iterations <num>', 'Max reflection iterations', '3')
.action(async (output, options) => {
try {
const ReflectionEngine = require('../sf-core/agents/reflection-engine');
const engine = new ReflectionEngine(process.cwd());
await engine.initialize();
const result = await engine.reflect(output, {
agent: options.agent,
threshold: parseFloat(options.threshold),
maxIterations: parseInt(options.maxIterations),
});
if (result.success) {
console.log(`\nā
Reflection completed successfully!`);
console.log(`Final score: ${(result.score * 100).toFixed(1)}%`);
console.log(`Iterations: ${result.iterations}\n`);
} else {
console.log(`\nā ļø Reflection completed with issues`);
console.log(`Score: ${(result.score * 100).toFixed(1)}%`);
console.log(`Pending improvements: ${result.improvements?.length || 0}\n`);
}
} catch (error) {
console.error('ā Reflection failed:', error.message);
process.exit(1);
}
});
program
.command('reflect:history')
.description('View reflection history')
.option('-l, --limit <num>', 'Limit results', '10')
.action(async (options) => {
try {
const ReflectionEngine = require('../sf-core/agents/reflection-engine');
const engine = new ReflectionEngine(process.cwd());
await engine.initialize();
const history = engine.getHistory(parseInt(options.limit));
if (history.length === 0) {
console.log('\nNo reflection history found.\n');
return;
}
console.log('\nš Reflection History\n');
history.forEach((reflection, i) => {
console.log(`${i + 1}. ${reflection.id}`);
console.log(` Agent: ${reflection.agent}`);
console.log(` Score: ${(reflection.finalScore * 100).toFixed(1)