Skip to content

feat(engines): add Kilocode engine adapter (#417)#451

Merged
frankbria merged 3 commits into
mainfrom
feat/kilocode-engine-adapter-417
Mar 19, 2026
Merged

feat(engines): add Kilocode engine adapter (#417)#451
frankbria merged 3 commits into
mainfrom
feat/kilocode-engine-adapter-417

Conversation

@frankbria

@frankbria frankbria commented Mar 19, 2026

Copy link
Copy Markdown
Owner

Summary

  • Implements KilocodeAdapter as a subprocess-based external engine, allowing cf work start <id> --execute --engine kilocode
  • Follows the same SubprocessAdapter pattern used by claude-code and opencode adapters
  • Kilocode takes the prompt as a positional CLI argument (kilo run <prompt> --auto --workspace <path>) rather than via stdin
  • Exit code 124 (Kilocode's timeout sentinel) is mapped to a failed result with a descriptive message
  • Configurable via KILOCODE_PATH, KILOCODE_MODEL, and KILOCODE_FLAGS environment variables

Changes

  • codeframe/core/adapters/kilocode.py — new KilocodeAdapter extending SubprocessAdapter
  • codeframe/core/engine_registry.py — registers "kilocode" in VALID_ENGINES and EXTERNAL_ENGINES; wires factory functions
  • codeframe/cli/app.py — updates --engine help text (both work start and work batch run)
  • .env.example — documents KILOCODE_PATH, KILOCODE_MODEL, KILOCODE_FLAGS
  • tests/core/adapters/test_kilocode.py — 14 unit tests covering all adapter behaviors
  • tests/core/test_engine_registry.py — adds kilocode coverage to registry tests

Test plan

  • 14 new unit tests for KilocodeAdapter — all passing
  • 50 engine registry tests — all passing
  • 142 core adapter tests — all passing
  • cf work start <id> --execute --engine kilocode routes correctly through execute_agent()

Closes #417

Summary by CodeRabbit

  • New Features

    • Support for the Kilocode engine as an alternative agent engine option.
    • New environment variables to configure Kilocode: KILOCODE_PATH, KILOCODE_MODEL, KILOCODE_FLAGS.
    • Kilocode is selectable via the --engine kilocode option in relevant commands.
  • Tests

    • Added tests validating Kilocode adapter readiness, command construction, execution outcomes, and event streaming.

Implements KilocodeAdapter as an external subprocess engine that delegates
code execution to the `kilo run --auto` CLI. Follows the same SubprocessAdapter
pattern as claude-code and opencode adapters.

- Add codeframe/core/adapters/kilocode.py with KilocodeAdapter
  - Passes prompt as positional CLI arg (not stdin)
  - Handles exit code 124 as timeout failure
  - Configurable via KILOCODE_PATH, KILOCODE_MODEL, KILOCODE_FLAGS env vars
- Register "kilocode" in engine_registry.py (VALID_ENGINES + EXTERNAL_ENGINES)
- Update CLI --engine help text to include "kilocode"
- Document env vars in .env.example
- Add 14 unit tests covering all adapter behaviors

Closes #417
@coderabbitai

coderabbitai Bot commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Added a Kilocode external engine: CLI help updated, engine registry extended, a Subprocess-based KilocodeAdapter implemented to run kilo run ... (with model/flags env overrides), .env.example updated, and tests added for adapter behavior and registry integration.

Changes

Cohort / File(s) Summary
Configuration
\.env.example
Added optional env vars: KILOCODE_PATH, KILOCODE_MODEL, KILOCODE_FLAGS. No other defaults changed.
CLI Integration
codeframe/cli/app.py
Updated --engine help strings in work_start and batch_run to list kilocode as a supported engine.
Adapter Implementation
codeframe/core/adapters/kilocode.py
New KilocodeAdapter (subclass of SubprocessAdapter) that resolves kilo via KILOCODE_PATH (default kilo), builds kilo run <prompt> --auto --workspace <path>, optionally adds --model and parsed KILOCODE_FLAGS, returns no stdin, implements readiness check, and maps exit code 124 to a timeout failure.
Engine Registry
codeframe/core/engine_registry.py
Registered "kilocode" in VALID_ENGINES and EXTERNAL_ENGINES; _get_adapter_class and get_external_adapter now return/instantiate KilocodeAdapter.
Tests
tests/core/adapters/test_kilocode.py, tests/core/test_engine_registry.py
Added tests for binary resolution, command construction (including KILOCODE_MODEL and KILOCODE_FLAGS parsing), readiness/requirements, subprocess execution outcomes (success, failure, timeout 124), event emission per stdout line, and registry integration for kilocode.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant CLI as CLI (app.py)
    participant Registry as Engine Registry
    participant Adapter as Kilocode Adapter
    participant Subprocess as kilo (subprocess)
    participant Workspace as Workspace Files

    User->>CLI: cf work start --engine kilocode
    CLI->>Registry: get_external_adapter("kilocode")
    Registry->>Adapter: instantiate KilocodeAdapter()
    Adapter->>Adapter: check_ready() -> shutil.which(KILOCODE_PATH)
    CLI->>Adapter: execute(prompt, workspace_path)
    Adapter->>Adapter: build_command(prompt, workspace_path)
    Adapter->>Subprocess: Popen(command, stdout/stderr)
    Subprocess->>Workspace: may read/modify files
    Subprocess-->>Adapter: exit_code, stdout, stderr
    Adapter->>Adapter: _map_result(exit_code, stdout, stderr)
    alt exit_code == 124
        Adapter-->>CLI: AgentResult(status="failed", error="timed out")
    else exit_code == 0
        Adapter-->>CLI: AgentResult(status="completed", output=...)
    else
        Adapter-->>CLI: AgentResult(status="failed", error=...)
    end
    CLI-->>User: report result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I hopped through PATH and flags tonight,
Kilo found, model set, the command took flight.
I watched the stdout lines parade,
Tests hummed softly — no errors made.
A tiny rabbit cheered the new engine's light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(engines): add Kilocode engine adapter (#417)' clearly and concisely describes the primary change—adding Kilocode engine support through a new adapter implementation.
Linked Issues check ✅ Passed The PR implements all core coding requirements from issue #417: KilocodeAdapter class conforming to AgentAdapter, CLI invocation via subprocess with proper argument handling, timeout mapping (exit code 124), environment variable configuration (KILOCODE_PATH, KILOCODE_MODEL, KILOCODE_FLAGS), registry integration, and comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing Kilocode engine support: adapter implementation, registry updates, CLI help text updates, environment documentation, and tests. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/kilocode-engine-adapter-417
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude

claude Bot commented Mar 19, 2026

Copy link
Copy Markdown

Code Review — feat(engines): add Kilocode engine adapter (#417)

Overall this is a clean, well-scoped addition that correctly follows the SubprocessAdapter pattern. Architecture is sound and the test coverage is solid. A few issues worth addressing before merge:


Critical / Bug

Shell-injection risk in KILOCODE_FLAGS parsing

codeframe/core/adapters/kilocode.py, line 83:

cmd.extend(extra_flags_str.split())

str.split() on whitespace is not shell-aware. A value like --flag "value with spaces" will be split into four tokens instead of two, silently corrupting the command. It also creates a shell-injection foothold if any flag value contains characters like ; or | when passed through — though since this goes to subprocess.Popen as a list (not shell=True) the injection risk is limited to argument smuggling rather than shell execution.

The same pattern is used in the other adapters (claude-code uses allowlist, opencode has none), so this is inherited debt — but since KILOCODE_FLAGS is the first env var that takes a free-form multi-token string in this codebase, now is the right time to fix it. Use shlex.split() instead:

import shlex
cmd.extend(shlex.split(extra_flags_str))

This correctly handles quoted strings and is the idiomatic approach for user-supplied CLI flag strings.


Important

Prompt injected as a positional argument is unsafe for untrusted content

codeframe/core/adapters/kilocode.py, lines 68–74:

cmd = [
    self._binary_path,
    "run",
    prompt,          # ← entire rich context prompt as one token
    "--auto",
    ...
]

The prompt assembled by TaskContextPackager can be hundreds of lines of text including PRD content, file contents, and blocker history from the workspace. Passing this as a single positional CLI argument works in practice on Linux (kernel limit is ~2MB per argument), but it will silently truncate or fail on macOS (256KB ARG_MAX per argument on some configurations) or Windows, and will break loudly if the prompt contains a newline that kilo's arg parser mishandles.

The existing adapters (claude-code, opencode) avoid this by piping via stdin. If kilo's run subcommand supports stdin as an alternative (e.g., kilo run --stdin), that would be safer. If not, this is worth a comment in the docstring warning that very long prompts may fail on certain platforms.

check_ready() and __init__() read KILOCODE_PATH independently

codeframe/core/adapters/kilocode.py, lines 42 and 52:
Both __init__ and check_ready resolve the binary path from the environment variable separately. If KILOCODE_PATH changes between the two calls (unlikely but possible in test scenarios), they diverge. More practically, check_ready tests a different path than the one the adapter will actually use. Extracting _resolve_binary() as a shared @staticmethod (as the test for test_check_ready_when_binary_present already patches shutil.which directly) would remove the duplication.


Nice-to-have

Missing requirements() classmethod

CodexAdapter implements a requirements() classmethod that documents the OPENAI_API_KEY env var and integrates with check_requirements() in engine_registry.py. KilocodeAdapter skips this. Kilocode presumably needs an API key (KILO_API_KEY or similar). Even if it doesn't, adding an empty requirements() makes the pattern consistent and ensures cf engines check kilocode returns a meaningful result rather than {}.

Test: test_raises_if_kilo_not_installed is not testing the right path

tests/core/adapters/test_kilocode.py, line 34:

def test_raises_if_kilo_not_installed(self) -> None:
    with patch("shutil.which", return_value=None):
        with pytest.raises(EnvironmentError, match="not found on PATH"):
            KilocodeAdapter()

This patches the global shutil.which rather than codeframe.core.adapters.subprocess_adapter.shutil.which (which is where SubprocessAdapter.__init__ actually calls it). The test may pass only because Python's import caching happens to redirect the lookup. The other tests in this file patch shutil.which at the same level, so this is consistent — but it's fragile. The safe pattern is patch("codeframe.core.adapters.subprocess_adapter.shutil.which", ...).

timeout_s parameter not forwarded in get_external_adapter()

codeframe/core/engine_registry.py, line 103:

return KilocodeAdapter()

KilocodeAdapter.__init__ accepts timeout_s, but get_external_adapter drops any **kwargs on the floor for the kilocode branch (whereas the claude-code branch correctly forwards **kwargs via ClaudeCodeAdapter(**kwargs)). This means --stall-timeout / custom timeout values set at the CLI level will silently be ignored for kilocode. Should be KilocodeAdapter(**kwargs).


Positive notes

  • The _map_result override for exit code 124 is exactly the right pattern — minimal and composable.
  • The autouse _no_git fixture in the test class is the correct way to isolate subprocess tests from git side-effects.
  • The get_stdin returning None is clean documentation of the interface difference vs. stdin-based adapters.
  • Registry integration in both VALID_ENGINES, EXTERNAL_ENGINES, get_external_adapter, and _get_adapter_class is complete and consistent.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/core/test_engine_registry.py (1)

1-7: ⚠️ Potential issue | 🟠 Major

Add the required v2 pytest marker for this test module.

This test file is missing pytestmark = pytest.mark.v2 at the module level. All v2 functionality tests must be marked according to repository guidelines.

Suggested fix
 """Tests for engine registry."""
 
 import os
 
 import pytest
 from unittest.mock import MagicMock, patch
 
+pytestmark = pytest.mark.v2
+
 from codeframe.core.adapters.agent_adapter import AgentAdapter
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/core/test_engine_registry.py` around lines 1 - 7, Add the module-level
pytest marker by declaring pytestmark = pytest.mark.v2 at the top-level of the
test module so pytest treats all tests here as v2; since pytest is already
imported, insert a top-level assignment named pytestmark referencing
pytest.mark.v2 (ensure it’s placed outside any function/class so it applies to
the whole module).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@codeframe/core/engine_registry.py`:
- Around line 101-104: The kilocode branch in get_external_adapter currently
instantiates KilocodeAdapter without passing through adapter-specific kwargs, so
calls like get_adapter("kilocode", timeout_s=...) are ignored; update the branch
that imports KilocodeAdapter in engine_registry (the elif engine == "kilocode"
block) to forward the received **kwargs into the adapter constructor (i.e.,
construct KilocodeAdapter with the kwargs) so adapter-specific options are
honored when returning the adapter instance.

---

Outside diff comments:
In `@tests/core/test_engine_registry.py`:
- Around line 1-7: Add the module-level pytest marker by declaring pytestmark =
pytest.mark.v2 at the top-level of the test module so pytest treats all tests
here as v2; since pytest is already imported, insert a top-level assignment
named pytestmark referencing pytest.mark.v2 (ensure it’s placed outside any
function/class so it applies to the whole module).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a1e0d9d-ad0d-4bc2-bfd2-03fa136b97af

📥 Commits

Reviewing files that changed from the base of the PR and between 4308b0a and a3896a5.

📒 Files selected for processing (6)
  • .env.example
  • codeframe/cli/app.py
  • codeframe/core/adapters/kilocode.py
  • codeframe/core/engine_registry.py
  • tests/core/adapters/test_kilocode.py
  • tests/core/test_engine_registry.py

Comment thread codeframe/core/engine_registry.py Outdated
Test User added 2 commits March 19, 2026 15:47
- Use shlex.split() for KILOCODE_FLAGS instead of str.split() — handles
  quoted values correctly and avoids argument smuggling
- Extract _resolve_binary() staticmethod to eliminate duplicated KILOCODE_PATH
  resolution between __init__ and check_ready
- Add requirements() classmethod for consistency with CodexAdapter and
  compatibility with `cf engines check kilocode`
- Forward **kwargs in get_external_adapter() so timeout_s and other params
  reach the adapter (was silently dropped)
- Fix shutil.which patch path in tests to target subprocess_adapter module
- Add pytestmark = pytest.mark.v2 to both test files for explicit marking
- Add docstring note on prompt-as-positional-arg platform limits (macOS 256KB)

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
codeframe/core/adapters/kilocode.py (1)

84-91: Consider optional warning for oversized prompts on macOS.

The docstring correctly documents that macOS caps arguments at 256KB, but there's no runtime check. For very large task contexts, the subprocess call would fail with an OS-level error that may be cryptic.

This is a low-priority improvement since the limitation is documented and failures would still be caught, but a proactive warning could improve debuggability.

💡 Optional: Add prompt length warning
     def build_command(self, prompt: str, workspace_path: Path) -> list[str]:
+        # macOS caps individual CLI arguments at 256 KB
+        _MACOS_ARG_LIMIT = 256 * 1024
+        if len(prompt.encode("utf-8")) > _MACOS_ARG_LIMIT:
+            import warnings
+            warnings.warn(
+                f"Prompt size ({len(prompt.encode('utf-8'))} bytes) exceeds macOS "
+                f"argument limit ({_MACOS_ARG_LIMIT} bytes). "
+                "Execution may fail on macOS systems.",
+                RuntimeWarning,
+                stacklevel=2,
+            )
         cmd = [
             self._binary_path,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@codeframe/core/adapters/kilocode.py` around lines 84 - 91, Add a proactive
macOS prompt-size check before building/running the subprocess command (the
block that constructs cmd using self._binary_path, "run", prompt, "--auto",
"--workspace", str(workspace_path)) to warn when the prompt will exceed the
macOS argv cap (~256KB). Detect platform (e.g., sys.platform == "darwin"),
compute the byte-length of prompt (prompt.encode("utf-8")), and if it exceeds
256*1024 emit a clear warning via the module/class logger or warnings.warn that
the subprocess may fail due to macOS argument-size limits; then continue to
build cmd as before. Ensure you reference the same variables (prompt,
self._binary_path, workspace_path) and do not alter subprocess invocation
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@codeframe/core/adapters/kilocode.py`:
- Around line 84-91: Add a proactive macOS prompt-size check before
building/running the subprocess command (the block that constructs cmd using
self._binary_path, "run", prompt, "--auto", "--workspace", str(workspace_path))
to warn when the prompt will exceed the macOS argv cap (~256KB). Detect platform
(e.g., sys.platform == "darwin"), compute the byte-length of prompt
(prompt.encode("utf-8")), and if it exceeds 256*1024 emit a clear warning via
the module/class logger or warnings.warn that the subprocess may fail due to
macOS argument-size limits; then continue to build cmd as before. Ensure you
reference the same variables (prompt, self._binary_path, workspace_path) and do
not alter subprocess invocation behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b72a14d0-f736-43bc-959a-9f0724ef05f2

📥 Commits

Reviewing files that changed from the base of the PR and between a3896a5 and d3502c7.

📒 Files selected for processing (4)
  • codeframe/core/adapters/kilocode.py
  • codeframe/core/engine_registry.py
  • tests/core/adapters/test_kilocode.py
  • tests/core/test_engine_registry.py
✅ Files skipped from review due to trivial changes (2)
  • tests/core/test_engine_registry.py
  • tests/core/adapters/test_kilocode.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • codeframe/core/engine_registry.py

@claude

claude Bot commented Mar 19, 2026

Copy link
Copy Markdown

Code Review - PR 451: Kilocode Engine Adapter

Overall this is a clean, well-structured addition. The implementation follows the established SubprocessAdapter pattern correctly, tests are comprehensive, and the registry wiring is complete. A few issues worth addressing before merge.


Critical

Shell injection via KILOCODE_FLAGS

kilocode.py line 96 calls shlex.split(extra_flags_str). shlex.split prevents naive word-splitting bugs, but it does not sanitize malicious flag injection. Since subprocess.Popen is called with a list (not shell=True), shell metacharacters are passed as literal arguments rather than interpreted by a shell. However, flags like --workspace and --model could be shadowed by duplicate flags injected via KILOCODE_FLAGS, potentially overriding values CodeFrame intends to control. Consider validating that no flags from KILOCODE_FLAGS start with --workspace or --model, or at minimum documenting this explicitly.


Important

Prompt as positional argument - argument-length risk on macOS not enforced

The docstring at line 28 correctly notes that macOS caps individual arguments at 256 KB. However, there is no runtime guard. If TaskContextPackager produces a prompt larger than 256 KB, the subprocess will fail with E2BIG on macOS and the user will see a confusing OSError rather than a clear message. A size check with an early descriptive error before calling build_command would improve the experience significantly.

check_ready uses a different shutil.which import path than the test mock

The adapter check_ready calls shutil.which via codeframe.core.adapters.kilocode.shutil.which, while the constructor binary resolution goes through SubprocessAdapter.init using codeframe.core.adapters.subprocess_adapter.shutil.which. The test file handles this with two different patch constants (_WHICH and _WHICH_KILOCODE). This is correct but fragile. A comment in the test explaining why two different patch paths are needed would help future contributors.

No timeout_s passthrough exercised anywhere

KilocodeAdapter.init accepts timeout_s but none of the call sites in the registry tests or CLI code actually pass it. The other subprocess-based adapters (OpenCodeAdapter, ClaudeCodeAdapter) have no timeout_s parameter at all. Either add a test covering timeout_s forwarding or document it as a future hook for completeness.


Minor

requirements() only lists KILOCODE_PATH but not KILOCODE_MODEL or KILOCODE_FLAGS. Both are documented in .env.example but absent from requirements(), so cf engines check kilocode will not surface them. They are optional so this is not wrong, but a comment noting they are intentionally excluded would be helpful.

The test_kilocode_adapter test in test_engine_registry.py patches bare shutil.which which is broader than necessary. Prefer patching codeframe.core.adapters.subprocess_adapter.shutil.which (same as _WHICH in test_kilocode.py).


Positive observations

  • _KILO_TIMEOUT_EXIT_CODE = 124 constant is well-placed and self-documenting.
  • Using shlex.split for KILOCODE_FLAGS is correct and handles quoted values properly.
  • Test coverage is thorough: success, nonzero failure, timeout exit code 124, event callbacks, env var overrides, and binary resolution all have dedicated tests.
  • The _no_git autouse fixture cleanly prevents subprocess calls to git during unit tests.
  • Lazy import pattern in engine_registry.py is consistent with how other adapters are wired and avoids loading unused adapter code.

Summary

The two issues most worth addressing before merge are: (1) documenting or guarding against KILOCODE_FLAGS flag injection that could shadow controlled flags like --workspace and --model, and (2) adding a runtime guard or clear error for the macOS 256 KB per-argument limit on prompt length. The rest are polish items.

@claude

claude Bot commented Mar 19, 2026

Copy link
Copy Markdown

Follow-up Review - Round 3

Good progress since my earlier reviews. Summarizing what landed and what is still open.

Confirmed fixed

  • shlex.split for KILOCODE_FLAGS: correct, handles quoted values properly.
  • timeout_s forwarded: get_external_adapter now calls KilocodeAdapter(**kwargs). Both coderabbitai and I flagged this; resolved.
  • check_ready / _resolve_binary duplication: check_ready is now a classmethod calling cls._resolve_binary(), so both call sites use the same resolution path.
  • pytestmark = pytest.mark.v2: present in tests/core/adapters/test_kilocode.py.

Still outstanding

  1. test_kilocode_adapter in test_engine_registry.py patches the wrong shutil.which

The test uses patch("shutil.which", ...) which patches the global shutil.which rather than codeframe.core.adapters.subprocess_adapter.shutil.which (the correct import path, already used as the _WHICH constant throughout test_kilocode.py). The test passes incidentally due to import caching, but will silently fail if import order changes. Should use:

patch("codeframe.core.adapters.subprocess_adapter.shutil.which", return_value="/usr/bin/kilo")
  1. KILOCODE_FLAGS can shadow controlled flags (raised in my last review, still unaddressed)

build_command appends extra flags after --workspace and --model. If kilo respects last-wins semantics for duplicate flags, KILOCODE_FLAGS="--workspace /tmp/evil" would override the workspace CodeFrame controls. Since the other adapters do not expose a free-form flags env var, at minimum a docstring note is appropriate. A simple validation guard is also an option: iterate shlex.split(extra_flags_str) and raise ValueError on any flag in {"--workspace", "--model", "--auto"}.

  1. Prompt size - no runtime guard (CodeRabbit also noted this in their second pass)

The docstring correctly warns about the 256 KB macOS per-argument limit, but there is no check before the subprocess call. A warnings.warn before building the command list is sufficient and non-breaking. Not a blocker, but easy to add now.

The adapter is clean and consistent with the established pattern. The shutil.which patch path is a test-quality issue; the KILOCODE_FLAGS shadowing concern and the prompt-size guard are the two items most worth addressing before merge.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/core/test_engine_registry.py`:
- Around line 58-60: Add the v2 marker to the new Kilocode test: update the test
function test_kilocode_is_external to be decorated with `@pytest.mark.v2` (or add
module-level pytestmark = pytest.mark.v2) so it follows the project's v2 test
convention; place the decorator immediately above the def
test_kilocode_is_external(...) or add the pytestmark declaration at top of the
file and ensure pytest is imported.
- Around line 81-86: The test function test_kilocode_adapter is missing the
required v2 marker; add the pytest v2 marker by decorating test_kilocode_adapter
with `@pytest.mark.v2` (or set pytestmark = pytest.mark.v2 at the top of the file)
so the Kilocode adapter test follows the same convention as
test_claude_code_adapter and test_opencode_adapter and will be picked up as a v2
test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5191c907-eb33-4cb4-b4c4-b075563ef0f5

📥 Commits

Reviewing files that changed from the base of the PR and between d3502c7 and f1d50d1.

📒 Files selected for processing (2)
  • tests/core/adapters/test_kilocode.py
  • tests/core/test_engine_registry.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/core/adapters/test_kilocode.py

Comment on lines +58 to +60
def test_kilocode_is_external(self):
assert is_external_engine("kilocode") is True

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.

⚠️ Potential issue | 🟡 Minor

Missing @pytest.mark.v2 decorator.

The new test for Kilocode engine should be marked with @pytest.mark.v2 to comply with the project's test conventions for v2 functionality.

Proposed fix
+    `@pytest.mark.v2`
     def test_kilocode_is_external(self):
         assert is_external_engine("kilocode") is True

As per coding guidelines: "New v2 Python tests must be marked with @pytest.mark.v2 decorator or pytestmark = pytest.mark.v2"

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_kilocode_is_external(self):
assert is_external_engine("kilocode") is True
`@pytest.mark.v2`
def test_kilocode_is_external(self):
assert is_external_engine("kilocode") is True
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/core/test_engine_registry.py` around lines 58 - 60, Add the v2 marker
to the new Kilocode test: update the test function test_kilocode_is_external to
be decorated with `@pytest.mark.v2` (or add module-level pytestmark =
pytest.mark.v2) so it follows the project's v2 test convention; place the
decorator immediately above the def test_kilocode_is_external(...) or add the
pytestmark declaration at top of the file and ensure pytest is imported.

Comment on lines +81 to +86
def test_kilocode_adapter(self):
with patch("shutil.which", return_value="/usr/bin/kilo"):
adapter = get_external_adapter("kilocode")
assert adapter.name == "kilocode"
assert isinstance(adapter, AgentAdapter)

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.

⚠️ Potential issue | 🟡 Minor

Missing @pytest.mark.v2 decorator.

The new test for the Kilocode adapter should be marked with @pytest.mark.v2. The test logic itself looks good—it correctly mocks shutil.which and validates the adapter name and type, following the same pattern as the existing test_claude_code_adapter and test_opencode_adapter tests.

Proposed fix
+    `@pytest.mark.v2`
     def test_kilocode_adapter(self):
         with patch("shutil.which", return_value="/usr/bin/kilo"):
             adapter = get_external_adapter("kilocode")
             assert adapter.name == "kilocode"
             assert isinstance(adapter, AgentAdapter)

As per coding guidelines: "New v2 Python tests must be marked with @pytest.mark.v2 decorator or pytestmark = pytest.mark.v2"

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_kilocode_adapter(self):
with patch("shutil.which", return_value="/usr/bin/kilo"):
adapter = get_external_adapter("kilocode")
assert adapter.name == "kilocode"
assert isinstance(adapter, AgentAdapter)
`@pytest.mark.v2`
def test_kilocode_adapter(self):
with patch("shutil.which", return_value="/usr/bin/kilo"):
adapter = get_external_adapter("kilocode")
assert adapter.name == "kilocode"
assert isinstance(adapter, AgentAdapter)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/core/test_engine_registry.py` around lines 81 - 86, The test function
test_kilocode_adapter is missing the required v2 marker; add the pytest v2
marker by decorating test_kilocode_adapter with `@pytest.mark.v2` (or set
pytestmark = pytest.mark.v2 at the top of the file) so the Kilocode adapter test
follows the same convention as test_claude_code_adapter and
test_opencode_adapter and will be picked up as a v2 test.

@frankbria frankbria merged commit 4f37b43 into main Mar 19, 2026
16 checks passed
@frankbria frankbria deleted the feat/kilocode-engine-adapter-417 branch March 19, 2026 23:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Phase 4] Kilocode Engine Adapter (VS Code Extension Protocol)

1 participant