@kody-ade/kody-engine-lite 0.1.25 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +74 -224
  2. package/dist/agent-runner.d.ts +4 -0
  3. package/dist/agent-runner.js +122 -0
  4. package/dist/bin/cli.js +365 -100
  5. package/dist/ci/parse-inputs.d.ts +6 -0
  6. package/dist/ci/parse-inputs.js +76 -0
  7. package/dist/ci/parse-safety.d.ts +6 -0
  8. package/dist/ci/parse-safety.js +22 -0
  9. package/dist/cli/args.d.ts +13 -0
  10. package/dist/cli/args.js +42 -0
  11. package/dist/cli/litellm.d.ts +2 -0
  12. package/dist/cli/litellm.js +85 -0
  13. package/dist/cli/task-resolution.d.ts +2 -0
  14. package/dist/cli/task-resolution.js +41 -0
  15. package/dist/config.d.ts +49 -0
  16. package/dist/config.js +72 -0
  17. package/dist/context.d.ts +4 -0
  18. package/dist/context.js +83 -0
  19. package/dist/definitions.d.ts +3 -0
  20. package/dist/definitions.js +59 -0
  21. package/dist/entry.d.ts +1 -0
  22. package/dist/entry.js +236 -0
  23. package/dist/git-utils.d.ts +13 -0
  24. package/dist/git-utils.js +174 -0
  25. package/dist/github-api.d.ts +14 -0
  26. package/dist/github-api.js +114 -0
  27. package/dist/kody-utils.d.ts +1 -0
  28. package/dist/kody-utils.js +9 -0
  29. package/dist/learning/auto-learn.d.ts +2 -0
  30. package/dist/learning/auto-learn.js +169 -0
  31. package/dist/logger.d.ts +14 -0
  32. package/dist/logger.js +51 -0
  33. package/dist/memory.d.ts +1 -0
  34. package/dist/memory.js +20 -0
  35. package/dist/observer.d.ts +9 -0
  36. package/dist/observer.js +80 -0
  37. package/dist/pipeline/complexity.d.ts +3 -0
  38. package/dist/pipeline/complexity.js +12 -0
  39. package/dist/pipeline/executor-registry.d.ts +3 -0
  40. package/dist/pipeline/executor-registry.js +20 -0
  41. package/dist/pipeline/hooks.d.ts +17 -0
  42. package/dist/pipeline/hooks.js +110 -0
  43. package/dist/pipeline/questions.d.ts +2 -0
  44. package/dist/pipeline/questions.js +44 -0
  45. package/dist/pipeline/runner-selection.d.ts +2 -0
  46. package/dist/pipeline/runner-selection.js +13 -0
  47. package/dist/pipeline/state.d.ts +4 -0
  48. package/dist/pipeline/state.js +37 -0
  49. package/dist/pipeline.d.ts +3 -0
  50. package/dist/pipeline.js +213 -0
  51. package/dist/preflight.d.ts +1 -0
  52. package/dist/preflight.js +69 -0
  53. package/dist/retrospective.d.ts +26 -0
  54. package/dist/retrospective.js +211 -0
  55. package/dist/stages/agent.d.ts +2 -0
  56. package/dist/stages/agent.js +94 -0
  57. package/dist/stages/gate.d.ts +2 -0
  58. package/dist/stages/gate.js +32 -0
  59. package/dist/stages/review.d.ts +2 -0
  60. package/dist/stages/review.js +32 -0
  61. package/dist/stages/ship.d.ts +3 -0
  62. package/dist/stages/ship.js +154 -0
  63. package/dist/stages/verify.d.ts +2 -0
  64. package/dist/stages/verify.js +94 -0
  65. package/dist/types.d.ts +61 -0
  66. package/dist/types.js +1 -0
  67. package/dist/validators.d.ts +8 -0
  68. package/dist/validators.js +42 -0
  69. package/dist/verify-runner.d.ts +11 -0
  70. package/dist/verify-runner.js +110 -0
  71. package/package.json +1 -1
package/README.md CHANGED
@@ -1,269 +1,119 @@
1
1
  # Kody Engine Lite
2
2
 
3
- Autonomous SDLC pipeline. Comment `@kody` on a GitHub issue Kody classifies, plans, builds, tests, reviews, fixes, and ships a PR. Zero human intervention required.
4
-
5
- ## How it works
3
+ **Issue PR in one command.** Comment `@kody` on a GitHub issue and Kody autonomously classifies, plans, builds, tests, reviews, fixes, and ships a pull request.
6
4
 
