You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Kanon's adapter backlog is now broad enough that adding every new agent directly to core will become a product bottleneck. There are open proposals for Cursor/opencode/Copilot CLI, Continue, Roo Code, Cascade/Windsurf, VS Code Copilot, Junie, Goose, Kiro, and Zed (#23, #62, #66, #72, #73, #74, #79, #91, #92). Those are valuable, but each one currently requires a core code change, a release, and ongoing maintenance as that agent's native config format changes.
Proposal: add an opt-in external render-adapter protocol. Kanon would keep ownership of source loading, validation, diff, merge policy, conflict handling, backups, and apply. A community or internal adapter executable would only receive a normalized render request on stdin and return a list of target files plus warnings on stdout.
This creates a stable integration surface for experimental or niche agents without weakening Kanon's source/target/destination model.
Evidence from the current codebase
The only agent constants are codex, claude, and all (internal/core/types.go).
adaptersFor is a hard-coded switch that can return only codexAdapter{} and claudeAdapter{} (internal/core/render.go).
The CLI rejects any --agent outside all|codex|claude (internal/cli/root.go).
validateTargets rejects every targets: value except all, codex, and claude (internal/core/config.go).
The existing Adapter interface is clean but compile-time only: Name(), Render(), Import() (internal/core/types.go).
RenderedFile already has the wire shape an external renderer needs to return: agent, destination path, content, mode, and merge strategy (internal/core/types.go).
Net: Kanon has a good internal adapter seam, but no runtime extension seam.
HashiCorp's Go plugin system launches subprocesses and communicates over RPC, explicitly avoiding shared-library loading and keeping plugin execution out of the host process: https://github.com/hashicorp/go-plugin
Git hooks are executable programs discovered by name and run at well-defined lifecycle points; Git keeps the core workflow and delegates custom behavior to external programs: https://git-scm.com/docs/githooks
The common lesson: keep the host's lifecycle and state model stable, and let explicit external executables handle extension-specific behavior.
Proposed config
Keep this explicit and local-only in v1. No auto-discovery and no remote adapter execution from a shared kanon.yaml.
Kanon then validates paths, decodes content, sorts/deduplicates, and feeds the resulting RenderedFiles into the existing diff / apply path.
Merge policy
Do not let external adapters mutate destination files directly. Kanon should remain the only writer.
For v1, allow:
replace: adapter owns the whole returned file.
json_top_level, toml_top_level, yaml_top_level: adapter owns named top-level keys, and Kanon performs the structured merge while preserving unmanaged keys.
Unknown merge strategies should fail validation. Richer merge drivers can come later, but the key point is that external adapters describe ownership and Kanon executes the merge.
adapter inspect calls the executable with an inspect operation and prints content-free capabilities: protocol version, supported target names, render/import support, merge strategies used, and native files it may emit.
Import support should be deferred. Rendering is the growth bottleneck; import requires each adapter to normalize destination state back into Kanon source, which is much riskier.
Guardrails
External adapters are never auto-discovered in v1. They must be declared locally and explicitly.
Do not pass secret values beyond what already exists in the resolved config/environment. Redact adapter stderr in the same places Kanon redacts git URLs.
Adapter stderr should be included only on failure and should be capped to avoid dumping large prompts or secrets.
Returned paths must normalize under allowed roots and must not escape via symlinks or .. segments.
Same-path collisions across built-in and external adapters should fail unless content and merge ownership are identical.
External adapters should be opt-in targets; existing --agent all behavior should not start executing external commands unless the config explicitly enables them.
Use cases unlocked
Internal enterprise agents: a company can maintain kanon-adapter-internal for a private IDE/agent without upstreaming proprietary native config logic.
Fast-moving public agents: community adapters for Zed/Kiro/Goose/etc. can iterate at the agent's release pace while Kanon core stays stable.
Experimental targets: users can prototype render mappings before asking Kanon to accept a built-in adapter.
Platform-team specialization: orgs can enforce local destination policies and merge ownership while reusing Kanon's neutral source schema and apply planner.
Suggested MVP scope
Add ExternalAdapters map[string]ExternalAdapter to Config.
Extend target validation so names declared by external adapters are valid in targets: and --agent.
Add an externalAdapter implementation that invokes command render with JSON stdin/stdout.
Support replace plus one generic structured top-level merge strategy, preferably JSON first.
Add path-root validation and same-path collision detection before planning.
Add kanon adapter list and kanon adapter inspect.
Add tests with a small fake adapter executable/script covering successful render, protocol mismatch, invalid path escape, unsupported merge, target filtering, and adapter stderr redaction.
Backward compatibility
Existing kanon.yaml files are unchanged.
Built-in Codex and Claude behavior stays the default.
External adapters are opt-in and local-only.
Existing render/diff/apply/import commands keep their current behavior when no external adapter is declared.
Non-goals
Do not create a remote adapter registry in v1.
Do not manage agent-native plugin marketplaces or install state.
Do not require existing built-in adapters to move out of core.
Do not implement import protocol in the first increment.
Do not let adapters write destination files themselves; Kanon remains the applier.
🤖 Kelos Strategist Agent @gjkim42
Area: Integration Opportunities
Summary
Kanon's adapter backlog is now broad enough that adding every new agent directly to core will become a product bottleneck. There are open proposals for Cursor/opencode/Copilot CLI, Continue, Roo Code, Cascade/Windsurf, VS Code Copilot, Junie, Goose, Kiro, and Zed (#23, #62, #66, #72, #73, #74, #79, #91, #92). Those are valuable, but each one currently requires a core code change, a release, and ongoing maintenance as that agent's native config format changes.
Proposal: add an opt-in external render-adapter protocol. Kanon would keep ownership of source loading, validation, diff, merge policy, conflict handling, backups, and apply. A community or internal adapter executable would only receive a normalized render request on stdin and return a list of target files plus warnings on stdout.
This creates a stable integration surface for experimental or niche agents without weakening Kanon's source/target/destination model.
Evidence from the current codebase
codex,claude, andall(internal/core/types.go).adaptersForis a hard-coded switch that can return onlycodexAdapter{}andclaudeAdapter{}(internal/core/render.go).--agentoutsideall|codex|claude(internal/cli/root.go).validateTargetsrejects everytargets:value exceptall,codex, andclaude(internal/core/config.go).Adapterinterface is clean but compile-time only:Name(),Render(),Import()(internal/core/types.go).RenderedFilealready has the wire shape an external renderer needs to return: agent, destination path, content, mode, and merge strategy (internal/core/types.go).Net: Kanon has a good internal adapter seam, but no runtime extension seam.
Existing issue overlap check
This is intentionally distinct from:
I searched open and closed issues for external adapter/plugin/protocol wording and did not find a focused proposal.
External signal
This is a proven pattern in mature CLI/config tools:
kubectlthrough standalone executables whose names map to subcommands; the official docs emphasize filename-based dispatch and explicit argument parsing by the plugin: https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/The common lesson: keep the host's lifecycle and state model stable, and let explicit external executables handle extension-specific behavior.
Proposed config
Keep this explicit and local-only in v1. No auto-discovery and no remote adapter execution from a shared
kanon.yaml.Notes:
external_adapters.<name>.targetsdeclares target names that become valid intargets:and--agent.commandis resolved fromPATHor an absolute path on the local machine.allow_pathsconstrains returned destination paths. If omitted, default to paths underUserHomeand, when--projectis set, underProject.protocollets Kanon fail fast when an adapter speaks an incompatible wire contract.Wire protocol sketch
Kanon invokes the adapter only during render:
Request on stdin:
{ "protocol": "render.v1", "operation": "render", "agent": "zed", "config": { "version": 1, "instructions": {}, "skills": [], "mcp": {}, "hooks": [] }, "target": { "kanonHome": "/home/alice/.config/kanon", "userHome": "/home/alice", "project": "/home/alice/src/repo" } }Response on stdout:
{ "protocol": "render.v1", "files": [ { "agent": "zed", "path": "/home/alice/src/repo/.zed/settings.json", "mode": "0644", "contentBase64": "eyJjb250ZXh0U2VydmVycyI6e319Cg==", "merge": { "strategy": "replace" } } ], "warnings": [] }Kanon then validates paths, decodes content, sorts/deduplicates, and feeds the resulting
RenderedFiles into the existingdiff/applypath.Merge policy
Do not let external adapters mutate destination files directly. Kanon should remain the only writer.
For v1, allow:
replace: adapter owns the whole returned file.json_top_level,toml_top_level,yaml_top_level: adapter owns named top-level keys, and Kanon performs the structured merge while preserving unmanaged keys.Unknown merge strategies should fail validation. Richer merge drivers can come later, but the key point is that external adapters describe ownership and Kanon executes the merge.
CLI surface
Suggested minimal additions:
adapter inspectcalls the executable with aninspectoperation and prints content-free capabilities: protocol version, supported target names, render/import support, merge strategies used, and native files it may emit.Import support should be deferred. Rendering is the growth bottleneck; import requires each adapter to normalize destination state back into Kanon source, which is much riskier.
Guardrails
kanon.yaml; Declare agent runtime dependencies for MCP servers and hooks #52-style runtime/dependency metadata can describe installation later...segments.--agent allbehavior should not start executing external commands unless the config explicitly enables them.Use cases unlocked
kanon-adapter-internalfor a private IDE/agent without upstreaming proprietary native config logic.Suggested MVP scope
ExternalAdapters map[string]ExternalAdaptertoConfig.targets:and--agent.externalAdapterimplementation that invokescommand renderwith JSON stdin/stdout.replaceplus one generic structured top-level merge strategy, preferably JSON first.kanon adapter listandkanon adapter inspect.Backward compatibility
kanon.yamlfiles are unchanged.Non-goals