UNPKG

mycoder

Version:

A command line tool using agent that can do arbitrary tasks, including coding tasks

192 lines 8.88 kB
import * as fs from 'fs/promises'; import chalk from 'chalk'; import { toolAgent, Logger, getTools, getProviderApiKeyError, providerConfig, userPrompt, LogLevel, agentExecuteTool, errorToString, DEFAULT_CONFIG, SessionTracker, ShellTracker, AgentTracker, consoleOutputLogger, } from 'mycoder-agent'; import { TokenTracker } from 'mycoder-agent/dist/core/tokens.js'; import { initInteractiveInput } from 'mycoder-agent/dist/utils/interactiveInput.js'; import { captureException } from '../sentry/index.js'; import { getConfigFromArgv, loadConfig } from '../settings/config.js'; import { checkGitCli } from '../utils/gitCliCheck.js'; import { nameToLogIndex } from '../utils/nameToLogIndex.js'; import { checkForUpdates, getPackageInfo } from '../utils/versionCheck.js'; /** * Executes a prompt with the given configuration * This function is exported to be reused by custom commands */ export async function executePrompt(prompt, config) { const packageInfo = getPackageInfo(); const logger = new Logger({ name: 'Default', logLevel: nameToLogIndex(config.logLevel), customPrefix: agentExecuteTool.logPrefix, }); logger.listeners.push(consoleOutputLogger); logger.info(`MyCoder v${packageInfo.version} - AI-powered coding assistant`); // Skip version check if upgradeCheck is false if (config.upgradeCheck !== false) { await checkForUpdates(logger); } // Check for git and gh CLI tools if GitHub mode is enabled if (config.githubMode) { logger.debug('GitHub mode is enabled, checking for git and gh CLI tools...'); const gitCliCheck = await checkGitCli(logger); if (gitCliCheck.errors.length > 0) { logger.warn('GitHub mode is enabled but there are issues with git/gh CLI tools:'); gitCliCheck.errors.forEach((error) => logger.warn(`- ${error}`)); if (!gitCliCheck.gitAvailable || !gitCliCheck.ghAvailable) { logger.warn('GitHub mode requires git and gh CLI tools to be installed.'); logger.warn('Please install the missing tools or disable GitHub mode with --githubMode false'); // Disable GitHub mode if git or gh CLI is not available logger.info('Disabling GitHub mode due to missing CLI tools.'); config.githubMode = false; } else if (!gitCliCheck.ghAuthenticated) { logger.warn('GitHub CLI is not authenticated. Please run "gh auth login" to authenticate.'); // Disable GitHub mode if gh CLI is not authenticated logger.info('Disabling GitHub mode due to unauthenticated GitHub CLI.'); config.githubMode = false; } } else { logger.info('GitHub mode is enabled and all required CLI tools are available.'); } } const tokenTracker = new TokenTracker('Root', undefined, config.tokenUsage ? LogLevel.info : LogLevel.debug); // Use command line option if provided, otherwise use config value tokenTracker.tokenCache = config.tokenCache; // Initialize interactive input if enabled let cleanupInteractiveInput; try { // Early API key check based on model provider const providerSettings = providerConfig[config.provider]; if (!providerSettings) { // Unknown provider logger.info(`Unknown provider: ${config.provider}`); throw new Error(`Unknown provider: ${config.provider}`); } // only validate key if baseUrl is not set, otherwise we assume the user is using a local provider let apiKey = undefined; const { keyName } = providerSettings; if (keyName) { // Then fall back to environment variable logger.info(`Looking API key in env: ${keyName}`); apiKey = process.env[keyName]; if (!config.baseUrl) { if (!apiKey) { logger.error(getProviderApiKeyError(config.provider)); throw new Error(`${config.provider} API key not found`); } } } logger.info(`LLM: ${config.provider}/${config.model ?? providerSettings.model}`); if (apiKey) { logger.info(`Using API key: ${apiKey.slice(0, 4)}...`); } if (config.baseUrl) { // For Ollama, we check if the base URL is set logger.info(`Using base url: ${config.baseUrl}`); } console.log(); // Add the standard suffix to all prompts prompt += [ 'Please ask for clarifications if required or if the tasks is confusing.', "If you need more context, don't be scared to create a sub-agent to investigate and generate report back, this can save a lot of time and prevent obvious mistakes.", 'Once the task is complete ask the user, via the userPrompt tool if the results are acceptable or if changes are needed or if there are additional follow on tasks.', ].join('\\n'); const tools = getTools({ userPrompt: config.userPrompt, mcpConfig: config.mcp, }); // Error handling process.on('SIGINT', () => { logger.log(tokenTracker.logLevel, chalk.blueBright(`[Token Usage Total] ${tokenTracker.toString()}`)); process.exit(0); }); // Initialize interactive input if enabled if (config.interactive) { logger.info(chalk.green('Interactive correction mode enabled. Press Ctrl+M to send a correction to the agent.')); cleanupInteractiveInput = initInteractiveInput(); } // Create a config for the agent const agentConfig = { ...DEFAULT_CONFIG, }; const result = await toolAgent(prompt, tools, agentConfig, { logger, headless: config.headless, userSession: config.userSession, pageFilter: config.pageFilter, workingDirectory: '.', tokenTracker, githubMode: config.githubMode, customPrompt: config.customPrompt, tokenCache: config.tokenCache, userPrompt: config.userPrompt, provider: config.provider, baseUrl: config.baseUrl, model: config.model, maxTokens: config.maxTokens, temperature: config.temperature, shellTracker: new ShellTracker('mainAgent'), agentTracker: new AgentTracker('mainAgent'), browserTracker: new SessionTracker('mainAgent'), apiKey, }); const output = typeof result.result === 'string' ? result.result : JSON.stringify(result.result, null, 2); logger.info('\\n=== Result ===\\n', output); } catch (error) { logger.error('An error occurred:', errorToString(error), error instanceof Error ? error.stack : ''); // Capture the error with Sentry captureException(error); } finally { // Clean up interactive input if it was initialized if (cleanupInteractiveInput) { cleanupInteractiveInput(); } // Other cleanup is handled by the cleanup utility } logger.log(tokenTracker.logLevel, chalk.blueBright(`[Token Usage Total] ${tokenTracker.toString()}`)); } export const command = { command: '* [prompt]', describe: 'Execute a prompt or start interactive mode', builder: (yargs) => { return yargs.positional('prompt', { type: 'string', description: 'The prompt to execute', }); }, handler: async (argv) => { // Get configuration for model provider and name const argvConfig = getConfigFromArgv(argv); const config = await loadConfig(argvConfig); let prompt; // If promptFile is specified, read from file if (argv.file) { prompt = await fs.readFile(argv.file, 'utf-8'); } // If interactive mode if (argv.interactive) { prompt = await userPrompt("Type your request below or 'help' for usage information. Use Ctrl+C to exit."); } else if (!prompt) { // Use command line prompt if provided prompt = argv.prompt; } if (!prompt) { const logger = new Logger({ name: 'Default', logLevel: nameToLogIndex(config.logLevel), customPrefix: agentExecuteTool.logPrefix, }); logger.error('No prompt provided. Either specify a prompt, use --promptFile, or run in --interactive mode.'); throw new Error('No prompt provided'); } // Execute the prompt await executePrompt(prompt, config); }, }; //# sourceMappingURL=$default.js.map