7
5
  ```
8
- @kody (comment on a GitHub issue)
9
-
10
- GitHub Actions workflow
11
-
12
- 7-stage pipeline:
13
- 1. taskify — classify task, detect complexity, ask questions if unclear
14
- 2. plan — TDD implementation plan (opus — deep reasoning)
15
- 3. build — implement code changes (sonnet — tool use via Claude Code)
16
- 4. verify — typecheck + tests + lint (auto-fix on failure)
17
- 5. review — code review with severity levels (opus)
18
- 6. review-fix — fix Critical/Major findings (sonnet)
19
- 7. ship — push branch + create PR with Closes #N
20
-
21
- PR created with What/Scope/Changes description
6
+ @kody → taskify plan build → verify → review → fix → ship → PR created
22
7
  ```
23
8
 
9
+ Kody is a 7-stage autonomous SDLC pipeline that runs in GitHub Actions. It uses Claude Code (or any LLM via LiteLLM) to turn issues into production-ready PRs — with quality gates, AI-powered failure diagnosis, risk-based human approval, and self-improving memory.
10
+
11
+ ## Why Kody?
12
+
13
+ Most AI coding tools are **autocomplete** (Copilot) or **chat-based** (Cursor, Cline). You still drive. Kody is different: it's an **autonomous pipeline** that takes an issue and delivers a tested, reviewed PR.
14
+
15
+ | | Kody | Copilot Workspace | Devin | Cursor Agent |
16
+ |---|---|---|---|---|
17
+ | **Runs in CI** | GitHub Actions | GitHub Cloud | Devin Cloud | Local IDE |
18
+ | **Fire and forget** | Comment `@kody`, walk away | Must interact | Must interact | Must be open |
19
+ | **Quality gates** | typecheck + tests + lint + AI diagnosis + auto-retry | Basic | Runs tests | Runs tests |
20
+ | **Risk gate** | Pauses HIGH-risk tasks for human approval | No | No | No |
21
+ | **Model flexible** | Any LLM via LiteLLM | GitHub models only | Proprietary | Cursor models |
22
+ | **Open source** | MIT | Proprietary | Proprietary | Proprietary |
23
+ | **Cost** | Your API costs only | $10-39/month | $20-500/month | Subscription |
24
+
25
+ [Full comparison →](docs/COMPARISON.md)
26
+
24
27
  ## Quick Start
25
28
 
26
29
  ```bash
27
- # Install
30
+ # 1. Install
28
31
  npm install -g @kody-ade/kody-engine-lite
29
32
 
30
- # Init (in your project root) — auto-detects everything
33
+ # 2. Initialize (auto-detects everything)
31
34
  cd your-project
32
35
  kody-engine-lite init
33
36
 
34
- # Push and use
37
+ # 3. Set up GitHub
38
+ gh secret set ANTHROPIC_API_KEY --repo owner/repo
39
+ # Settings → Actions → "Allow GitHub Actions to create and approve pull requests"
40
+
41
+ # 4. Push
35
42
  git add .github/workflows/kody.yml kody.config.json .kody/
36
43
  git commit -m "chore: add kody engine"
37
44
  git push
38
45
 
39
- # Comment on any issue
46
+ # 5. Comment on any issue
40
47
  @kody
41
48
  ```
42
49
 
43
- ### What `init` does
50
+ `init` spawns Claude Code to analyze your project and generates: workflow file, config with auto-detected quality commands, project memory (architecture + conventions), and 14 GitHub labels.
44
51
 
