Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/tidy-camels-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

Added comprehensive JSDoc documentation for core tools (readFileTool.ts and executeCommandTool.ts) to improve developer experience and code maintainability.
102 changes: 102 additions & 0 deletions src/core/tools/executeCommandTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,46 @@ import { Terminal } from "../../integrations/terminal/Terminal"
import { Package } from "../../shared/package"
import { t } from "../../i18n"

/**
* Error thrown when shell integration fails
* This error is used to switch to fallback mode
*/
class ShellIntegrationError extends Error {}

/**
* Main tool for executing commands in KiloCode
*
* This function handles shell command execution with support for:
* - Command validation via RooIgnore
* - User approval
* - YOLO mode to bypass approvals
* - Command timeout management
* - VSCode terminal integration
* - Fallback mode in case of integration error
*
* @param task - Instance of the currently running task
* @param block - Tool block containing command parameters
* @param askApproval - Function to request user approval
* @param handleError - Function to handle errors
* @param pushToolResult - Function to push results
* @param removeClosingTag - Function to remove closing tags
*
* @returns Promise<void> - Results are pushed via pushToolResult
*
* @example
* ```typescript
* await executeCommandTool(task, {
* params: { command: "npm install" }
* }, askApproval, handleError, pushToolResult, removeClosingTag);
*
* await executeCommandTool(task, {
* params: {
* command: "ls -la",
* cwd: "/home/user/project"
* }
* }, askApproval, handleError, pushToolResult, removeClosingTag);
* ```
*/
export async function executeCommandTool(
task: Task,
block: ToolUse,
Expand Down Expand Up @@ -133,16 +171,51 @@ export async function executeCommandTool(
}
}

/**
* Configuration options for command execution
*/
export type ExecuteCommandOptions = {
/** Unique identifier for this command execution */
executionId: string
/** Command to execute */
command: string
/** Custom working directory (optional) */
customCwd?: string
/** Disable terminal shell integration */
terminalShellIntegrationDisabled?: boolean
/** Line limit for terminal output */
terminalOutputLineLimit?: number
/** Character limit for terminal output */
terminalOutputCharacterLimit?: number
/** Execution timeout in milliseconds (0 = no timeout) */
commandExecutionTimeout?: number
}

