diff --git a/packages/types/src/events.ts b/packages/types/src/events.ts index 17c5cd5d40..52bae14e1a 100644 --- a/packages/types/src/events.ts +++ b/packages/types/src/events.ts @@ -34,6 +34,9 @@ export enum RooCodeEventName { TaskTokenUsageUpdated = "taskTokenUsageUpdated", TaskToolFailed = "taskToolFailed", + // Command Execution + TaskCommandExecuted = "taskCommandExecuted", + // Evals EvalPass = "evalPass", EvalFail = "evalFail", @@ -79,7 +82,7 @@ export const rooCodeEventsSchema = z.object({ [RooCodeEventName.TaskTokenUsageUpdated]: z.tuple([z.string(), tokenUsageSchema]), // Command Execution - taskCommandExecuted: z.tuple([ + [RooCodeEventName.TaskCommandExecuted]: z.tuple([ z.string(), z.object({ command: z.string(), @@ -188,6 +191,13 @@ export const taskEventSchema = z.discriminatedUnion("eventName", [ taskId: z.number().optional(), }), + // Command Execution + z.object({ + eventName: z.literal(RooCodeEventName.TaskCommandExecuted), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskCommandExecuted], + taskId: z.number().optional(), + }), + // Evals z.object({ eventName: z.literal(RooCodeEventName.EvalPass), diff --git a/packages/types/src/task.ts b/packages/types/src/task.ts index 03a5c40fd8..5821f2d2fb 100644 --- a/packages/types/src/task.ts +++ b/packages/types/src/task.ts @@ -98,7 +98,7 @@ export type TaskEvents = { [RooCodeEventName.TaskTokenUsageUpdated]: [taskId: string, tokenUsage: TokenUsage] // Command Execution - taskCommandExecuted: [ + [RooCodeEventName.TaskCommandExecuted]: [ taskId: string, details: { command: string diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 9a17a56f4f..e48622e4ad 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -114,7 +114,7 @@ export type ClineEvents = { taskCompleted: [taskId: string, tokenUsage: TokenUsage, toolUsage: ToolUsage] taskTokenUsageUpdated: [taskId: string, tokenUsage: TokenUsage] taskToolFailed: [taskId: string, tool: ToolName, error: string] - taskCommandExecuted: [ + [RooCodeEventName.TaskCommandExecuted]: [ taskId: string, details: { command: string diff --git a/src/core/tools/executeCommandTool.ts b/src/core/tools/executeCommandTool.ts index d54a89552a..cf687d1002 100644 --- a/src/core/tools/executeCommandTool.ts +++ b/src/core/tools/executeCommandTool.ts @@ -4,7 +4,7 @@ import * as vscode from "vscode" import delay from "delay" -import { CommandExecutionStatus, DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT } from "@roo-code/types" +import { CommandExecutionStatus, DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT, RooCodeEventName } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" import { Task } from "../task/Task" @@ -143,6 +143,26 @@ export type ExecuteCommandOptions = { commandExecutionTimeout?: number } +/** + * Helper function to emit taskCommandExecuted event with consistent payload + */ +function emitCommandExecutedEvent( + task: Task, + command: string, + exitCode: number | undefined, + output: string, + succeeded: boolean, + failureReason?: string, +) { + task.emit(RooCodeEventName.TaskCommandExecuted, task.taskId, { + command, + exitCode, + output, + succeeded, + failureReason, + }) +} + export async function executeCommand( task: Task, { @@ -275,13 +295,14 @@ export async function executeCommand( task.terminalProcess = undefined // Emit taskCommandExecuted event for timeout - task.emit("taskCommandExecuted", task.taskId, { + emitCommandExecutedEvent( + task, command, - exitCode: undefined, - output: accumulatedOutput, // Use accumulatedOutput instead of result - succeeded: false, - failureReason: `Command timed out after ${commandExecutionTimeoutSeconds}s`, - }) + undefined, + accumulatedOutput, + false, + `Command timed out after ${commandExecutionTimeoutSeconds}s`, + ) return [ false, @@ -321,13 +342,14 @@ export async function executeCommand( await task.say("user_feedback", text, images) // Emit taskCommandExecuted event for running command with user feedback - task.emit("taskCommandExecuted", task.taskId, { + emitCommandExecutedEvent( + task, command, - exitCode: undefined, - output: accumulatedOutput, // Use accumulatedOutput instead of result - succeeded: false, - failureReason: "Command is still running (user provided feedback)", - }) + undefined, + accumulatedOutput, + false, + "Command is still running (user provided feedback)", + ) return [ true, @@ -371,13 +393,7 @@ export async function executeCommand( // Emit taskCommandExecuted event const succeeded = exitCode === 0 - task.emit("taskCommandExecuted", task.taskId, { - command, - exitCode, - output: result, - succeeded, - failureReason: succeeded ? undefined : exitStatus, - }) + emitCommandExecutedEvent(task, command, exitCode, result, succeeded, succeeded ? undefined : exitStatus) return [false, `Command executed in terminal ${workingDirInfo}. ${exitStatus}\nOutput:\n${result}`] } else {