45
- Spawns Claude Code to analyze your project and auto-generates:
46
- - `.github/workflows/kody.yml` — full CI/CD workflow
47
- - `kody.config.json` — quality commands auto-detected from `package.json`
48
- - `.kody/memory/architecture.md` — project-specific tech stack and structure
49
- - `.kody/memory/conventions.md` — coding patterns, references to existing docs
50
- - 14 GitHub labels — lifecycle, complexity, and type labels
51
- - Health checks — CLI tools, GitHub auth, secrets, config validation
52
+ **Prerequisites:** Node.js >= 22, [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code), [GitHub CLI](https://cli.github.com/), git
52
53
 
53
- ### GitHub Setup
54
+ ## Pipeline
54
55
 
55
- **Required secret:**
56
- ```bash
57
- gh secret set ANTHROPIC_API_KEY --repo owner/repo
56
+ ```
57
+ @kody on issue
58
+
59
+ 1. taskify — classify task, detect complexity, ask questions → task.json
60
+ 2. plan — TDD implementation plan (deep reasoning) → plan.md
61
+ ↓ HIGH risk? pause for human approval
62
+ 3. build — implement code via Claude Code tools → code changes
63
+ 4. verify — typecheck + tests + lint (AI diagnosis + autofix) → verify.md
64
+ 5. review — code review: PASS/FAIL + Critical/Major/Minor → review.md
65
+ 6. review-fix — fix Critical and Major findings → code changes
66
+ 7. ship — push branch + create PR with Closes #N → ship.md
67
+
68
+ PR created
58
69
  ```
59
70
 
60
- **Required permission:**
61
- Settings → Actions → General → "Allow GitHub Actions to create and approve pull requests"
71
+ Complexity auto-detected: **low** skips plan/review (4 stages), **medium** skips review-fix (6 stages), **high** runs all 7.
72
+
73
+ [Pipeline details →](docs/PIPELINE.md)
62
74
 
63
75
  ## Commands
64
76
 
65
77
  ### GitHub Comments
66
78
 
67
- ```
68
- @kody Run full pipeline (auto-generates task-id)
69
- @kody approve Answer questions and resume paused pipeline
70
- @kody fix Re-build from build stage (on issues or PRs)
71
- @kody fix Comment body = feedback for the fix
72
- fix the auth middleware
73
- to return 401 not 500
74
- @kody rerun Resume from failed/paused stage
75
- @kody rerun --from <stage> Resume from specific stage
79
+ ```bash
80
+ @kody # Full pipeline
81
+ @kody approve # Resume after questions or risk gate
82
+ @kody fix # Re-build (comment body = feedback)
83
+ @kody rerun --from <stage> # Resume from specific stage
76
84
  ```
77
85
 
78
86
  ### CLI
79
87
 
80
88
  ```bash
81
- kody-engine-lite run --task "Add feature X" --cwd /path/to/project
82
- kody-engine-lite run --issue-number 42 --cwd /path/to/project
89
+ kody-engine-lite run --issue-number 42 --local --cwd ./project
90
+ kody-engine-lite run --task "Add retry utility" --local
83
91
  kody-engine-lite fix --issue-number 42 --feedback "Use middleware pattern"
84
- kody-engine-lite rerun --issue-number 42
85
- kody-engine-lite status --task-id <id>
92
+ kody-engine-lite rerun --issue-number 42 --from verify
93
+ kody-engine-lite status --task-id 42-260327-102254
86
94
  kody-engine-lite init [--force]
87
95
  ```
88
96
 
89
- ## Pipeline Stages
90
-
91
- | Stage | Model | What it does |
92
- |-------|-------|-------------|
93
- | taskify | haiku | Classify task → `task.json` (type, scope, risk, questions) |
94
- | plan | opus | Create TDD plan → `plan.md` (deep reasoning) |
95
- | build | sonnet | Implement code using Claude Code tools (Read/Write/Edit/Bash) |
96
- | verify | — | Run typecheck + tests + lint from `kody.config.json` |
97
- | review | opus | Code review → `review.md` (PASS/FAIL + Critical/Major/Minor) |
98
- | review-fix | sonnet | Fix Critical and Major findings |
99
- | ship | — | Push branch, create PR, comment on issue |
100
-
101
97
  ## Key Features
102
98
 
103
- ### Question Gates
104
-
105
- Kody asks before building if something is unclear:
106
-
107
- - **Taskify** asks product/requirements questions ("Should search be case-sensitive?")
108
- - **Plan** asks architecture/technical questions ("Recommend middleware pattern approve?")
109
- - Pipeline pauses with `kody:waiting` label
110
- - Resume with `@kody approve` + your answers in the comment body
111
-
112
- ### Complexity-Based Stage Skipping
113
-
114
- Auto-detected from taskify's risk assessment, or override with `--complexity`:
115
-
116
- | Complexity | Runs | Skips |
117
- |-----------|------|-------|
118
- | low | taskify build → verify → ship | plan, review, review-fix |
119
- | medium | taskify plan build verify → review → ship | review-fix |
120
- | high | all 7 stages | nothing |
121
-
122
- ### Branch Syncing
123
-
124
- Every run merges latest from the default branch into the feature branch before building.
125
-
126
- ### Verify + Autofix Loop
127
-
128
- If verify fails: runs lint-fix → format-fix → autofix agent → retries (up to 2 attempts).
129
-
130
- ### Review + Fix Loop
131
-
132
- If review verdict is FAIL: runs review-fix agent → re-reviews.
133
-
134
- ### Rich PR Description
135
-
136
- ```markdown
137
- ## What
138
- Add authentication middleware to 8 unprotected API routes
139
-
140
- ## Scope
141
- - `src/middleware/auth.ts`
142
- - `src/app/api/cron/route.ts`
143
-
144
- **Type:** bugfix | **Risk:** high
145
-
146
- ## Changes
147
- Added auth middleware to all cron routes and copilotkit endpoint.
148
-
149
- **Review:** ✅ PASS
150
- **Verify:** ✅ typecheck + tests + lint passed
151
-
152
- <details><summary>📋 Implementation plan</summary>
153
- ...
154
- </details>
155
-
156
- Closes #42
157
- ```
158
-
159
- ### Labels
160
-
161
- `init` creates 14 labels:
162
- - **Lifecycle:** `kody:planning`, `kody:building`, `kody:review`, `kody:done`, `kody:failed`, `kody:waiting`
163
- - **Complexity:** `kody:low`, `kody:medium`, `kody:high`
164
- - **Type:** `kody:feature`, `kody:bugfix`, `kody:refactor`, `kody:docs`, `kody:chore`
165
-
166
- ### Memory System
167
-
168
- `.kody/memory/` files are prepended to every agent prompt:
169
- - `architecture.md` — auto-generated by `init`
170
- - `conventions.md` — auto-learned after each successful run
171
-
172
- ## Using Non-Anthropic Models (LiteLLM)
173
-
174
- Claude Code can use **any model** through a LiteLLM proxy. Tested with MiniMax (full tool use: Write, Read, Edit, Bash, Grep).
175
-
176
- ```bash
177
- # Start LiteLLM proxy
178
- docker run -d -p 4000:4000 \
179
- -e MINIMAX_API_KEY=your-key \
180
- -v config.yaml:/app/config.yaml \
181
- ghcr.io/berriai/litellm:main-latest --config /app/config.yaml
182
- ```
183
-
184
- ```yaml
185
- # config.yaml
186
- model_list:
187
- - model_name: minimax
188
- litellm_params:
189
- model: minimax/MiniMax-M2.7-highspeed
190
- api_key: os.environ/MINIMAX_API_KEY
191
- ```
192
-
193
- ```json
194
- // kody.config.json
195
- {
196
- "agent": {
197
- "litellmUrl": "http://localhost:4000",
198
- "modelMap": { "cheap": "minimax", "mid": "minimax", "strong": "minimax" }
199
- }
200
- }
201
- ```
202
-
203
- LiteLLM translates Anthropic's tool-use protocol to the target provider's format. No code changes needed.
204
-
205
- ## Configuration
206
-
207
- ### `kody.config.json`
208
-
209
- | Field | Description | Default |
210
- |-------|-------------|---------|
211
- | `quality.typecheck` | Typecheck command | `pnpm -s tsc --noEmit` |
212
- | `quality.lint` | Lint command | `""` |
213
- | `quality.lintFix` | Auto-fix lint | `""` |
214
- | `quality.testUnit` | Unit test command | `pnpm -s test` |
215
- | `git.defaultBranch` | Default branch | `dev` |
216
- | `github.owner` | GitHub org/user | auto-detected |
217
- | `github.repo` | Repository name | auto-detected |
218
- | `agent.modelMap.cheap` | Taskify model | `haiku` |
219
- | `agent.modelMap.mid` | Build/fix model | `sonnet` |
220
- | `agent.modelMap.strong` | Plan/review model | `opus` |
221
- | `agent.litellmUrl` | LiteLLM proxy URL | — |
222
-
223
- ### Environment Variables
224
-
225
- | Variable | Required | Description |
226
- |----------|----------|-------------|
227
- | `ANTHROPIC_API_KEY` | Yes | Claude API key (repo secret) |
228
- | `GH_TOKEN` | Auto | GitHub token (provided by Actions) |
229
- | `LOG_LEVEL` | No | `debug`, `info`, `warn`, `error` |
230
-
231
- ## Architecture
232
-
233
- ```
234
- src/
235
- ├── entry.ts — CLI: run/rerun/fix/status, arg parsing, CI mode
236
- ├── state-machine.ts — Pipeline loop, stage dispatch, question gates, complexity
237
- ├── agent-runner.ts — Claude Code subprocess wrapper (thin — spawn, pipe, timeout)
238
- ├── context.ts — Prompt assembly + memory + task context injection
239
- ├── definitions.ts — 7-stage pipeline config
240
- ├── types.ts — TypeScript interfaces
241
- ├── config.ts — kody.config.json loader + constants
242
- ├── git-utils.ts — Branch, commit, push, sync, diff
243
- ├── github-api.ts — Issues, labels, PRs, comments via gh CLI
244
- ├── verify-runner.ts — Quality gate execution
245
- ├── validators.ts — Output validation (task.json, plan.md, review.md)
246
- ├── memory.ts — .kody/memory/ reader
247
- ├── logger.ts — Structured logging with CI groups
248
- ├── preflight.ts — Startup health checks
249
- ├── kody-utils.ts — Task directory utilities
250
- └── bin/cli.ts — Package CLI: init (LLM-powered), run, version
251
- ```
252
-
253
- ## Development
254
-
255
- ```bash
256
- pnpm install # Install deps
257
- pnpm typecheck # Type check
258
- pnpm test # 125 tests (16 files)
259
- pnpm build # Build npm package
260
- pnpm kody run ... # Dev mode (tsx)
261
- npm publish --access public # Publish
262
- ```
263
-
264
- ## Security
265
-
266
- Only GitHub collaborators (COLLABORATOR, MEMBER, OWNER) can trigger `@kody`. External contributors cannot.
99
+ - **Risk Gate** — HIGH-risk tasks pause for human plan approval before building ([details](docs/FEATURES.md#risk-gate))
100
+ - **AI Failure Diagnosis** — classifies errors as fixable/infrastructure/pre-existing/abort before retry ([details](docs/FEATURES.md#ai-powered-failure-diagnosis))
101
+ - **Question Gates** — asks product/architecture questions when the task is unclear ([details](docs/FEATURES.md#question-gates))
102
+ - **Retrospective** — analyzes each run, identifies patterns, suggests improvements ([details](docs/FEATURES.md#retrospective-system))
103
+ - **Auto-Learning** extracts coding conventions from each successful run ([details](docs/FEATURES.md#auto-learning-memory))
104
+ - **Any LLM** route through LiteLLM to use MiniMax, GPT, Gemini, local models ([setup guide](docs/LITELLM.md))
105
+
106
+ ## Documentation
107
+
108
+ | Doc | What's in it |
109
+ |-----|-------------|
110
+ | [Pipeline](docs/PIPELINE.md) | Stage details, complexity skipping, artifacts, state machine |
111
+ | [Features](docs/FEATURES.md) | Risk gate, diagnosis, retrospective, auto-learn, labels |
112
+ | [LiteLLM](docs/LITELLM.md) | Non-Anthropic model setup, auto-start, tested providers |
113
+ | [Configuration](docs/CONFIGURATION.md) | Full config reference, env vars, workflow setup |
114
+ | [Comparison](docs/COMPARISON.md) | vs Copilot, Devin, Cursor, Cline, SWE-agent, OpenHands |
115
+ | [Architecture](docs/ARCHITECTURE.md) | Source tree, state machine diagram, development guide |
116
+ | [FAQ](docs/FAQ.md) | Common questions about usage, models, security, cost |
267
117
 
268
118
  ## License
269
119
 
@@ -0,0 +1,4 @@
1
+ import type { AgentRunner } from "./types.js";
2
+ import type { KodyConfig } from "./config.js";
3
+ export declare function createClaudeCodeRunner(): AgentRunner;
4
+ export declare function createRunners(config: KodyConfig): Record<string, AgentRunner>;
@@ -0,0 +1,122 @@
1
+ import { spawn, execFileSync } from "child_process";
2
+ const SIGKILL_GRACE_MS = 5000;
3
+ const STDERR_TAIL_CHARS = 500;
4
+ function writeStdin(child, prompt) {
5
+ return new Promise((resolve, reject) => {
6
+ if (!child.stdin) {
7
+ resolve();
8
+ return;
9
+ }
10
+ child.stdin.write(prompt, (err) => {
11
+ if (err)
12
+ reject(err);
13
+ else {
14
+ child.stdin.end();
15
+ resolve();
16
+ }
17
+ });
18
+ });
19
+ }
20
+ function waitForProcess(child, timeout) {
21
+ return new Promise((resolve) => {
22
+ const stdoutChunks = [];
23
+ const stderrChunks = [];
24
+ child.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
25
+ child.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
26
+ const timer = setTimeout(() => {
27
+ child.kill("SIGTERM");
28
+ setTimeout(() => {
29
+ if (!child.killed)
30
+ child.kill("SIGKILL");
31
+ }, SIGKILL_GRACE_MS);
32
+ }, timeout);
33
+ child.on("exit", (code) => {
34
+ clearTimeout(timer);
35
+ resolve({
36
+ code,
37
+ stdout: Buffer.concat(stdoutChunks).toString(),
38
+ stderr: Buffer.concat(stderrChunks).toString(),
39
+ });
40
+ });
41
+ child.on("error", (err) => {
42
+ clearTimeout(timer);
43
+ resolve({ code: -1, stdout: "", stderr: err.message });
44
+ });
45
+ });
46
+ }
47
+ async function runSubprocess(command, args, prompt, timeout, options) {
48
+ const child = spawn(command, args, {
49
+ cwd: options?.cwd ?? process.cwd(),
50
+ env: {
51
+ ...process.env,
52
+ SKIP_BUILD: "1",
53
+ SKIP_HOOKS: "1",
54
+ ...options?.env,
55
+ },
56
+ stdio: ["pipe", "pipe", "pipe"],
57
+ });
58
+ try {
59
+ await writeStdin(child, prompt);
60
+ }
61
+ catch (err) {
62
+ return {
63
+ outcome: "failed",
64
+ error: `Failed to send prompt: ${err instanceof Error ? err.message : String(err)}`,
65
+ };
66
+ }
67
+ const { code, stdout, stderr } = await waitForProcess(child, timeout);
68
+ if (code === 0) {
69
+ return { outcome: "completed", output: stdout };
70
+ }
71
+ return {
72
+ outcome: code === null ? "timed_out" : "failed",
73
+ error: `Exit code ${code}\n${stderr.slice(-STDERR_TAIL_CHARS)}`,
74
+ };
75
+ }
76
+ function checkCommand(command, args) {
77
+ try {
78
+ execFileSync(command, args, { timeout: 10_000, stdio: "pipe" });
79
+ return true;
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ }
85
+ // ─── Claude Code Runner ──────────────────────────────────────────────────────
86
+ export function createClaudeCodeRunner() {
87
+ return {
88
+ async run(_stageName, prompt, model, timeout, _taskDir, options) {
89
+ return runSubprocess("claude", [
90
+ "--print",
91
+ "--model", model,
92
+ "--dangerously-skip-permissions",
93
+ "--allowedTools", "Bash,Edit,Read,Write,Glob,Grep",
94
+ ], prompt, timeout, options);
95
+ },
96
+ async healthCheck() {
97
+ return checkCommand("claude", ["--version"]);
98
+ },
99
+ };
100
+ }
101
+ // ─── Runner Factory ──────────────────────────────────────────────────────────
102
+ const RUNNER_FACTORIES = {
103
+ "claude-code": createClaudeCodeRunner,
104
+ };
105
+ export function createRunners(config) {
106
+ // New multi-runner config
107
+ if (config.agent.runners && Object.keys(config.agent.runners).length > 0) {
108
+ const runners = {};
109
+ for (const [name, runnerConfig] of Object.entries(config.agent.runners)) {
110
+ const factory = RUNNER_FACTORIES[runnerConfig.type];
111
+ if (factory) {
112
+ runners[name] = factory();
113
+ }
114
+ }
115
+ return runners;
116
+ }
117
+ // Legacy single-runner fallback
118
+ const runnerType = config.agent.runner ?? "claude-code";
119
+ const factory = RUNNER_FACTORIES[runnerType];
120
+ const defaultName = config.agent.defaultRunner ?? "claude";
121
+ return { [defaultName]: factory ? factory() : createClaudeCodeRunner() };
122
+ }