/**
* Executes a system command with full lifecycle management
*
* This function handles command execution with:
* - Working directory validation
* - Terminal configuration (VSCode or execa)
* - Timeout management
* - Execution status tracking
* - Real-time output capture
* - Exit code handling
*
* @param task - Instance of the currently running task
* @param options - Configuration options for execution
* @returns Promise<[boolean, ToolResponse]> - Tuple containing [rejected, result]
*
* @example
* ```typescript
* const [rejected, result] = await executeCommand(task, {
* executionId: "cmd-123",
* command: "npm test",
* customCwd: "./project",
* commandExecutionTimeout: 30000
* });
* ```
*/
export async function executeCommand(
task: Task,
{
Expand Down Expand Up @@ -184,7 +257,16 @@ export async function executeCommand(
const provider = await task.providerRef.deref()

let accumulatedOutput = ""
/**
* Callbacks for tracking command execution
*/
const callbacks: RooTerminalCallbacks = {
/**
* Callback called when receiving new output lines
*
* @param lines - Output lines received from terminal
* @param process - Terminal process to control execution
*/
onLine: async (lines: string, process: RooTerminalProcess) => {
accumulatedOutput += lines
const compressedOutput = Terminal.compressTerminalOutput(
Expand All @@ -209,6 +291,11 @@ export async function executeCommand(
}
} catch (_error) {}
},
/**
* Callback called when command execution is completed
*
* @param output - Final output of the command
*/
onCompleted: (output: string | undefined) => {
result = Terminal.compressTerminalOutput(
output ?? "",
Expand All @@ -219,11 +306,21 @@ export async function executeCommand(
task.say("command_output", result)
completed = true
},
/**
* Callback called when shell execution starts
*
* @param pid - Shell process ID (if available)
*/
onShellExecutionStarted: (pid: number | undefined) => {
console.log(`[executeCommand] onShellExecutionStarted: ${pid}`)
const status: CommandExecutionStatus = { executionId, status: "started", pid, command }
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
},
/**
* Callback called when shell execution ends
*
* @param details - Process exit details
*/
onShellExecutionComplete: (details: ExitCodeDetails) => {
const status: CommandExecutionStatus = { executionId, status: "exited", exitCode: details.exitCode }
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
Expand All @@ -232,6 +329,11 @@ export async function executeCommand(
}

if (terminalProvider === "vscode") {
/**
* Callback called when shell integration fails
*
* @param error - Integration error message
*/
callbacks.onNoShellIntegration = async (error: string) => {
TelemetryService.instance.captureShellIntegrationError(task.taskId)
shellIntegrationError = error
Expand Down
93 changes: 87 additions & 6 deletions src/core/tools/readFileTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ import {
ImageMemoryTracker,
} from "./helpers/imageHelpers"

/**
* Generates a formatted description for the file reading tool
*
* @param blockName - Name of the tool block (usually "read_file")
* @param blockParams - Block parameters containing information about files to read
* @returns A string describing the current read operation
*
* @example
* ```typescript
* const description = getReadFileToolDescription("read_file", {
* files: [{ path: "src/app.ts" }, { path: "src/utils.ts" }]
* });
* // Result: "[read_file for 'src/app.ts', 'src/utils.ts']"
* ```
*/
export function getReadFileToolDescription(blockName: string, blockParams: any): string {
// Handle both single path and multiple files via args
// kilocode_change start
Expand Down Expand Up @@ -60,30 +75,91 @@ export function getReadFileToolDescription(blockName: string, blockParams: any):
}
}
// Types

/**
* Interface representing a line range for selective file reading
*/
interface LineRange {
/** Starting line number (inclusive) */
start: number
/** Ending line number (inclusive) */
end: number
}

/**
* Interface representing a file entry to be processed
*/
interface FileEntry {
/** Relative path of the file to read */
path?: string
/** Array of line ranges to read (optional) */
lineRanges?: LineRange[]
}

// New interface to track file processing state
/**
* Interface to track the processing status of a file
*/
interface FileResult {
/** Path of the processed file */
path: string
/** Current processing status of the file */
status: "approved" | "denied" | "blocked" | "error" | "pending"
/** Text content of the file (if applicable) */
content?: string
/** Error message (if processing failed) */
error?: string
/** Information or warning message */
notice?: string
/** Requested line ranges (if applicable) */
lineRanges?: LineRange[]
xmlContent?: string // Final XML content for this file
imageDataUrl?: string // Image data URL for image files
feedbackText?: string // User feedback text from approval/denial
feedbackImages?: any[] // User feedback images from approval/denial
/** Final XML content for this file */
xmlContent?: string
/** Data URL for image files */
imageDataUrl?: string
/** User feedback text after approval/denial */
feedbackText?: string
/** User feedback images after approval/denial */
feedbackImages?: any[]
}

/**
* Main tool for reading files in KiloCode
*
* This function handles reading one or more files with support for:
* - Simultaneous multiple file reading
* - Specific line ranges
* - Binary files (images, PDFs, etc.)
* - User validation and approval
* - YOLO mode to bypass approvals
* - Size limits and memory management
*
* @param cline - Instance of the currently running task
* @param block - Tool block containing read parameters
* @param askApproval - Function to request user approval
* @param handleError - Function to handle errors
* @param pushToolResult - Function to push results
* @param _removeClosingTag - Function to remove closing tags (unused)
*
* @returns Promise<void> - Results are pushed via pushToolResult
*
* @example
* ```typescript
* // Reading a single file
* await readFileTool(task, {
* params: { files: [{ path: "src/app.ts" }] }
* }, askApproval, handleError, pushToolResult, removeClosingTag);
*
* // Reading with line ranges
* await readFileTool(task, {
* params: {
* files: [{
* path: "src/app.ts",
* lineRanges: [{ start: 10, end: 20 }]
* }]
* }
* }, askApproval, handleError, pushToolResult, removeClosingTag);
* ```
*/
export async function readFileTool(
cline: Task,
block: ToolUse,
Expand Down Expand Up @@ -210,7 +286,12 @@ export async function readFileTool(
lineRanges: entry.lineRanges,
}))

// Function to update file result status
/**
* Updates the status of a file result in the results array
*
* @param path - Path of the file to update
* @param updates - Partial updates to apply to the result
*/
const updateFileResult = (path: string, updates: Partial<FileResult>) => {
const index = fileResults.findIndex((result) => result.path === path)
if (index !== -1) {
Expand Down