Skip to content

Commit 715b8e5

Browse files
committed
feat: add mock engine and --engine CLI flag for zero-cost E2E testing
Mock engine simulates agent work (file creation, git commit, RETRO_JSON output) without calling any LLM. Also fixes sprintNumber not being passed to agent config, which prevented retro comments from being submitted.
1 parent b887491 commit 715b8e5

3 files changed

Lines changed: 40 additions & 2 deletions

File tree

src/cli.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { AgentRunner, type AgentRunnerOptions } from "./runner.js";
18+
import type { AgentType } from "./types.js";
1819
import {
1920
createApiClient,
2021
type Task,
@@ -49,6 +50,7 @@ interface CliArgs {
4950
noDocker: boolean;
5051
wsPort: number;
5152
debug: boolean;
53+
engine: AgentType;
5254
}
5355

5456
function printUsage(): void {
@@ -72,6 +74,7 @@ Options:
7274
--model <model> AI model for manager chat (default: claude-sonnet-4-20250514)
7375
--llm-base-url <url> OpenAI-compatible API base URL (or LLM_BASE_URL env)
7476
--llm-api-key <key> LLM provider API key (or LLM_API_KEY env)
77+
--engine <type> Agent engine: claude, codex, gemini, mock, custom (default: claude)
7578
--no-docker Disable Docker isolation (run agents directly on host)
7679
--ws-port <port> WebSocket server port for direct chat (default: 4000, 0=auto)
7780
--push Push the sprint tag to origin (sprint complete only)
@@ -139,6 +142,7 @@ function parseArgs(argv: string[]): CliArgs {
139142
noDocker: args.includes("--no-docker"),
140143
wsPort: parseInt(getFlag("--ws-port") ?? "4000", 10),
141144
debug: args.includes("--debug") || process.env.DEBUG === "1",
145+
engine: (getFlag("--engine") ?? "claude") as AgentType,
142146
};
143147
}
144148

@@ -532,14 +536,15 @@ async function runLoop(cliArgs: CliArgs, runner: AgentRunner): Promise<void> {
532536

533537
const agentConfig = {
534538
name: `${cliArgs.agentName}-${task.id.slice(0, 8)}`,
535-
type: "claude" as const,
539+
type: cliArgs.engine,
536540
taskId: task.id,
537541
workingDir: taskWorkingDir,
538542
branch: cliArgs.baseBranch,
539543
apiKey: cliArgs.apiKey,
540544
apiUrl: cliArgs.apiUrl,
541545
prompt,
542546
parentAgent: cliArgs.agentName,
547+
sprintNumber: sprintData?.sprint.number,
543548
...(Object.keys(secrets).length > 0 ? { secrets } : {}),
544549
};
545550

src/command-builder.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,36 @@ export interface CommandSpec {
55
args: string[];
66
}
77

8+
/**
9+
* Build a mock agent command that simulates work without calling an LLM.
10+
* Creates a small file, commits it, and outputs a retro comment.
11+
* Token cost: zero.
12+
*/
13+
function buildMockCommand(config: AgentConfig): CommandSpec {
14+
const taskId = config.taskId.slice(0, 8);
15+
const name = config.name;
16+
const script = `
17+
echo "[mock] Agent ${name} starting task ${taskId}..."
18+
sleep 2
19+
echo "[mock] Analyzing task requirements..."
20+
sleep 1
21+
echo "[mock] Implementing changes..."
22+
mkdir -p .mock-output
23+
echo "Mock output for task ${taskId} by ${name} at $(date -u +%Y-%m-%dT%H:%M:%SZ)" > .mock-output/${taskId}.txt
24+
git add .mock-output/${taskId}.txt 2>/dev/null
25+
git commit -m "mock: simulated work for task ${taskId}" --allow-empty 2>/dev/null || true
26+
echo "[mock] Changes committed."
27+
sleep 1
28+
echo "[mock] Task complete."
29+
echo 'RETRO_JSON:{"went_well":"Mock agent completed successfully","to_improve":"This was a simulated run","suggested_tasks":[]}'
30+
`.trim();
31+
32+
return {
33+
cmd: "bash",
34+
args: ["-c", script],
35+
};
36+
}
37+
838
/**
939
* Build the command and args for a given agent config.
1040
* If commandTemplate is set (from provider config), it takes precedence.
@@ -94,6 +124,9 @@ export function buildCommand(config: AgentConfig): CommandSpec {
94124
],
95125
};
96126

127+
case "mock":
128+
return buildMockCommand(config);
129+
97130
case "custom":
98131
if (!config.command) {
99132
throw new Error("Custom agent type requires a command");

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type AgentType = "claude" | "cursor" | "codex" | "gemini" | "custom";
1+
export type AgentType = "claude" | "cursor" | "codex" | "gemini" | "mock" | "custom";
22

33
export interface AgentConfig {
44
/** Agent display name */

0 commit comments

Comments
 (0)