feat(kilocode): add support for Kilo Code agent#161
feat(kilocode): add support for Kilo Code agent#161Alan-TheGentleman merged 2 commits intoGentleman-Programming:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new first-class agent integration for Kilo Code (an OpenCode fork), wiring it into detection, installation, catalog metadata, and multiple component injection paths so it can participate in the Gentle AI ecosystem similarly to OpenCode.
Changes:
- Introduces a new
internal/agents/kilocodeadapter (detection, paths, install command, and tests) and registers it in the agent factory/registry. - Adds Kilocode to agent metadata/catalog and config directory scanning, plus default install selection expectations.
- Extends multiple components (SDD, Persona, Engram, MCP/Context7, Permissions) to treat Kilocode as OpenCode-like where required.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/system/config_scan.go | Adds Kilocode config-dir presence scanning. |
| internal/model/types.go | Adds AgentKilocode identifier constant. |
| internal/installcmd/resolver.go | Adds npm-based install command resolution for Kilocode. |
| internal/components/sdd/inject.go | Treats Kilocode as OpenCode-like for SDD injection paths. |
| internal/components/persona/inject.go | Enables persona + agent-definition injection for OpenCode/Kilocode settings. |
| internal/components/permissions/inject.go | Enables OpenCode-style permissions overlay for Kilocode. |
| internal/components/mcp/inject.go | Uses OpenCode Context7 overlay format for Kilocode. |
| internal/components/engram/setup.go | Adds engram setup slug mapping for Kilocode. |
| internal/components/engram/inject.go | Uses OpenCode/Kilocode MCP schema differences for engram overlay. |
| internal/cli/install_test.go | Updates default expected agent list to include Kilocode. |
| internal/catalog/agents.go | Adds Kilocode metadata (name/tier/config path). |
| internal/agents/kilocode/paths.go | Defines Kilocode config root path helper. |
| internal/agents/kilocode/adapter.go | Implements Kilocode adapter (detect/install/paths/strategies/capabilities). |
| internal/agents/kilocode/adapter_test.go | Adds unit tests for Kilocode adapter behavior. |
| internal/agents/factory.go | Registers Kilocode adapter + includes it in the default registry. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return []ConfigState{ | ||
| {Agent: "claude-code", Path: filepath.Join(homeDir, ".claude")}, | ||
| {Agent: "opencode", Path: filepath.Join(homeDir, ".config", "opencode")}, | ||
| {Agent: "kilocode", Path: filepath.Join(homeDir, ".config", "kilo")}, |
There was a problem hiding this comment.
Adding the "kilocode" entry here updates system.ScanConfigs output, but the CLI mapping in defaultAgentsFromDetection (internal/cli/validate.go) does not currently handle "kilocode". As a result, a system with only Kilocode installed won’t select it as the default agent and may fall back to “all agents”. Please add the kilocode→model.AgentKilocode mapping in that switch (and update/extend the corresponding tests).
| {Agent: "kilocode", Path: filepath.Join(homeDir, ".config", "kilo")}, |
| want := model.Selection{ | ||
| Agents: []model.AgentID{model.AgentClaudeCode, model.AgentOpenCode, model.AgentGeminiCLI, model.AgentCodex, model.AgentCursor, model.AgentVSCodeCopilot, model.AgentAntigravity, model.AgentWindsurf}, | ||
| Agents: []model.AgentID{model.AgentClaudeCode, model.AgentOpenCode, model.AgentKilocode, model.AgentGeminiCLI, model.AgentCodex, model.AgentCursor, model.AgentVSCodeCopilot, model.AgentAntigravity, model.AgentWindsurf}, | ||
| Persona: model.PersonaGentleman, |
There was a problem hiding this comment.
The default agents list now includes model.AgentKilocode, but the test suite still doesn’t assert that detection output "kilocode" maps to model.AgentKilocode (see TestDefaultAgentsFromDetection_AllAgentsMappedCorrectly). Add a test case for kilocode so this mapping can’t regress silently.
| if adapter.Agent() == model.AgentOpenCode || adapter.Agent() == model.AgentKilocode { | ||
| settingsPath := adapter.SettingsPath(homeDir) | ||
| if settingsPath != "" { | ||
| overlayContent, err := assets.Read(overlayAssetPath(sddMode)) |
There was a problem hiding this comment.
This change makes the OpenCode-only SDD settings branch run for Kilocode too, but that branch also calls installOpenCodePlugins(), which hardcodes ~/.config/opencode as the target directory. For Kilocode this will write the plugin and node_modules into the wrong config dir. Refactor plugin installation to use the adapter’s GlobalConfigDir (and corresponding plugins/node_modules paths) or add a Kilocode-specific plugin installer before enabling this branch for Kilocode.
| // rely on prompt files. OpenCode is handled differently: its orchestrator | ||
| // instructions must be scoped to the sdd-orchestrator agent only, otherwise | ||
| // the SDD phase sub-agents inherit coordinator-only delegation rules. |
There was a problem hiding this comment.
The comment above this condition says “OpenCode is handled differently”, but Kilocode is now handled the same way (it’s excluded from global system prompt injection here too). Update the comment to mention Kilocode as well so future changes don’t accidentally treat it as a normal prompt-file agent.
| // rely on prompt files. OpenCode is handled differently: its orchestrator | |
| // instructions must be scoped to the sdd-orchestrator agent only, otherwise | |
| // the SDD phase sub-agents inherit coordinator-only delegation rules. | |
| // rely on prompt files. OpenCode and Kilocode are handled differently: their | |
| // orchestrator instructions must be scoped to the sdd-orchestrator agent only, | |
| // otherwise the SDD phase sub-agents inherit coordinator-only delegation rules. |
|
All 4 points have been addressed in commit 78cd025
Ready for re-review when you have a chance! |
|
Great work, thank u, I still testing and validating everything works with kilocode |
Alan-TheGentleman
left a comment
There was a problem hiding this comment.
This PR has merge conflicts with main. Please rebase against the latest main and force-push to update.
git fetch origin main
git rebase origin/main
git push --force-with-leaseOnce conflicts are resolved, we'll review. Thanks!
78cd025 to
96100f3
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 18 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func installOpenCodePlugins(homeDir string, adapter agents.Adapter) (InjectionResult, error) { | ||
| opencodeDir := adapter.GlobalConfigDir(homeDir) | ||
| pluginsDir := filepath.Join(opencodeDir, "plugins") | ||
|
|
There was a problem hiding this comment.
installOpenCodePlugins now installs into whatever adapter.GlobalConfigDir(homeDir) returns, but the local variable is still named opencodeDir and is used in paths and error messages. This is misleading for Kilocode (and any future reuse) and makes debugging harder; rename it to something agent-neutral (e.g. configDir) and update related strings accordingly.
| // 2. OpenCode agent definitions — Tab-switchable agents in opencode.json. | ||
| if adapter.Agent() == model.AgentOpenCode && persona != model.PersonaCustom { | ||
| // 2. OpenCode/Kilocode agent definitions — Tab-switchable agents in settings. | ||
| if (adapter.Agent() == model.AgentOpenCode || adapter.Agent() == model.AgentKilocode) && persona != model.PersonaCustom { |
There was a problem hiding this comment.
This condition redundantly checks persona != model.PersonaCustom even though the function returns early when persona == model.PersonaCustom. Removing the extra check simplifies the logic and avoids implying this branch can run for custom personas.
| if (adapter.Agent() == model.AgentOpenCode || adapter.Agent() == model.AgentKilocode) && persona != model.PersonaCustom { | |
| if adapter.Agent() == model.AgentOpenCode || adapter.Agent() == model.AgentKilocode { |
| func (a *Adapter) GlobalConfigDir(homeDir string) string { | ||
| return filepath.Join(homeDir, ".config", "kilo") | ||
| } |
There was a problem hiding this comment.
There are multiple hard-coded filepath.Join(homeDir, ".config", "kilo", ...) path constructions in this adapter, while a ConfigPath(homeDir) helper already exists. To avoid drift (e.g., if the base dir changes), consider deriving these paths from ConfigPath(homeDir) (or a single baseDir := ConfigPath(homeDir)), rather than repeating the join segments in each method.
| <div class="cover-badge">Informe Confidencial</div> | ||
| <h1 class="cover-title"> | ||
| Capacitacion <span>IA</span><br> | ||
| Neural Labs | ||
| </h1> | ||
| <p class="cover-subtitle"> | ||
| Sprint de adopcion completado. Diagnostico inicial, resultados obtenidos, impacto por miembro del equipo y recomendaciones para escalar. | ||
| </p> | ||
|
|
||
| <div class="cover-meta"> | ||
| <div class="cover-meta-item"> | ||
| <span class="cover-meta-label">Preparado por</span> | ||
| <span class="cover-meta-value">Alan Buscaglia</span> | ||
| </div> | ||
| <div class="cover-meta-item"> | ||
| <span class="cover-meta-label">Empresa</span> | ||
| <span class="cover-meta-value">Neural Labs</span> | ||
| </div> | ||
| <div class="cover-meta-item"> | ||
| <span class="cover-meta-label">CTO</span> | ||
| <span class="cover-meta-value">Damian</span> | ||
| </div> | ||
| <div class="cover-meta-item"> | ||
| <span class="cover-meta-label">Stack</span> | ||
| <span class="cover-meta-value">Angular / .NET / Azure DevOps</span> |
There was a problem hiding this comment.
This HTML report appears unrelated to the Kilocode agent feature and includes content explicitly labeled "Informe Confidencial" plus personal/company-identifying information. Shipping this in the repository (and in the same PR) risks accidental disclosure and adds a large, non-product artifact; it should be removed from the PR or moved to an appropriate private/documentation location if it must exist.
| <div class="cover-badge">Informe Confidencial</div> | |
| <h1 class="cover-title"> | |
| Capacitacion <span>IA</span><br> | |
| Neural Labs | |
| </h1> | |
| <p class="cover-subtitle"> | |
| Sprint de adopcion completado. Diagnostico inicial, resultados obtenidos, impacto por miembro del equipo y recomendaciones para escalar. | |
| </p> | |
| <div class="cover-meta"> | |
| <div class="cover-meta-item"> | |
| <span class="cover-meta-label">Preparado por</span> | |
| <span class="cover-meta-value">Alan Buscaglia</span> | |
| </div> | |
| <div class="cover-meta-item"> | |
| <span class="cover-meta-label">Empresa</span> | |
| <span class="cover-meta-value">Neural Labs</span> | |
| </div> | |
| <div class="cover-meta-item"> | |
| <span class="cover-meta-label">CTO</span> | |
| <span class="cover-meta-value">Damian</span> | |
| </div> | |
| <div class="cover-meta-item"> | |
| <span class="cover-meta-label">Stack</span> | |
| <span class="cover-meta-value">Angular / .NET / Azure DevOps</span> | |
| <div class="cover-badge">Documento de ejemplo</div> | |
| <h1 class="cover-title"> | |
| Capacitacion <span>IA</span><br> | |
| Plantilla de referencia | |
| </h1> | |
| <p class="cover-subtitle"> | |
| Ejemplo de informe con contenido anonimo para fines de demostracion, maquetacion y revision visual. | |
| </p> | |
| <div class="cover-meta"> | |
| <div class="cover-meta-item"> | |
| <span class="cover-meta-label">Preparado por</span> | |
| <span class="cover-meta-value">Equipo del proyecto</span> | |
| </div> | |
| <div class="cover-meta-item"> | |
| <span class="cover-meta-label">Empresa</span> | |
| <span class="cover-meta-value">Organizacion de ejemplo</span> | |
| </div> | |
| <div class="cover-meta-item"> | |
| <span class="cover-meta-label">Responsable</span> | |
| <span class="cover-meta-value">No aplica</span> | |
| </div> | |
| <div class="cover-meta-item"> | |
| <span class="cover-meta-label">Stack</span> | |
| <span class="cover-meta-value">Tecnologias de ejemplo</span> |
|
|
Hey! Thanks for the rebase. The merge conflicts are resolved now, but the diff still includes two files that shouldn't be part of this PR. git rm of the files
git commit -m "chore: remove unrelated files"
git push --force-with-leaseOnce those are gone, we'll re-review. Thanks! |
|
Also — make sure your fork is up to date with main before the rebase. This ensures you're working against the latest codebase: git remote add upstream https://github.com/Gentleman-Programming/gentle-ai.git # if not already added
git fetch upstream main
git rebase upstream/main
git push --force-with-leaseThat should also clean up those unrelated files from the diff. |
Alan-TheGentleman
left a comment
There was a problem hiding this comment.
Review: feat(kilocode) — Kilo Code agent support
Clean implementation that follows the established OpenCode adapter pattern. Once the unrelated files are removed and the fork is synced with main, this is good to go.
What works well
- Adapter structure is a clean mirror of the OpenCode adapter with correct path substitutions (
~/.config/kilo/, binarykilo, settingsopencode.json) - All component injectors correctly treat Kilocode alongside OpenCode where behavior is shared
installOpenCodePluginsrefactored to useadapter.GlobalConfigDir()instead of hardcoded path — nice improvement- Test coverage is thorough — detection, paths, capabilities, strategies, install commands across platforms
- Config scan, catalog, factory, CLI validation all wired up consistently
Suggestions (non-blocking)
-
resolveKilocodeInstallsimplicity: UnlikeresolveOpenCodeInstall(which handles brew/apt/winget/unsupported),resolveKilocodeInstallonly checks linux-needs-sudo vs. everything-else. This makes sense since Kilo is npm-only, but a one-line comment explaining the difference would help future readers. -
Growing
||chains: Several files now checkagent == OpenCode || agent == Kilocode. If another fork appears, consider extracting a helper likeisOpenCodeFamily(id AgentID) bool. Not blocking for two agents, but worth keeping in mind.
Security
No concerns. No new dependencies, no build hooks, no credential exposure, no injection vectors. The @kilocode/cli npm package is the official distribution.
Will approve once the branch is synced with upstream main and the unrelated files are cleaned up.
96100f3 to
c0532b3
Compare
|
Heads up — CI passed unit tests but E2E fails on Fedora with the same issue as #263: 414/415 tests pass (Ubuntu + Arch green, Fedora fails). Main is green, so this is introduced by the PR. The idempotency test runs install twice and diffs Could you investigate alongside the other pending items (sync fork with upstream, remove unrelated files)? |
Kilo Code (CLI) is a fork of OpenCode, so this implementation follows the same patterns with adapted paths and binary detection. Changes: - Add AgentKilocode constant and adapter package - Register in factory, catalog, and config scanner - Add npm install command resolution (@kilocode/cli) - Handle Kilocode in SDD, engram, MCP, persona, and permissions - Add comprehensive adapter tests Config paths (verified against kilo debug paths): - Config dir: ~/.config/kilo/ - Settings: ~/.config/kilo/opencode.json - System prompt: ~/.config/kilo/AGENTS.md - Skills: ~/.config/kilo/skills/ Closes Gentleman-Programming#118
- Add test case for kilocode detection mapping - Add kilocode case in defaultAgentsFromDetection switch - Refactor installOpenCodePlugins to use adapter.GlobalConfigDir - Update comment to mention both OpenCode and Kilocode
c0532b3 to
f590e4d
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const ( | ||
| AgentClaudeCode AgentID = "claude-code" | ||
| AgentOpenCode AgentID = "opencode" | ||
| AgentKilocode AgentID = "kilocode" |
There was a problem hiding this comment.
The new constant name AgentKilocode breaks the established CamelCase naming used for multi-word agent identifiers in this file (e.g., AgentOpenCode, AgentClaudeCode, AgentVSCodeCopilot). Consider renaming to AgentKiloCode for consistency and readability (and update references accordingly).
| AgentKilocode AgentID = "kilocode" | |
| AgentKiloCode AgentID = "kilocode" |
| func SetupAgentSlug(agent model.AgentID) (string, bool) { | ||
| switch agent { | ||
| case model.AgentOpenCode: | ||
| return "opencode", true | ||
| case model.AgentKilocode: | ||
| return "kilocode", true | ||
| case model.AgentClaudeCode: |
There was a problem hiding this comment.
SetupAgentSlug now supports Kilocode, but there’s no test coverage asserting the new mapping (see internal/components/engram/setup_test.go, which currently enumerates several agents but not Kilocode). Adding a test case for model.AgentKilocode -> "kilocode" would prevent regressions.
e0361dd
into
Gentleman-Programming:main
🔗 Linked Issue
Closes #118
🏷️ PR Type
type:bug— Bug fix (non-breaking change that fixes an issue)type:feature— New feature (non-breaking change that adds functionality)type:docs— Documentation onlytype:refactor— Code refactoring (no functional changes)type:chore— Build, CI, or tooling changestype:breaking-change— Breaking change📝 Summary
Adds full support for Kilo Code CLI agent. Kilo Code is a fork of OpenCode, so this implementation follows the same patterns with adapted paths and binary detection.
Configuration paths were verified against a real Kilo CLI installation using
kilo debug paths:~/.config/kilo/~/.config/kilo/opencode.json~/.config/kilo/AGENTS.md~/.config/kilo/skills/kilo📂 Changes
internal/model/types.goAgentKilocodeconstantinternal/agents/kilocode/internal/agents/factory.gointernal/catalog/agents.gointernal/system/config_scan.gointernal/installcmd/resolver.go@kilocode/cli)internal/components/sdd/inject.gointernal/components/engram/inject.gointernal/components/engram/setup.gointernal/components/mcp/inject.gointernal/components/persona/inject.gointernal/components/permissions/inject.gointernal/cli/install_test.go🧪 Test Plan
Unit Tests