adpa-enterprise-framework-automation
Version:
Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe
1,002 lines • 47.6 kB
JavaScript
#!/usr/bin/env node
// filepath: c:\Users\menno\Source\Repos\requirements-gathering-agent\src\cli.ts
/**
* Command Line Interface for Requirements Gathering Agent
*
* Provides comprehensive CLI functionality for project documentation generation
* with support for multiple AI providers and PMBOK compliance validation.
*
* @version 2.1.3
* @author Requirements Gathering Agent Team
* @created 2024
* @updated June 2025
*
* Key Features:
* - Full CLI argument parsing and validation
* - Multi-provider AI configuration support
* - Interactive document generation workflows
* - PMBOK validation and compliance checking
* - Comprehensive error handling and user feedback
*
* @filepath c:\Users\menno\Source\Repos\requirements-gathering-agent\src\cli.ts
*/
// 1. Node.js built-ins
import { execSync } from 'child_process';
import { existsSync } from 'fs';
import os from 'os';
import { dirname } from 'path';
import process from 'process';
import readline from 'readline';
import { fileURLToPath } from 'url';
// 2. Third-party dependencies
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
// 3. Internal modules
import { InteractiveProviderMenu } from './modules/ai/interactive-menu.js';
import { promptsCommand } from './commands/prompts.js';
// 4. Constants and configuration
import { DEFAULT_OUTPUT_DIR, DEFAULT_RETRY_COUNT, DEFAULT_RETRY_BACKOFF, DEFAULT_RETRY_MAX_DELAY, SUPPORTED_FORMATS, CONFIG_FILENAME, PACKAGE_JSON_FILENAME, TSCONFIG_JSON_FILENAME, README_FILENAME, PROCESSOR_CONFIG_FILENAME } from './constants.js';
// 5. Command handlers
import { handleStatusCommand, handleAnalyzeCommand, handleSetupCommand, handleGenerateCommand, handleGenerateCategoryCommand, handleGenerateAllCommand, handleGenerateCoreAnalysisCommand, handleListTemplatesCommand, handleValidateCommand,
// Confluence commands
handleConfluenceInitCommand, handleConfluenceTestCommand, handleConfluencePublishCommand, handleConfluenceStatusCommand, handleConfluenceOAuth2LoginCommand, handleConfluenceOAuth2StatusCommand, handleConfluenceOAuth2DebugCommand,
// SharePoint commands
handleSharePointInitCommand, handleSharePointTestCommand, handleSharePointPublishCommand, handleSharePointStatusCommand, handleSharePointOAuth2LoginCommand, handleSharePointOAuth2StatusCommand, handleSharePointOAuth2DebugCommand,
// VCS commands
handleVcsInitCommand, handleVcsStatusCommand, handleVcsCommitCommand, handleVcsPushCommand,
// Stakeholder Analysis commands
handleStakeholderAnalysisCommand, handleStakeholderRegisterCommand, handleStakeholderEngagementPlanCommand, handleStakeholderAutomationCommand, displayStakeholderHelp } from './commands/index.js';
// 6. Utilities and version management
import { getLegacyDisplayName } from './utils/version.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
function checkGitInstalled() {
try {
execSync('git --version', { stdio: 'ignore' });
return true;
}
catch {
return false;
}
}
async function promptAndInstallGitWindows() {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
return new Promise((resolve) => {
rl.question('Would you like to install Git automatically using winget? (y/n): ', async (answer) => {
rl.close();
if (answer.trim().toLowerCase().startsWith('y')) {
console.log('⚠️ This will require elevated (administrator) privileges.');
console.log('Attempting to install Git using winget...');
try {
const { execSync } = await import('child_process');
execSync('winget install --id Git.Git -e', { stdio: 'inherit' });
console.log('✅ Git installation complete. Please restart your terminal and re-run your command.');
}
catch (e) {
console.error('❌ Automatic installation failed. Please install Git manually from https://git-scm.com/downloads');
}
process.exit(0);
}
else {
console.log('Please install Git manually from https://git-scm.com/downloads');
process.exit(1);
}
});
});
}
function promptGitInstallIfMissing() {
if (!checkGitInstalled()) {
console.error('❌ Git is not installed or not in your PATH.');
console.log('Please install Git: https://git-scm.com/downloads');
if (os.platform() === 'win32') {
console.log('Or install automatically by running:');
console.log(' winget install --id Git.Git -e');
return promptAndInstallGitWindows();
}
process.exit(1);
}
}
// Call this function before any git VCS operation
// promptGitInstallIfMissing();
async function ensureGitRepoInitialized(documentsDir = DEFAULT_OUTPUT_DIR) {
const path = (await import('path')).default;
const gitDir = path.join(process.cwd(), documentsDir, '.git');
if (!existsSync(gitDir)) {
console.log(`Initializing git repository in ${documentsDir}/ ...`);
try {
execSync('git init', { cwd: path.join(process.cwd(), documentsDir), stdio: 'inherit' });
console.log('✅ Git repository initialized.');
}
catch (e) {
console.error('❌ Failed to initialize git repository:', e);
process.exit(1);
}
}
}
// Yargs CLI definition
yargs(hideBin(process.argv))
.scriptName('adpa')
.usage('Usage: $0 <command> [options]')
.version(getLegacyDisplayName())
.command('generate [key]', 'Generate a specific document by key', (yargs) => {
return yargs
.positional('key', { type: 'string', describe: 'Document key' })
.option('output', { type: 'string', default: DEFAULT_OUTPUT_DIR, describe: 'Output directory' })
.option('quiet', { type: 'boolean', default: false, describe: 'Suppress output' })
.option('format', { type: 'string', default: 'markdown', choices: SUPPORTED_FORMATS, describe: 'Output format' })
.option('retries', { type: 'number', default: DEFAULT_RETRY_COUNT, describe: 'Number of retries for generation' })
.option('retry-backoff', { type: 'number', default: DEFAULT_RETRY_BACKOFF, describe: 'Initial retry backoff (ms)' })
.option('retry-max-delay', { type: 'number', default: DEFAULT_RETRY_MAX_DELAY, describe: 'Max retry backoff (ms)' });
}, async (argv) => {
await handleGenerateCommand(argv.key, {
output: argv.output || DEFAULT_OUTPUT_DIR,
format: argv.format || 'markdown',
quiet: argv.quiet,
retries: argv.retries,
retryBackoff: argv['retry-backoff'],
retryMaxDelay: argv['retry-max-delay']
});
})
.command('generate-category <category>', 'Generate all documents in a category', (yargs) => {
return yargs
.positional('category', { type: 'string', describe: 'Document category' })
.option('output', { type: 'string', default: DEFAULT_OUTPUT_DIR })
.option('quiet', { type: 'boolean', default: false, describe: 'Suppress output' })
.option('retries', { type: 'number', default: DEFAULT_RETRY_COUNT, describe: 'Number of retries for generation' })
.option('retry-backoff', { type: 'number', default: DEFAULT_RETRY_BACKOFF, describe: 'Initial retry backoff (ms)' })
.option('retry-max-delay', { type: 'number', default: DEFAULT_RETRY_MAX_DELAY, describe: 'Max retry backoff (ms)' });
}, async (argv) => {
await handleGenerateCategoryCommand(argv.category, {
output: argv.output || DEFAULT_OUTPUT_DIR,
quiet: argv.quiet,
retries: argv.retries,
retryBackoff: argv['retry-backoff'],
retryMaxDelay: argv['retry-max-delay']
});
})
.command('generate-all', 'Generate all available documents', (yargs) => {
return yargs
.option('output', { type: 'string', default: DEFAULT_OUTPUT_DIR })
.option('quiet', { type: 'boolean', default: false, describe: 'Suppress output' })
.option('retries', { type: 'number', default: DEFAULT_RETRY_COUNT, describe: 'Number of retries for generation' })
.option('retry-backoff', { type: 'number', default: DEFAULT_RETRY_BACKOFF, describe: 'Initial retry backoff (ms)' })
.option('retry-max-delay', { type: 'number', default: DEFAULT_RETRY_MAX_DELAY, describe: 'Max retry backoff (ms)' });
}, async (argv) => {
await handleGenerateAllCommand({
output: argv.output || DEFAULT_OUTPUT_DIR,
quiet: argv.quiet,
retries: argv.retries,
retryBackoff: argv['retry-backoff'],
retryMaxDelay: argv['retry-max-delay']
});
})
.command('list-templates', 'Show all available document templates', {}, async () => {
await handleListTemplatesCommand();
})
.command('status', 'Show configuration and system status', (yargs) => {
return yargs
.option('quiet', { type: 'boolean', default: false, describe: 'Suppress output' })
.option('verbose', { type: 'boolean', default: false, describe: 'Show detailed information' });
}, async (argv) => {
if (!argv.quiet) {
await handleStatusCommand();
}
})
.command('setup', 'Interactive setup wizard for AI providers', {}, async () => {
await handleSetupCommand();
})
.command('interactive', 'Launch interactive CLI menu interface', (yargs) => {
return yargs
.option('mode', {
type: 'string',
choices: ['beginner', 'advanced'],
default: 'beginner',
describe: 'Interface mode for different user experience levels'
})
.option('skip-intro', {
type: 'boolean',
default: false,
describe: 'Skip the introduction message'
})
.option('debug', {
type: 'boolean',
default: false,
describe: 'Enable debug mode for troubleshooting'
})
.option('enhanced', {
type: 'boolean',
default: false,
describe: 'Use enhanced navigation with inquirer (recommended)'
});
}, async (argv) => {
const { handleInteractiveCommand, checkInteractiveSupport, showInteractiveNotSupportedMessage } = await import('./commands/interactive.js');
// Check if interactive mode is supported
if (!checkInteractiveSupport()) {
showInteractiveNotSupportedMessage();
process.exit(1);
}
await handleInteractiveCommand({
mode: argv.mode,
skipIntro: argv.skipIntro,
debug: argv.debug,
enhanced: argv.enhanced
});
})
.command('analyze', 'Analyze workspace without generating docs', {}, async () => {
await handleAnalyzeCommand();
})
.command('generate-core-analysis', 'Generate core analysis documents', (yargs) => {
return yargs
.option('output', { type: 'string', default: DEFAULT_OUTPUT_DIR })
.option('quiet', { type: 'boolean', default: false, describe: 'Suppress output' })
.option('retries', { type: 'number', default: DEFAULT_RETRY_COUNT })
.option('retry-backoff', { type: 'number', default: DEFAULT_RETRY_BACKOFF })
.option('retry-max-delay', { type: 'number', default: DEFAULT_RETRY_MAX_DELAY });
}, async (argv) => {
await handleGenerateCoreAnalysisCommand({
output: argv.output || DEFAULT_OUTPUT_DIR,
quiet: argv.quiet,
retries: argv.retries,
retryBackoff: argv['retry-backoff'],
retryMaxDelay: argv['retry-max-delay']
});
})
.command('validate', 'Validate generated documents against PMBOK', (yargs) => {
return yargs
.option('output', { type: 'string', default: DEFAULT_OUTPUT_DIR })
.option('quiet', { type: 'boolean', default: false, describe: 'Suppress output' });
}, async (argv) => {
await handleValidateCommand({
outputDir: argv.output || DEFAULT_OUTPUT_DIR,
quiet: argv.quiet
});
})
// Confluence commands
.command('confluence', 'Confluence integration commands', (yargs) => {
return yargs
.command('init', 'Initialize Confluence configuration', {}, async (argv) => {
await handleConfluenceInitCommand({ quiet: argv.quiet });
})
.command('test', 'Test Confluence connection', {}, async (argv) => {
await handleConfluenceTestCommand({ quiet: argv.quiet });
})
.command('publish', 'Publish documents to Confluence', (yargs) => {
return yargs
.option('documents-path', { type: 'string', describe: 'Path to documents directory' })
.option('parent-page', { type: 'string', describe: 'Parent page title' })
.option('label-prefix', { type: 'string', describe: 'Label prefix for metadata' })
.option('dry-run', { type: 'boolean', default: false, describe: 'Preview only' })
.option('force', { type: 'boolean', default: false, describe: 'Force publish' });
}, async (argv) => {
await handleConfluencePublishCommand({
documentsPath: argv['documents-path'],
parentPageTitle: argv['parent-page'],
labelPrefix: argv['label-prefix'],
dryRun: argv['dry-run'],
force: argv.force,
quiet: argv.quiet
});
})
.command('status', 'Show Confluence integration status', {}, async (argv) => {
await handleConfluenceStatusCommand({ quiet: argv.quiet });
})
.command('oauth2', 'OAuth2 authentication commands', (yargs) => {
return yargs
.command('login', 'Start OAuth2 authentication', {}, async (argv) => {
await handleConfluenceOAuth2LoginCommand({ quiet: argv.quiet });
})
.command('status', 'Check OAuth2 authentication status', {}, async (argv) => {
await handleConfluenceOAuth2StatusCommand({ quiet: argv.quiet });
})
.command('debug', 'Debug OAuth2 authentication', {}, async (argv) => {
await handleConfluenceOAuth2DebugCommand({ quiet: argv.quiet });
})
.demandCommand(1, 'You must provide a valid oauth2 command.');
})
.demandCommand(1, 'You must provide a valid confluence command.');
})
// SharePoint commands
.command('sharepoint', 'SharePoint integration commands', (yargs) => {
return yargs
.command('init', 'Initialize SharePoint configuration', {}, async (argv) => {
await handleSharePointInitCommand({ quiet: argv.quiet });
})
.command('test', 'Test SharePoint connection', {}, async (argv) => {
await handleSharePointTestCommand({ quiet: argv.quiet });
})
.command('publish', 'Publish documents to SharePoint', (yargs) => {
return yargs
.option('documents-path', { type: 'string', describe: 'Path to documents directory' })
.option('folder-path', { type: 'string', describe: 'Target folder path' })
.option('label-prefix', { type: 'string', describe: 'Label prefix for metadata' })
.option('dry-run', { type: 'boolean', default: false, describe: 'Preview only' })
.option('force', { type: 'boolean', default: false, describe: 'Force publish' });
}, async (argv) => {
await handleSharePointPublishCommand({
documentsPath: argv['documents-path'],
folderPath: argv['folder-path'],
labelPrefix: argv['label-prefix'],
dryRun: argv['dry-run'],
force: argv.force,
quiet: argv.quiet
});
})
.command('status', 'Show SharePoint integration status', {}, async (argv) => {
await handleSharePointStatusCommand({ quiet: argv.quiet });
})
.command('oauth2', 'OAuth2 authentication commands', (yargs) => {
return yargs
.command('login', 'Start OAuth2 authentication', {}, async (argv) => {
await handleSharePointOAuth2LoginCommand({ quiet: argv.quiet });
})
.command('status', 'Check OAuth2 authentication status', {}, async (argv) => {
await handleSharePointOAuth2StatusCommand({ quiet: argv.quiet });
})
.command('debug', 'Debug OAuth2 authentication', {}, async (argv) => {
await handleSharePointOAuth2DebugCommand({ quiet: argv.quiet });
})
.demandCommand(1, 'You must provide a valid oauth2 command.');
})
.demandCommand(1, 'You must provide a valid sharepoint command.');
})
// VCS commands
.command('vcs', 'Version control system commands', (yargs) => {
return yargs
.command('init', 'Initialize Git repository', (yargs) => {
return yargs
.option('output', { type: 'string', default: DEFAULT_OUTPUT_DIR, describe: 'Output directory' });
}, async (argv) => {
await handleVcsInitCommand({
outputDir: argv.output,
quiet: argv.quiet
});
})
.command('status', 'Show Git repository status', (yargs) => {
return yargs
.option('output', { type: 'string', default: DEFAULT_OUTPUT_DIR, describe: 'Output directory' });
}, async (argv) => {
await handleVcsStatusCommand({
outputDir: argv.output,
quiet: argv.quiet
});
})
.command('commit', 'Commit changes to Git repository', (yargs) => {
return yargs
.option('output', { type: 'string', default: DEFAULT_OUTPUT_DIR, describe: 'Output directory' })
.option('message', { type: 'string', default: 'Update generated documents', describe: 'Commit message' });
}, async (argv) => {
await handleVcsCommitCommand({
outputDir: argv.output,
message: argv.message,
quiet: argv.quiet
});
})
.command('push', 'Push changes to remote repository', (yargs) => {
return yargs
.option('output', { type: 'string', default: DEFAULT_OUTPUT_DIR, describe: 'Output directory' })
.option('remote', { type: 'string', default: 'origin', describe: 'Remote name' })
.option('branch', { type: 'string', default: 'main', describe: 'Branch name' });
}, async (argv) => {
await handleVcsPushCommand({
outputDir: argv.output,
remote: argv.remote,
branch: argv.branch,
quiet: argv.quiet
});
})
.demandCommand(1, 'You must provide a valid vcs command.');
})
// Feedback commands
.command('feedback', 'Manage document feedback and AI improvements', (yargs) => {
return yargs
.command('analyze', 'Analyze feedback patterns to identify improvement opportunities', (yargs) => {
return yargs
.option('document-type', { type: 'string', describe: 'Analyze specific document type' })
.option('project', { type: 'string', describe: 'Analyze specific project' })
.option('days', { type: 'number', default: 30, describe: 'Number of days to analyze' });
}, async (argv) => {
const { FeedbackIntegrationService } = await import('./services/FeedbackIntegrationService.js');
try {
console.log('🔍 Analyzing feedback patterns...');
const service = new FeedbackIntegrationService();
const insights = await service.analyzeFeedbackPatterns(argv['document-type'], argv.days);
if (insights.length === 0) {
console.log('📊 No feedback patterns found for the specified criteria.');
return;
}
console.log(`\n📈 Feedback Analysis Results (${argv.days} days):`);
console.log('='.repeat(60));
insights.forEach((insight, index) => {
console.log(`\n${index + 1}. Document Type: ${insight.documentType}`);
console.log(` Quality Trend: ${insight.qualityTrends.ratingTrend} (${insight.qualityTrends.averageRating.toFixed(1)}/5)`);
console.log(` Feedback Volume: ${insight.qualityTrends.feedbackVolume} items`);
if (insight.commonIssues.length > 0) {
console.log(` Common Issues:`);
insight.commonIssues.slice(0, 3).forEach(issue => {
console.log(` • ${issue}`);
});
}
if (insight.suggestedPromptImprovements.length > 0) {
console.log(` Suggested Improvements:`);
insight.suggestedPromptImprovements.slice(0, 2).forEach(improvement => {
console.log(` • ${improvement}`);
});
}
});
console.log('\n💡 Next Steps:');
console.log(' • Use "adpa feedback apply" to implement improvements');
console.log(' • Review specific document types with low ratings');
console.log(' • Monitor trends after implementing changes');
}
catch (error) {
console.error('❌ Error analyzing feedback:', error);
process.exit(1);
}
})
.command('apply', 'Apply feedback-driven improvements to document generation', (yargs) => {
return yargs
.option('project', { type: 'string', required: true, describe: 'Project ID to apply improvements to' })
.option('dry-run', { type: 'boolean', default: false, describe: 'Show what would be improved without applying changes' });
}, async (argv) => {
const { FeedbackIntegrationService } = await import('./services/FeedbackIntegrationService.js');
try {
console.log(`🔧 ${argv['dry-run'] ? 'Analyzing' : 'Applying'} feedback improvements for project ${argv.project}...`);
const service = new FeedbackIntegrationService();
if (argv['dry-run']) {
const recommendations = await service.generateRecommendations(argv.project);
console.log('\n📋 Recommended Improvements:');
console.log('='.repeat(50));
if (recommendations.immediateActions.length > 0) {
console.log('\n🚨 Immediate Actions:');
recommendations.immediateActions.forEach((action, i) => {
console.log(` ${i + 1}. ${action}`);
});
}
console.log('\n💡 Run without --dry-run to apply these improvements');
}
else {
const result = await service.applyFeedbackImprovements(argv.project);
console.log('\n✅ Feedback Improvements Applied:');
console.log('='.repeat(50));
console.log(`📝 Documents Improved: ${result.documentsImproved.length}`);
console.log(`📈 Predicted Quality Improvement: +${result.qualityPrediction}%`);
}
}
catch (error) {
console.error('❌ Error applying feedback improvements:', error);
process.exit(1);
}
})
.command('generate', 'Generate documents with feedback-driven enhancements', (yargs) => {
return yargs
.option('context', { type: 'string', required: true, describe: 'Project context for document generation' })
.option('project', { type: 'string', describe: 'Project ID for feedback integration' })
.option('learning', { type: 'boolean', default: false, describe: 'Enable learning mode for iterative improvement' })
.option('threshold', { type: 'number', default: 80, describe: 'Quality threshold for iterative improvement' });
}, async (argv) => {
const { FeedbackEnhancedGenerator } = await import('./modules/documentGenerator/FeedbackEnhancedGenerator.js');
try {
console.log('🧠 Starting feedback-enhanced document generation...');
const generator = new FeedbackEnhancedGenerator(argv.context, {
projectId: argv.project,
applyFeedbackImprovements: true,
learningMode: argv.learning,
qualityThreshold: argv.threshold
});
const result = await generator.generateWithFeedbackEnhancement();
console.log('\n📊 Generation Results:');
console.log('='.repeat(50));
console.log(`✅ Success: ${result.success}`);
console.log(`📝 Documents Generated: ${result.successCount}`);
console.log(`⏱️ Duration: ${(result.duration / 1000).toFixed(2)}s`);
if (result.feedbackInsights) {
console.log('\n🔧 Feedback Integration:');
console.log(` Applied Improvements: ${result.feedbackInsights.appliedImprovements.length}`);
console.log(` Quality Prediction: +${result.feedbackInsights.qualityPrediction}%`);
}
}
catch (error) {
console.error('❌ Error in feedback-enhanced generation:', error);
process.exit(1);
}
})
.command('stats', 'Show feedback statistics and trends', (yargs) => {
return yargs
.option('project', { type: 'string', describe: 'Show stats for specific project' })
.option('days', { type: 'number', default: 30, describe: 'Number of days to analyze' });
}, async (argv) => {
const { DocumentFeedback } = await import('./models/DocumentFeedback.js');
try {
console.log('📊 Gathering feedback statistics...');
const filter = {};
if (argv.project) {
filter.projectId = argv.project;
}
const startDate = new Date();
startDate.setDate(startDate.getDate() - argv.days);
filter.submittedAt = { $gte: startDate };
const totalFeedback = await DocumentFeedback.countDocuments(filter);
const avgRating = await DocumentFeedback.aggregate([
{ $match: filter },
{ $group: { _id: null, avgRating: { $avg: '$rating' } } }
]);
console.log('\n📈 Feedback Statistics:');
console.log('='.repeat(50));
console.log(`📝 Total Feedback: ${totalFeedback}`);
console.log(`⭐ Average Rating: ${avgRating[0]?.avgRating?.toFixed(1) || 'N/A'}/5`);
console.log(`📅 Period: Last ${argv.days} days`);
console.log('\n💡 Recommendations:');
if (avgRating[0]?.avgRating < 3) {
console.log(' • Focus on improving overall document quality');
}
console.log(' • Use "adpa feedback analyze" for detailed insights');
}
catch (error) {
console.error('❌ Error gathering feedback statistics:', error);
process.exit(1);
}
})
.demandCommand(1, 'You must provide a valid feedback command.');
})
.command(promptsCommand)
.command(require('./commands/environment.js').environmentCommandModule)
// Stakeholder Analysis commands
.command('stakeholder', 'Automated stakeholder analysis and management', (yargs) => {
return yargs
.command('analysis', 'Generate comprehensive stakeholder analysis', (yargs) => {
return yargs
.option('output-dir', { type: 'string', default: DEFAULT_OUTPUT_DIR, describe: 'Output directory' })
.option('format', { type: 'string', default: 'markdown', choices: ['markdown', 'json'], describe: 'Output format' })
.option('verbose', { type: 'boolean', default: false, describe: 'Show detailed output' })
.option('include-register', { type: 'boolean', default: true, describe: 'Include stakeholder register' })
.option('include-engagement-plan', { type: 'boolean', default: false, describe: 'Include engagement plan' })
.option('analysis-depth', { type: 'string', default: 'comprehensive', choices: ['basic', 'detailed', 'comprehensive'], describe: 'Analysis depth' });
}, async (argv) => {
await handleStakeholderAnalysisCommand({
outputDir: argv['output-dir'],
format: argv.format,
verbose: argv.verbose,
includeRegister: argv['include-register'],
includeEngagementPlan: argv['include-engagement-plan'],
analysisDepth: argv['analysis-depth']
});
})
.command('register', 'Generate stakeholder register only', (yargs) => {
return yargs
.option('output-dir', { type: 'string', default: DEFAULT_OUTPUT_DIR, describe: 'Output directory' })
.option('format', { type: 'string', default: 'markdown', choices: ['markdown', 'json'], describe: 'Output format' })
.option('verbose', { type: 'boolean', default: false, describe: 'Show detailed output' });
}, async (argv) => {
await handleStakeholderRegisterCommand({
outputDir: argv['output-dir'],
format: argv.format,
verbose: argv.verbose
});
})
.command('engagement-plan', 'Generate stakeholder engagement plan', (yargs) => {
return yargs
.option('output-dir', { type: 'string', default: DEFAULT_OUTPUT_DIR, describe: 'Output directory' })
.option('format', { type: 'string', default: 'markdown', choices: ['markdown', 'json'], describe: 'Output format' })
.option('verbose', { type: 'boolean', default: false, describe: 'Show detailed output' });
}, async (argv) => {
await handleStakeholderEngagementPlanCommand({
outputDir: argv['output-dir'],
format: argv.format,
verbose: argv.verbose
});
})
.command('automate', 'Generate all stakeholder documents (comprehensive automation)', (yargs) => {
return yargs
.option('output-dir', { type: 'string', default: DEFAULT_OUTPUT_DIR, describe: 'Output directory' })
.option('format', { type: 'string', default: 'markdown', choices: ['markdown', 'json'], describe: 'Output format' })
.option('verbose', { type: 'boolean', default: false, describe: 'Show detailed output' })
.option('analysis-depth', { type: 'string', default: 'comprehensive', choices: ['basic', 'detailed', 'comprehensive'], describe: 'Analysis depth' });
}, async (argv) => {
await handleStakeholderAutomationCommand({
outputDir: argv['output-dir'],
format: argv.format,
verbose: argv.verbose,
includeRegister: true,
includeEngagementPlan: true,
analysisDepth: argv['analysis-depth']
});
})
.command('help', 'Show stakeholder analysis help', {}, () => {
displayStakeholderHelp();
})
.demandCommand(1, 'You must provide a valid stakeholder command.');
})
.command(promptsCommand)
.command('risk-compliance', 'Generate comprehensive risk and compliance assessments', (yargs) => {
return yargs
.option('project', {
alias: 'p',
type: 'string',
description: 'Project name',
demandOption: true
})
.option('type', {
alias: 't',
type: 'string',
description: 'Project type (SOFTWARE_DEVELOPMENT, INFRASTRUCTURE, etc.)',
default: 'SOFTWARE_DEVELOPMENT'
})
.option('description', {
alias: 'd',
type: 'string',
description: 'Project description'
})
.option('output', {
alias: 'o',
type: 'string',
description: 'Output directory',
default: 'generated-documents/risk-compliance'
})
.option('integrated', {
type: 'boolean',
description: 'Generate integrated assessment using compliance engine',
default: false
})
.option('pmbok-only', {
type: 'boolean',
description: 'Generate PMBOK-focused assessment only',
default: false
})
.option('format', {
type: 'string',
description: 'Output format (markdown, json)',
choices: ['markdown', 'json'],
default: 'markdown'
});
}, async (argv) => {
try {
const { createRiskComplianceCommand } = await import('./commands/risk-compliance.js');
const command = createRiskComplianceCommand();
// Execute the command with the provided arguments
await command.parseAsync([
'risk-compliance',
'--project', argv.project,
'--type', argv.type || 'SOFTWARE_DEVELOPMENT',
...(argv.description ? ['--description', argv.description] : []),
'--output', argv.output,
...(argv.integrated ? ['--integrated'] : []),
...(argv.pmbokOnly ? ['--pmbok-only'] : []),
'--format', argv.format
], { from: 'user' });
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error('❌ Error executing risk-compliance command:', message);
process.exit(1);
}
})
.option('quiet', {
alias: 'q',
type: 'boolean',
description: 'Suppress output',
global: true,
})
.help()
.alias('help', 'h')
.demandCommand(1, 'You must provide a valid command.')
.parse();
// ...existing code for utility functions, command logic, etc...
// Missing function implementations
async function scaffoldNewProcessor(category, name) {
console.log(`🏗️ Scaffolding new processor: ${name} in category: ${category}`);
console.log('This feature is not yet implemented.');
process.exit(0);
}
async function runProviderSelectionMenu() {
console.log('🤖 AI Provider Selection Menu');
try {
const menu = new InteractiveProviderMenu();
await menu.showMenu();
}
catch (error) {
console.error('Error running provider selection menu:', error);
process.exit(1);
}
}
async function analyzeWorkspace() {
const { promises: fs } = await import('fs');
const path = await import('path');
const cwd = process.cwd();
console.log('🔍 Analyzing workspace...\n');
const summary = [];
// Check for key files
const filesToCheck = [
PACKAGE_JSON_FILENAME,
TSCONFIG_JSON_FILENAME,
'.env',
README_FILENAME,
'api-specs/',
'admin-interface/',
'dist/',
CONFIG_FILENAME,
PROCESSOR_CONFIG_FILENAME
];
for (const file of filesToCheck) {
try {
const stat = await fs.stat(path.join(cwd, file));
summary.push(`✅ ${file} found (${stat.isDirectory() ? 'directory' : 'file'})`);
}
catch {
summary.push(`❌ ${file} missing`);
}
}
// Parse package.json
try {
const pkgRaw = await fs.readFile(path.join(cwd, PACKAGE_JSON_FILENAME), 'utf-8');
const pkg = JSON.parse(pkgRaw);
summary.push(`\n📦 Package: ${pkg.name} v${pkg.version}`);
summary.push(` Description: ${pkg.description}`);
summary.push(` Main: ${pkg.main}`);
summary.push(` Bin: ${JSON.stringify(pkg.bin)}`);
summary.push(` Scripts: ${Object.keys(pkg.scripts).length} scripts defined`);
summary.push(` Dependencies: ${Object.keys(pkg.dependencies || {}).length}`);
summary.push(` DevDependencies: ${Object.keys(pkg.devDependencies || {}).length}`);
}
catch {
summary.push('⚠️ Could not parse package.json');
}
// Parse tsconfig.json
try {
const tsconfigRaw = await fs.readFile(path.join(cwd, TSCONFIG_JSON_FILENAME), 'utf-8');
const tsconfig = JSON.parse(tsconfigRaw);
summary.push(`\n🛠️ TypeScript config: target=${tsconfig.compilerOptions?.target}, module=${tsconfig.compilerOptions?.module}`);
}
catch {
summary.push('⚠️ Could not parse tsconfig.json');
}
// Check for .env and required variables
try {
const envRaw = await fs.readFile(path.join(cwd, '.env'), 'utf-8');
summary.push('\n🔑 .env file present');
const requiredVars = ['GOOGLE_AI_API_KEY', 'AZURE_OPENAI_ENDPOINT', 'AZURE_AI_API_KEY', 'GITHUB_TOKEN', 'OLLAMA_ENDPOINT'];
for (const v of requiredVars) {
if (envRaw.includes(v)) {
summary.push(` • ${v} present`);
}
else {
summary.push(` • ${v} missing`);
}
}
}
catch {
summary.push('⚠️ .env file missing or unreadable');
}
// Parse config-rga.json
try {
const configRgaRaw = await fs.readFile(path.join(cwd, CONFIG_FILENAME), 'utf-8');
const configRga = JSON.parse(configRgaRaw);
summary.push(`\n⚙️ config-rga.json loaded: currentProvider=${configRga.currentProvider}, outputDir=${configRga.defaultOutputDir}`);
summary.push(` Providers: ${Object.keys(configRga.providers || {}).join(', ')}`);
summary.push(` VCS: enabled=${configRga.docsVcs?.enabled}, autoCommit=${configRga.docsVcs?.autoCommit}`);
summary.push(` Confluence: ${configRga.confluence?.baseUrl || 'not set'}`);
summary.push(` SharePoint: ${configRga.sharepoint?.siteUrl || 'not set'}`);
}
catch {
summary.push('⚠️ Could not parse config-rga.json');
}
// Parse processor-config.json
try {
const procConfigPath = path.join(cwd, PROCESSOR_CONFIG_FILENAME);
const procConfigRaw = await fs.readFile(procConfigPath, 'utf-8');
const procConfig = JSON.parse(procConfigRaw);
summary.push(`\n🧩 processor-config.json loaded: ${Object.keys(procConfig).length} keys`);
}
catch {
summary.push('⚠️ Could not parse processor-config.json');
}
// Output summary
console.log(summary.join('\n'));
console.log('\nAnalysis complete.');
}
async function runEnhancedSetupWizard() {
const readline = (await import('readline')).createInterface({ input: process.stdin, output: process.stdout });
const fs = await import('fs/promises');
const path = await import('path');
const cwd = process.cwd();
console.log('🧙♂️ Interactive Setup Wizard\n');
function ask(question) {
return new Promise(resolve => readline.question(question, answer => resolve(answer.trim())));
}
// Choose provider
const provider = await ask('Select AI provider (google/azure/github/ollama): ');
let envVars = {};
switch (provider.toLowerCase()) {
case 'google':
envVars = {
GOOGLE_AI_API_KEY: await ask('Enter your Google AI API Key: '),
GOOGLE_AI_MODEL: await ask('Enter Google AI Model (default: gemini-1.5-flash): ') || 'gemini-1.5-flash'
};
break;
case 'azure':
envVars = {
AZURE_OPENAI_ENDPOINT: await ask('Enter your Azure OpenAI Endpoint: '),
DEPLOYMENT_NAME: await ask('Enter Azure deployment name (default: gpt-4): ') || 'gpt-4',
USE_ENTRA_ID: await ask('Use Entra ID? (true/false, default: false): ') || 'false',
AZURE_AI_API_KEY: await ask('Enter Azure API Key (optional, press enter to skip): ')
};
break;
case 'github':
envVars = {
GITHUB_TOKEN: await ask('Enter your GitHub AI Token: '),
GITHUB_ENDPOINT: await ask('Enter GitHub AI Endpoint (default: https://models.github.ai/inference/): ') || 'https://models.github.ai/inference/',
REQUIREMENTS_AGENT_MODEL: await ask('Enter model (default: gpt-4o-mini): ') || 'gpt-4o-mini'
};
break;
case 'ollama':
envVars = {
OLLAMA_ENDPOINT: await ask('Enter Ollama endpoint (default: http://localhost:11434): ') || 'http://localhost:11434',
REQUIREMENTS_AGENT_MODEL: await ask('Enter model (default: llama3.1): ') || 'llama3.1'
};
break;
default:
console.log('Unknown provider. Exiting setup.');
readline.close();
process.exit(1);
}
// Write .env file
let envContent = '';
for (const [k, v] of Object.entries(envVars)) {
if (v)
envContent += `${k}=${v}\n`;
}
try {
await fs.writeFile(path.join(cwd, '.env'), envContent, { encoding: 'utf-8' });
console.log('\n✅ .env file created/updated.');
}
catch (e) {
console.error('❌ Failed to write .env:', e);
}
// Optionally update config-rga.json
const updateConfig = (await ask('Update config-rga.json with provider/model? (y/n): ')).toLowerCase() === 'y';
if (updateConfig) {
try {
const configPath = path.join(cwd, CONFIG_FILENAME);
const configRaw = await fs.readFile(configPath, 'utf-8');
const config = JSON.parse(configRaw);
config.currentProvider = provider + (provider.endsWith('-ai') ? '' : '-ai');
if (!config.providers)
config.providers = {};
config.providers[config.currentProvider] = { model: envVars.REQUIREMENTS_AGENT_MODEL || envVars.GOOGLE_AI_MODEL || envVars.DEPLOYMENT_NAME || '' };
await fs.writeFile(configPath, JSON.stringify(config, null, 2), { encoding: 'utf-8' });
console.log('✅ config-rga.json updated.');
}
catch (e) {
console.error('❌ Failed to update config-rga.json:', e);
}
}
// Optionally update processor-config.json
const updateProc = (await ask('Update processor-config.json (for advanced users)? (y/n): ')).toLowerCase() === 'y';
if (updateProc) {
try {
const procPath = path.join(cwd, PROCESSOR_CONFIG_FILENAME);
let procConfig = {};
try {
const procRaw = await fs.readFile(procPath, 'utf-8');
procConfig = JSON.parse(procRaw);
}
catch { }
// Example: add a timestamp or note
procConfig['lastSetup'] = new Date().toISOString();
await fs.writeFile(procPath, JSON.stringify(procConfig, null, 2), { encoding: 'utf-8' });
console.log('✅ processor-config.json updated.');
}
catch (e) {
console.error('❌ Failed to update processor-config.json:', e);
}
}
// Offer to run npm install
const doInstall = (await ask('Run npm install now? (y/n): ')).toLowerCase() === 'y';
if (doInstall) {
const { execSync } = await import('child_process');
try {
execSync('npm install', { stdio: 'inherit' });
console.log('✅ npm install complete.');
}
catch (e) {
console.error('❌ npm install failed:', e);
}
}
// Offer to run build
const doBuild = (await ask('Run npm run build now? (y/n): ')).toLowerCase() === 'y';
if (doBuild) {
const { execSync } = await import('child_process');
try {
execSync('npm run build', { stdio: 'inherit' });
console.log('✅ Build complete.');
}
catch (e) {
console.error('❌ Build failed:', e);
}
}
readline.close();
console.log('\nSetup complete. You may now use the CLI.');
}
/**
* Run User Stories menu for implementing the user story requirements
*/
async function runUserStoriesMenu() {
const readline = (await import('readline')).createInterface({
input: process.stdin,
output: process.stdout
});
function ask(question) {
return new Promise(resolve => readline.question(question, answer => resolve(answer.trim())));
}
console.log('\n🎯 User Stories Implementation Menu');
console.log('=====================================');
console.log('This menu implements the user stories from the requirements document.');
console.log('');
try {
// Get common inputs
const businessProblem = await ask('📋 Enter the business problem: ');
if (!businessProblem || businessProblem.length < 10) {
console.log('❌ Business problem must be at least 10 characters long.');
readline.close();
return;
}
const techStackInput = await ask('🔧 Enter technology stack (comma-separated): ');
const technologyStack = techStackInput ? techStackInput.split(',').map(s => s.trim()) : [];
const contextBundle = await ask('📝 Enter additional context (optional): ');
const outputDir = await ask('📁 Enter output directory (default: ./output): ') || './output';
const format = await ask('📄 Enter output format (json/markdown, default: markdown): ') || 'markdown';
console.log('\n🚀 Available User Story Commands:');
console.log('1. Strategic Planning (User Story 2)');
console.log('2. Requirements Generation (User Story 3)');
console.log('3. Technology Analysis (User Story 7)');
console.log('4. Risk Management (User Story 8)');
console.log('5. Comprehensive Analysis (All User Stories)');
console.log('6. Exit');
const choice = await ask('\nSelect an option (1-6): ');
const options = {
businessProblem,
technologyStack,
contextBundle,
output: outputDir,
format: format,
quiet: false
};
const { handleStrategicPlanningCommand, handleRequirementsGenerationCommand, handleTechnologyAnalysisCommand, handleRiskManagementCommand, handleComprehensiveAnalysisCommand } = await import('./commands/user-stories.js');
switch (choice) {
case '1':
console.log('\n🎯 Generating Strategic Planning Documents...');
await handleStrategicPlanningCommand(options);
break;
case '2':
console.log('\n📋 Generating Comprehensive Requirements...');
// Force JSON format for requirements to ensure strict JSON output (User Story 9)
await handleRequirementsGenerationCommand({ ...options, format: 'json' });
break;
case '3':
console.log('\n🔧 Analyzing Technology Stack...');
await handleTechnologyAnalysisCommand(options);
break;
case '4':
console.log('\n⚠️ Generating Risk Management Plan...');
await handleRiskManagementCommand(options);
break;
case '5':
console.log('\n🚀 Running Comprehensive Analysis...');
await handleComprehensiveAnalysisCommand(options);
break;
case '6':
console.log('👋 Goodbye!');
break;
default:
console.log('❌ Invalid choice. Please select 1-6.');
}
}
catch (error) {
console.error('❌ Error in user stories menu:', error);
}
finally {
readline.close();
}
}
// --- FUTURE TESTING REMINDER ---
// When updating retry/backoff logic or AI/model integration, add/maintain integration tests that simulate rate limits, network errors, and provider failures.\n// Use CLI flags --retries, --retry-backoff, --retry-max-delay for test scenarios.\n// See documentation for test strategies and update as needed.
//# sourceMappingURL=cli.js.map