feat(daemon): add Kiro CLI agent adapter (#185)

* feat(daemon): add Kiro CLI agent adapter

* docs: add Kiro CLI to README and agent-adapters

* test(daemon): add kiro fetchModels fallback test
This commit is contained in:
GeorgePSpark 2026-05-01 16:25:26 +12:00 committed by GitHub
parent 147a438c1a
commit 534349aa4c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 43 additions and 5 deletions

View file

@ -1,6 +1,6 @@
# Open Design
> **The open-source alternative to [Claude Design][cd].** Local-first, web-deployable, BYOK at every layer — **10 coding-agent CLIs** auto-detected on your `PATH` (Claude Code, Codex, Cursor Agent, Gemini CLI, OpenCode, Qwen, GitHub Copilot CLI, Hermes, Kimi, Pi) become the design engine, driven by **31 composable Skills** and **72 brand-grade Design Systems**. No CLI? An OpenAI-compatible BYOK proxy is the same loop minus the spawn.
> **The open-source alternative to [Claude Design][cd].** Local-first, web-deployable, BYOK at every layer — **11 coding-agent CLIs** auto-detected on your `PATH` (Claude Code, Codex, Cursor Agent, Gemini CLI, OpenCode, Qwen, GitHub Copilot CLI, Hermes, Kimi, Pi, Kiro) become the design engine, driven by **31 composable Skills** and **72 brand-grade Design Systems**. No CLI? An OpenAI-compatible BYOK proxy is the same loop minus the spawn.
<p align="center">
<img src="docs/assets/banner.png" alt="Open Design — editorial cover: design with the agent on your laptop" width="100%" />
@ -49,7 +49,7 @@ OD stands on four open-source shoulders:
| | What you get |
|---|---|
| **Coding-agent CLIs (10)** | Claude Code · Codex CLI · Cursor Agent · Gemini CLI · OpenCode · Qwen Code · GitHub Copilot CLI · Hermes (ACP) · Kimi CLI (ACP) · Pi (RPC) — auto-detected on `PATH`, swap with one click |
| **Coding-agent CLIs (11)** | Claude Code · Codex CLI · Cursor Agent · Gemini CLI · OpenCode · Qwen Code · GitHub Copilot CLI · Hermes (ACP) · Kimi CLI (ACP) · Pi (RPC) · Kiro CLI (ACP) — auto-detected on `PATH`, swap with one click |
| **BYOK fallback** | OpenAI-compatible proxy at `/api/proxy/stream` — paste `baseUrl` + `apiKey` + `model` and any vendor (Anthropic-via-OpenAI, DeepSeek, Groq, MiMo, OpenRouter, your self-hosted vLLM, or any other OpenAI-compatible provider) becomes the engine. Internal-IP/SSRF blocked at the daemon edge. |
| **Design systems built-in** | **72** — 2 hand-authored starters + 70 product systems (Linear, Stripe, Vercel, Airbnb, Tesla, Notion, Anthropic, Apple, Cursor, Supabase, Figma, Xiaohongshu, …) imported from [`awesome-design-md`][acd2] |
| **Skills built-in** | **31** — 27 in `prototype` mode (web-prototype, saas-landing, dashboard, mobile-app, gamified-app, social-carousel, magazine-poster, dating-web, sprite-animation, motion-frames, critique, tweaks, wireframe-sketch, pm-spec, eng-runbook, finance-report, hr-onboarding, invoice, kanban-board, team-okrs, …) + 4 in `deck` mode (`guizang-ppt` · `simple-deck` · `replit-deck` · `weekly-update`). Grouped in the picker by `scenario`: design / marketing / operation / engineering / product / finance / hr / sale / personal. |
@ -215,7 +215,7 @@ Adding a skill takes one folder. Read [`docs/skills-protocol.md`](docs/skills-pr
### 1 · We don't ship an agent. Yours is good enough.
The daemon scans your `PATH` for [`claude`](https://docs.anthropic.com/en/docs/claude-code), [`codex`](https://github.com/openai/codex), [`cursor-agent`](https://www.cursor.com/cli), [`gemini`](https://github.com/google-gemini/gemini-cli), [`opencode`](https://opencode.ai/), [`qwen`](https://github.com/QwenLM/qwen-code), [`copilot`](https://github.com/features/copilot/cli), `hermes`, `kimi`, and [`pi`](https://github.com/mariozechner/pi-ai) on startup. Whichever ones it finds become candidate design engines — driven over stdio with one adapter per CLI, swappable from the model picker. Inspired by [`multica`](https://github.com/multica-ai/multica) and [`cc-switch`](https://github.com/farion1231/cc-switch). No CLI installed? `POST /api/proxy/stream` is the same pipeline minus the spawn — paste any OpenAI-compatible `baseUrl` + `apiKey` and the daemon forwards SSE chunks back, with loopback / link-local / RFC1918 destinations rejected at the edge.
The daemon scans your `PATH` for [`claude`](https://docs.anthropic.com/en/docs/claude-code), [`codex`](https://github.com/openai/codex), [`cursor-agent`](https://www.cursor.com/cli), [`gemini`](https://github.com/google-gemini/gemini-cli), [`opencode`](https://opencode.ai/), [`qwen`](https://github.com/QwenLM/qwen-code), [`copilot`](https://github.com/features/copilot/cli), `hermes`, `kimi`, [`pi`](https://github.com/mariozechner/pi-ai), and [`kiro-cli`](https://kiro.dev) on startup. Whichever ones it finds become candidate design engines — driven over stdio with one adapter per CLI, swappable from the model picker. Inspired by [`multica`](https://github.com/multica-ai/multica) and [`cc-switch`](https://github.com/farion1231/cc-switch). No CLI installed? `POST /api/proxy/stream` is the same pipeline minus the spawn — paste any OpenAI-compatible `baseUrl` + `apiKey` and the daemon forwards SSE chunks back, with loopback / link-local / RFC1918 destinations rejected at the edge.
### 2 · Skills are files, not plugins.
@ -277,7 +277,7 @@ Every layer is composable. Every layer is a file you can edit. Read [`apps/web/s
┌──────────────────────────────────────────────────────────────────┐
│ claude · codex · gemini · opencode · cursor-agent · qwen │
│ copilot · hermes (ACP) · kimi (ACP) · pi (RPC)
│ copilot · hermes (ACP) · kimi (ACP) · pi (RPC) · kiro (ACP)
│ reads SKILL.md + DESIGN.md, writes artifacts to disk │
└──────────────────────────────────────────────────────────────────┘
```
@ -286,7 +286,7 @@ Every layer is composable. Every layer is a file you can edit. Read [`apps/web/s
|---|---|
| Frontend | Next.js 16 App Router + React 18 + TypeScript, Vercel-deployable |
| Daemon | Node 24 · Express · SSE streaming · `better-sqlite3`; tables: `projects` · `conversations` · `messages` · `tabs` · `templates` |
| Agent transport | `child_process.spawn`; typed-event parsers for `claude-stream-json` (Claude Code), `copilot-stream-json` (Copilot), `json-event-stream` per-CLI parsers (Codex / Gemini / OpenCode / Cursor Agent), `acp-json-rpc` (Hermes / Kimi via Agent Client Protocol), `pi-rpc` (Pi via stdio JSON-RPC), `plain` (Qwen Code) |
| Agent transport | `child_process.spawn`; typed-event parsers for `claude-stream-json` (Claude Code), `copilot-stream-json` (Copilot), `json-event-stream` per-CLI parsers (Codex / Gemini / OpenCode / Cursor Agent), `acp-json-rpc` (Hermes / Kimi / Kiro via Agent Client Protocol), `pi-rpc` (Pi via stdio JSON-RPC), `plain` (Qwen Code) |
| BYOK proxy | `POST /api/proxy/stream` → OpenAI-compatible `/v1/chat/completions`, SSE pass-through; rejects loopback / link-local / RFC1918 hosts at the daemon edge |
| Storage | Plain files in `.od/projects/<id>/` + SQLite at `.od/app.sqlite` (gitignored, auto-created). Override the root with `OD_DATA_DIR` for test isolation |
| Preview | Sandboxed iframe via `srcdoc` + per-skill `<artifact>` parser ([`apps/web/src/artifacts/parser.ts`](apps/web/src/artifacts/parser.ts)) |
@ -553,6 +553,7 @@ Auto-detected from `PATH` on daemon boot. No config required. Streaming dispatch
| [GitHub Copilot CLI](https://github.com/features/copilot/cli) | `copilot` | `copilot-stream-json` (typed events) | `copilot -p <prompt> --allow-all-tools --output-format json [--model …] [--add-dir …]` |
| [Hermes](https://github.com/eqlabs/hermes) | `hermes` | `acp-json-rpc` (Agent Client Protocol) | `hermes acp --accept-hooks` |
| Kimi CLI | `kimi` | `acp-json-rpc` | `kimi acp` |
| [Kiro CLI](https://kiro.dev) | `kiro-cli` | `acp-json-rpc` | `kiro-cli acp` |
| [Pi](https://github.com/mariozechner/pi-ai) | `pi` | `pi-rpc` (stdio JSON-RPC) | `pi --mode rpc --no-session [--model …] [--thinking …]` (prompt sent as RPC `prompt` command) |
| **OpenAI-compatible BYOK** | n/a | SSE pass-through | `POST /api/proxy/stream``<baseUrl>/v1/chat/completions`; SSRF-guarded against loopback / link-local / RFC1918 |

View file

@ -472,6 +472,24 @@ export const AGENT_DEFS = [
promptViaStdin: true,
streamFormat: 'pi-rpc',
},
{
id: 'kiro',
name: 'Kiro CLI',
bin: 'kiro-cli',
versionArgs: ['--version'],
fetchModels: async (resolvedBin) =>
detectAcpModels({
bin: resolvedBin,
args: ['acp'],
timeoutMs: 15_000,
defaultModelOption: DEFAULT_MODEL_OPTION,
}),
fallbackModels: [
DEFAULT_MODEL_OPTION,
],
buildArgs: () => ['acp'],
streamFormat: 'acp-json-rpc',
},
];
export function resolveOnPath(bin) {

View file

@ -5,6 +5,7 @@ import { AGENT_DEFS } from '../src/agents.js';
const codex = AGENT_DEFS.find((agent) => agent.id === 'codex');
const cursorAgent = AGENT_DEFS.find((agent) => agent.id === 'cursor-agent');
const kiro = AGENT_DEFS.find((agent) => agent.id === 'kiro');
const originalDisablePlugins = process.env.OD_CODEX_DISABLE_PLUGINS;
afterEach(() => {
@ -67,3 +68,20 @@ test('cursor-agent args deliver prompts via stdin without passing a literal dash
'/tmp/od-project',
]);
});
test('kiro args use acp subcommand for json-rpc streaming', () => {
const args = kiro.buildArgs('', [], [], {});
assert.deepEqual(args, ['acp']);
assert.equal(kiro.streamFormat, 'acp-json-rpc');
});
test('kiro fetchModels falls back to fallbackModels when detection fails', async () => {
// fetchModels rejects when the binary doesn't exist; the daemon's
// probe() catches this and uses fallbackModels instead.
const result = await kiro.fetchModels('/nonexistent/kiro-cli').catch(() => null);
assert.equal(result, null);
assert.ok(Array.isArray(kiro.fallbackModels));
assert.equal(kiro.fallbackModels[0].id, 'default');
});

View file

@ -91,6 +91,7 @@ If both signals agree, detection is confident. If only one signal fires, we mark
| **opencode** | `opencode` | `~/.opencode/` | 〜 | 〜 | ✅ | P2 |
| **openclaw** | `openclaw` | `~/.openclaw/` | 〜 | 〜 | 〜 | P2 |
| **copilot** | `copilot` | `~/.copilot/` | ❌ | ✅ (`edit` tool) | ✅ (`--output-format json` JSONL) | P2 |
| **kiro** | `kiro-cli` | `~/.kiro/` | ❌ | ✅ | ✅ (`acp-json-rpc`) | P2 |
"P0/P1/P2" correspond to the roadmap phases in [`roadmap.md`](roadmap.md).