UNPKG

autoagent-cli

Version:

Run autonomous AI agents using Claude or Gemini for task execution

732 lines (731 loc) 31.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.registerConfigCommand = registerConfigCommand; const config_manager_1 = require("../../core/config-manager"); const logger_1 = require("../../utils/logger"); const fs = __importStar(require("fs/promises")); const path = __importStar(require("path")); function registerConfigCommand(program) { const config = program .command('config') .description('Manage configuration'); config .command('get [key]') .description('Get configuration value(s)') .option('--show-source', 'Show the source of each configuration value') .action(async (key, options) => { try { const configManager = new config_manager_1.ConfigManager(); const localConfigPath = path.join(process.cwd(), '.autoagent', 'config.json'); let hasCorruptedJson = false; try { const content = await fs.readFile(localConfigPath, 'utf-8'); JSON.parse(content); } catch (error) { if (error instanceof SyntaxError) { hasCorruptedJson = true; } } if (hasCorruptedJson) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } await configManager.loadConfig(); const currentConfig = configManager.getConfig(); if (key !== null && key !== undefined && key.length > 0) { const value = getConfigValue(key, currentConfig); const envValue = getEnvValue(key); const displayValue = envValue !== undefined ? envValue : value; if (displayValue === undefined) { logger_1.Logger.error(`Unknown configuration key: ${key}`); process.exit(1); } let output = `${key}: ${displayValue !== null ? String(displayValue) : 'undefined'}`; if (options?.showSource === true) { const source = envValue !== undefined ? 'environment' : 'local'; output += ` (${source})`; } logger_1.Logger.info(output); } else { const allKeys = Object.keys(currentConfig); for (const k of allKeys) { const value = getConfigValue(k, currentConfig); const envValue = getEnvValue(k); const displayValue = envValue !== undefined ? envValue : value; let output = `${k}: ${displayValue !== null ? String(displayValue) : 'undefined'}`; if (options?.showSource === true) { const source = envValue !== undefined ? 'environment' : 'local'; output += ` (${source})`; } logger_1.Logger.info(output); } } } catch (error) { if (!(error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON')))) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); } process.exit(1); } }); config .command('set <key> <value>') .description('Set configuration value') .option('-g, --global', 'Set in global configuration') .action(async (key, value, options) => { try { const configManager = new config_manager_1.ConfigManager(); try { await configManager.loadConfig(); } catch (error) { if (error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON'))) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } throw error; } const validatedValue = validateConfigValue(key, value); const updates = {}; setConfigValue(key, validatedValue, updates); try { await configManager.updateConfig(updates, options.global === true ? 'global' : 'local'); } catch (error) { if (error.code === 'EACCES' || error.code === 'EPERM') { logger_1.Logger.error('Permission denied: Cannot write to configuration file'); process.exit(1); } throw error; } logger_1.Logger.success(`Configuration updated: ${key} = ${value}`); } catch (error) { const isJsonError = error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON')); const isPermissionError = error.code === 'EACCES' || error.code === 'EPERM'; if (!isJsonError && !isPermissionError) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); } process.exit(1); } }); config .command('reset') .description('Reset configuration to defaults') .option('-g, --global', 'Reset global configuration') .action(async (options) => { try { const configPath = options.global === true ? (() => { const homeDir = process.env.HOME; if (homeDir === null || homeDir === undefined || homeDir.length === 0) { throw new Error('HOME environment variable not found'); } return path.join(homeDir, '.autoagent', 'config.json'); })() : path.join(process.cwd(), '.autoagent', 'config.json'); try { await fs.unlink(configPath); logger_1.Logger.success(`Configuration reset ${options.global === true ? 'globally' : 'locally'}`); } catch (error) { if (error.code !== 'ENOENT') { throw error; } logger_1.Logger.info('No configuration file to reset'); } } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('export <filename>') .description('Export configuration to file') .action(async (filename) => { try { const configManager = new config_manager_1.ConfigManager(); try { await configManager.loadConfig(); } catch (error) { if (error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON'))) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } throw error; } const currentConfig = configManager.getConfig(); const exportPath = path.resolve(process.cwd(), filename); await fs.writeFile(exportPath, JSON.stringify(currentConfig, null, 2)); logger_1.Logger.success(`Configuration exported to ${filename}`); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('import <filename>') .description('Import configuration from file') .option('-g, --global', 'Import to global configuration') .action(async (filename, options) => { try { const configManager = new config_manager_1.ConfigManager(); const importPath = path.resolve(process.cwd(), filename); const content = await fs.readFile(importPath, 'utf-8'); const importedConfig = JSON.parse(content); for (const [key, value] of Object.entries(importedConfig)) { validateConfigValue(key, String(value)); } await configManager.saveConfig(importedConfig, options.global); logger_1.Logger.success(`Configuration imported from ${filename}`); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('init') .description('Initialize configuration') .option('-g, --global', 'Initialize global configuration') .action(async (options) => { try { const configManager = new config_manager_1.ConfigManager(); await configManager.initConfig(options.global); logger_1.Logger.success(`Configuration initialized ${options.global === true ? 'globally' : 'locally'}`); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('set-provider <provider>') .description('Set default AI provider (claude or gemini)') .option('-g, --global', 'Set globally') .action(async (provider, options) => { try { const configManager = new config_manager_1.ConfigManager(); try { await configManager.loadConfig(); } catch (error) { if (error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON'))) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } throw error; } await configManager.setProvider(provider, options.global); logger_1.Logger.success(`Default provider set to ${provider}`); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('set-failover <providers...>') .description('Set failover providers in order of preference') .option('-g, --global', 'Set globally') .action(async (providers, options) => { try { const configManager = new config_manager_1.ConfigManager(); try { await configManager.loadConfig(); } catch (error) { if (error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON'))) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } throw error; } await configManager.setFailoverProviders(providers, options.global); logger_1.Logger.success(`Failover providers set to: ${providers.join(', ')}`); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('set-auto-commit <enabled>') .description('Enable or disable automatic git commits via hooks (true/false)') .option('-g, --global', 'Set globally') .action(async (enabled, options) => { try { const configManager = new config_manager_1.ConfigManager(); let userConfig; try { userConfig = await configManager.loadConfig(); } catch (error) { if (error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON'))) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } throw error; } if (!userConfig.hooks) { userConfig.hooks = {}; } const hookPoint = 'PostExecutionEnd'; const gitCommitHook = { type: 'git-commit', message: 'Complete issue from {{issueFile}}' }; if (enabled === 'true') { if (!userConfig.hooks[hookPoint]) { userConfig.hooks[hookPoint] = []; } const existingCommitHook = userConfig.hooks[hookPoint].find(h => h.type === 'git-commit'); if (!existingCommitHook) { userConfig.hooks[hookPoint].push(gitCommitHook); } logger_1.Logger.success('✅ Auto-commit enabled via git-commit hook'); logger_1.Logger.info('ℹ️ Git commits will be created after each successful execution'); } else { if (userConfig.hooks[hookPoint]) { userConfig.hooks[hookPoint] = userConfig.hooks[hookPoint].filter(h => h.type !== 'git-commit'); if (userConfig.hooks[hookPoint].length === 0) { delete userConfig.hooks[hookPoint]; } } logger_1.Logger.success('✅ Auto-commit disabled'); logger_1.Logger.info('ℹ️ Git commits will not be created automatically'); } await configManager.saveConfig(userConfig, options.global); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('set-co-authored-by <enabled>') .description('Include AI co-authorship in commits (true/false)') .option('-g, --global', 'Set globally') .action(async (enabled, options) => { try { const configManager = new config_manager_1.ConfigManager(); let userConfig; try { userConfig = await configManager.loadConfig(); } catch (error) { if (error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON'))) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } throw error; } userConfig.includeCoAuthoredBy = enabled === 'true'; await configManager.saveConfig(userConfig, options.global); logger_1.Logger.success(`Co-authorship ${userConfig.includeCoAuthoredBy ? 'enabled' : 'disabled'}`); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('set-git-no-verify <value>') .description('Skip git pre-commit and commit-msg hooks when committing (true/false)') .option('-g, --global', 'Set globally') .action(async (value, options) => { try { const configManager = new config_manager_1.ConfigManager(); let userConfig; try { userConfig = await configManager.loadConfig(); } catch (error) { if (error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON'))) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } throw error; } const normalizedValue = value.toLowerCase(); if (normalizedValue !== 'true' && normalizedValue !== 'false') { logger_1.Logger.error('Invalid value: must be "true" or "false"'); process.exit(1); } const noVerify = normalizedValue === 'true'; if (!userConfig.hooks) { userConfig.hooks = {}; } for (const hookPoint in userConfig.hooks) { const hooks = userConfig.hooks[hookPoint]; if (Array.isArray(hooks)) { for (const hook of hooks) { if (hook.type === 'git-commit') { hook.noVerify = noVerify; } } } } await configManager.saveConfig(userConfig, options.global); logger_1.Logger.success(`Git hooks ${noVerify ? 'disabled (--no-verify)' : 'enabled'} for commits`); logger_1.Logger.info(noVerify ? 'ℹ️ Pre-commit and commit-msg hooks will be skipped when AutoAgent commits' : 'ℹ️ Pre-commit and commit-msg hooks will run when AutoAgent commits'); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('set-auto-push <value>') .description('Enable or disable automatic git push via hooks (true/false)') .option('-g, --global', 'Set globally') .action(async (value, options) => { try { const configManager = new config_manager_1.ConfigManager(); let userConfig; try { userConfig = await configManager.loadConfig(); } catch (error) { if (error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON'))) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } throw error; } const normalizedValue = value.toLowerCase(); if (normalizedValue !== 'true' && normalizedValue !== 'false') { logger_1.Logger.error('Invalid value: must be "true" or "false"'); process.exit(1); } if (!userConfig.hooks) { userConfig.hooks = {}; } const hookPoint = 'PostExecutionEnd'; const gitPushHook = { type: 'git-push', remote: 'origin' }; const autoPush = normalizedValue === 'true'; if (autoPush) { if (!userConfig.hooks[hookPoint]) { userConfig.hooks[hookPoint] = []; } const existingPushHook = userConfig.hooks[hookPoint].find(h => h.type === 'git-push'); if (!existingPushHook) { userConfig.hooks[hookPoint].push(gitPushHook); } logger_1.Logger.success('✅ Auto-push enabled via git-push hook'); logger_1.Logger.info('ℹ️ Git commits will automatically be pushed to the remote repository'); logger_1.Logger.warning('Make sure you have a git remote configured with proper permissions'); } else { if (userConfig.hooks[hookPoint]) { userConfig.hooks[hookPoint] = userConfig.hooks[hookPoint].filter(h => h.type !== 'git-push'); if (userConfig.hooks[hookPoint].length === 0) { delete userConfig.hooks[hookPoint]; } } logger_1.Logger.success('✅ Auto-push disabled'); logger_1.Logger.info('ℹ️ Git commits will not be pushed automatically'); } await configManager.saveConfig(userConfig, options.global); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('set-push-remote <remote>') .description('Set the git remote for git-push hooks') .option('-g, --global', 'Set globally') .action(async (remote, options) => { try { const configManager = new config_manager_1.ConfigManager(); let userConfig; try { userConfig = await configManager.loadConfig(); } catch (error) { if (error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON'))) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } throw error; } let hooksUpdated = 0; if (userConfig.hooks) { for (const hookPoint of Object.keys(userConfig.hooks)) { const hooks = userConfig.hooks[hookPoint]; if (hooks) { for (const hook of hooks) { if (hook.type === 'git-push') { hook.remote = remote; hooksUpdated++; } } } } } await configManager.saveConfig(userConfig, options.global); logger_1.Logger.success(`✅ Push remote set to: ${remote}`); if (hooksUpdated > 0) { logger_1.Logger.info(`ℹ️ Updated ${hooksUpdated} git-push hook${hooksUpdated > 1 ? 's' : ''} to use '${remote}' remote`); } else { logger_1.Logger.info('ℹ️ No git-push hooks found. Run \'autoagent config set-auto-push true\' to enable auto-push'); } logger_1.Logger.info('ℹ️ Make sure this remote exists in your git repository'); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('show') .description('Show current configuration') .action(async () => { try { const configManager = new config_manager_1.ConfigManager(); try { await configManager.loadConfig(); } catch (error) { if (error instanceof SyntaxError || (error instanceof Error && error.message.includes('JSON'))) { logger_1.Logger.error('Failed to parse configuration'); process.exit(1); } throw error; } await configManager.showConfig(); } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); config .command('clear-limits') .description('Clear rate limit records') .option('-p, --provider <provider>', 'Clear specific provider') .action(async (options) => { try { const configManager = new config_manager_1.ConfigManager(); if (options.provider !== undefined && options.provider !== '') { await configManager.clearRateLimit(options.provider); logger_1.Logger.success(`Rate limit cleared for ${options.provider}`); } else { await configManager.clearRateLimit('claude'); await configManager.clearRateLimit('gemini'); logger_1.Logger.success('All rate limits cleared'); } } catch (error) { logger_1.Logger.error(`Failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } }); } function getConfigValue(key, config) { switch (key) { case 'provider': return config.providers?.[0]; case 'providers': return config.providers; case 'verbose': return config.logLevel === 'debug'; case 'logLevel': return config.logLevel; case 'maxTokens': return config.maxTokens; case 'retryAttempts': return config.retryAttempts; case 'failoverDelay': return config.failoverDelay; case 'includeCoAuthoredBy': return config.includeCoAuthoredBy; case 'customInstructions': return config.customInstructions; case 'rateLimitCooldown': return config.rateLimitCooldown; case 'hooks': return config.hooks; case 'strictCompletion': return config.strictCompletion; case 'completionConfidence': return config.completionConfidence; case 'ignoreToolFailures': return config.ignoreToolFailures; case 'maxRetryAttempts': return config.maxRetryAttempts; default: return undefined; } } function setConfigValue(key, value, updates) { switch (key) { case 'provider': updates.providers = [value]; break; case 'providers': updates.providers = value; break; case 'verbose': updates.logLevel = Boolean(value) === true ? 'debug' : 'info'; break; case 'logLevel': updates.logLevel = value; break; case 'maxTokens': updates.maxTokens = value; break; case 'retryAttempts': updates.retryAttempts = value; break; case 'failoverDelay': updates.failoverDelay = value; break; case 'includeCoAuthoredBy': updates.includeCoAuthoredBy = value; break; case 'customInstructions': updates.customInstructions = value; break; case 'rateLimitCooldown': updates.rateLimitCooldown = value; break; case 'strictCompletion': updates.strictCompletion = value; break; case 'completionConfidence': updates.completionConfidence = value; break; case 'ignoreToolFailures': updates.ignoreToolFailures = value; break; case 'maxRetryAttempts': updates.maxRetryAttempts = value; break; default: throw new Error(`Unknown configuration key: ${key}`); } } function getEnvValue(key) { const envMap = { 'verbose': 'AUTOAGENT_VERBOSE', 'provider': 'AUTOAGENT_PROVIDER', 'logLevel': 'AUTOAGENT_LOG_LEVEL', 'maxTokens': 'AUTOAGENT_MAX_TOKENS', 'retryAttempts': 'AUTOAGENT_RETRY_ATTEMPTS', 'failoverDelay': 'AUTOAGENT_FAILOVER_DELAY', 'includeCoAuthoredBy': 'AUTOAGENT_INCLUDE_CO_AUTHORED_BY', 'customInstructions': 'AUTOAGENT_CUSTOM_INSTRUCTIONS', 'rateLimitCooldown': 'AUTOAGENT_RATE_LIMIT_COOLDOWN', 'strictCompletion': 'AUTOAGENT_STRICT_COMPLETION', 'completionConfidence': 'AUTOAGENT_COMPLETION_CONFIDENCE', 'ignoreToolFailures': 'AUTOAGENT_IGNORE_TOOL_FAILURES', 'maxRetryAttempts': 'AUTOAGENT_MAX_RETRY_ATTEMPTS' }; const envKey = envMap[key]; if (envKey === null || envKey === undefined || envKey.length === 0 || process.env[envKey] === null || process.env[envKey] === undefined || process.env[envKey] === '') { return undefined; } const envValue = process.env[envKey]; if (key === 'verbose' || key === 'includeCoAuthoredBy' || key === 'strictCompletion' || key === 'ignoreToolFailures') { return envValue === 'true'; } if (key === 'maxTokens' || key === 'retryAttempts' || key === 'failoverDelay' || key === 'rateLimitCooldown' || key === 'completionConfidence' || key === 'maxRetryAttempts') { return parseInt(envValue, 10); } return envValue; } function validateConfigValue(key, value) { switch (key) { case 'provider': if (!['claude', 'gemini'].includes(value)) { throw new Error(`Invalid value for provider: ${value}. Must be 'claude' or 'gemini'`); } return value; case 'verbose': case 'includeCoAuthoredBy': case 'strictCompletion': case 'ignoreToolFailures': if (!['true', 'false'].includes(value)) { throw new Error(`Invalid value for ${key}: ${value}. Value must be boolean (true/false)`); } return value === 'true'; case 'logLevel': if (!['debug', 'info', 'warn', 'error'].includes(value)) { throw new Error(`Invalid value for logLevel: ${value}. Must be one of: debug, info, warn, error`); } return value; case 'maxTokens': case 'retryAttempts': case 'failoverDelay': case 'rateLimitCooldown': case 'maxRetryAttempts': { const num = parseInt(value, 10); if (isNaN(num) || num < 0) { throw new Error(`Invalid value for ${key}: ${value}. Must be a positive number`); } return num; } case 'completionConfidence': { const num = parseInt(value, 10); if (isNaN(num) || num < 0 || num > 100) { throw new Error(`Invalid value for completionConfidence: ${value}. Must be a number between 0 and 100`); } return num; } case 'customInstructions': return value; default: throw new Error(`Unknown configuration key: ${key}`); } }