docs: delete internal add-agent-cli guide describing removed protocols (#3010)
Co-authored-by: nova28 <17953305+nova28@users.noreply.github.com>
This commit is contained in:
@@ -94,7 +94,7 @@ Connect Kandev to the tools your team already uses — pull issues into the kanb
|
|||||||
| **Grok** | `grok` *(install `@xai-official/grok` with npm)* |
|
| **Grok** | `grok` *(install `@xai-official/grok` with npm)* |
|
||||||
| **Hermes** | `hermes` *(install with the official Hermes installer)* |
|
| **Hermes** | `hermes` *(install with the official Hermes installer)* |
|
||||||
|
|
||||||
> All agents communicate via [ACP](https://agentclientprotocol.com) (Agent Client Protocol). Some agents support ACP natively, while others use ACP adapter packages that bridge their native protocols. **CLI Passthrough mode** is available when an integration provides a passthrough command. If your agent isn't supported yet, open an issue or submit a PR with the integration. See [Adding a New Agent CLI](docs/add-agent-cli.md) for a step-by-step guide.
|
> All agents communicate via [ACP](https://agentclientprotocol.com) (Agent Client Protocol). Some agents support ACP natively, while others use ACP adapter packages that bridge their native protocols. **CLI Passthrough mode** is available when an integration provides a passthrough command. If your agent isn't supported yet, open an issue or submit a PR with the integration. See [Adding a New Agent CLI](docs/public/add-agent-cli.md) for a step-by-step guide.
|
||||||
|
|
||||||
Kandev does not pin the managed npm runtimes for Claude, Codex, OpenCode,
|
Kandev does not pin the managed npm runtimes for Claude, Codex, OpenCode,
|
||||||
Copilot, or Gemini. Normal launches can reuse npm's best-effort execution
|
Copilot, or Gemini. Normal launches can reuse npm's best-effort execution
|
||||||
|
|||||||
@@ -1,243 +0,0 @@
|
|||||||
# Adding a New Agent CLI Integration
|
|
||||||
|
|
||||||
This guide explains how to add support for a new coding agent CLI to Kandev.
|
|
||||||
|
|
||||||
Agents are defined entirely in Go. The backend discovers them through a registry, and the frontend picks them up automatically via the API - **no frontend changes are needed**.
|
|
||||||
|
|
||||||
For a clean reference implementation, see [`gemini.go`](../apps/backend/internal/agent/agents/gemini.go) (ACP protocol). For a CLI-only TUI agent, see the `TUIAgent` declarative type in [`tui_agent.go`](../apps/backend/internal/agent/agents/tui_agent.go).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## CLI-Only Agent (Passthrough Only) - Recommended for TUI tools
|
|
||||||
|
|
||||||
If an agent has a CLI but no structured protocol (no JSON-RPC, no streaming API), use the **`TUIAgent` declarative type**. The agent runs in a terminal session and input/output passes through a WebSocket - users interact with the full agent TUI directly.
|
|
||||||
|
|
||||||
This is the simplest path. A complete agent definition is ~20 lines.
|
|
||||||
|
|
||||||
### Step 1: Create the agent file
|
|
||||||
|
|
||||||
Create `apps/backend/internal/agent/agents/{agent_id}.go`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package agents
|
|
||||||
|
|
||||||
func NewClaude() *TUIAgent {
|
|
||||||
return NewTUIAgent(TUIAgentConfig{
|
|
||||||
AgentID: "claude",
|
|
||||||
AgentName: "Claude",
|
|
||||||
Command: "claude",
|
|
||||||
Desc: "Claude Code - an agentic coding tool by Anthropic.",
|
|
||||||
|
|
||||||
// Optional: let users select a model in profile settings.
|
|
||||||
// The {model} placeholder is replaced with the user's chosen model at launch.
|
|
||||||
// Omit this if the CLI doesn't accept a model flag.
|
|
||||||
ModelFlag: NewParam("--model", "{model}"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Logos are optional - the UI shows a blank placeholder when none is provided. To add them, embed SVGs and pass `LogoLight` / `LogoDark`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package agents
|
|
||||||
|
|
||||||
import _ "embed"
|
|
||||||
|
|
||||||
//go:embed logos/claude_light.svg
|
|
||||||
var claudeLogoLight []byte
|
|
||||||
|
|
||||||
//go:embed logos/claude_dark.svg
|
|
||||||
var claudeLogoDark []byte
|
|
||||||
|
|
||||||
func NewClaude() *TUIAgent {
|
|
||||||
return NewTUIAgent(TUIAgentConfig{
|
|
||||||
AgentID: "claude",
|
|
||||||
AgentName: "Claude",
|
|
||||||
Command: "claude",
|
|
||||||
Desc: "Claude Code - an agentic coding tool by Anthropic.",
|
|
||||||
ModelFlag: NewParam("--model", "{model}"),
|
|
||||||
LogoLight: claudeLogoLight,
|
|
||||||
LogoDark: claudeLogoDark,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`TUIAgentConfig` fields (all optional fields have sensible defaults):
|
|
||||||
|
|
||||||
| Field | Required | Description |
|
|
||||||
|-------|----------|-------------|
|
|
||||||
| `AgentID` | Yes | Unique lowercase identifier (e.g. `"claude"`) |
|
|
||||||
| `AgentName` | Yes | Display name (e.g. `"Claude"`) |
|
|
||||||
| `Command` | Yes | Binary name to run (e.g. `"claude"`) |
|
|
||||||
| `Desc` | Yes | Description shown in the UI |
|
|
||||||
| `ModelFlag` | No | Model CLI flag, e.g. `NewParam("--model", "{model}")`. Lets users pick a model in profile settings. Omit if the CLI doesn't support model selection. |
|
|
||||||
| `CommandArgs` | No | Extra args appended after `Command` (e.g. `[]string{"--debug"}`) |
|
|
||||||
| `WaitForTerm` | No | When `true`, delays process start until the terminal WebSocket connects and sends its first resize event. Use this for TUI apps that need accurate terminal dimensions on startup (e.g. ncurses-based UIs). Most agents don't need this. Defaults to `false`. |
|
|
||||||
| `LogoLight` / `LogoDark` | No | Embedded SVG bytes for light/dark themes. The UI shows a blank placeholder if omitted. |
|
|
||||||
| `DetectOpts` | No | Custom detection options. Defaults to `WithCommand(Command)` which checks if the binary is in `$PATH`. |
|
|
||||||
|
|
||||||
### Step 2: Register the agent
|
|
||||||
|
|
||||||
Add your agent to `LoadDefaults()` in [`apps/backend/internal/agent/registry/registry.go`](../apps/backend/internal/agent/registry/registry.go):
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (r *Registry) LoadDefaults() {
|
|
||||||
all := []agents.Agent{
|
|
||||||
// ... existing agents ...
|
|
||||||
agents.NewMyAgent(), // add here
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Build and test
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make -C apps/backend build && make -C apps/backend test && make -C apps/backend lint
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: passthrough-only agents provide terminal interaction only. There are no structured chat messages, tool call visibility, or progress tracking in the chat UI.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Adding a New Agent (Full Integration)
|
|
||||||
|
|
||||||
For agents with a structured protocol (JSON-RPC, streaming JSON, etc.), implement the full [`Agent`](../apps/backend/internal/agent/agents/agent.go) interface.
|
|
||||||
|
|
||||||
### Step 1: Create the agent definition file
|
|
||||||
|
|
||||||
Create `apps/backend/internal/agent/agents/{agent_id}.go`.
|
|
||||||
|
|
||||||
This file implements the [`Agent`](../apps/backend/internal/agent/agents/agent.go) interface. See the interface definition in `agent.go` for the full contract.
|
|
||||||
|
|
||||||
Embed `StandardPassthrough` to get CLI passthrough support for free:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type MyAgent struct {
|
|
||||||
StandardPassthrough
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMyAgent() *MyAgent {
|
|
||||||
return &MyAgent{
|
|
||||||
StandardPassthrough: StandardPassthrough{
|
|
||||||
PermSettings: myAgentPermSettings,
|
|
||||||
Cfg: PassthroughConfig{
|
|
||||||
Supported: true,
|
|
||||||
Label: "CLI Passthrough",
|
|
||||||
Description: "Show terminal directly instead of chat interface",
|
|
||||||
PassthroughCmd: NewCommand("myagent"),
|
|
||||||
ModelFlag: NewParam("--model", "{model}"),
|
|
||||||
PromptFlag: NewParam("--prompt", "{prompt}"),
|
|
||||||
IdleTimeout: 3 * time.Second,
|
|
||||||
BufferMaxBytes: DefaultBufferMaxBytes,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Key implementation notes:
|
|
||||||
|
|
||||||
- **`BuildCommand()`** returns the CLI command for structured protocol mode (the agent communicates via stdin/stdout with a protocol adapter). Include the protocol flag here (e.g. `--acp`, `--stream-json`).
|
|
||||||
- **`Runtime()`** returns configuration used when running the agent in Docker or as a standalone process. Set `Protocol` to tell the adapter factory which transport to use.
|
|
||||||
- **`IsInstalled()`** uses the `Detect()` helper with `WithFileExists()` to check for known installation paths. Set `SupportsMCP` and `MCPConfigPaths` on the result.
|
|
||||||
- **MCP servers in passthrough** — set `MCPStrategy` on `PassthroughConfig` so the agent receives kandev's own tools server (plus any profile-configured MCP servers) when run in passthrough mode. Omitting it means **no MCP servers are injected in passthrough**. Pick the strategy that matches how your CLI loads MCP servers: `mcpconfig.ClaudeStrategy{}` (temp JSON file + `--mcp-config` flag), `mcpconfig.CodexStrategy{}` (repeated `-c mcp_servers.…` overrides), `mcpconfig.CursorStrategy{}` (project-local `.cursor/mcp.json`), `mcpconfig.PiStrategy{}` (project-local `.pi/mcp.json`), or `mcpconfig.OpenCodeStrategy{}` (temp JSON + `OPENCODE_CONFIG` env var). To add a strategy for a CLI with a different mechanism, implement `mcpconfig.PassthroughMCPStrategy`. See ADR 0014, ADR 0020, and `internal/agent/mcpconfig/passthrough.go`.
|
|
||||||
- **MCP project files in protocol mode** — set `RuntimeConfig.ProjectMCPStrategy` only when the structured protocol adapter does not pass ACP `session/new` MCP servers through to the underlying CLI. Pi uses this to write `.pi/mcp.json` before the subprocess starts.
|
|
||||||
- **`ListModels()`** can return a static list or dynamically query the CLI. Use `execAndParse()` from `helpers.go` for dynamic model discovery.
|
|
||||||
- Use `Cmd()` builder for constructing commands fluently: `Cmd("myagent", "--flag").Model(...).Settings(...).Build()`.
|
|
||||||
|
|
||||||
### Step 2: Add logos (optional)
|
|
||||||
|
|
||||||
Logos are optional - the UI shows a blank placeholder when none is provided. To add them, place SVGs in:
|
|
||||||
|
|
||||||
```
|
|
||||||
apps/backend/internal/agent/agents/logos/{agent_id}_light.svg
|
|
||||||
apps/backend/internal/agent/agents/logos/{agent_id}_dark.svg
|
|
||||||
```
|
|
||||||
|
|
||||||
Embed them in your agent file:
|
|
||||||
|
|
||||||
```go
|
|
||||||
//go:embed logos/myagent_light.svg
|
|
||||||
var myagentLogoLight []byte
|
|
||||||
|
|
||||||
//go:embed logos/myagent_dark.svg
|
|
||||||
var myagentLogoDark []byte
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Register the agent
|
|
||||||
|
|
||||||
Add your agent to `LoadDefaults()` in [`apps/backend/internal/agent/registry/registry.go`](../apps/backend/internal/agent/registry/registry.go):
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (r *Registry) LoadDefaults() {
|
|
||||||
all := []agents.Agent{
|
|
||||||
// ... existing agents ...
|
|
||||||
agents.NewMyAgent(), // add here
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it for registration. The frontend discovers agents through the API automatically.
|
|
||||||
|
|
||||||
### Step 4: Choose a protocol
|
|
||||||
|
|
||||||
Check [`apps/backend/pkg/agent/protocol.go`](../apps/backend/pkg/agent/protocol.go) for available protocols:
|
|
||||||
|
|
||||||
| Protocol | Constant | Transport | Used By |
|
|
||||||
|----------|----------|-----------|---------|
|
|
||||||
| ACP | `ProtocolACP` | JSON-RPC 2.0 over stdin/stdout | Gemini, Auggie |
|
|
||||||
| stream-json | `ProtocolClaudeCode` | Streaming JSON over stdin/stdout | Claude Code |
|
|
||||||
| Codex | `ProtocolCodex` | JSON-RPC variant over stdin/stdout | Codex |
|
|
||||||
| OpenCode | `ProtocolOpenCode` | REST/SSE over HTTP | OpenCode |
|
|
||||||
| Copilot | `ProtocolCopilot` | Go SDK (JSON-RPC internally) | GitHub Copilot |
|
|
||||||
| Amp | `ProtocolAmp` | Streaming JSON over stdin/stdout | Amp |
|
|
||||||
|
|
||||||
**If an existing protocol fits your agent**, reuse it - set `Protocol` in `Runtime()`. The adapter factory in `factory.go` selects the right transport automatically based on this value.
|
|
||||||
|
|
||||||
**If you need a new protocol**, you'll need to:
|
|
||||||
|
|
||||||
1. Add a new `Protocol` constant in `apps/backend/pkg/agent/protocol.go`
|
|
||||||
2. Create a transport adapter in `apps/backend/internal/agentctl/server/adapter/transport/{protocol}/`
|
|
||||||
3. Add the case to the factory switch in [`apps/backend/internal/agentctl/server/adapter/factory.go`](../apps/backend/internal/agentctl/server/adapter/factory.go)
|
|
||||||
|
|
||||||
### Step 5: Build and test
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make -C apps/backend build && make -C apps/backend test && make -C apps/backend lint
|
|
||||||
```
|
|
||||||
|
|
||||||
Start the dev server and verify:
|
|
||||||
- The agent appears in the agent selector in the UI
|
|
||||||
- Agent execution works (start a task, send a prompt)
|
|
||||||
- Passthrough mode works (toggle to CLI passthrough in the session)
|
|
||||||
- Model listing works
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Updating an Existing Agent
|
|
||||||
|
|
||||||
| Change | Where |
|
|
||||||
|--------|-------|
|
|
||||||
| Update CLI version | `BuildCommand()` and `Runtime().Cmd` in the agent file |
|
|
||||||
| Add/remove models | `ListModels()` - update static list or dynamic command |
|
|
||||||
| Update permissions | `PermissionSettings()` variable and `PassthroughConfig` |
|
|
||||||
| Update Docker image | `Runtime()` - change `Image`/`Tag` fields |
|
|
||||||
| Change protocol | `Runtime().Protocol` in the agent file |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Reference
|
|
||||||
|
|
||||||
| File | When to modify |
|
|
||||||
|------|---------------|
|
|
||||||
| `apps/backend/internal/agent/agents/{agent_id}.go` | Always - agent definition |
|
|
||||||
| `apps/backend/internal/agent/agents/logos/{agent_id}_{light,dark}.svg` | Optional - agent logos (UI shows blank placeholder if omitted) |
|
|
||||||
| `apps/backend/internal/agent/registry/registry.go` | Always - register in `LoadDefaults()` |
|
|
||||||
| `apps/backend/internal/agent/agents/tui_agent.go` | Never - provides `TUIAgent` declarative type (read-only reference) |
|
|
||||||
| `apps/backend/pkg/agent/protocol.go` | Only if adding a new protocol |
|
|
||||||
| `apps/backend/internal/agentctl/server/adapter/factory.go` | Only if adding a new protocol |
|
|
||||||
| `apps/backend/internal/agentctl/server/adapter/transport/{proto}/` | Only if adding a new protocol |
|
|
||||||
| `apps/backend/internal/agent/agents/passthrough.go` | Never - provides `StandardPassthrough` (read-only reference) |
|
|
||||||
| `apps/backend/internal/agent/agents/agent.go` | Never - defines the `Agent` interface (read-only reference) |
|
|
||||||
Reference in New Issue
Block a user