diff --git a/_primitives/cli-backends.toml b/_primitives/cli-backends.toml new file mode 100644 index 0000000..742960c --- /dev/null +++ b/_primitives/cli-backends.toml @@ -0,0 +1,45 @@ +# cli-backends.toml — SSoT for external LLM CLIs that can host KeiSeiKit agents. +# +# Each backend is a CLI you have a subscription / local install of. The +# `kei run-via ""` launcher composes an agent's +# assembled .md prompt with the task and invokes the backend's +# non-interactive (print) mode. +# +# Add a backend by appending a `[backend.]` table. The launcher +# (`scripts/kei-agent-cli.sh`) reads `bin` + `prompt_flag` and execs. + +[backend.claude] +bin = "claude" +prompt_flag = "-p" +notes = "Claude Code (Anthropic) — native --agent flag also supported" +homepage = "https://claude.com/claude-code" + +[backend.grok] +bin = "grok" +prompt_flag = "--print" +notes = "xAI Grok Build TUI — native --agent flag also supported" +homepage = "https://x.ai/grok" + +[backend.agy] +bin = "agy" +prompt_flag = "--print" +notes = "Google Antigravity (alias: antigravity)" +aliases = ["antigravity"] + +[backend.copilot] +bin = "copilot" +prompt_flag = "--prompt" +notes = "GitHub Copilot CLI (@github/copilot npm)" +homepage = "https://github.com/github/copilot-cli" + +[backend.kimi] +bin = "kimi" +prompt_flag = "stdin" +notes = "Moonshot Kimi CLI — primarily TUI/ACP; non-interactive via stdin" +homepage = "https://moonshotai.github.io/kimi-cli/" + +[backend.codex] +bin = "codex" +prompt_flag = "-p" +notes = "OpenAI Codex CLI — register here, install separately" +homepage = "https://github.com/openai/codex" diff --git a/bin/kei b/bin/kei index c7fc534..4aa3961 100644 --- a/bin/kei +++ b/bin/kei @@ -9,6 +9,11 @@ # kei --no-splash # skip splash → exec claude # kei --status # status only, don't launch claude # kei message ... # inter-session mailbox (send/inbox/list) — see kei-message.sh +# kei configure # re-pick stack profile + opt-in hook packs +# kei run-via "" +# # invoke a KeiSeiKit agent via an external LLM CLI +# # backends: claude grok agy copilot kimi codex +# # `kei run-via list` shows install status # kei [args...] # splash → claude args... (forwarded verbatim) # # The splash shows: substrate version, agent count, last sleep run, @@ -20,7 +25,7 @@ set -e # --- subcommand dispatch (before splash) --------------------------------- # `kei message ...` → mailbox CLI; `kei configure` → hook/stack re-picker; -# everything else falls through to launch. +# `kei run-via ...` → invoke agent through external LLM CLI; rest = launch. case "${1:-}" in message|msg|m) shift @@ -30,6 +35,10 @@ case "${1:-}" in shift exec "$HOME/.claude/scripts/kei-configure.sh" "$@" ;; + run-via|via|run|agent-via) + shift + exec "$HOME/.claude/scripts/kei-agent-cli.sh" "$@" + ;; esac # --- args ---------------------------------------------------------------- diff --git a/docs/encyclopedia/multi-cli-agents.md b/docs/encyclopedia/multi-cli-agents.md new file mode 100644 index 0000000..3342317 --- /dev/null +++ b/docs/encyclopedia/multi-cli-agents.md @@ -0,0 +1,87 @@ +# Multi-CLI agent invocation + +> *Cross-LLM agent execution. Same agent definition, different backend.* + +KeiSeiKit agents are markdown files. Any LLM CLI that takes a prompt can +host them — `kei run-via` is the launcher that bridges them. + +## Backends + +Registered in `_primitives/cli-backends.toml` (SSoT). Installed locally +via your own subscription / package manager: + +| Backend | CLI binary | Non-interactive flag | Native `--agent` | Notes | +|----------|-----------|----------------------|------------------|-------| +| claude | `claude` | `-p` | yes | Claude Code (Anthropic) | +| grok | `grok` | `--print` | yes | xAI Grok Build TUI | +| agy | `agy` | `--print` | no | Google Antigravity (alias: `antigravity`) | +| copilot | `copilot` | `--prompt` | no | GitHub Copilot CLI (`@github/copilot`) | +| kimi | `kimi` | stdin | no | Moonshot Kimi (primarily TUI/ACP) | +| codex | `codex` | `-p` | no | OpenAI Codex (register-only) | + +Run `kei run-via list` to see which are installed on the current machine +and to list available agent names. + +## Usage + +```bash +# Invoke the 'critic' agent through Grok with a task: +kei run-via grok critic "review src/auth.rs for variant analysis" + +# Same agent, different backend: +kei run-via agy critic "review src/auth.rs" +kei run-via copilot critic "review src/auth.rs" +kei run-via claude critic "review src/auth.rs" + +# Point at an arbitrary agent .md (not in ~/.claude/agents/): +kei run-via grok --file=/tmp/my-agent.md "do the thing" + +# Backend's native --agent flag (grok/claude only): +KEI_NATIVE_AGENT=1 kei run-via grok critic "review src/auth.rs" +``` + +## How it works + +1. Reads `~/.claude/agents/.md` (assembler-generated prompt). +2. Strips YAML frontmatter. +3. Composes with task as: `\n\n---\n\nTASK FOR THIS RUN:\n`. +4. Execs the backend's non-interactive CLI with the composed prompt. + +No agent file is modified. No new tokens are issued. Subscription +authentication is whatever each CLI uses (its own login / config dir). + +## When to use each + +This is a tool, not a recommendation. Each backend has different +strengths; the substrate is agnostic about which you pick. Pick by: + +- **Familiarity** — the CLI you already use day-to-day. +- **Subscription cost** — burn the one with cheaper marginal cost first. +- **Specific feature** — e.g. `grok --agent` for native sub-agent + switching mid-conversation; `agy --sandbox` for terminal restriction. +- **Independent second opinion** — same agent, different model, see if + conclusions diverge. + +## Adding a new backend + +1. Add a `[backend.]` table to `_primitives/cli-backends.toml`. +2. Add a case arm in `scripts/kei-agent-cli.sh` `backend_bin()` and + `backend_invoke()` for the new CLI's print-flag. +3. Add a row to the table above. + +## What it is NOT + +- Not a router — picks no backend for you; you ask, it dispatches. +- Not a federation — each backend runs independently with its own + context; there is no cross-backend state. +- Not a wrapper around the backend's tool surface — what the CLI can + do (Bash, file edits, MCP, etc.) is determined by that CLI, not + KeiSeiKit. The substrate only ships the prompt. + +## Related + +- `_primitives/_rust/kei-llm-router/` — Beta-posterior router for + *programmatic* model selection inside Rust code (a different layer). +- `_primitives/_rust/kei-mcp/` — MCP server that exposes KeiSeiKit + primitives to ANY MCP-compatible client (Cursor / Continue / Zed / + Aider / Cline / Windsurf / OpenClaw). diff --git a/scripts/kei-agent-cli.sh b/scripts/kei-agent-cli.sh new file mode 100644 index 0000000..502dc8a --- /dev/null +++ b/scripts/kei-agent-cli.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash +# kei-agent-cli — invoke a KeiSeiKit agent via an external LLM CLI backend. +# +# Usage: +# kei run-via "" # by agent name +# kei run-via --file= "" # by agent file path +# kei run-via list # show backends + status +# kei run-via --help +# +# Backends (SSoT: _primitives/cli-backends.toml, fallback table below): +# claude Claude Code (claude -p) +# grok xAI Grok (grok --print, native --agent supported) +# agy Antigravity (agy --print) — alias: antigravity +# copilot GitHub Copilot (copilot --prompt) +# kimi Moonshot Kimi (stdin, TUI primary) +# codex OpenAI Codex (codex -p) — register-only if not installed +# +# Reads agent prompt from ~/.claude/agents/.md (assembler output). +# Strips YAML frontmatter, composes with task, execs the CLI. +# +# Env overrides: +# KEI_AGENTS_DIR agent .md dir (default: ~/.claude/agents) +# KEI_NATIVE_AGENT=1 prefer backend's native --agent flag (grok/claude) + +set -euo pipefail + +KEI_AGENTS_DIR="${KEI_AGENTS_DIR:-$HOME/.claude/agents}" +KEI_NATIVE_AGENT="${KEI_NATIVE_AGENT:-0}" + +usage() { sed -n '2,20p' "$0" | sed 's|^# \{0,1\}||'; } + +# ---- backend table (SSoT mirror; kept in sync with cli-backends.toml) ----- +backend_bin() { + case "$1" in + claude) echo "claude" ;; + grok) echo "grok" ;; + agy|antigravity) echo "agy" ;; + copilot) echo "copilot" ;; + kimi) echo "kimi" ;; + codex) echo "codex" ;; + *) return 1 ;; + esac +} + +backend_supports_native_agent() { + case "$1" in claude|grok) return 0 ;; *) return 1 ;; esac +} + +# Invoke backend with composed prompt as argument or stdin per backend. +backend_invoke() { + local backend="$1" prompt="$2" bin agent_name="${3:-}" + bin=$(backend_bin "$backend") || { + printf '[kei-agent-cli] unknown backend: %s\n' "$backend" >&2 + return 2 + } + command -v "$bin" >/dev/null 2>&1 || { + printf '[kei-agent-cli] %s not on PATH. Install it or pick another backend.\n' "$bin" >&2 + return 127 + } + + # Native --agent path (grok/claude) — pass agent name + task directly. + if [ "$KEI_NATIVE_AGENT" = "1" ] \ + && [ -n "$agent_name" ] \ + && backend_supports_native_agent "$backend"; then + printf '[kei-agent-cli] %s --agent %s\n' "$bin" "$agent_name" >&2 + exec "$bin" --agent "$agent_name" --print "${prompt##*TASK FOR THIS RUN:}" + fi + + case "$backend" in + claude) exec "$bin" -p "$prompt" ;; + grok|agy|antigravity) exec "$bin" --print "$prompt" ;; + copilot) exec "$bin" --prompt "$prompt" ;; + kimi) # Kimi non-interactive surface is limited; + # stdin works against TUI default mode. + printf '%s\n' "$prompt" | exec "$bin" ;; + codex) exec "$bin" -p "$prompt" ;; + esac +} + +# ---- agent loader ------------------------------------------------------- +load_agent() { + local name="$1" path + # explicit path via --file= + case "$name" in + --file=*) path="${name#--file=}" ;; + /*|./*|*/*) path="$name" ;; + *) path="$KEI_AGENTS_DIR/$name.md" ;; + esac + if [ ! -f "$path" ]; then + printf '[kei-agent-cli] agent not found: %s\n' "$path" >&2 + if [ -d "$KEI_AGENTS_DIR" ]; then + printf ' Available (%s): %s\n' "$KEI_AGENTS_DIR" \ + "$(find "$KEI_AGENTS_DIR" -maxdepth 1 -name '*.md' -not -name '_*' 2>/dev/null \ + | xargs -n1 basename 2>/dev/null | sed 's/\.md$//' \ + | sort | head -8 | tr '\n' ' ')..." >&2 + fi + return 1 + fi + # strip YAML frontmatter (---\n...\n---) if present + awk ' + BEGIN { in_fm=0; past_fm=0 } + NR==1 && /^---$/ { in_fm=1; next } + in_fm && /^---$/ { in_fm=0; past_fm=1; next } + in_fm { next } + { print } + ' "$path" +} + +# ---- subcommands -------------------------------------------------------- +case "${1:-}" in + ""|-h|--help|help) usage; exit 0 ;; + list|--list) + printf 'Backends (✓ installed, ✗ missing):\n' + for b in claude grok agy copilot kimi codex; do + bin=$(backend_bin "$b") + if p=$(command -v "$bin" 2>/dev/null); then + printf ' %-10s ✓ %s\n' "$b" "$p" + else + printf ' %-10s ✗ (not on PATH)\n' "$b" + fi + done + printf '\nAgents (%s):\n' "$KEI_AGENTS_DIR" + if [ -d "$KEI_AGENTS_DIR" ]; then + find "$KEI_AGENTS_DIR" -maxdepth 1 -name '*.md' -not -name '_*' 2>/dev/null \ + | xargs -n1 basename 2>/dev/null | sed 's/\.md$/ /' | sort | column 2>/dev/null \ + || (find "$KEI_AGENTS_DIR" -maxdepth 1 -name '*.md' -not -name '_*' \ + | xargs -n1 basename | sed 's/\.md$//' | sort | head -20) + fi + exit 0 + ;; +esac + +# ---- main --------------------------------------------------------------- +if [ $# -lt 3 ]; then + usage + exit 2 +fi + +BACKEND="$1"; AGENT_REF="$2"; shift 2 +TASK="$*" + +if ! AGENT_PROMPT=$(load_agent "$AGENT_REF"); then + exit 1 +fi + +# Compose: agent system + task delimiter. +COMPOSED=$(printf '%s\n\n---\n\nTASK FOR THIS RUN:\n%s\n' "$AGENT_PROMPT" "$TASK") + +# Derive a clean agent name for KEI_NATIVE_AGENT path. +AGENT_NAME=$(basename "${AGENT_REF#--file=}") +AGENT_NAME="${AGENT_NAME%.md}" + +backend_invoke "$BACKEND" "$COMPOSED" "$AGENT_NAME"