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
3 changes: 2 additions & 1 deletion cli/esbuild.config.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import esbuild from "esbuild"
import { chmodSync, mkdirSync, copyFileSync } from "fs"
import { rimrafSync } from "rimraf"
Expand All @@ -14,7 +15,7 @@ function copyPostBuildFiles() {
try {
copyFileSync(".env", "dist/.env")
copyFileSync(".env", "dist/kilocode/.env")
} catch (err) {
} catch {
// .env might not exist, that's okay
}

Expand Down
13 changes: 13 additions & 0 deletions cli/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { config } from "@roo-code/config-eslint/base"

export default [
...config,
{
rules: {
// "@typescript-eslint/no-explicit-any": "off",
},
},
{
ignores: ["dist/*"],
},
]
5 changes: 3 additions & 2 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"scripts": {
"build": "node esbuild.config.mjs",
"dev": "pnpm build --watch",
"deps:install": "npm install --prefix ./dist",
"start": "node dist/index.js",
"clean": "rimraf dist",
Expand All @@ -20,7 +21,8 @@
"logs": "clear && tail -f ~/.kilocode/cli/logs/cli.txt",
"logs:clear": "rimraf ~/.kilocode/cli/logs/* && touch ~/.kilocode/cli/logs/cli.txt",
"clean:kilocode": "npx del-cli ./dist/kilocode --force && npx mkdirp ./dist/kilocode",
"copy:kilocode": "npx cpy '../bin-unpacked/extension/**' './dist/kilocode' --parents"
"copy:kilocode": "npx cpy '../bin-unpacked/extension/**' './dist/kilocode' --parents",
"lint": "eslint ."
},
"dependencies": {
"@anthropic-ai/bedrock-sdk": "^0.22.0",
Expand Down Expand Up @@ -136,7 +138,6 @@
"@types/react": "^18.3.23",
"cpy-cli": "^5.0.0",
"del-cli": "^5.1.0",
"eslint": "^9.27.0",
"ink-testing-library": "^4.0.0",
"mkdirp": "^3.0.1",
"prettier": "^3.4.2",
Expand Down
4 changes: 1 addition & 3 deletions cli/src/commands/__tests__/new.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ import type { CommandContext } from "../core/types.js"

describe("/new command", () => {
let mockContext: CommandContext
let stdoutWriteSpy: any

beforeEach(() => {
// Mock process.stdout.write to capture terminal clearing
stdoutWriteSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true)
vi.spyOn(process.stdout, "write").mockImplementation(() => true)

mockContext = {
input: "/new",
Expand Down
3 changes: 2 additions & 1 deletion cli/src/commands/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -533,10 +533,11 @@ export const modelCommand: Command = {
await selectModel(context, args[1])
break

case "list":
case "list": {
const filter = args.length > 1 ? args[1] : undefined
await listModels(context, filter)
break
}

default:
context.addMessage({
Expand Down
3 changes: 2 additions & 1 deletion cli/src/communication/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface IPCOptions {
* Replicates the webview message passing pattern used in the VSCode extension
*/
export class IPCChannel extends EventEmitter {
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's probably a better way to fix this, but I didn't want to spend too much time on this PR

private pendingRequests = new Map<string, { resolve: Function; reject: Function; timeout: NodeJS.Timeout }>()
private messageId = 0
private options: Required<IPCOptions>
Expand Down Expand Up @@ -133,7 +134,7 @@ export class IPCChannel extends EventEmitter {

dispose(): void {
// Clear all pending requests
for (const [id, pending] of this.pendingRequests) {
for (const [, pending] of this.pendingRequests) {
clearTimeout(pending.timeout)
pending.reject(new Error("IPC channel disposed"))
}
Expand Down
2 changes: 1 addition & 1 deletion cli/src/config/__tests__/persistence-merge.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"
import * as fs from "fs/promises"
import * as path from "path"
import { loadConfig, saveConfig, setConfigPaths, resetConfigPaths } from "../persistence.js"
import { loadConfig, setConfigPaths, resetConfigPaths } from "../persistence.js"
import { DEFAULT_AUTO_APPROVAL } from "../defaults.js"
import type { CLIConfig } from "../types.js"

Expand Down
1 change: 0 additions & 1 deletion cli/src/config/validation.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Ajv from "ajv"
import * as fs from "fs/promises"
import * as path from "path"
import type { CLIConfig } from "./types.js"

// __dirname is provided by the banner in the bundled output
declare const __dirname: string
Expand Down
6 changes: 4 additions & 2 deletions cli/src/host/ExtensionHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class ExtensionHost extends EventEmitter {
*/
private setupGlobalErrorHandlers(): void {
// Handle unhandled promise rejections from extension
this.unhandledRejectionHandler = (reason: any, promise: Promise<any>) => {
this.unhandledRejectionHandler = (reason: any) => {
const error = reason instanceof Error ? reason : new Error(String(reason))

// Check if this is an expected error
Expand Down Expand Up @@ -593,9 +593,10 @@ export class ExtensionHost extends EventEmitter {
}
break

case "messageUpdated":
case "messageUpdated": {
// Extension is sending an individual message update
// The extension uses 'clineMessage' property (legacy name)

const chatMessage = message.clineMessage || message.chatMessage
if (chatMessage) {
// Forward the message update to the CLI
Expand All @@ -606,6 +607,7 @@ export class ExtensionHost extends EventEmitter {
this.emit("message", emitMessage)
}
break
}

case "taskHistoryResponse":
// Extension is sending task history data
Expand Down
70 changes: 35 additions & 35 deletions cli/src/host/VSCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface IdentityInfo {
}

// Basic VSCode API types and enums
export interface Thenable<T> extends Promise<T> {}
export type Thenable<T> = Promise<T>

// VSCode EventEmitter implementation
export interface Disposable {
Expand Down Expand Up @@ -351,7 +351,7 @@ export class TextEdit {
return new TextEdit(range, "")
}

static setEndOfLine(eol: EndOfLine): TextEdit {
static setEndOfLine(): TextEdit {
// Simplified implementation
return new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), "")
}
Expand Down Expand Up @@ -817,7 +817,7 @@ export class FileSystemAPI {
mtime: stats.mtimeMs,
size: stats.size,
}
} catch (error) {
} catch {
// If file doesn't exist, assume it's a file for CLI purposes
return {
type: FileType.File,
Expand All @@ -832,31 +832,31 @@ export class FileSystemAPI {
try {
const content = fs.readFileSync(uri.fsPath)
return new Uint8Array(content)
} catch (error) {
} catch {
throw new Error(`Failed to read file: ${uri.fsPath}`)
}
}

async writeFile(uri: Uri, content: Uint8Array): Promise<void> {
try {
fs.writeFileSync(uri.fsPath, content)
} catch (error) {
} catch {
throw new Error(`Failed to write file: ${uri.fsPath}`)
}
}

async delete(uri: Uri): Promise<void> {
try {
fs.unlinkSync(uri.fsPath)
} catch (error) {
} catch {
throw new Error(`Failed to delete file: ${uri.fsPath}`)
}
}

async createDirectory(uri: Uri): Promise<void> {
try {
fs.mkdirSync(uri.fsPath, { recursive: true })
} catch (error) {
} catch {
throw new Error(`Failed to create directory: ${uri.fsPath}`)
}
}
Expand Down Expand Up @@ -917,7 +917,7 @@ export class WorkspaceAPI {
return new MockWorkspaceConfiguration(section, this.context)
}

findFiles(include: string, exclude?: string): Thenable<Uri[]> {
findFiles(_include: string, _exclude?: string): Thenable<Uri[]> {
// Basic implementation - could be enhanced with glob patterns
return Promise.resolve([])
}
Expand Down Expand Up @@ -1051,10 +1051,10 @@ export class WorkspaceAPI {
}

createFileSystemWatcher(
globPattern: any,
ignoreCreateEvents?: boolean,
ignoreChangeEvents?: boolean,
ignoreDeleteEvents?: boolean,
_globPattern: any,
_ignoreCreateEvents?: boolean,
_ignoreChangeEvents?: boolean,
_ignoreDeleteEvents?: boolean,
): any {
return {
onDidChange: () => ({ dispose: () => {} }),
Expand All @@ -1064,7 +1064,7 @@ export class WorkspaceAPI {
}
}

registerTextDocumentContentProvider(scheme: string, provider: any): Disposable {
registerTextDocumentContentProvider(_scheme: string, _provider: any): Disposable {
return { dispose: () => {} }
}
}
Expand All @@ -1079,7 +1079,7 @@ export interface WorkspaceConfiguration {
get<T>(section: string): T | undefined
get<T>(section: string, defaultValue: T): T
has(section: string): boolean
inspect<T>(section: string): any
inspect(section: string): any
update(section: string, value: any, configurationTarget?: ConfigurationTarget): Thenable<void>
}

Expand Down Expand Up @@ -1142,7 +1142,7 @@ export class MockWorkspaceConfiguration implements WorkspaceConfiguration {
return this.workspaceMemento.get(fullSection) !== undefined || this.globalMemento.get(fullSection) !== undefined
}

inspect<T>(section: string): any {
inspect(section: string): any {
const fullSection = this.section ? `${this.section}.${section}` : section
const workspaceValue = this.workspaceMemento.get(fullSection)
const globalValue = this.globalMemento.get(fullSection)
Expand Down Expand Up @@ -1301,44 +1301,44 @@ export class WindowAPI {
return new OutputChannel(name)
}

createTextEditorDecorationType(options: any): TextEditorDecorationType {
createTextEditorDecorationType(_options: any): TextEditorDecorationType {
return new TextEditorDecorationType(`decoration-${Date.now()}`)
}

showInformationMessage(message: string, ...items: string[]): Thenable<string | undefined> {
showInformationMessage(message: string, ..._items: string[]): Thenable<string | undefined> {
logs.info(message, "VSCode.Window")
return Promise.resolve(undefined)
}

showWarningMessage(message: string, ...items: string[]): Thenable<string | undefined> {
showWarningMessage(message: string, ..._items: string[]): Thenable<string | undefined> {
logs.warn(message, "VSCode.Window")
return Promise.resolve(undefined)
}

showErrorMessage(message: string, ...items: string[]): Thenable<string | undefined> {
showErrorMessage(message: string, ..._items: string[]): Thenable<string | undefined> {
logs.error(message, "VSCode.Window")
return Promise.resolve(undefined)
}

showQuickPick(items: string[], options?: any): Thenable<string | undefined> {
showQuickPick(items: string[], _options?: any): Thenable<string | undefined> {
// Return first item for CLI
return Promise.resolve(items[0])
}

showInputBox(options?: any): Thenable<string | undefined> {
showInputBox(_options?: any): Thenable<string | undefined> {
// Return empty string for CLI
return Promise.resolve("")
}

showOpenDialog(options?: any): Thenable<Uri[] | undefined> {
showOpenDialog(_options?: any): Thenable<Uri[] | undefined> {
// Return empty array for CLI
return Promise.resolve([])
}

async showTextDocument(
documentOrUri: any | Uri,
columnOrOptions?: ViewColumn | any,
preserveFocus?: boolean,
_preserveFocus?: boolean,
): Promise<any> {
// Mock implementation for CLI
// In a real VSCode environment, this would open the document in an editor
Expand Down Expand Up @@ -1390,7 +1390,7 @@ export class WindowAPI {
return placeholderEditor
}

registerWebviewViewProvider(viewId: string, provider: any, options?: any): Disposable {
registerWebviewViewProvider(viewId: string, provider: any, _options?: any): Disposable {
// Store the provider for later use by ExtensionHost
if ((global as any).__extensionHost) {
;(global as any).__extensionHost.registerWebviewProvider(viewId, provider)
Expand Down Expand Up @@ -1454,7 +1454,7 @@ export class WindowAPI {
}
}

registerUriHandler(handler: any): Disposable {
registerUriHandler(_handler: any): Disposable {
// Store the URI handler for later use
return {
dispose: () => {},
Expand All @@ -1476,23 +1476,23 @@ export class WindowAPI {
}

// Terminal event handlers
onDidCloseTerminal(listener: (terminal: any) => void): Disposable {
onDidCloseTerminal(_listener: (terminal: any) => void): Disposable {
return { dispose: () => {} }
}

onDidOpenTerminal(listener: (terminal: any) => void): Disposable {
onDidOpenTerminal(_listener: (terminal: any) => void): Disposable {
return { dispose: () => {} }
}

onDidChangeActiveTerminal(listener: (terminal: any) => void): Disposable {
onDidChangeActiveTerminal(_listener: (terminal: any) => void): Disposable {
return { dispose: () => {} }
}

onDidChangeTerminalDimensions(listener: (event: any) => void): Disposable {
onDidChangeTerminalDimensions(_listener: (event: any) => void): Disposable {
return { dispose: () => {} }
}

onDidWriteTerminalData(listener: (event: any) => void): Disposable {
onDidWriteTerminalData(_listener: (event: any) => void): Disposable {
return { dispose: () => {} }
}

Expand All @@ -1515,7 +1515,7 @@ export interface WorkspaceConfiguration {
get<T>(section: string): T | undefined
get<T>(section: string, defaultValue: T): T
has(section: string): boolean
inspect<T>(section: string): any
inspect(_section: string): any
update(section: string, value: any, configurationTarget?: ConfigurationTarget): Thenable<void>
}

Expand Down Expand Up @@ -1561,7 +1561,7 @@ export class CommandsAPI {
}
}

private async handleDiffCommand(originalUri: Uri, modifiedUri: Uri, title?: string, options?: any): Promise<void> {
private async handleDiffCommand(originalUri: Uri, modifiedUri: Uri, title?: string, _options?: any): Promise<void> {
// The DiffViewProvider is waiting for the modified document to appear in visibleTextEditors
// We need to simulate this by opening the document and adding it to visible editors

Expand Down Expand Up @@ -1616,15 +1616,15 @@ export class CommandsAPI {
edit: async (callback: (editBuilder: any) => void) => {
// Create a mock edit builder
const editBuilder = {
replace: (range: Range, text: string) => {
replace: (_range: Range, _text: string) => {
// In CLI mode, we don't actually edit here
// The DiffViewProvider will handle the actual edits
logs.debug("Mock edit builder replace called", "VSCode.Commands")
},
insert: (position: Position, text: string) => {
insert: (_position: Position, _text: string) => {
logs.debug("Mock edit builder insert called", "VSCode.Commands")
},
delete: (range: Range) => {
delete: (_range: Range) => {
logs.debug("Mock edit builder delete called", "VSCode.Commands")
},
}
Expand Down
Loading