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
93 changes: 93 additions & 0 deletions docs/SentryIntegration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Sentry.io Integration

MyCoder CLI now includes integration with Sentry.io for error tracking and monitoring.

## How It Works

The Sentry.io integration is initialized at the start of the CLI application to capture any errors that occur during execution. This helps us identify and fix issues more quickly.

## Installation

The Sentry Node SDK is included as a dependency in the CLI package:

```bash
npm install @sentry/node --save
```

## Configuration

By default, Sentry is:
- Enabled in production environments
- Disabled in development environments (unless explicitly enabled)
- Configured to capture 100% of transactions

### Environment Variables

You can control Sentry behavior with the following environment variables:

- `NODE_ENV`: Set to "production" to enable Sentry (default behavior)
- `ENABLE_SENTRY`: Set to "true" to explicitly enable Sentry in any environment
- `SENTRY_DSN`: Override the default Sentry DSN (optional)

### Command Line Options

You can also configure Sentry through command-line options:

```bash
# Use a custom Sentry DSN
mycoder --sentryDsn="https://[email protected]/project"
```

## Version Tracking

All errors reported to Sentry include the package version information in the format `[email protected]`. This allows us to trace errors to specific releases and understand which versions of the software are affected by particular issues.

## Implementation Details

The Sentry SDK is initialized as early as possible in the application lifecycle:

```javascript
import * as Sentry from '@sentry/node';
import { createRequire } from 'module';

// Initialize Sentry with version information
Sentry.init({
dsn: 'https://2873d2518b60f645918b6a08ae5e69ae@o4508898407481344.ingest.us.sentry.io/4508898476687360',
tracesSampleRate: 1.0,
environment: process.env.NODE_ENV || 'development',
release: `mycoder@${packageVersion}`,
enabled: process.env.NODE_ENV !== 'development' || process.env.ENABLE_SENTRY === 'true',
});

// Capture errors
try {
// Application code
} catch (error) {
Sentry.captureException(error);
}
```

## Testing Sentry Integration

A hidden command is available to test the Sentry integration:

```bash
mycoder test-sentry
```

This command will:
1. Generate a test error that includes the package version
2. Report it to Sentry.io
3. Output the result to the console

Note: In development environments, you may need to set `ENABLE_SENTRY=true` for the test to actually send data to Sentry.

## Privacy

Error reports sent to Sentry include:
- Stack traces
- Error messages
- Environment information
- Release version

Personal or sensitive information is not intentionally collected. If you discover any privacy concerns with the Sentry integration, please report them to the project maintainers.
6 changes: 6 additions & 0 deletions packages/agent/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# mycoder-agent

## 0.4.0

### Minor Changes

- Adds sentry error reporting for quality monitoring.

## 0.3.0

### Minor Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/agent/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mycoder-agent",
"version": "0.3.0",
"version": "0.4.0",
"description": "Agent module for mycoder - an AI-powered software development assistant",
"type": "module",
"main": "dist/index.js",
Expand Down
11 changes: 11 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# mycoder

## 0.4.0

### Minor Changes

- Adds sentry error reporting for quality monitoring.

### Patch Changes

- Updated dependencies
- [email protected]

## 0.3.0

