Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
522 changes: 522 additions & 0 deletions apps/server/src/providers/cursor-provider.ts

Large diffs are not rendered by default.

19 changes: 10 additions & 9 deletions apps/server/src/providers/provider-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { BaseProvider } from './base-provider.js';
import { ClaudeProvider } from './claude-provider.js';
import { CursorProvider } from './cursor-provider.js';
import type { InstallationStatus } from './types.js';

export class ProviderFactory {
Expand All @@ -25,10 +26,12 @@ export class ProviderFactory {
return new ClaudeProvider();
}

// Cursor models (cursor-*)
if (lowerModel.startsWith('cursor-')) {
return new CursorProvider();
}

// Future providers:
// if (lowerModel.startsWith("cursor-")) {
// return new CursorProvider();
// }
// if (lowerModel.startsWith("opencode-")) {
// return new OpenCodeProvider();
// }
Expand All @@ -42,10 +45,7 @@ export class ProviderFactory {
* Get all available providers
*/
static getAllProviders(): BaseProvider[] {
return [
new ClaudeProvider(),
// Future providers...
];
return [new ClaudeProvider(), new CursorProvider()];
}

/**
Expand Down Expand Up @@ -80,9 +80,10 @@ export class ProviderFactory {
case 'anthropic':
return new ClaudeProvider();

case 'cursor':
return new CursorProvider();

// Future providers:
// case "cursor":
// return new CursorProvider();
// case "opencode":
// return new OpenCodeProvider();

Expand Down
41 changes: 41 additions & 0 deletions apps/server/src/routes/setup/get-cursor-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Business logic for getting Cursor CLI status
* Uses CursorProvider as the single source of truth for CLI detection
*/

import { CursorProvider } from '../../providers/cursor-provider.js';

export interface CursorStatus {
status: 'installed' | 'not_installed';
installed: boolean;
method: string;
version: string;
path: string;
auth: {
authenticated: boolean;
method: string;
hasApiKey: boolean;
};
}

export async function getCursorStatus(): Promise<CursorStatus> {
const provider = new CursorProvider({});
const installStatus = await provider.detectInstallation();
// Determine auth method
let authMethod = 'none';
if (installStatus.authenticated) {
authMethod = process.env.CURSOR_API_KEY ? 'api_key_env' : 'oauth';
}
return {
status: installStatus.installed ? 'installed' : 'not_installed',
installed: installStatus.installed,
method: installStatus.method || 'none',
version: installStatus.version || '',
path: installStatus.path || '',
auth: {
authenticated: installStatus.authenticated || false,
method: authMethod,
hasApiKey: installStatus.hasApiKey || false,
},
};
}
Comment on lines 21 to 41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This file contains complex logic to detect the cursor-agent CLI installation path. This logic is duplicated and more advanced than the detection logic within CursorProvider itself.

As mentioned in my other comment on cursor-provider.ts, this can cause inconsistencies where the CLI is detected for status checks but not found for execution.

This detection logic should be moved into the CursorProvider class to act as a single source of truth for interacting with the cursor-agent CLI. This file's getCursorStatus function could then be a thin wrapper around a method on the CursorProvider.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 656dde2 - this file now uses CursorProvider.detectInstallation() as the single source of truth. Reduced from 129 lines to 41 lines.

2 changes: 2 additions & 0 deletions apps/server/src/routes/setup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { Router } from 'express';
import { createClaudeStatusHandler } from './routes/claude-status.js';
import { createCursorStatusHandler } from './routes/cursor-status.js';
import { createInstallClaudeHandler } from './routes/install-claude.js';
import { createAuthClaudeHandler } from './routes/auth-claude.js';
import { createStoreApiKeyHandler } from './routes/store-api-key.js';
Expand All @@ -17,6 +18,7 @@ export function createSetupRoutes(): Router {
const router = Router();

router.get('/claude-status', createClaudeStatusHandler());
router.get('/cursor-status', createCursorStatusHandler());
router.post('/install-claude', createInstallClaudeHandler());
router.post('/auth-claude', createAuthClaudeHandler());
router.post('/store-api-key', createStoreApiKeyHandler());
Expand Down
22 changes: 22 additions & 0 deletions apps/server/src/routes/setup/routes/cursor-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* GET /cursor-status endpoint - Get Cursor CLI status
*/

import type { Request, Response } from 'express';
import { getCursorStatus } from '../get-cursor-status.js';
import { getErrorMessage, logError } from '../common.js';

export function createCursorStatusHandler() {
return async (_req: Request, res: Response): Promise<void> => {
try {
const status = await getCursorStatus();
res.json({
success: true,
...status,
});
} catch (error) {
logError(error, 'Get Cursor status failed');
res.status(500).json({ success: false, error: getErrorMessage(error) });
}
};
}
Loading