Skip to content

Commit 296a9b2

Browse files
add kilo tool to silo
1 parent 0b6fb0d commit 296a9b2

7 files changed

Lines changed: 95 additions & 9 deletions

File tree

‎README.md‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Run AI coding assistants in containers/vms.
1111
╚══════╝╚═╝╚══════╝ ╚═════╝
1212
```
1313

14-
Silo lets you run AI tools like Claude Code, Cline, Codex, OpenCode, Paperclip AI, GitHub Copilot CLI, and Mistral Vibe in isolated Docker containers, Apple containers (lightweight VMs), Fly.io machines (remote VMs), or [Namespace devboxes](https://namespace.so/devbox) (remote VMs). The coding tools are configured to run in auto-approve mode.
14+
Silo lets you run AI tools like Claude Code, Cline, Codex, OpenCode, Paperclip AI, GitHub Copilot CLI, Mistral Vibe, and Kilo Code in isolated Docker containers, Apple containers (lightweight VMs), Fly.io machines (remote VMs), or [Namespace devboxes](https://namespace.so/devbox) (remote VMs). The coding tools are configured to run in auto-approve mode.
1515

1616
> [!WARNING]
1717
> Built using AI. No isolation is perfect. Use at your own risk.
@@ -56,6 +56,7 @@ Silo resolves this by running AI tools in isolated containers/vms with:
5656
| Paperclip AI | `silo paperclipai` | Multi-agent orchestration CLI |
5757
| GitHub Copilot CLI | `silo copilot` | GitHub's Copilot CLI |
5858
| Mistral Vibe | `silo vibe` | Mistral's CLI coding assistant |
59+
| Kilo Code CLI | `silo kilo` | Kilo's terminal coding agent |
5960

6061
## Installation
6162

@@ -102,6 +103,7 @@ silo opencode
102103
silo paperclipai
103104
silo copilot
104105
silo vibe
106+
silo kilo
105107

106108
# Pass arguments to the tool (after --)
107109
silo claude -- --help
@@ -290,7 +292,7 @@ Silo uses JSONC (JSON with Comments). All fields are optional.
290292
// Backend: "docker", "container", "fly", or "namespace" (default: container if installed, else docker)
291293
"backend": "container",
292294

293-
// Default tool: "claude", "cline", "codex", "opencode", "paperclipai", "copilot", or "vibe" (if not set, interactive prompt is shown)
295+
// Default tool: "claude", "cline", "codex", "opencode", "paperclipai", "copilot", "vibe", or "kilo" (if not set, interactive prompt is shown)
294296
"tool": "claude",
295297

296298
// Backend-specific configuration
@@ -421,6 +423,7 @@ Silo automatically mounts these paths (read-write):
421423
| Paperclip AI | `~/.paperclip/`, `~/.claude.json`, `~/.claude/`, `~/.codex/` |
422424
| Copilot | `~/.config/.copilot/` (respecting XDG env vars) |
423425
| Vibe | `~/.vibe/` |
426+
| Kilo | `~/.config/kilo/` |
424427

425428
Additionally, some tools mount paths read-only to share configuration:
426429

@@ -526,7 +529,7 @@ This means:
526529

527530
### Auto-rebuild on Tool Updates
528531

529-
Silo automatically detects when a new version of a tracked tool is available and triggers a rebuild. Today that includes Cline, Claude Code, Codex CLI, Paperclip AI, and GitHub Copilot CLI. On each run, a background fetch checks the latest version and caches it to disk. The cached version is included in the image hash, so when a new release is published the image tag changes and a rebuild is triggered on the next run.
532+
Silo automatically detects when a new version of a tracked tool is available and triggers a rebuild. Today that includes Cline, Claude Code, Codex CLI, Paperclip AI, GitHub Copilot CLI, and Kilo Code CLI. On each run, a background fetch checks the latest version and caches it to disk. The cached version is included in the image hash, so when a new release is published the image tag changes and a rebuild is triggered on the next run.
530533

531534
This adds zero latency — the version fetch happens asynchronously and the cached value from the previous run is used. New versions are picked up on the run after they are detected. Use `--force-build` to force a rebuild at any time.
532535

‎dockerfile_test.go‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func TestAvailableTools(t *testing.T) {
5353
"paperclipai": true,
5454
"copilot": true,
5555
"vibe": true,
56+
"kilo": true,
5657
}
5758

5859
for _, tool := range tools {
@@ -79,6 +80,7 @@ func TestToolDescription(t *testing.T) {
7980
{"paperclipai", "Paperclip"},
8081
{"copilot", "Copilot"},
8182
{"vibe", "Vibe"},
83+
{"kilo", "Kilo"},
8284
{"unknown", "Unknown"},
8385
}
8486

‎main.go‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/leighmcculloch/silo/tools/cline"
3232
"github.com/leighmcculloch/silo/tools/codexcli"
3333
"github.com/leighmcculloch/silo/tools/copilotcli"
34+
"github.com/leighmcculloch/silo/tools/kilocode"
3435
"github.com/leighmcculloch/silo/tools/mistralvibe"
3536
"github.com/leighmcculloch/silo/tools/opencode"
3637
"github.com/leighmcculloch/silo/tools/paperclipai"
@@ -51,6 +52,7 @@ var (
5152
paperclipai.Tool,
5253
copilotcli.Tool,
5354
mistralvibe.Tool,
55+
kilocode.Tool,
5456
}
5557
)
5658

@@ -143,7 +145,7 @@ func newRootCmd(stdout, stderr io.Writer) *cobra.Command {
143145
███████║██║███████╗╚██████╔╝
144146
╚══════╝╚═╝╚══════╝ ╚═════╝
145147
`) + `
146-
Run AI tools (Claude Code, Cline, Codex, OpenCode, Paperclip AI, Copilot, Mistral Vibe) in isolated
148+
Run AI tools (Claude Code, Cline, Codex, OpenCode, Paperclip AI, Copilot, Mistral Vibe, Kilo Code) in isolated
147149
Docker containers with proper security sandboxing.
148150
149151
The container is configured with:
@@ -167,6 +169,7 @@ Configuration is loaded from (in order, merged):
167169
silo paperclipai
168170
silo copilot
169171
silo vibe
172+
silo kilo
170173
171174
# Pass arguments to the tool
172175
silo claude -- --help`,