### Minor Changes
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mycoder",
"description": "A command line tool using agent that can do arbitrary tasks, including coding tasks",
"version": "0.3.0",
"version": "0.4.0",
"type": "module",
"bin": "./bin/cli.js",
"main": "./dist/index.js",
Expand Down Expand Up @@ -47,6 +47,7 @@
"author": "Ben Houston",
"license": "MIT",
"dependencies": {
"@sentry/node": "^9.3.0",
"chalk": "^5",
"dotenv": "^16",
"mycoder-agent": "workspace:*",
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/src/commands/$default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { TokenTracker } from 'mycoder-agent/dist/core/tokens.js';

import { SharedOptions } from '../options.js';
import { initSentry, captureException } from '../sentry/index.js';
import { hasUserConsented, saveUserConsent } from '../settings/settings.js';
import { nameToLogIndex } from '../utils/nameToLogIndex.js';
import { checkForUpdates, getPackageInfo } from '../utils/versionCheck.js';
Expand All @@ -34,6 +35,11 @@ export const command: CommandModule<SharedOptions, DefaultArgs> = {
}) as Argv<DefaultArgs>;
},
handler: async (argv) => {
// Initialize Sentry with custom DSN if provided
if (argv.sentryDsn) {
initSentry(argv.sentryDsn);
}

const logger = new Logger({
name: 'Default',
logLevel: nameToLogIndex(argv.logLevel),
Expand Down Expand Up @@ -143,6 +149,8 @@ export const command: CommandModule<SharedOptions, DefaultArgs> = {
logger.info('\n=== Result ===\n', output);
} catch (error) {
logger.error('An error occurred:', error);
// Capture the error with Sentry
captureException(error);
}

logger.log(
Expand Down
40 changes: 40 additions & 0 deletions packages/cli/src/commands/test-sentry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import chalk from 'chalk';
import { Logger } from 'mycoder-agent';

import { SharedOptions } from '../options.js';
import { testSentryErrorReporting } from '../sentry/index.js';
import { nameToLogIndex } from '../utils/nameToLogIndex.js';

import type { CommandModule } from 'yargs';

type TestSentryArgs = SharedOptions;

export const command: CommandModule<SharedOptions, TestSentryArgs> = {
command: 'test-sentry',
describe: false, // Hide from help output
handler: async (argv) => {
const logger = new Logger({
name: 'TestSentry',
logLevel: nameToLogIndex(argv.logLevel),
});

logger.info(chalk.yellow('Testing Sentry.io error reporting...'));

try {
// Test error reporting
const error = testSentryErrorReporting();

logger.info(
chalk.green('Successfully sent test error to Sentry.io:'),
chalk.red(error instanceof Error ? error.message : String(error)),
);

logger.info(
chalk.blue('Note:'),
'If this is a development environment, the error may not be sent to Sentry unless ENABLE_SENTRY=true is set.',
);
} catch (error) {
logger.error('Failed to test Sentry error reporting:', error);
}
},
};
6 changes: 6 additions & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { fileCommands } from 'yargs-file-commands';

// Initialize Sentry as early as possible
import { initSentry, captureException } from './sentry/index.js';
initSentry();

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

import type { PackageJson } from 'type-fest';
Expand Down Expand Up @@ -46,5 +50,7 @@ const main = async () => {

await main().catch((error) => {
console.error(error);
// Capture the error with Sentry
captureException(error);
process.exit(1);
});
6 changes: 6 additions & 0 deletions packages/cli/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type SharedOptions = {
readonly headless?: boolean;
readonly userSession?: boolean;
readonly pageFilter?: 'simple' | 'none' | 'readability';
readonly sentryDsn?: string;
};

export const sharedOptions = {
Expand Down Expand Up @@ -49,4 +50,9 @@ export const sharedOptions = {
default: 'none',
choices: ['simple', 'none', 'readability'],
} as const,
sentryDsn: {
type: 'string',
description: 'Custom Sentry DSN for error tracking',
hidden: true,
} as const,
};
87 changes: 87 additions & 0 deletions packages/cli/src/sentry/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as Sentry from '@sentry/node';
import { createRequire } from 'module';

/**
* Initialize Sentry for error tracking
* @param dsn Optional custom DSN to use instead of the default
*/
export function initSentry(dsn?: string) {
// Get package version using createRequire for ES modules
let packageVersion = 'unknown';
try {
const require = createRequire(import.meta.url);
packageVersion = process.env.npm_package_version || require('../../package.json').version;
} catch (error) {
console.warn('Could not determine package version for Sentry:', error);
}

// Initialize Sentry
Sentry.init({
// Default DSN from Sentry.io integration instructions
dsn: dsn || 'https://2873d2518b60f645918b6a08ae5e69ae@o4508898407481344.ingest.us.sentry.io/4508898476687360',

// No profiling integration as requested

// Capture 100% of the transactions
tracesSampleRate: 1.0,

// Set environment based on NODE_ENV
environment: process.env.NODE_ENV || 'development',

// Add release version from package.json
release: `mycoder@${packageVersion}`,

// Don't capture errors in development mode unless explicitly enabled
enabled: process.env.NODE_ENV !== 'development' || process.env.ENABLE_SENTRY === 'true',
});

// Log confirmation that Sentry is initialized with version info
if (process.env.NODE_ENV !== 'test') {
console.log(`Sentry initialized for mycoder@${packageVersion}`);
}
}

/**
* Capture an exception with Sentry
* @param error Error to capture
*/
export function captureException(error: Error | unknown) {
Sentry.captureException(error);
}

/**
* Capture a message with Sentry
* @param message Message to capture
* @param level Optional severity level
*/
export function captureMessage(message: string, level?: Sentry.SeverityLevel) {
Sentry.captureMessage(message, level);
}

/**
* Test Sentry error reporting by throwing a test error
*/
export function testSentryErrorReporting() {
try {
// Get package version for the error message
let packageVersion = 'unknown';
try {
const require = createRequire(import.meta.url);
packageVersion = process.env.npm_package_version || require('../../package.json').version;
} catch (error) {
console.warn('Could not determine package version for test error:', error);
}

// Throw a test error with version information
throw new Error(`Test error for Sentry.io integration from mycoder@${packageVersion}`);
} catch (error) {
// Capture the error with Sentry
Sentry.captureException(error);

// Log a message about the test
console.log('Test error sent to Sentry.io');

// Return the error for inspection
return error;
}
}
Loading
Loading