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
4 changes: 3 additions & 1 deletion cli/src/cli/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function createProgram(): Command {
program
.name("ralphy")
.description(
"Autonomous AI Coding Loop - Supports Claude Code, OpenCode, Codex, Cursor, Qwen-Code, Factory Droid and GitHub Copilot",
"Autonomous AI Coding Loop - Supports Claude Code, OpenCode, Codex, Cursor, Qwen-Code, Factory Droid, GitHub Copilot and Kimi Code",
)
.version(VERSION)
.argument("[task]", "Single task to execute (brownfield mode)")
Expand All @@ -30,6 +30,7 @@ export function createProgram(): Command {
.option("--droid", "Use Factory Droid")
.option("--copilot", "Use GitHub Copilot")
.option("--gemini", "Use Gemini CLI")
.option("--kimi", "Use Kimi Code")
.option("--dry-run", "Show what would be done without executing")
.option("--max-iterations <n>", "Maximum iterations (0 = unlimited)", "0")
.option("--max-retries <n>", "Maximum retries per task", "3")
Expand Down Expand Up @@ -98,6 +99,7 @@ export function parseArgs(args: string[]): {
else if (opts.droid) aiEngine = "droid";
else if (opts.copilot) aiEngine = "copilot";
else if (opts.gemini) aiEngine = "gemini";
else if (opts.kimi) aiEngine = "kimi";

// Determine model override (--sonnet is shortcut for --model sonnet)
const modelOverride = opts.sonnet ? "sonnet" : opts.model || undefined;
Expand Down
4 changes: 4 additions & 0 deletions cli/src/engines/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ export * from "./qwen.ts";
export * from "./droid.ts";
export * from "./copilot.ts";
export * from "./gemini.ts";
export * from "./kimi.ts";

import { ClaudeEngine } from "./claude.ts";
import { CodexEngine } from "./codex.ts";
import { CopilotEngine } from "./copilot.ts";
import { CursorEngine } from "./cursor.ts";
import { DroidEngine } from "./droid.ts";
import { GeminiEngine } from "./gemini.ts";
import { KimiEngine } from "./kimi.ts";
import { OpenCodeEngine } from "./opencode.ts";
import { QwenEngine } from "./qwen.ts";
import type { AIEngine, AIEngineName } from "./types.ts";
Expand All @@ -40,6 +42,8 @@ export function createEngine(name: AIEngineName): AIEngine {
return new CopilotEngine();
case "gemini":
return new GeminiEngine();
case "kimi":
return new KimiEngine();
default:
throw new Error(`Unknown AI engine: ${name}`);
}
Expand Down
163 changes: 163 additions & 0 deletions cli/src/engines/kimi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import {
BaseAIEngine,
checkForErrors,
detectStepFromOutput,
execCommand,
execCommandStreaming,
formatCommandError,
parseStreamJsonResult,
} from "./base.ts";
import type { AIResult, EngineOptions, ProgressCallback } from "./types.ts";

const isWindows = process.platform === "win32";

/**
* Kimi Code CLI AI Engine
* https://github.com/MoonshotAI/kimi-cli
*/
export class KimiEngine extends BaseAIEngine {
name = "Kimi Code";
cliCommand = "kimi";

async execute(prompt: string, workDir: string, options?: EngineOptions): Promise<AIResult> {
const args = ["--yolo", "--output-format", "stream-json"];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Argument order differs from Gemini engine (["--output-format", "stream-json", "--yolo"]). Verify the order is correct for Kimi CLI.

Suggested change
const args = ["--yolo", "--output-format", "stream-json"];
const args = ["--output-format", "stream-json", "--yolo"];
Prompt To Fix With AI
This is a comment left during a code review.
Path: cli/src/engines/kimi.ts
Line: 23:23

Comment:
Argument order differs from Gemini engine (`["--output-format", "stream-json", "--yolo"]`). Verify the order is correct for Kimi CLI.

```suggestion
		const args = ["--output-format", "stream-json", "--yolo"];
```

How can I resolve this? If you propose a fix, please make it concise.

if (options?.modelOverride) {
args.push("--model", options.modelOverride);
}
// Add any additional engine-specific arguments
if (options?.engineArgs && options.engineArgs.length > 0) {
args.push(...options.engineArgs);
}

// On Windows, pass prompt via stdin to avoid cmd.exe argument parsing issues with multi-line content
let stdinContent: string | undefined;
if (isWindows) {
args.push("-p");
stdinContent = prompt;
} else {
args.push("-p", prompt);
}

const { stdout, stderr, exitCode } = await execCommand(
this.cliCommand,
args,
workDir,
undefined,
stdinContent,
);

const output = stdout + stderr;

// Check for errors
const error = checkForErrors(output);
if (error) {
return {
success: false,
response: "",
inputTokens: 0,
outputTokens: 0,
error,
};
}

// Parse result (same stream-json format as Claude/Qwen/Gemini)
const { response, inputTokens, outputTokens } = parseStreamJsonResult(output);

// If command failed with non-zero exit code, provide a meaningful error
if (exitCode !== 0) {
return {
success: false,
response,
inputTokens,
outputTokens,
error: formatCommandError(exitCode, output),
};
}

return {
success: true,
response,
inputTokens,
outputTokens,
};
}

async executeStreaming(
prompt: string,
workDir: string,
onProgress: ProgressCallback,
options?: EngineOptions,
): Promise<AIResult> {
const args = ["--yolo", "--output-format", "stream-json"];
if (options?.modelOverride) {
args.push("--model", options.modelOverride);
}
// Add any additional engine-specific arguments
if (options?.engineArgs && options.engineArgs.length > 0) {
args.push(...options.engineArgs);
}

// On Windows, pass prompt via stdin to avoid cmd.exe argument parsing issues with multi-line content
let stdinContent: string | undefined;
if (isWindows) {
args.push("-p");
stdinContent = prompt;
} else {
args.push("-p", prompt);
}

const outputLines: string[] = [];

const { exitCode } = await execCommandStreaming(
this.cliCommand,
args,
workDir,
(line) => {
outputLines.push(line);

// Detect and report step changes
const step = detectStepFromOutput(line);
if (step) {
onProgress(step);
}
},
undefined,
stdinContent,
);

const output = outputLines.join("\n");

// Check for errors
const error = checkForErrors(output);
if (error) {
return {
success: false,
response: "",
inputTokens: 0,
outputTokens: 0,
error,
};
}

// Parse result (same stream-json format as Claude/Qwen/Gemini)
const { response, inputTokens, outputTokens } = parseStreamJsonResult(output);

// If command failed with non-zero exit code, provide a meaningful error
if (exitCode !== 0) {
return {
success: false,
response,
inputTokens,
outputTokens,
error: formatCommandError(exitCode, output),
};
}

return {
success: true,
response,
inputTokens,
outputTokens,
};
}
}
3 changes: 2 additions & 1 deletion cli/src/engines/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@ export type AIEngineName =
| "qwen"
| "droid"
| "copilot"
| "gemini";
| "gemini"
| "kimi";