wifi-densepose/vendor/sublinear-time-solver/npx/goalie/src/cli.ts

758 lines
26 KiB
JavaScript

#!/usr/bin/env node
/**
* GOAP MCP CLI
* Command-line interface for the GOAP MCP server
*/
import { Command } from 'commander';
import { GoapMCPServer } from './mcp/server.js';
import { SearchResult } from './core/types.js';
import dotenv from 'dotenv';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
// Load environment variables
dotenv.config();
// Get package.json version
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
const program = new Command();
program
.name('goalie')
.description('AI-powered research assistant using Goal-Oriented Action Planning')
.version(packageJson.version);
// Start MCP Server Command
program
.command('start')
.description('Start the MCP server')
.option('--verbose', 'Enable verbose logging')
.option('--plugins <paths>', 'Comma-separated paths to external plugins')
.option('--extensions <paths>', 'Comma-separated paths to external extensions')
.option('--port <number>', 'Port to run HTTP server on (if not stdio)')
.action(async (options) => {
try {
// Set environment variables from options
if (options.plugins) {
process.env.GOAP_PLUGINS = options.plugins;
}
if (options.extensions) {
process.env.GOAP_EXTENSIONS = options.extensions;
}
if (options.verbose) {
console.error('🔧 Verbose logging enabled');
console.error('🌐 Environment:');
console.error(` • Perplexity API Key: ${process.env.PERPLEXITY_API_KEY ? '✅ Set' : '❌ Missing'}`);
console.error(` • Plugins: ${process.env.GOAP_PLUGINS || 'None'}`);
console.error(` • Extensions: ${process.env.GOAP_EXTENSIONS || 'None'}`);
}
// Validate required environment variables
if (!process.env.PERPLEXITY_API_KEY) {
console.error('❌ ERROR: PERPLEXITY_API_KEY environment variable is required');
console.error('💡 Get your API key from: https://www.perplexity.ai/settings/api');
process.exit(1);
}
const server = new GoapMCPServer();
await server.initialize();
await server.run();
} catch (error) {
console.error('💥 Failed to start GOAP MCP server:', error);
process.exit(1);
}
});
// Main Search Command (goap.search equivalent)
program
.command('search <query>')
.description('Execute intelligent search using GOAP planning')
.option('-d, --domains <domains>', 'Domain restrictions (comma-separated, e.g., edu,gov)')
.option('-r, --recency <recency>', 'Recency filter (hour|day|week|month|year)')
.option('-m, --mode <mode>', 'Search mode (web|academic)', 'web')
.option('--max-results <number>', 'Maximum search results (1-20)', '10')
.option('--model <model>', 'Perplexity model (sonar|sonar-pro|sonar-deep-research)', 'sonar-pro')
.option('--no-reasoning', 'Disable Advanced Reasoning Engine')
.option('--timeout <seconds>', 'Planning timeout in seconds', '30')
.option('--output <path>', 'Output directory', '.research')
.option('--format <format>', 'Output format (json|markdown|both)', 'both')
.option('--no-save', 'Do not save to file')
.option('--no-subfolder', 'Do not create query-based subfolder')
.option('--page <number>', 'Page number for pagination', '1')
.option('--page-size <number>', 'Items per page (5-50)', '10')
.option('--verify', 'Enable Ed25519 signature verification')
.option('--strict-verify', 'Require all citations to be signed')
.option('--sign', 'Sign result with Ed25519')
.option('--sign-key <key>', 'Base64 encoded Ed25519 private key')
.option('--key-id <id>', 'Key identifier for signing')
.option('--cert-id <id>', 'Certificate ID for mandate chain')
.option('--trusted-issuers <issuers>', 'Trusted certificate issuers (comma-separated)')
.action(async (query, options) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
console.log('🔍 Executing GOAP search...');
console.log(`📝 Query: ${query}`);
// Parse comma-separated values
const domains = options.domains ? options.domains.split(',').map((d: string) => d.trim()) : undefined;
const trustedIssuers = options.trustedIssuers
? options.trustedIssuers.split(',').map((i: string) => i.trim())
: ['perplexity-ai', 'openai', 'anthropic'];
// Build Ed25519 verification config if needed
const ed25519Verification = (options.verify || options.strictVerify || options.sign) ? {
enabled: true,
requireSignatures: options.strictVerify || false,
signResult: options.sign || false,
privateKey: options.signKey,
keyId: options.keyId,
certId: options.certId,
trustedIssuers
} : undefined;
// Add timeout wrapper
const timeout = parseInt(options.timeout) * 1000 || 30000;
const resultPromise = tools.executeGoapSearch({
query,
domains,
recency: options.recency,
mode: options.mode,
maxResults: parseInt(options.maxResults),
model: options.model,
enableReasoning: options.reasoning !== false,
planningTimeout: parseInt(options.timeout),
outputToFile: options.save !== false,
outputFormat: options.format,
outputPath: options.output,
useQuerySubfolder: options.subfolder !== false,
pagination: {
page: parseInt(options.page),
pageSize: parseInt(options.pageSize)
},
ed25519Verification
});
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error(`Search timed out after ${timeout/1000} seconds`)), timeout);
});
const result = await Promise.race([resultPromise, timeoutPromise]) as SearchResult;
// Display results
console.log('\n✅ Search completed!');
console.log('━'.repeat(50));
const answerPreview = result.answer.length > 500
? result.answer.substring(0, 500) + '...\n\n[Full answer in files]'
: result.answer;
console.log('\n📄 Answer:');
console.log(answerPreview);
console.log('\n📊 Metadata:');
console.log(` • Citations: ${result.citations.length}`);
console.log(` • Execution time: ${result.metadata.executionTime}ms`);
console.log(` • Replanned: ${result.metadata.replanned ? 'Yes' : 'No'}`);
if (result.paginationInfo) {
console.log(` • Page: ${result.paginationInfo.currentPage || 1}/${result.paginationInfo.totalPages || 1}`);
console.log(` • Total results: ${result.paginationInfo.totalResults || result.citations?.length || 0}`);
}
if (result.metadata.ed25519Verification) {
const v = result.metadata.ed25519Verification;
console.log(`\n🔐 Verification:`);
console.log(` • Verified citations: ${v.verified}/${v.total}`);
if (v.untrusted.length > 0) {
console.log(` • Untrusted sources: ${v.untrusted.join(', ')}`);
}
}
if (result.metadata.savedFiles && options.save !== false) {
console.log('\n💾 Files saved:');
for (const file of result.metadata.savedFiles) {
console.log(`${file}`);
}
}
// Force exit immediately
process.exit(0);
} catch (error) {
console.error('💥 Search failed:', error);
process.exit(1);
}
});
// Plan Explanation Command
program
.command('explain <query>')
.description('Explain GOAP planning for a query without executing')
.option('--no-steps', 'Hide step-by-step breakdown')
.option('--no-reasoning', 'Hide reasoning analysis')
.action(async (query, options) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
console.log('🧠 Generating plan explanation...');
console.log(`📝 Query: ${query}`);
const explanation = await tools.executePlanExplain({
query,
showSteps: options.steps !== false,
showReasoning: options.reasoning !== false
});
console.log('\n📋 Plan Explanation:');
console.log(JSON.stringify(explanation, null, 2));
process.exit(0);
} catch (error) {
console.error('💥 Explanation failed:', error);
process.exit(1);
}
});
// Raw Perplexity Search Command
program
.command('raw <queries...>')
.description('Direct Perplexity search without GOAP planning')
.option('-d, --domains <domains>', 'Domain restrictions (comma-separated)')
.option('-r, --recency <recency>', 'Recency filter (hour|day|week|month|year)')
.option('-m, --mode <mode>', 'Search mode (web|academic)', 'web')
.option('--max-results <number>', 'Maximum results (1-20)', '10')
.action(async (queries, options) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
console.log('🔍 Executing raw Perplexity search...');
console.log(`📝 Queries: ${queries.join(', ')}`);
const domains = options.domains ? options.domains.split(',').map((d: string) => d.trim()) : undefined;
const result = await tools.executeRawSearch({
query: queries,
domains,
recency: options.recency,
mode: options.mode,
maxResults: parseInt(options.maxResults)
});
console.log('\n✅ Raw search completed!');
console.log(JSON.stringify(result, null, 2));
process.exit(0);
} catch (error) {
console.error('💥 Raw search failed:', error);
process.exit(1);
}
});
// Plugin Management Commands
const pluginsCmd = program
.command('plugins')
.description('Plugin management');
pluginsCmd
.command('list')
.description('List all available plugins')
.action(async () => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
const plugins = await tools.executeToolByName('plugin.list', {});
console.log('🔌 Available Plugins:');
console.log(JSON.stringify(plugins, null, 2));
process.exit(0);
} catch (error) {
console.error('💥 Failed to list plugins:', error);
process.exit(1);
}
});
pluginsCmd
.command('enable <name>')
.description('Enable a plugin')
.action(async (name) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
await tools.executeToolByName('plugin.enable', { name });
console.log(`✅ Plugin '${name}' enabled`);
process.exit(0);
} catch (error) {
console.error('💥 Failed to enable plugin:', error);
process.exit(1);
}
});
pluginsCmd
.command('disable <name>')
.description('Disable a plugin')
.action(async (name) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
await tools.executeToolByName('plugin.disable', { name });
console.log(`✅ Plugin '${name}' disabled`);
process.exit(0);
} catch (error) {
console.error('💥 Failed to disable plugin:', error);
process.exit(1);
}
});
pluginsCmd
.command('info <name>')
.description('Get plugin information')
.action(async (name) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
const info = await tools.executeToolByName('plugin.info', { name });
console.log(`🔌 Plugin Information for '${name}':`);
console.log(JSON.stringify(info, null, 2));
process.exit(0);
} catch (error) {
console.error('💥 Failed to get plugin info:', error);
process.exit(1);
}
});
// Advanced Reasoning Commands
const reasoningCmd = program
.command('reasoning')
.description('Advanced reasoning capabilities');
reasoningCmd
.command('chain-of-thought <query>')
.description('Apply Chain-of-Thought reasoning with Tree-of-Thoughts')
.option('--depth <number>', 'Reasoning depth (1-5)', '3')
.option('--branches <number>', 'Number of branches (2-10)', '3')
.action(async (query, options) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
console.log('🧠 Applying Chain-of-Thought reasoning...');
const result = await tools.executeToolByName('reasoning.chain_of_thought', {
query,
depth: parseInt(options.depth),
branches: parseInt(options.branches)
});
console.log('\n✅ Reasoning complete:');
console.log(JSON.stringify(result, null, 2));
process.exit(0);
} catch (error) {
console.error('💥 Reasoning failed:', error);
process.exit(1);
}
});
reasoningCmd
.command('consistency <query>')
.description('Check reasoning consistency with majority voting')
.option('--samples <number>', 'Number of samples (3-10)', '5')
.action(async (query, options) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
console.log('🧠 Checking reasoning consistency...');
const result = await tools.executeToolByName('reasoning.self_consistency', {
query,
samples: parseInt(options.samples)
});
console.log('\n✅ Consistency check complete:');
console.log(JSON.stringify(result, null, 2));
process.exit(0);
} catch (error) {
console.error('💥 Consistency check failed:', error);
process.exit(1);
}
});
reasoningCmd
.command('verify <claims...>')
.description('Verify claims with citation grounding')
.option('--citations <citations>', 'Available citations (comma-separated)')
.action(async (claims, options) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
console.log('🧠 Verifying claims...');
const citations = options.citations
? options.citations.split(',').map((c: string) => c.trim())
: [];
const result = await tools.executeToolByName('reasoning.anti_hallucination', {
claims,
citations
});
console.log('\n✅ Verification complete:');
console.log(JSON.stringify(result, null, 2));
process.exit(0);
} catch (error) {
console.error('💥 Verification failed:', error);
process.exit(1);
}
});
reasoningCmd
.command('agents <query>')
.description('Orchestrate multiple research agents')
.option('--agents <types>', 'Agent types (comma-separated)', 'researcher,fact_checker,synthesizer,critic,summarizer')
.option('--sequential', 'Execute agents sequentially instead of in parallel')
.action(async (query, options) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
console.log('🤖 Orchestrating research agents...');
const agents = options.agents.split(',').map((a: string) => a.trim());
const result = await tools.executeToolByName('reasoning.agentic_research', {
query,
agents,
parallel: !options.sequential
});
console.log('\n✅ Agent orchestration complete:');
console.log(JSON.stringify(result, null, 2));
process.exit(0);
} catch (error) {
console.error('💥 Agent orchestration failed:', error);
process.exit(1);
}
});
// Legacy test command with updated features
program
.command('test')
.description('Test the GOAP planner with a sample query')
.option('--query <string>', 'Test query', 'What are the latest developments in AI?')
.option('--explain', 'Show plan explanation without executing')
.action(async (options) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
console.log('🧪 Testing GOAP planner...');
console.log(`📝 Query: ${options.query}`);
if (options.explain) {
const explanation = await tools.executePlanExplain({
query: options.query,
showSteps: true,
showReasoning: true
});
console.log('📋 Plan Explanation:');
console.log(JSON.stringify(explanation, null, 2));
} else {
// Add timeout wrapper
const timeout = 30000; // 30 seconds
const resultPromise = tools.executeGoapSearch({
query: options.query,
enableReasoning: true,
maxResults: 5,
outputToFile: true,
outputPath: '.research',
useQuerySubfolder: false,
outputFormat: 'both'
});
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Test timed out after 30 seconds')), timeout);
});
const result = await Promise.race([resultPromise, timeoutPromise]) as SearchResult;
console.log('✅ Test Results:');
console.log(`📝 Answer: ${result.answer.substring(0, 200)}...`);
console.log(`📚 Citations: ${result.citations.length}`);
console.log(`⏱️ Execution time: ${result.metadata.executionTime}ms`);
console.log(`🔄 Replanned: ${result.metadata.replanned}`);
if (result.metadata.savedFiles) {
console.log(`💾 Output saved to: ${result.metadata.savedFiles.join(', ')}`);
}
}
// Force exit after a brief delay to ensure all output is flushed
setTimeout(() => {
process.exit(0);
}, 100);
} catch (error) {
console.error('💥 Test failed:', error);
process.exit(1);
}
});
// Legacy query command for backward compatibility
program
.command('query <question>')
.description('Execute a research query (legacy, use "search" instead)')
.option('--no-save', 'Do not save output to files')
.option('--output <path>', 'Output directory path', '.research')
.option('--format <format>', 'Output format (json, markdown, both)', 'both')
.option('--max-results <number>', 'Maximum search results', '10')
.option('--model <model>', 'Perplexity model', 'sonar-pro')
.option('--explain', 'Show plan explanation without executing')
.action(async (question, options) => {
try {
const { GoapMCPTools } = await import('./mcp/tools.js');
const tools = new GoapMCPTools();
await tools.initialize();
console.log('🔍 Executing research query...');
console.log(`📝 Query: ${question}`);
if (options.explain) {
const explanation = await tools.executePlanExplain({
query: question,
showSteps: true,
showReasoning: true
});
console.log('📋 Plan Explanation:');
console.log(JSON.stringify(explanation, null, 2));
process.exit(0);
return;
}
// Add timeout wrapper
const timeout = 30000; // 30 seconds
const resultPromise = tools.executeGoapSearch({
query: question,
enableReasoning: true,
maxResults: parseInt(options.maxResults),
model: options.model,
outputToFile: options.save !== false,
outputPath: options.output,
useQuerySubfolder: true,
outputFormat: options.format
});
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Query timed out after 30 seconds')), timeout);
});
const result = await Promise.race([resultPromise, timeoutPromise]) as SearchResult;
// Display summary
console.log('\n✅ Research completed!');
console.log('━'.repeat(50));
const answerPreview = result.answer.length > 500
? result.answer.substring(0, 500) + '...\n\n[Full answer saved to file]'
: result.answer;
console.log('\n📄 Answer:');
console.log(answerPreview);
console.log('\n📊 Statistics:');
console.log(` • Citations: ${result.citations.length}`);
console.log(` • Execution time: ${result.metadata.executionTime}ms`);
console.log(` • Replanned: ${result.metadata.replanned ? 'Yes' : 'No'}`);
if (result.usage) {
console.log(` • Tokens used: ${result.usage.tokens || 'N/A'}`);
}
if (result.metadata.savedFiles && options.save !== false) {
console.log('\n💾 Files saved:');
for (const file of result.metadata.savedFiles) {
console.log(`${file}`);
}
}
// Force exit immediately
process.exit(0);
} catch (error) {
console.error('💥 Query failed:', error);
process.exit(1);
}
});
// Validation command
program
.command('validate')
.description('Validate configuration and dependencies')
.action(async () => {
console.log('🔍 Validating GOAP MCP configuration...');
// Check environment variables
const checks = [
{
name: 'Perplexity API Key',
check: () => !!process.env.PERPLEXITY_API_KEY,
fix: 'Set PERPLEXITY_API_KEY environment variable'
},
{
name: 'Node.js version',
check: () => {
const version = process.version;
const major = parseInt(version.slice(1).split('.')[0]);
return major >= 18;
},
fix: 'Update Node.js to version 18 or higher'
}
];
let allPassed = true;
for (const check of checks) {
const passed = check.check();
const status = passed ? '✅' : '❌';
console.log(`${status} ${check.name}`);
if (!passed) {
console.log(` 💡 ${check.fix}`);
allPassed = false;
}
}
// Test Advanced Reasoning Engine WASM
try {
const { AdvancedReasoningEngine } = await import('./core/advanced-reasoning-engine');
const engine = new AdvancedReasoningEngine();
await engine.initialize();
console.log('✅ Advanced Reasoning Engine integration');
} catch (error) {
console.log('⚠️ Advanced Reasoning Engine (will use fallback)');
}
// Test MCP SDK
try {
await import('@modelcontextprotocol/sdk/server/index.js');
console.log('✅ MCP SDK');
} catch (error) {
console.log('❌ MCP SDK - npm install required');
allPassed = false;
}
if (allPassed) {
console.log('🎉 All validations passed! Ready to run GOAP MCP server.');
} else {
console.log('⚠️ Some validations failed. Please fix the issues above.');
process.exit(1);
}
});
// Info command
program
.command('info')
.description('Show system information and capabilities')
.action(async () => {
console.log('🎯 GOAP MCP Server Information');
console.log('==============================');
console.log('');
console.log('📋 Core Features:');
console.log(' • STRIPS-style preconditions and effects');
console.log(' • A* pathfinding for optimal plans');
console.log(' • Dynamic re-planning on failure');
console.log(' • Advanced Reasoning Engine enhanced reasoning');
console.log(' • Perplexity API integration');
console.log(' • Extensible plugin system');
console.log('');
console.log('🔧 Available Tools:');
console.log(' • goap.search - Intelligent search with planning');
console.log(' • goap.plan.explain - Plan explanation');
console.log(' • search.raw - Direct Perplexity search');
console.log(' • plugin.* - Plugin management tools');
console.log(' • reasoning.* - Advanced reasoning tools');
console.log('');
console.log('🎪 Plugin System:');
console.log(' • cost-tracker - Track execution costs');
console.log(' • performance-monitor - Monitor execution performance');
console.log(' • logger - Comprehensive logging');
console.log(' • query-diversifier - Enhance search queries');
console.log(' • chain-of-thought - CoT reasoning');
console.log(' • self-consistency - Consistency checking');
console.log(' • anti-hallucination - Citation grounding');
console.log(' • agentic-research - Multi-agent coordination');
console.log('');
console.log('🧠 Advanced Reasoning Engine:');
console.log(' • Pattern analysis algorithms');
console.log(' • Predictive modeling capabilities');
console.log(' • State-enhanced reasoning');
console.log(' • Multi-agent coordination');
console.log(' • Ed25519 cryptographic verification');
console.log('');
console.log('🌟 Advantages over standard web search:');
console.log(' • Multi-step planning with dependencies');
console.log(' • Automatic query optimization');
console.log(' • Enhanced reasoning with Advanced Reasoning Engine');
console.log(' • Dynamic re-planning on failures');
console.log(' • Comprehensive answer verification');
console.log(' • Cost optimization with A* pathfinding');
console.log(' • Extensible plugin architecture');
console.log(' • Cryptographic citation verification');
console.log('');
console.log('💡 Quick Start Examples:');
console.log(' npx goalie search "latest AI developments"');
console.log(' npx goalie explain "quantum computing breakthroughs"');
console.log(' npx goalie reasoning chain-of-thought "solve climate change"');
console.log(' npx goalie plugins list');
});
// Default command runs the server
program.parse();
// If no command provided, show help
if (!process.argv.slice(2).length) {
program.outputHelp();
}