‎silo.jsonc.example‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "https://raw.githubusercontent.com/leighmcculloch/silo/main/silo.schema.json",
33
// Backend to use: "docker", "container", "fly", or "namespace" (default: "container" if installed, else "docker")
44
// "backend": "docker",
5-
// Default tool to run: "claude", "cline", "codex", "opencode", "paperclipai", "copilot", or "vibe" (prompts if not set)
5+
// Default tool to run: "claude", "cline", "codex", "opencode", "paperclipai", "copilot", "vibe", or "kilo" (prompts if not set)
66
// "tool": "claude",
77
// Backend-specific configuration
88
// "backends": {

‎silo.schema.json‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
},
1818
"tool": {
1919
"type": "string",
20-
"enum": ["claude", "cline", "codex", "opencode", "paperclipai", "copilot", "vibe"],
20+
"enum": ["claude", "cline", "codex", "opencode", "paperclipai", "copilot", "vibe", "kilo"],
2121
"description": "Default tool to run. If not set, an interactive prompt is shown.",
22-
"examples": ["claude", "cline", "codex", "opencode", "paperclipai", "copilot", "vibe"]
22+
"examples": ["claude", "cline", "codex", "opencode", "paperclipai", "copilot", "vibe", "kilo"]
2323
},
2424
"mounts_ro": {
2525
"type": "array",
@@ -113,7 +113,7 @@
113113
},
114114
"tools": {
115115
"type": "object",
116-
"description": "Tool-specific configuration. Each key is a tool name (e.g., 'claude', 'cline', 'codex', 'opencode', 'paperclipai', 'copilot', 'vibe').",
116+
"description": "Tool-specific configuration. Each key is a tool name (e.g., 'claude', 'cline', 'codex', 'opencode', 'paperclipai', 'copilot', 'vibe', 'kilo').",
117117
"additionalProperties": {
118118
"$ref": "#/$defs/toolConfig"
119119
},
@@ -199,7 +199,7 @@
199199
"properties": {
200200
"tool": {
201201
"type": "string",
202-
"description": "Tool to use for this repository (e.g., 'claude', 'cline', 'opencode', 'paperclipai', 'copilot')."
202+
"description": "Tool to use for this repository (e.g., 'claude', 'cline', 'opencode', 'paperclipai', 'copilot', 'kilo')."
203203
},
204204
"mounts_ro": {
205205
"type": "array",

‎tools/kilocode/Dockerfile‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# ============================================
2+
# Kilo Code CLI stage
3+
# ============================================
4+
FROM base AS kilo
5+
6+
ARG HOME
7+
ARG TOOL_VERSION
8+
9+
RUN set -e && \
10+
npm install -g @kilocode/cli${TOOL_VERSION:+"@$TOOL_VERSION"} && \
11+
kilo --version
12+
13+
# SILO_POST_BUILD_HOOKS_KILO

‎tools/kilocode/kilocode.go‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package kilocode
2+
3+
import (
4+
"context"
5+
_ "embed"
6+
"encoding/json"
7+
"io"
8+
"net/http"
9+
10+
"github.com/leighmcculloch/silo/config"
11+
"github.com/leighmcculloch/silo/tools"
12+
)
13+
14+
//go:embed Dockerfile
15+
var dockerfileStage string
16+
17+
// Tool is the Kilo Code CLI tool definition.
18+
var Tool = tools.Tool{
19+
Name: "kilo",
20+
Description: "Kilo Code CLI - Kilo's terminal coding agent",
21+
DockerfileStage: dockerfileStage,
22+
Command: func(home string) []string {
23+
return []string{home + "/.local/node/bin/kilo"}
24+
},
25+
DefaultConfig: func() config.ToolConfig {
26+
return config.ToolConfig{
27+
MountsRW: []string{
28+
"~/.config/kilo",
29+
},
30+
}
31+
},
32+
LatestVersion: fetchLatestVersion,
33+
}
34+
35+
// fetchLatestVersion queries the npm registry for the latest @kilocode/cli
36+
// version.
37+
func fetchLatestVersion(ctx context.Context) string {
38+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://registry.npmjs.org/@kilocode/cli/latest", nil)
39+
if err != nil {
40+
return ""
41+
}
42+
43+
resp, err := http.DefaultClient.Do(req)
44+
if err != nil {
45+
return ""
46+
}
47+
defer resp.Body.Close()
48+
49+
if resp.StatusCode != http.StatusOK {
50+
return ""
51+
}
52+
53+
body, err := io.ReadAll(resp.Body)
54+
if err != nil {
55+
return ""
56+
}
57+
58+
var pkg struct {
59+
Version string `json:"version"`
60+
}
61+
if err := json.Unmarshal(body, &pkg); err != nil {
62+
return ""
63+
}
64+
return pkg.Version
65+
}

0 commit comments

Comments
 (0)