From ba9a4f49b3a28d9431c5608817f827e7b6a0a06f Mon Sep 17 00:00:00 2001 From: Ben Houston Date: Wed, 5 Mar 2025 08:37:07 -0500 Subject: [PATCH 1/5] improve README.md to mention Anthropic Claude works best so far. --- packages/cli/README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 49a268b..e87b280 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -82,17 +82,19 @@ mycoder config set modelName gpt-4o-2024-05-13 ### Model Selection +NOTE: Anthropic Claude 3.7 works the best by far in our testing. + MyCoder supports Anthropic, OpenAI, xAI/Grok, Mistral AI, and Ollama models. You can configure which model provider and model name to use with the following commands: ```bash +# Use Anthropic models [These work the best at this time] +mycoder config set modelProvider anthropic +mycoder config set modelName claude-3-7-sonnet-20250219 # or any other Anthropic model + # Use OpenAI models mycoder config set modelProvider openai mycoder config set modelName gpt-4o-2024-05-13 # or any other OpenAI model -# Use Anthropic models -mycoder config set modelProvider anthropic -mycoder config set modelName claude-3-7-sonnet-20250219 # or any other Anthropic model - # Use xAI/Grok models mycoder config set modelProvider xai mycoder config set modelName grok-1 # or any other xAI model @@ -117,7 +119,7 @@ mycoder --modelProvider openai --modelName gpt-4o-2024-05-13 "Your prompt here" ### Available Configuration Options -- `githubMode`: Enable GitHub mode for working with issues and PRs (default: `false`) +- `githubMode`: Enable GitHub mode (requires "gh" cli to be installed) for working with issues and PRs (default: `false`) - `headless`: Run browser in headless mode with no UI showing (default: `true`) - `userSession`: Use user's existing browser session instead of sandboxed session (default: `false`) - `pageFilter`: Method to process webpage content: 'simple', 'none', or 'readability' (default: `none`) From d0e5cf6cc623127a42990c25f1d65a1ecfe7187e Mon Sep 17 00:00:00 2001 From: Ben Houston Date: Wed, 5 Mar 2025 08:50:57 -0500 Subject: [PATCH 2/5] Add CLI startup profiling feature to diagnose Windows performance issues --- packages/cli/src/commands/test-profile.ts | 14 ++++ packages/cli/src/index.ts | 38 ++++++++-- packages/cli/src/options.ts | 6 ++ packages/cli/src/utils/performance.ts | 88 +++++++++++++++++++++++ 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 packages/cli/src/commands/test-profile.ts create mode 100644 packages/cli/src/utils/performance.ts diff --git a/packages/cli/src/commands/test-profile.ts b/packages/cli/src/commands/test-profile.ts new file mode 100644 index 0000000..43f4dda --- /dev/null +++ b/packages/cli/src/commands/test-profile.ts @@ -0,0 +1,14 @@ +import { CommandModule } from 'yargs'; +import { SharedOptions } from '../options.js'; + +export const command: CommandModule = { + command: 'test-profile', + describe: 'Test the profiling feature', + handler: async () => { + console.log('Profile test completed successfully'); + // Profiling report will be automatically displayed by the main function + + // Force a delay to simulate some processing + await new Promise(resolve => setTimeout(resolve, 100)); + }, +}; \ No newline at end of file diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 7a61e28..4638fe2 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -8,24 +8,47 @@ import { hideBin } from 'yargs/helpers'; import { command as defaultCommand } from './commands/$default.js'; import { command as configCommand } from './commands/config.js'; import { command as testSentryCommand } from './commands/test-sentry.js'; +import { command as testProfileCommand } from './commands/test-profile.js'; import { command as toolsCommand } from './commands/tools.js'; import { sharedOptions } from './options.js'; import { initSentry, captureException } from './sentry/index.js'; -initSentry(); +import { enableProfiling, mark, reportTimings } from './utils/performance.js'; + +mark('After imports'); import type { PackageJson } from 'type-fest'; // Add global declaration for our patched toolAgent +mark('Before sourceMapSupport install'); sourceMapSupport.install(); +mark('After sourceMapSupport install'); const main = async () => { + // Parse argv early to check for profiling flag + const parsedArgv = await yargs(hideBin(process.argv)).options(sharedOptions).parse(); + + // Enable profiling if --profile flag is set + enableProfiling(Boolean(parsedArgv.profile)); + + mark('Main function start'); + dotenv.config(); - + mark('After dotenv config'); + + // Only initialize Sentry if needed + if (process.env.NODE_ENV !== 'development' || process.env.ENABLE_SENTRY === 'true') { + initSentry(); + mark('After Sentry init'); + } + + mark('Before package.json load'); const require = createRequire(import.meta.url); const packageInfo = require('../package.json') as PackageJson; - + mark('After package.json load'); + // Set up yargs with the new CLI interface + mark('Before yargs setup'); await yargs(hideBin(process.argv)) .scriptName(packageInfo.name!) .version(packageInfo.version!) @@ -35,16 +58,23 @@ const main = async () => { .command([ defaultCommand, testSentryCommand, + testProfileCommand, toolsCommand, configCommand, ] as CommandModule[]) .strict() .showHelpOnFail(true) .help().argv; + mark('After yargs setup'); + + // Report timings if profiling is enabled + await reportTimings(); }; -await main().catch((error) => { +await main().catch(async (error) => { console.error(error); + // Report profiling data even if there's an error + await reportTimings(); // Capture the error with Sentry captureException(error); process.exit(1); diff --git a/packages/cli/src/options.ts b/packages/cli/src/options.ts index cd875a5..d03f5b3 100644 --- a/packages/cli/src/options.ts +++ b/packages/cli/src/options.ts @@ -9,6 +9,7 @@ export type SharedOptions = { readonly sentryDsn?: string; readonly modelProvider?: string; readonly modelName?: string; + readonly profile?: boolean; }; export const sharedOptions = { @@ -19,6 +20,11 @@ export const sharedOptions = { default: 'info', choices: ['debug', 'verbose', 'info', 'warn', 'error'], } as const, + profile: { + type: 'boolean', + description: 'Enable performance profiling of CLI startup', + default: false, + } as const, modelProvider: { type: 'string', description: 'AI model provider to use', diff --git a/packages/cli/src/utils/performance.ts b/packages/cli/src/utils/performance.ts new file mode 100644 index 0000000..b67f6f5 --- /dev/null +++ b/packages/cli/src/utils/performance.ts @@ -0,0 +1,88 @@ +import { performance } from 'perf_hooks'; + +// Store start time as soon as this module is imported +const cliStartTime = performance.now(); +const timings: Record = {}; +let isEnabled = false; + +/** + * Enable or disable performance tracking + */ +export function enableProfiling(enabled: boolean): void { + isEnabled = enabled; +} + +/** + * Mark a timing point in the application + */ +export function mark(label: string): void { + if (!isEnabled) return; + timings[label] = performance.now() - cliStartTime; +} + +/** + * Log all collected performance metrics + */ +export async function reportTimings(): Promise { + if (!isEnabled) return; + + console.log('\nšŸ“Š Performance Profile:'); + console.log('======================='); + + // Sort timings by time value + const sortedTimings = Object.entries(timings) + .sort((a, b) => a[1] - b[1]); + + // Calculate durations between steps + let previousTime = 0; + for (const [label, time] of sortedTimings) { + const duration = time - previousTime; + console.log(`${label}: ${time.toFixed(2)}ms (${duration.toFixed(2)}ms)`); + previousTime = time; + } + + console.log(`Total startup time: ${previousTime.toFixed(2)}ms`); + console.log('=======================\n'); + + // Report platform-specific information if on Windows + if (process.platform === 'win32') { + await reportPlatformInfo(); + } +} + +/** + * Collect and report platform-specific information + */ +async function reportPlatformInfo(): Promise { + if (!isEnabled) return; + + console.log('\nšŸ–„ļø Platform Information:'); + console.log('======================='); + console.log(`Platform: ${process.platform}`); + console.log(`Architecture: ${process.arch}`); + console.log(`Node.js version: ${process.version}`); + + // Windows-specific information + if (process.platform === 'win32') { + console.log('Windows-specific details:'); + console.log(`- Current working directory: ${process.cwd()}`); + console.log(`- Path length: ${process.cwd().length} characters`); + + // Check for antivirus markers by measuring file read time + try { + // Using dynamic import to avoid require + const fs = await import('fs'); + const startTime = performance.now(); + fs.readFileSync(process.execPath); + console.log(`- Time to read Node.js executable: ${(performance.now() - startTime).toFixed(2)}ms`); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.log(`- Error reading Node.js executable: ${errorMessage}`); + } + } + + console.log('=======================\n'); +} + +// Initial mark for module load time +mark('Module initialization'); \ No newline at end of file From f3cb823916b4b752bc93ded1dacdf90a8d5ff20a Mon Sep 17 00:00:00 2001 From: Ben Houston Date: Wed, 5 Mar 2025 08:53:13 -0500 Subject: [PATCH 3/5] Add profile option to persistent config --- packages/cli/src/index.ts | 8 ++++++-- packages/cli/src/settings/config.ts | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 4638fe2..c1390c8 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -11,6 +11,7 @@ import { command as testSentryCommand } from './commands/test-sentry.js'; import { command as testProfileCommand } from './commands/test-profile.js'; import { command as toolsCommand } from './commands/tools.js'; import { sharedOptions } from './options.js'; +import { getConfig } from './settings/config.js'; import { initSentry, captureException } from './sentry/index.js'; import { enableProfiling, mark, reportTimings } from './utils/performance.js'; @@ -28,8 +29,11 @@ const main = async () => { // Parse argv early to check for profiling flag const parsedArgv = await yargs(hideBin(process.argv)).options(sharedOptions).parse(); - // Enable profiling if --profile flag is set - enableProfiling(Boolean(parsedArgv.profile)); + // Get config to check for profile setting + const config = getConfig(); + + // Enable profiling if --profile flag is set or if enabled in config + enableProfiling(Boolean(parsedArgv.profile) || Boolean(config.profile)); mark('Main function start'); diff --git a/packages/cli/src/settings/config.ts b/packages/cli/src/settings/config.ts index 7f131ac..796e037 100644 --- a/packages/cli/src/settings/config.ts +++ b/packages/cli/src/settings/config.ts @@ -16,6 +16,7 @@ const defaultConfig = { modelName: 'claude-3-7-sonnet-20250219', ollamaBaseUrl: 'http://localhost:11434/api', customPrompt: '', + profile: false, }; export type Config = typeof defaultConfig; From f7302c16dfe9e636a2d223af2d8b9903edecdd6a Mon Sep 17 00:00:00 2001 From: Ben Houston Date: Wed, 5 Mar 2025 09:01:59 -0500 Subject: [PATCH 4/5] better formatting for performance results --- packages/cli/src/commands/test-profile.ts | 7 ++-- packages/cli/src/index.ts | 50 ++++++++++++----------- packages/cli/src/utils/performance.ts | 37 ++++++++++------- 3 files changed, 53 insertions(+), 41 deletions(-) diff --git a/packages/cli/src/commands/test-profile.ts b/packages/cli/src/commands/test-profile.ts index 43f4dda..50b54e3 100644 --- a/packages/cli/src/commands/test-profile.ts +++ b/packages/cli/src/commands/test-profile.ts @@ -1,4 +1,5 @@ import { CommandModule } from 'yargs'; + import { SharedOptions } from '../options.js'; export const command: CommandModule = { @@ -7,8 +8,8 @@ export const command: CommandModule = { handler: async () => { console.log('Profile test completed successfully'); // Profiling report will be automatically displayed by the main function - + // Force a delay to simulate some processing - await new Promise(resolve => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); }, -}; \ No newline at end of file +}; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index c1390c8..357d272 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -7,12 +7,12 @@ import { hideBin } from 'yargs/helpers'; import { command as defaultCommand } from './commands/$default.js'; import { command as configCommand } from './commands/config.js'; -import { command as testSentryCommand } from './commands/test-sentry.js'; import { command as testProfileCommand } from './commands/test-profile.js'; +import { command as testSentryCommand } from './commands/test-sentry.js'; import { command as toolsCommand } from './commands/tools.js'; import { sharedOptions } from './options.js'; -import { getConfig } from './settings/config.js'; import { initSentry, captureException } from './sentry/index.js'; +import { getConfig } from './settings/config.js'; import { enableProfiling, mark, reportTimings } from './utils/performance.js'; mark('After imports'); @@ -27,30 +27,35 @@ mark('After sourceMapSupport install'); const main = async () => { // Parse argv early to check for profiling flag - const parsedArgv = await yargs(hideBin(process.argv)).options(sharedOptions).parse(); - + const parsedArgv = await yargs(hideBin(process.argv)) + .options(sharedOptions) + .parse(); + // Get config to check for profile setting const config = getConfig(); - + // Enable profiling if --profile flag is set or if enabled in config enableProfiling(Boolean(parsedArgv.profile) || Boolean(config.profile)); - + mark('Main function start'); - + dotenv.config(); mark('After dotenv config'); - + // Only initialize Sentry if needed - if (process.env.NODE_ENV !== 'development' || process.env.ENABLE_SENTRY === 'true') { + if ( + process.env.NODE_ENV !== 'development' || + process.env.ENABLE_SENTRY === 'true' + ) { initSentry(); mark('After Sentry init'); } - + mark('Before package.json load'); const require = createRequire(import.meta.url); const packageInfo = require('../package.json') as PackageJson; mark('After package.json load'); - + // Set up yargs with the new CLI interface mark('Before yargs setup'); await yargs(hideBin(process.argv)) @@ -69,17 +74,16 @@ const main = async () => { .strict() .showHelpOnFail(true) .help().argv; - mark('After yargs setup'); - - // Report timings if profiling is enabled - await reportTimings(); }; -await main().catch(async (error) => { - console.error(error); - // Report profiling data even if there's an error - await reportTimings(); - // Capture the error with Sentry - captureException(error); - process.exit(1); -}); +await main() + .catch(async (error) => { + console.error(error); + // Capture the error with Sentry + captureException(error); + process.exit(1); + }) + .finally(async () => { + // Report timings if profiling is enabled + await reportTimings(); + }); diff --git a/packages/cli/src/utils/performance.ts b/packages/cli/src/utils/performance.ts index b67f6f5..736b43c 100644 --- a/packages/cli/src/utils/performance.ts +++ b/packages/cli/src/utils/performance.ts @@ -25,22 +25,26 @@ export function mark(label: string): void { */ export async function reportTimings(): Promise { if (!isEnabled) return; - - console.log('\nšŸ“Š Performance Profile:'); + + console.log('\nšŸ“Š Performance Profile Results'); console.log('======================='); - + console.log( + `${'Label'.padEnd(40, ' ')}${'Time'.padStart(10, ' ')}${'Duration'.padStart(10, ' ')}`, + ); + // Sort timings by time value - const sortedTimings = Object.entries(timings) - .sort((a, b) => a[1] - b[1]); - + const sortedTimings = Object.entries(timings).sort((a, b) => a[1] - b[1]); + // Calculate durations between steps let previousTime = 0; for (const [label, time] of sortedTimings) { const duration = time - previousTime; - console.log(`${label}: ${time.toFixed(2)}ms (${duration.toFixed(2)}ms)`); + console.log( + `${label.padEnd(40, ' ')}${`${time.toFixed(2)}ms`.padStart(10, ' ')}${`${duration.toFixed(2)}ms`.padStart(10, ' ')}`, + ); previousTime = time; } - + console.log(`Total startup time: ${previousTime.toFixed(2)}ms`); console.log('=======================\n'); @@ -55,34 +59,37 @@ export async function reportTimings(): Promise { */ async function reportPlatformInfo(): Promise { if (!isEnabled) return; - + console.log('\nšŸ–„ļø Platform Information:'); console.log('======================='); console.log(`Platform: ${process.platform}`); console.log(`Architecture: ${process.arch}`); console.log(`Node.js version: ${process.version}`); - + // Windows-specific information if (process.platform === 'win32') { console.log('Windows-specific details:'); console.log(`- Current working directory: ${process.cwd()}`); console.log(`- Path length: ${process.cwd().length} characters`); - + // Check for antivirus markers by measuring file read time try { // Using dynamic import to avoid require const fs = await import('fs'); const startTime = performance.now(); fs.readFileSync(process.execPath); - console.log(`- Time to read Node.js executable: ${(performance.now() - startTime).toFixed(2)}ms`); + console.log( + `- Time to read Node.js executable: ${(performance.now() - startTime).toFixed(2)}ms`, + ); } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = + error instanceof Error ? error.message : String(error); console.log(`- Error reading Node.js executable: ${errorMessage}`); } } - + console.log('=======================\n'); } // Initial mark for module load time -mark('Module initialization'); \ No newline at end of file +mark('Module initialization'); From 790fe119d5f47e12eb8eeb9a7120bbc1a1170541 Mon Sep 17 00:00:00 2001 From: Ben Houston Date: Wed, 5 Mar 2025 09:16:58 -0500 Subject: [PATCH 5/5] Fix double yargs initialization in CLI profiling feature --- packages/cli/src/index.ts | 22 ++++++++++------------ packages/cli/src/utils/performance.ts | 3 ++- packages/cli/tests/settings/config.test.ts | 2 ++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 357d272..ead4f8e 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -26,17 +26,6 @@ sourceMapSupport.install(); mark('After sourceMapSupport install'); const main = async () => { - // Parse argv early to check for profiling flag - const parsedArgv = await yargs(hideBin(process.argv)) - .options(sharedOptions) - .parse(); - - // Get config to check for profile setting - const config = getConfig(); - - // Enable profiling if --profile flag is set or if enabled in config - enableProfiling(Boolean(parsedArgv.profile) || Boolean(config.profile)); - mark('Main function start'); dotenv.config(); @@ -56,9 +45,11 @@ const main = async () => { const packageInfo = require('../package.json') as PackageJson; mark('After package.json load'); + console.log('packageInfo', packageInfo); + // Set up yargs with the new CLI interface mark('Before yargs setup'); - await yargs(hideBin(process.argv)) + const argv = await yargs(hideBin(process.argv)) .scriptName(packageInfo.name!) .version(packageInfo.version!) .options(sharedOptions) @@ -74,6 +65,13 @@ const main = async () => { .strict() .showHelpOnFail(true) .help().argv; + + // Get config to check for profile setting + const config = getConfig(); + + // Enable profiling if --profile flag is set or if enabled in config + enableProfiling(Boolean(argv.profile) || Boolean(config.profile)); + mark('After yargs setup'); }; await main() diff --git a/packages/cli/src/utils/performance.ts b/packages/cli/src/utils/performance.ts index 736b43c..97646f6 100644 --- a/packages/cli/src/utils/performance.ts +++ b/packages/cli/src/utils/performance.ts @@ -14,9 +14,10 @@ export function enableProfiling(enabled: boolean): void { /** * Mark a timing point in the application + * Always collect data, but only report if profiling is enabled */ export function mark(label: string): void { - if (!isEnabled) return; + // Always collect timing data regardless of whether profiling is enabled timings[label] = performance.now() - cliStartTime; } diff --git a/packages/cli/tests/settings/config.test.ts b/packages/cli/tests/settings/config.test.ts index 0ada2c2..9af4894 100644 --- a/packages/cli/tests/settings/config.test.ts +++ b/packages/cli/tests/settings/config.test.ts @@ -44,6 +44,7 @@ describe('Config', () => { modelProvider: 'anthropic', modelName: 'claude-3-7-sonnet-20250219', ollamaBaseUrl: 'http://localhost:11434/api', + profile: false, customPrompt: '', }); expect(fs.existsSync).toHaveBeenCalledWith(mockConfigFile); @@ -77,6 +78,7 @@ describe('Config', () => { modelProvider: 'anthropic', modelName: 'claude-3-7-sonnet-20250219', ollamaBaseUrl: 'http://localhost:11434/api', + profile: false, customPrompt: '', }); });