New Cursor instance opens correctly, TODO: get acces to the Roo code extension in the cursor instance and send the prompt

This commit is contained in:
ColemanRoo 2025-02-01 22:55:59 -06:00
parent efd86a22cf
commit ce64c7f33e
5 changed files with 192 additions and 23 deletions

View file

@ -2481,12 +2481,14 @@ export class Cline {
case "open_cursor": {
const mode: string | undefined = block.params.mode
const prompt: string | undefined = block.params.prompt
const projectDir: string | undefined = block.params.projectDir
try {
if (block.partial) {
const partialMessage = JSON.stringify({
tool: "openCursor",
mode: removeClosingTag("mode", mode),
prompt: removeClosingTag("prompt", prompt),
projectDir: removeClosingTag("projectDir", projectDir),
})
await this.ask("tool", partialMessage, block.partial).catch(() => {})
break
@ -2501,11 +2503,18 @@ export class Cline {
pushToolResult(await this.sayAndCreateMissingParamError("open_cursor", "prompt"))
break
}
if (!projectDir) {
this.consecutiveMistakeCount++
pushToolResult(
await this.sayAndCreateMissingParamError("open_cursor", "projectDir"),
)
break
}
this.consecutiveMistakeCount = 0
const provider = this.providerRef.deref()
if (provider) {
await provider.openCursorInstance(prompt, mode)
await provider.openCursorInstance(prompt, mode, projectDir)
pushToolResult(
`Successfully opened new Cursor instance in ${mode} mode with prompt: ${prompt}`,
)

View file

@ -59,6 +59,7 @@ export const toolParamNames = [
"message",
"prompt",
"monitor",
"projectDir",
] as const
export type ToolParamName = (typeof toolParamNames)[number]

View file

@ -9,17 +9,20 @@ Parameters:
- prompt: (required) The initial prompt or instructions to send to the new Cursor instance.
- mode: (optional) The mode to start the new instance in (e.g., "code", "ask", "architect"). If not provided, will use the current mode.
- monitor: (optional) Whether to monitor the task's progress. Defaults to true.
- projectDir: (optional) The directory to open Cursor in. If not provided, will use the current project directory.
Usage:
<open_cursor>
<prompt>Your initial prompt here</prompt>
<mode>Mode to start in (optional)</mode>
<projectDir>Project directory to open Cursor in (optional)</projectDir>
</open_cursor>
Example:
<open_cursor>
<prompt>Create a new React component</prompt>
<mode>code</mode>
<projectDir>/Users/rooCode/Projects/Roo-Code</projectDir>
</open_cursor>
`
}

View file

@ -1,7 +1,7 @@
import { Anthropic } from "@anthropic-ai/sdk"
import delay from "delay"
import axios from "axios"
import fs from "fs/promises"
import fs, { cp } from "fs/promises"
import os from "os"
import pWaitFor from "p-wait-for"
import * as path from "path"
@ -44,7 +44,8 @@ import { EXPERIMENT_IDS, experiments as Experiments, experimentDefault, Experime
import { CustomSupportPrompts, supportPrompt } from "../../shared/support-prompt"
import { ACTION_NAMES } from "../CodeActionProvider"
import { spawn } from "child_process"
import { openCursorInstance, startRooCodeTask } from "./openCursorInstance"
/*
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
@ -1413,26 +1414,12 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.postStateToWebview()
}
public async openCursorInstance(prompt: string, mode: string) {
// Construct command to open new Cursor instance
const cursorCommand = {
command: "workbench.action.newWindow",
args: [
{
mode,
prompt,
},
],
}
// Execute command to open new window
const result = await vscode.commands.executeCommand(cursorCommand.command, ...cursorCommand.args)
console.log("result", result)
const windows = vscode.window.state.active
console.log("windows", windows)
await vscode.commands.executeCommand("workbench.view.extension.roo-cline-ActivityBar")
await this.postMessageToWebview({ type: "action", action: "openCursorInstance" })
await this.postStateToWebview()
public async openCursorInstance(prompt: string, mode: string, projectDir: string) {
const workingDir =
projectDir || vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) || ""
const instanceId = await openCursorInstance(prompt, mode, workingDir)
await startRooCodeTask(instanceId, prompt, mode)
this.outputChannel.appendLine(`Cursor instance opened in ${workingDir}`)
}
private async updateApiConfiguration(apiConfiguration: ApiConfiguration) {

View file

@ -0,0 +1,169 @@
import { spawn, ChildProcess } from "child_process"
import * as path from "path"
import * as vscode from "vscode"
import * as fs from "fs/promises"
interface CursorInstance {
process: ChildProcess
projectDir: string
prompt: string
mode?: string
}
// Track active cursor instances
const activeCursorInstances: Map<string, CursorInstance> = new Map()
/**
* Gets the path to the Cursor application
* @returns Promise<{command: string, args: string[]}> Command and args to launch Cursor
*/
async function getCursorLaunchCommand(projectDir: string): Promise<{ command: string; args: string[] }> {
// Use code command with Cursor profile
return {
command: "cursor",
args: ["--new-window", projectDir],
}
}
/**
* Opens a new instance of Cursor at a specific project directory
* @param prompt The initial prompt to send to Roo Code
* @param mode Optional mode to start in (code, ask, architect)
* @param projectDir The directory to open Cursor in
* @returns Promise<string> Instance ID if successful
*/
export async function openCursorInstance(prompt: string, mode: string = "code", projectDir: string): Promise<string> {
try {
// Ensure project directory exists
const normalizedPath = path.normalize(projectDir)
await fs.access(normalizedPath)
// Generate unique instance ID
const instanceId = `cursor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
// Get platform-specific launch command
const { command, args } = await getCursorLaunchCommand(normalizedPath)
// Spawn process with inherited stdio
const cursorProcess = spawn(command, args, {
detached: false,
stdio: "inherit",
})
// Handle process error
cursorProcess.on("error", (error) => {
console.error(`Error launching Cursor instance ${instanceId}:`, error)
activeCursorInstances.delete(instanceId)
throw error
})
// Store instance information
const instance: CursorInstance = {
process: cursorProcess,
projectDir: normalizedPath,
prompt,
mode,
}
activeCursorInstances.set(instanceId, instance)
console.log(instance)
cursorProcess.stdin?.write(prompt)
// Show information message
vscode.window.showInformationMessage(`New Cursor instance opened at ${normalizedPath}`)
return instanceId
} catch (error) {
console.error("Failed to open Cursor instance:", error)
if (error instanceof Error) {
vscode.window.showErrorMessage(`Failed to open Cursor: ${error.message}`)
}
throw error
}
}
/**
* Get all active cursor instances
* @returns Map of active cursor instances
*/
export function getActiveCursorInstances(): Map<string, CursorInstance> {
return new Map(activeCursorInstances)
}
/**
* Close a specific cursor instance
* @param instanceId The ID of the instance to close
* @returns boolean indicating if the instance was successfully closed
*/
export function closeCursorInstance(instanceId: string): boolean {
const instance = activeCursorInstances.get(instanceId)
if (!instance) {
return false
}
try {
instance.process.kill()
activeCursorInstances.delete(instanceId)
return true
} catch (error) {
console.error(`Error closing Cursor instance ${instanceId}:`, error)
return false
}
}
/**
* Close all active cursor instances
*/
export function closeAllCursorInstances(): void {
for (const [instanceId] of activeCursorInstances) {
closeCursorInstance(instanceId)
}
}
/**
* Send a command to the Roo Code extension in a specific Cursor instance
* @param instanceId The ID of the Cursor instance to send the command to
* @param command The Roo Code command to execute (e.g., 'startNewTask')
* @param args Optional arguments for the command
* @returns Promise<void>
*/
export async function sendRooCodeCommand(instanceId: string, command: string, args?: any): Promise<void> {
const instance = activeCursorInstances.get(instanceId)
if (!instance) {
throw new Error(`No active Cursor instance found with ID: ${instanceId}`)
}
try {
// Wait for Cursor to be ready
await new Promise((resolve) => setTimeout(resolve, 2000))
// Focus the window first
await vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(instance.projectDir), true)
await new Promise((resolve) => setTimeout(resolve, 1000))
// Send the command
await vscode.commands.executeCommand(command, args)
// Log command for debugging
console.log(`Executed command in Cursor instance ${instanceId}:`, command, args)
} catch (error) {
console.error(`Failed to send command to Cursor instance ${instanceId}:`, error)
throw error
}
}
/**
* Start a new Roo Code task in a specific Cursor instance
* @param instanceId The ID of the Cursor instance
* @param prompt The prompt for the new task
* @param mode Optional mode to start in (code, ask, architect)
* @returns Promise<void>
*/
export async function startRooCodeTask(instanceId: string, prompt: string, mode: string = "code"): Promise<void> {
await sendRooCodeCommand(instanceId, "roo-cline.startTask", {
prompt,
mode,
projectDir: activeCursorInstances.get(instanceId)?.projectDir,
})
}