Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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`)
Expand Down
15 changes: 15 additions & 0 deletions packages/cli/src/commands/test-profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CommandModule } from 'yargs';

import { SharedOptions } from '../options.js';

export const command: CommandModule<object, SharedOptions> = {
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));
},
};
52 changes: 44 additions & 8 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,49 @@ import { hideBin } from 'yargs/helpers';

import { command as defaultCommand } from './commands/$default.js';
import { command as configCommand } from './commands/config.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 { initSentry, captureException } from './sentry/index.js';
initSentry();
import { getConfig } from './settings/config.js';
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 () => {
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');

console.log('packageInfo', packageInfo);

// Set up yargs with the new CLI interface
await yargs(hideBin(process.argv))
mark('Before yargs setup');
const argv = await yargs(hideBin(process.argv))
.scriptName(packageInfo.name!)
.version(packageInfo.version!)
.options(sharedOptions)
Expand All @@ -35,17 +58,30 @@ const main = async () => {
.command([
defaultCommand,
testSentryCommand,
testProfileCommand,
toolsCommand,
configCommand,
] as CommandModule[])
.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().catch((error) => {
console.error(error);
// 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();
});
6 changes: 6 additions & 0 deletions packages/cli/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type SharedOptions = {
readonly sentryDsn?: string;
readonly modelProvider?: string;
readonly modelName?: string;
readonly profile?: boolean;
};

export const sharedOptions = {
Expand All @@ -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',
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/settings/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
96 changes: 96 additions & 0 deletions packages/cli/src/utils/performance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { performance } from 'perf_hooks';

// Store start time as soon as this module is imported
const cliStartTime = performance.now();
const timings: Record<string, number> = {};
let isEnabled = false;

/**
* Enable or disable performance tracking
*/
export function enableProfiling(enabled: boolean): void {
isEnabled = enabled;
}

/**
* Mark a timing point in the application
* Always collect data, but only report if profiling is enabled
*/
export function mark(label: string): void {
// Always collect timing data regardless of whether profiling is enabled
timings[label] = performance.now() - cliStartTime;
}

/**
* Log all collected performance metrics
*/
export async function reportTimings(): Promise<void> {
if (!isEnabled) return;

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]);

// Calculate durations between steps
let previousTime = 0;
for (const [label, time] of sortedTimings) {
const duration = time - previousTime;
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');

// Report platform-specific information if on Windows
if (process.platform === 'win32') {
await reportPlatformInfo();
}
}

/**
* Collect and report platform-specific information
*/
async function reportPlatformInfo(): Promise<void> {
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');
2 changes: 2 additions & 0 deletions packages/cli/tests/settings/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -77,6 +78,7 @@ describe('Config', () => {
modelProvider: 'anthropic',
modelName: 'claude-3-7-sonnet-20250219',
ollamaBaseUrl: 'http://localhost:11434/api',
profile: false,
customPrompt: '',
});
});
Expand Down
Loading